天天看點

java 類加載器解除安裝_java - Android解除安裝類加載器導緻崩潰 - 堆棧記憶體溢出

我正在開發一個Android應用,并且正在嘗試類似插件的功能,該功能将允許加載其他dex檔案以擴充應用功能。 我已經弄清楚了如何加載擴充PathClassLoader的其他dex檔案,而隻需進行一些小的更改即可允許其他子產品進行通信。 問題是,當dex檔案首次在應用程式運作時被加載到應用程式中時,它們都能正常運作,然後,如果我決定禁用此子產品,以便解除安裝類加載器,則該應用程式将繼續正常工作幾秒鐘,然後它将引發異常(仍然繼續正常工作),然後在幾秒鐘/分鐘後再次出現(有時甚至需要5分鐘),應用程式因本機堆棧跟蹤而崩潰。 如果我決定再次加載之前禁用的子產品,則隻會增加崩潰的機會。

這是解除安裝子產品類加載器幾秒鐘後發生的情況:

12-27 01:57:10.839 E/System: Uncaught exception thrown by finalizer

12-27 01:57:10.840 E/System: java.lang.AssertionError: Failed to close dex file in finalizer.

at dalvik.system.DexFile.finalize(DexFile.java:336)

at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:250)

at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:237)

at java.lang.Daemons$Daemon.run(Daemons.java:103)

at java.lang.Thread.run(Thread.java:764)

12-27 01:57:10.840 E/System: Uncaught exception thrown by finalizer

12-27 01:57:10.840 E/System: java.lang.AssertionError: Failed to close dex file in finalizer.

at dalvik.system.DexFile.finalize(DexFile.java:336)

at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:250)

at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:237)

at java.lang.Daemons$Daemon.run(Daemons.java:103)

at java.lang.Thread.run(Thread.java:764)

12-27 01:57:10.841 E/System: Uncaught exception thrown by finalizer

12-27 01:57:10.841 E/System: java.lang.AssertionError: Failed to close dex file in finalizer.

at dalvik.system.DexFile.finalize(DexFile.java:336)

at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:250)

at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:237)

at java.lang.Daemons$Daemon.run(Daemons.java:103)

at java.lang.Thread.run(Thread.java:764)

然後幾秒鐘或幾分鐘後,發生本機崩潰:

12-27 01:57:15.409 A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

12-27 01:57:15.409 A/DEBUG: Build fingerprint: 'Sony/G8341/G8341:8.0.0/47.1.A.8.49/3744219090:user/release-keys'

12-27 01:57:15.409 A/DEBUG: Revision: '0'

12-27 01:57:15.409 A/DEBUG: ABI: 'arm64'

12-27 01:57:15.409 A/DEBUG: pid: 17551, tid: 17697, name: Profile Saver >>> com.rowl.plugdj

12-27 01:57:15.409 A/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x657a6983

12-27 01:57:15.409 A/DEBUG: x0 00000000657a6973 x1 0000007bb0739460 x2 0000007bb0600000 x3 0000000000000002

12-27 01:57:15.409 A/DEBUG: x4 0000000000000139 x5 0000007bb073947f x6 2f6d65747379732f x7 726f77656d617266

12-27 01:57:15.409 A/DEBUG: x8 0000000013954588 x9 aec629c3012f5dc2 x10 0000000000000139 x11 000000000000004c

12-27 01:57:15.409 A/DEBUG: x12 656d6172662f6b72 x13 72616a2e6b726f77 x14 000000000000000d x15 aaaaaaaaaaaaaaab

12-27 01:57:15.409 A/DEBUG: x16 0000007bca457cc8 x17 0000007bca3f5f60 x18 0000007bc9c07eb0 x19 0000007baecba270

12-27 01:57:15.409 A/DEBUG: x20 0000007ba6ab68c0 x21 0000007baecba588 x22 0000007ba6ab4298 x23 0000007baa692b18

12-27 01:57:15.409 A/DEBUG: x24 0000000000000000 x25 0000000000002710 x26 00000000977434b0 x27 0000000097746934

12-27 01:57:15.409 A/DEBUG: x28 0000007bc99b6b70 x29 0000007baecba1d0 x30 0000007bc96cf67c

