这肯定是一个非常讨厌的内存泄漏问题。我已经为此
开了一个问题,因为似乎没有其他人报告过这个问题。
感谢emmby的“丑陋解决方案”,这很有帮助。尽管可能会对性能产生影响,但更安全的方法是完全禁用URLConnection缓存。由于URLConnection.defaultUseCaches标志是静态的,并且正如您所想象的那样是每个实例的useCaches标志的默认值,您可以将其设置为false,这样就不会再缓存它们的连接实例了。这将影响所有URLConnection的实现,因此可能会产生比预期更广泛的影响,但我认为这是一种合理的权衡。
你可以创建一个简单的类,像这样在你的应用程序的onCreate()中尽早实例化它:
public class URLConnectionNoCache extends URLConnection {
protected URLConnectionNoCache(URL url) {
super(url);
setDefaultUseCaches(false);
}
public void connect() throws IOException {
}
}
有趣的是,由于这发生在您的应用程序加载和运行之后,系统库应该已经被缓存,这只会阻止进一步缓存,因此这可能提供了最好的折衷方案:不缓存您的apk,同时允许缓存系统jar文件带来的性能优势。
在执行此操作之前,我稍微修改了emmby的解决方案,使其成为一个独立的类,创建一个后台线程以定期清除缓存。并且我限制它只清除应用程序的apk,尽管如果需要可以放松限制。要担心的问题是,在对象可能正在使用时修改它们通常不是一个好主意。如果您确实想走这条路,只需在上下文中调用start()方法,例如在应用程序的onCreate()中。
package com.example;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.jar.JarFile;
import java.util.regex.Pattern;
import android.content.Context;
public class JarURLMonitor {
private static JarURLMonitor instance;
private Pattern pat;
private Field jarCacheField;
public volatile boolean stop;
private static final long CHECK_INTERVAL = 60 * 1000;
public static synchronized void start(Context context) {
if (instance == null) {
instance = new JarURLMonitor(context);
}
}
public static synchronized void stop() {
if (instance != null) {
instance.stop = true;
}
}
private JarURLMonitor(Context context) {
try {
final Class<?> cls = Class.forName("libcore.net.url.JarURLConnectionImpl");
jarCacheField = cls.getDeclaredField("jarCache");
jarCacheField.setAccessible(true);
}
catch (Exception e) {
}
if (jarCacheField != null) {
pat = Pattern.compile("^.*/" + context.getPackageName() + "-.*\\.apk$");
new Thread("JarURLMonitor") {
@Override
public void run() {
try {
while (!stop) {
checkJarCache();
Thread.sleep(CHECK_INTERVAL);
}
}
catch (Exception e) {
}
}
}.start();
}
}
private void checkJarCache() throws Exception {
@SuppressWarnings("unchecked")
final HashMap<URL, JarFile> jarCache = (HashMap<URL, JarFile>)jarCacheField.get(null);
final Iterator<Map.Entry<URL, JarFile>> iterator = jarCache.entrySet().iterator();
while (iterator.hasNext()) {
final Map.Entry<URL, JarFile> entry = iterator.next();
final JarFile jarFile = entry.getValue();
final String file = jarFile.getName();
if (pat.matcher(file).matches()) {
try {
jarFile.close();
iterator.remove();
}
catch (Exception e) {
}
}
}
}
}