在严格模式下似乎发生了内存泄漏
PathClassLoader pathClassLoader = new PathClassLoader(apkName, apkContext.getClassLoader());
loadExternalClass方法在应用程序的许多位置被调用
private static Map<String, Class<?>> mCachedClasses = new HashMap<>();
public Class<?> loadExternalClass(String packageName, String className)
throws NameNotFoundException, ClassNotFoundException {
String key = packageName+"/"+className;
if (!mCachedClasses.containsKey(key)) {
// Dynamic class loading from different apk
Context apkContext = loadExternalContext(packageName);
String apkName = apkContext.getPackageManager().getApplicationInfo(packageName, 0).sourceDir;
PathClassLoader pathClassLoader = new PathClassLoader(apkName, apkContext.getClassLoader());
Class<?> clazz = Class.forName(className, true, pathClassLoader);
mCachedClasses.put(key, clazz);
}
return mCachedClasses.get(key);
}
08-15 13:17:22.257 E/StrictMode( 1761): A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
08-15 13:17:22.257 E/StrictMode( 1761): java.lang.Throwable: Explicit termination method 'close' not called
08-15 13:17:22.257 E/StrictMode( 1761): at dalvik.system.CloseGuard.open(CloseGuard.java:184)
08-15 13:17:22.257 E/StrictMode( 1761): at dalvik.system.DexFile.<init>(DexFile.java:80)
08-15 13:17:22.257 E/StrictMode( 1761): at dalvik.system.DexFile.<init>(DexFile.java:57)
08-15 13:17:22.257 E/StrictMode( 1761): at dalvik.system.DexPathList.loadDexFile(DexPathList.java:256)
08-15 13:17:22.257 E/StrictMode( 1761): at dalvik.system.DexPathList.makeDexElements(DexPathList.java:223)
08-15 13:17:22.257 E/StrictMode( 1761): at dalvik.system.DexPathList.<init>(DexPathList.java:106)
08-15 13:17:22.257 E/StrictMode( 1761): at dalvik.system.BaseDexClassLoader.<init>(BaseDexClassLoader.java:46)
08-15 13:17:22.257 E/StrictMode( 1761): at dalvik.system.PathClassLoader.<init>(PathClassLoader.java:38)
08-15 13:17:22.257 E/StrictMode( 1761): at com.cnh.android.windowmanager.util.WmUtils.loadExternalClass(WmUtils.java:249)
我尝试了一个Dummy应用程序,并尝试最终组合以释放资源,但它在同一时刻仍然给出相同的错误
public class MainActivity_leak extends AppCompatActivity {
private Context mContext;
public String packageName= "com.test.android.device";
public String className = "com.test.android.device.elements.UpperDrawerButton" ;
public int i = 0;
private static Map<String, Class<?> > mCachedClasses = new HashMap<>();
private static Map<String, Context> mCachedContexts = new HashMap<>();
public MainActivity_leak(){
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_leak);
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.penaltyFlashScreen()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.build());
Log.w("", "Strict mode enabled");
mContext = this.getApplicationContext();
try {
for (int i =0 ; i<10000 ; i++)
{
View view = loadExternalView(packageName,className);
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
public Class<?> loadExternalClass(String packageName, String className)
throws PackageManager.NameNotFoundException, ClassNotFoundException {
String key =null;
Context apkContext = null;
String apkName = null;
PathClassLoader pathClassLoader =null;
Class<?> clazz = null;
try {
key = packageName + "/" + className;
if (!mCachedClasses.containsKey(key)) {
apkContext = loadExternalContext(packageName);
apkName = apkContext.getPackageManager().getApplicationInfo(packageName, 0).sourceDir;
pathClassLoader = new PathClassLoader(apkName, apkContext.getClassLoader());
clazz = Class.forName(className, true, pathClassLoader);
mCachedClasses.put(key, clazz);
}
return mCachedClasses.get(key);
}finally{
if (apkContext != null) {
apkContext.deleteFile(packageName);
apkContext=null;
pathClassLoader = null;
key = null;
apkName = null;
}
}
}
public synchronized Context loadExternalContext(String packageName)
throws PackageManager.NameNotFoundException, NullPointerException {
if (!mCachedContexts.containsKey(packageName)) {
Context context = mContext.createPackageContext(packageName, CONTEXT_INCLUDE_CODE | CONTEXT_IGNORE_SECURITY);
mCachedContexts.put(packageName, context);
}
return mCachedContexts.get(packageName);
}
public synchronized View loadExternalView(String packageName, String className)
throws PackageManager.NameNotFoundException, ClassNotFoundException, ClassCastException, IllegalArgumentException, InstantiationException,
IllegalAccessException, InvocationTargetException, NoSuchMethodException, NullPointerException {
Context apkContext = loadExternalContext(packageName);
Class<?> cls = loadExternalClass(packageName, className);
clearInflaterCache(apkContext);
return (View) cls.getConstructor(Context.class).newInstance(apkContext);
}
public static void clearInflaterCache(Context context) {
try {
Field ctorMapField = LayoutInflater.class.getDeclaredField("sConstructorMap");
ctorMapField.setAccessible(true);
@SuppressWarnings("unchecked")
HashMap<String, Constructor<? extends View>> ctorMap = (HashMap<String, Constructor<? extends View>>)
ctorMapField.get(LayoutInflater.from(context));
Iterator<String> i = ctorMap.keySet().iterator();
while (i.hasNext()) {
String name = i.next();
if (name.startsWith("com.test")) i.remove();
}
} catch (Exception e) {
}
}
}
D/dalvikvm( 3344): GC_CONCURRENT freed 291K, 8% free 4542K/4908K, paused 3ms+3ms, total 104ms
E/StrictMode( 3344): A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
E/StrictMode( 3344): java.lang.Throwable: Explicit termination method 'close' not called
E/StrictMode( 3344): at dalvik.system.CloseGuard.open(CloseGuard.java:184)
E/StrictMode( 3344): at dalvik.system.DexFile.<init>(DexFile.java:80)
E/StrictMode( 3344): at dalvik.system.DexFile.<init>(DexFile.java:57)
E/StrictMode( 3344): at dalvik.system.DexPathList.loadDexFile(DexPathList.java:256)
E/StrictMode( 3344): at dalvik.system.DexPathList.makeDexElements(DexPathList.java:223)
E/StrictMode( 3344): at dalvik.system.DexPathList.<init>(DexPathList.java:106)
E/StrictMode( 3344): at dalvik.system.BaseDexClassLoader.<init>(BaseDexClassLoader.java:46)
E/StrictMode( 3344): at dalvik.system.PathClassLoader.<init>(PathClassLoader.java:38)
E/StrictMode( 3344): at com.example.a287470.myapplication_leak.MainActivity_leak.loadExternalClass(MainActivity_leak.java:130)
E/StrictMode( 3344): at com.example.a287470.myapplication_leak.MainActivity_leak.loadExternalView(MainActivity_leak.java:172)