12-27 01:57:15.409 A/DEBUG: sp 0000007baecba0c0 pc 0000007bc96cf634 pstate 0000000080000000

12-27 01:57:15.411 A/DEBUG: backtrace:

12-27 01:57:15.411 A/DEBUG: #00 pc 00000000002fe634 /system/lib64/libart.so (_ZN3art3jit12JitCodeCache18GetProfiledMethodsERKNSt3__13setINS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEENS2_4lessIS9_EENS7_IS9_EEEERNS2_6vectorINS_17ProfileMethodInfoENS7_ISH_EEEE+228)

12-27 01:57:15.411 A/DEBUG: #01 pc 000000000030ac08 /system/lib64/libart.so (_ZN3art12ProfileSaver20ProcessProfilingInfoEbPt+1452)

12-27 01:57:15.411 A/DEBUG: #02 pc 00000000003099ac /system/lib64/libart.so (_ZN3art12ProfileSaver3RunEv+704)

12-27 01:57:15.411 A/DEBUG: #03 pc 000000000030b7b8 /system/lib64/libart.so (_ZN3art12ProfileSaver21RunProfileSaverThreadEPv+88)

12-27 01:57:15.411 A/DEBUG: #04 pc 00000000000667d0 /system/lib64/libc.so (_ZL15__pthread_startPv+36)

12-27 01:57:15.411 A/DEBUG: #05 pc 000000000001f2a4 /system/lib64/libc.so (__start_thread+68)

這是我正在使用的類加載器

public class ExtensionClassLoader extends PathClassLoader {

private final Map> classes = new HashMap();

private final ExtensionManager extensionManager;

public ExtensionClassLoader(ExtensionManager extensionManager, String dexPath, String librarySearchPath, ClassLoader parent) {

super(dexPath, librarySearchPath, parent);

this.extensionManager = extensionManager;

}

@Override

protected Class> findClass(String name) throws ClassNotFoundException {

if (name.startsWith("com.rowl.") || name.startsWith("dj.plug.")) {

throw new ClassNotFoundException(name);

}

PlugDJ.d("Find Class: " + name);

Class clazz = classes.get(name);

if(clazz == null){

clazz = extensionManager.getClass(name);

if(clazz == null){

clazz = super.findClass(name);

if(clazz != null){

extensionManager.addClass(name, clazz);

}

}

if(clazz != null){

classes.put(name, clazz);

}

}

return clazz;

}

@Override

public String findLibrary(String name) {

return super.findLibrary(name);

}

public Set getClasses(){

return classes.keySet();

}

@Override

protected void finalize() throws Throwable {

super.finalize();

}

}

另外,這裡是加載這些子產品的類:

