diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt index 2d469f260..0468c45e5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt @@ -6,7 +6,6 @@ import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.os.Build import androidx.core.content.pm.PackageInfoCompat -import dalvik.system.PathClassLoader import eu.kanade.domain.extension.interactor.TrustExtension import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.tachiyomi.extension.model.Extension @@ -16,6 +15,7 @@ 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.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.runBlocking @@ -272,7 +272,7 @@ internal object ExtensionLoader { } val classLoader = try { - PathClassLoader(appInfo.sourceDir, null, context.classLoader) + ChildFirstPathClassLoader(appInfo.sourceDir, null, context.classLoader) } catch (e: Exception) { logcat(LogPriority.ERROR, e) { "Extension load error: $extName ($pkgName)" } return LoadResult.Error diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ChildFirstPathClassLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ChildFirstPathClassLoader.kt new file mode 100644 index 000000000..b63dfd032 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ChildFirstPathClassLoader.kt @@ -0,0 +1,86 @@ +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 { + 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 { + 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 + } + } +}