Skip to content

Fix child first classloader#1885

Open
tachimanga wants to merge 1 commit intoSuwayomi:masterfrom
tachimanga:fix/classloader
Open

Fix child first classloader#1885
tachimanga wants to merge 1 commit intoSuwayomi:masterfrom
tachimanga:fix/classloader

Conversation

@tachimanga
Copy link
Contributor

@tachimanga tachimanga commented Jan 31, 2026

The original ChildFirstURLClassLoader did not work as expected. It first loads classes using the SystemClassLoader. Unlike Android's system ClassLoader, which only loads core classes (java., android.), the JVM's one also loads classes from the CLASSPATH, meaning if a class exists both in the extension and the host, it will be loaded by the host's class loader. (NOT child-first)

The new implementation is based on Flink’s ChildFirstClassLoader, which adopts an allowlist approach. Core JRE classes are loaded by the parent class loader first(like Android's SystemClassLoader), then the child class loader attempts to load them. If the child fails to load a class, it falls back to the parent, effectively achieving child-first class loading.

@tachimanga
Copy link
Contributor Author

tachimanga commented Jan 31, 2026

These are the names of classes loaded by the Android systemclassloader. Printed via

try {
    c = systemClassLoader.loadClass(name)
    println("[ext]loaded by sys:$name")
} catch (_: ClassNotFoundException) {}
[ext]loaded by sys:java.lang.CharSequence
[ext]loaded by sys:java.lang.Object
[ext]loaded by sys:java.io.Serializable
[ext]loaded by sys:java.lang.Enum
[ext]loaded by sys:java.util.concurrent.TimeUnit
[ext]loaded by sys:java.util.List
[ext]loaded by sys:java.lang.String
[ext]loaded by sys:java.lang.StringBuilder
[ext]loaded by sys:java.lang.UnsupportedOperationException
[ext]loaded by sys:java.lang.Iterable
[ext]loaded by sys:java.util.Collection
[ext]loaded by sys:java.lang.Boolean
[ext]loaded by sys:java.util.ArrayList
[ext]loaded by sys:java.util.Iterator
[ext]loaded by sys:android.util.Base64
[ext]loaded by sys:java.nio.charset.Charset
[ext]loaded by sys:java.lang.Integer
[ext]loaded by sys:android.net.Uri
[ext]loaded by sys:java.lang.Exception
[ext]loaded by sys:java.lang.Number
[ext]loaded by sys:java.util.regex.Pattern
[ext]loaded by sys:java.util.Set
[ext]loaded by sys:java.io.ObjectInputStream
[ext]loaded by sys:java.io.InvalidObjectException
[ext]loaded by sys:java.util.regex.Matcher
[ext]loaded by sys:java.lang.IndexOutOfBoundsException
[ext]loaded by sys:java.util.EnumSet
[ext]loaded by sys:java.util.Collections
[ext]loaded by sys:java.lang.Double
[ext]loaded by sys:java.lang.Float
[ext]loaded by sys:java.lang.IllegalStateException
[ext]loaded by sys:java.lang.Throwable
[ext]loaded by sys:java.lang.ClassNotFoundException
[ext]loaded by sys:java.lang.Class
[ext]loaded by sys:java.lang.NullPointerException
[ext]loaded by sys:java.lang.Thread
[ext]loaded by sys:java.lang.StackTraceElement
[ext]loaded by sys:java.util.Arrays
[ext]loaded by sys:java.lang.AssertionError
[ext]loaded by sys:java.lang.IllegalArgumentException
[ext]loaded by sys:java.lang.RuntimeException
[ext]loaded by sys:java.lang.Error
[ext]loaded by sys:java.util.RandomAccess
[ext]loaded by sys:java.util.ListIterator
[ext]loaded by sys:java.lang.reflect.Type

These are the names of classes loaded by the JVM systemclassloader. Printed via

try {
    c = systemClassLoader.loadClass(name)
    println("[ext]loadClass sys: $name")
} catch (_: ClassNotFoundException) {}
[ext]loadClass sys: okhttp3.Response
[ext]loadClass sys: okhttp3.Request
[ext]loadClass sys: kotlin.Lazy
[ext]loadClass sys: uy.kohesive.injekt.InjektKt
[ext]loadClass sys: uy.kohesive.injekt.api.InjektFactory
[ext]loadClass sys: uy.kohesive.injekt.api.FullTypeReference
[ext]loadClass by ext: eu.kanade.tachiyomi.extension.es.leercapitulo.LeerCapitulo$special$$inlined$injectLazy$1$1
[ext]loadClass sys: kotlinx.serialization.json.Json
[ext]loadClass sys: uy.kohesive.injekt.api.TypeReference
[ext]loadClass sys: okhttp3.ResponseBody
[ext]loadClass sys: kotlinx.serialization.internal.ArrayListSerializer
[ext]loadClass by ext: eu.kanade.tachiyomi.extension.es.leercapitulo.LeerCapitulo$MangaDto
[ext]loadClass by ext: eu.kanade.tachiyomi.extension.es.leercapitulo.LeerCapitulo$MangaDto$Companion
[ext]loadClass sys: kotlinx.serialization.internal.GeneratedSerializer
[ext]loadClass by ext: eu.kanade.tachiyomi.extension.es.leercapitulo.LeerCapitulo$MangaDto$$serializer
[ext]loadClass sys: kotlinx.serialization.UnknownFieldException
[ext]loadClass sys: kotlinx.serialization.internal.PluginGeneratedSerialDescriptor
[ext]loadClass sys: kotlinx.serialization.descriptors.SerialDescriptor
[ext]loadClass sys: kotlinx.serialization.KSerializer
[ext]loadClass sys: kotlinx.serialization.DeserializationStrategy
[ext]loadClass sys: kotlinx.serialization.encoding.Decoder
[ext]loadClass sys: kotlinx.serialization.encoding.CompositeDecoder
[ext]loadClass by ext: kotlinx.serialization.internal.GeneratedSerializer$_CC
[ext]loadClass sys: kotlinx.serialization.internal.PluginHelperInterfacesKt
[ext]loadClass sys: kotlinx.serialization.internal.StringSerializer
[ext]loadClass sys: java.lang.Iterable
[ext]loadClass sys: kotlin.collections.CollectionsKt
[ext]loadClass sys: java.util.Iterator
[ext]loadClass sys: eu.kanade.tachiyomi.source.model.MangasPage

As you can see, Android's SystemClassLoader only loads Java or Android classes, while the JVM's SystemClassLoader not only loads Java classes but also loads classes from the classpath.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments