Use built-in DelegateLastClassLoader for extensions (#3874)

Assisted-by: Claude:claude-opus-5
This commit is contained in:
AntsyLich
2026-08-29 22:07:51 +06:00
committed by GitHub
parent b695940114
commit 7f342ca076
3 changed files with 72 additions and 88 deletions
@@ -13,13 +13,13 @@ import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
import eu.kanade.tachiyomi.util.lang.Hash
import eu.kanade.tachiyomi.util.storage.copyAndSetReadOnlyTo
import eu.kanade.tachiyomi.util.system.ChildFirstPathClassLoader
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import logcat.LogPriority
import mihon.app.di.appGraph
import mihon.data.dalvik.DelegateLastClassLoaderCompat
import tachiyomi.core.common.util.system.logcat
import java.io.File
@@ -277,7 +277,7 @@ internal object ExtensionLoader {
}
val classLoader = try {
ChildFirstPathClassLoader(appInfo.sourceDir, null, context.classLoader)
DelegateLastClassLoaderCompat(appInfo.sourceDir, null, context.classLoader)
} catch (e: Exception) {
logcat(LogPriority.ERROR, e) { "Extension load error: $extName ($pkgName)" }
return LoadResult.Error
@@ -1,86 +0,0 @@
package eu.kanade.tachiyomi.util.system
import dalvik.system.PathClassLoader
import java.io.IOException
import java.io.InputStream
import java.net.URL
import java.util.Enumeration
/**
* A parent-last class loader that will try in order:
* - the system class loader
* - the child class loader
* - the parent class loader.
*/
class ChildFirstPathClassLoader(
dexPath: String,
librarySearchPath: String?,
parent: ClassLoader,
) : PathClassLoader(dexPath, librarySearchPath, parent) {
private val systemClassLoader: ClassLoader? = getSystemClassLoader()
override fun loadClass(name: String?, resolve: Boolean): Class<*> {
var c = findLoadedClass(name)
if (c == null && systemClassLoader != null) {
try {
c = systemClassLoader.loadClass(name)
} catch (_: ClassNotFoundException) {}
}
if (c == null) {
c = try {
findClass(name)
} catch (_: ClassNotFoundException) {
super.loadClass(name, resolve)
}
}
if (resolve) {
resolveClass(c)
}
return c
}
override fun getResource(name: String?): URL? {
return systemClassLoader?.getResource(name)
?: findResource(name)
?: super.getResource(name)
}
override fun getResources(name: String?): Enumeration<URL> {
val systemUrls = systemClassLoader?.getResources(name)
val localUrls = findResources(name)
val parentUrls = parent?.getResources(name)
val urls = buildList {
while (systemUrls?.hasMoreElements() == true) {
add(systemUrls.nextElement())
}
while (localUrls?.hasMoreElements() == true) {
add(localUrls.nextElement())
}
while (parentUrls?.hasMoreElements() == true) {
add(parentUrls.nextElement())
}
}
return object : Enumeration<URL> {
val iterator = urls.iterator()
override fun hasMoreElements() = iterator.hasNext()
override fun nextElement() = iterator.next()
}
}
override fun getResourceAsStream(name: String?): InputStream? {
return try {
getResource(name)?.openStream()
} catch (_: IOException) {
return null
}
}
}
@@ -0,0 +1,70 @@
package mihon.data.dalvik
import android.os.Build
import dalvik.system.DelegateLastClassLoader
import dalvik.system.PathClassLoader
import java.net.URL
import java.util.Collections
import java.util.Enumeration
@Suppress("FunctionName")
fun DelegateLastClassLoaderCompat(
dexPath: String,
librarySearchPath: String?,
parent: ClassLoader,
): ClassLoader {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
DelegateLastClassLoader(dexPath, librarySearchPath, parent)
} else {
DelegateLastPathClassLoader(dexPath, librarySearchPath, parent)
}
}
/**
* Backport of [DelegateLastClassLoader], which was added in API 27.
*/
private class DelegateLastPathClassLoader(
dexPath: String,
librarySearchPath: String?,
parent: ClassLoader,
) : PathClassLoader(dexPath, librarySearchPath, parent) {
private val bootClassLoader: ClassLoader? = Any::class.java.classLoader
override fun loadClass(name: String?, resolve: Boolean): Class<*> {
findLoadedClass(name)?.let { return it }
if (bootClassLoader != null) {
try {
return bootClassLoader.loadClass(name)
} catch (_: ClassNotFoundException) {}
}
val fromSuper = try {
return findClass(name)
} catch (e: ClassNotFoundException) {
e
}
return try {
parent.loadClass(name)
} catch (_: ClassNotFoundException) {
throw fromSuper
}
}
override fun getResource(name: String?): URL? {
return bootClassLoader?.getResource(name)
?: findResource(name)
?: parent?.getResource(name)
}
override fun getResources(name: String?): Enumeration<URL> {
val resources = buildList {
bootClassLoader?.getResources(name)?.let { addAll(it.toList()) }
findResources(name)?.let { addAll(it.toList()) }
parent?.getResources(name)?.let { addAll(it.toList()) }
}
return Collections.enumeration(resources)
}
}