Skip to content

Commit 0f7feca

Browse files
committed
fix: Interrupt split APK merger immediately on cancellation
Replace `withContext` with `runInterruptible` in `Merger.merge` so that blocking operations (loadApkDirectory, mergeModules, writeApk) are interrupted immediately via `Thread.interrupt()` when the patcher worker is canceled. Previously, `withContext` would wait for the blocking call to finish naturally before throwing `CancellationException`, leaving temporary `split-<timestamp>` directories on disk and causing them to accumulate when the user restarted patching.
1 parent 099ba6c commit 0f7feca

2 files changed

Lines changed: 12 additions & 29 deletions

File tree

app/src/main/java/app/morphe/manager/patcher/split/Merger.kt

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@ import com.reandroid.app.AndroidManifest
88
import com.reandroid.archive.ZipEntryMap
99
import com.reandroid.arsc.chunk.xml.ResXmlElement
1010
import com.reandroid.arsc.container.SpecTypePair
11-
import com.reandroid.arsc.header.TableHeader
1211
import com.reandroid.arsc.model.ResourceEntry
1312
import com.reandroid.arsc.value.ValueType
1413
import kotlinx.coroutines.Dispatchers
15-
import kotlinx.coroutines.withContext
14+
import kotlinx.coroutines.runInterruptible
1615
import java.io.Closeable
1716
import java.io.File
1817
import java.io.FileNotFoundException
@@ -21,11 +20,12 @@ import java.nio.charset.CoderMalfunctionError
2120
import java.nio.file.Path
2221
import java.util.Locale
2322

23+
const val TAG = "APKEditor"
24+
2425
private class ApkEditorLogger(
2526
private val onProgress: ((String) -> Unit)? = null
2627
) : APKLogger {
2728
private companion object {
28-
const val TAG = "APKEditor"
2929
val MERGE_PATTERN = Regex("Merging\\s*:?\\s*(.+)", RegexOption.IGNORE_CASE)
3030
}
3131

@@ -72,7 +72,7 @@ internal object Merger {
7272
) {
7373
val closeables = mutableSetOf<Closeable>()
7474
try {
75-
val merged = withContext(Dispatchers.Default) {
75+
val merged = runInterruptible(Dispatchers.Default) {
7676
try {
7777
val logger = ApkEditorLogger(onProgress)
7878
val bundle = ApkBundle().apply {
@@ -196,7 +196,7 @@ internal object Merger {
196196
applyExtractNativeLibs(merged)
197197

198198
outputApk.parentFile?.mkdirs()
199-
withContext(Dispatchers.IO) {
199+
runInterruptible(Dispatchers.IO) {
200200
onProgress?.invoke("Writing merged APK")
201201
merged.writeApk(outputApk)
202202
}
@@ -227,18 +227,6 @@ internal object Merger {
227227
}
228228
}
229229

230-
private fun generateMergedModuleName(bundle: ApkBundle): String {
231-
val moduleNames = bundle.listModuleNames().toSet()
232-
val baseName = "merged"
233-
var candidate = baseName
234-
var index = 1
235-
while (moduleNames.contains(candidate)) {
236-
candidate = "${baseName}_$index"
237-
index += 1
238-
}
239-
return candidate
240-
}
241-
242230
private fun buildMergeOrder(
243231
modules: List<ApkModule>,
244232
baseModule: ApkModule
@@ -309,9 +297,9 @@ internal object Merger {
309297
val path = resValue.valueAsString
310298
if (!path.isNullOrBlank()) {
311299
zipEntryMap.remove(path)
312-
Log.i("APKEditor", "Removed table entry $path")
300+
Log.i(TAG, "Removed table entry $path")
313301
}
314-
resEntry.setNull(true)
302+
resEntry.isNull = true
315303
val specTypePair: SpecTypePair = resEntry.typeBlock.parentSpecTypePair
316304
specTypePair.removeNullEntries(resEntry.id)
317305
}
@@ -323,7 +311,7 @@ internal object Merger {
323311
} else {
324312
null
325313
}
326-
Log.i("APKEditor", "Applying: extractNativeLibs=$value")
314+
Log.i(TAG, "Applying: extractNativeLibs=$value")
327315
module.setExtractNativeLibs(value)
328316
}
329317
}

app/src/main/java/app/morphe/manager/patcher/split/SplitApkPreparer.kt

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ object SplitApkPreparer {
3232
suspend fun prepareIfNeeded(
3333
source: File,
3434
workspace: File,
35-
logger: Logger = defaultLogger,
35+
logger: Logger = DefaultLogger,
3636
stripNativeLibs: Boolean = false,
3737
skipUnneededSplits: Boolean = false,
3838
onProgress: ((String) -> Unit)? = null,
@@ -257,13 +257,8 @@ object SplitApkPreparer {
257257
}
258258

259259
private fun deviceLocaleTokens(): Set<String> {
260-
val locales = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
261-
val list = Resources.getSystem().configuration.locales
262-
(0 until list.size()).map { index -> list[index] }
263-
} else {
264-
listOf(Locale.getDefault())
265-
}
266-
260+
val list = Resources.getSystem().configuration.locales
261+
val locales = (0 until list.size()).map { index -> list[index] }
267262
return locales.flatMap { locale ->
268263
buildLocaleTokens(locale)
269264
}.map { it.lowercase(Locale.ROOT) }.toSet()
@@ -354,7 +349,7 @@ object SplitApkPreparer {
354349
}
355350
}
356351

357-
private object defaultLogger : Logger() {
352+
private object DefaultLogger : Logger() {
358353
override fun log(level: LogLevel, message: String) {
359354
Log.d("SplitApkPreparer", "[${level.name}] $message")
360355
}

0 commit comments

Comments
 (0)