class ExtensionManager(plugDJInstance: PlugDJ) : Manager(plugDJInstance) {

private val classes = HashMap>()

private val extensionLoaders = HashMap()

private val loadedExtensions = ArrayList()

private val availableExtensions = ArrayList()

val plugAPI = PlugAPIImpl(pdjInstance as PlugDJAndroid)

private val context by lazy { (pdjInstance as PlugDJAndroid).context }

private val extensionSettings by lazy { context.getSharedPreferences("ext", Context.MODE_PRIVATE)}

private val whitelistedExtensions by lazy { extensionSettings.getStringSet("whitelistedExtensions", HashSet()) }

fun addClass(name: String, clazz: Class){

if(!classes.containsKey(name)){

classes.put(name, clazz)

}

}

fun removeClass(name: String){

classes.remove(name)

}

fun getClass(name: String): Class?{

return classes[name]

}

private fun loadApk(dir: File, dexOutputDir: File, parent: ClassLoader): ExtensionClassLoader{

val files = StringBuilder()

dir.listFiles().filter { it.extension == "apk" }.forEach {

files.append(it.absolutePath).append(":")

}

files.deleteCharAt(files.length - 1)

return ExtensionClassLoader(this, files.toString(), dexOutputDir.absolutePath.toString(), parent)

}

fun isExtensionWhitelisted(extInfo: PDJExtensionInfo): Boolean{

return whitelistedExtensions.contains(extInfo.appPackage)

}

@SuppressLint("ApplySharedPref")

fun addExtensionToWhitelist(extInfo: PDJExtensionInfo){

whitelistedExtensions.add(extInfo.appPackage)

extensionSettings.edit().putStringSet("whitelistedExtensions", whitelistedExtensions).commit()

d("Added " + extInfo.appPackage + " to whitelist")

}

@SuppressLint("ApplySharedPref")

fun removeExtensionFromWhitelist(extInfo: PDJExtensionInfo){

whitelistedExtensions.remove(extInfo.appPackage)

extensionSettings.edit().putStringSet("whitelistedExtensions", if(whitelistedExtensions.isEmpty()) null else whitelistedExtensions).commit()

d("Checking... " + whitelistedExtensions)

d("Removed " + extInfo.appPackage + " from whitelist")

}

fun discoverExtensions() {

try{

whitelistedExtensions.forEach {

d("Whitelisted extension: $it")

}

val installedApps = context.packageManager.getInstalledApplications(PackageManager.GET_META_DATA)

for(app in installedApps){

if(app.sourceDir.contains("system")) continue

if(app.metaData != null && app.metaData.containsKey("pdjExtensionName")){

val extName = app.metaData.getString("pdjExtensionName")

val extMainClass = app.metaData.getString("pdjExtensionMainClass")

val sourceDir = File(app.sourceDir).parentFile

PlugDJ.i("Found extension: " + extName)

var extInfo = getAvailableExtensions().find { it.appPackage == app.packageName }

d(extInfo?.sourceDirectory.toString() )

d(sourceDir.toString() )

if(extInfo != null){

PlugDJ.d("Removing " + app.packageName)

availableExtensions.remove(extInfo)

}

extInfo = PDJExtensionInfoImpl(extName, sourceDir, app.packageName, extMainClass, "", "", null, null)

availableExtensions.add(extInfo)

}

}

}catch (e: Exception){

e.printStackTrace()

}

}

fun getAvailableExtensions(): List{

return Collections.unmodifiableList(ArrayList(availableExtensions))

}

fun getLoadedExtensions(): List{

return Collections.unmodifiableList(ArrayList(loadedExtensions))

}

fun loadExtension(extInfo: PDJExtensionInfo): PDJExtension{

var cl = context.classLoader

val dexOutputDir = context.getDir("dex", Context.MODE_PRIVATE)

val extcl = loadApk(extInfo.sourceDirectory, dexOutputDir, cl)

extensionLoaders[extInfo.name] = extcl

val mainClass = extcl.loadClass(extInfo.mainClass)

val apiField = mainClass.superclass.getDeclaredField("api")

val clField = mainClass.superclass.getDeclaredField("classLoader")

val extField = mainClass.superclass.getDeclaredField("extensionInfo")

val extension = mainClass.newInstance() as PDJExtension

apiField.apply {

isAccessible = true

set(extension, plugAPI)

isAccessible = false

}

clField.apply {

isAccessible = true

set(extension, mainClass.classLoader)

isAccessible = false

}

extField.apply {

isAccessible = true

set(extension, extInfo)

isAccessible = false

}

loadedExtensions.add(extension)

return extension

}

fun getLoadedExtension(appPackage: String): PDJExtension? {

return getLoadedExtensions().find { it.extensionInfo.appPackage == appPackage }

}

fun enableExtension(ext: PDJExtension){

ext.onEnable()

ext::class.java.superclass.getDeclaredField("isEnabled").apply {

isAccessible = true

set(ext, true)

isAccessible = false

}

}

fun disableExtension(ext: PDJExtension){

try{

plugAPI.removeEventListeners(ext)

ext.onDisable()

}catch (e: Exception){

e.printStackTrace()

}

ext::class.java.superclass.getDeclaredField("isEnabled").apply {

isAccessible = true

set(ext, false)

isAccessible = false

}

}

fun unloadExtension(ext: PDJExtension){

d("Unloading extension...")

ext::class.java.superclass.getDeclaredField("api").apply {

isAccessible = true

set(ext, null)

isAccessible = false

}

val cl = ext.classLoader as ExtensionClassLoader

loadedExtensions.remove(ext)

for(className in cl.classes){

removeClass(className)

}

extensionLoaders.remove(ext.extensionInfo.name)

d("Extension unloaded.")

System.runFinalization()

System.gc()

}

}

知道是什麼原因造成的嗎?