内存泄漏,关于异步回调导致的内存泄漏,使用LeakCanary检测内存泄漏
而在 Android 中如何处理好异步请求,则是一个非常宽泛的话题,从这篇开始的若干篇,都会围绕这个来聊一聊。而这篇要讲的,就是看看界面中的异步回调,经常会引发什么问题,要注意些什么。
要在异步回调中判断界面状态
最典型的问题,就是回调时触碰界面而引发崩溃,一个简单示例如下:
// 发起一个异步请求,执行完成后更新界面状态
requestManager.execute(aRequest, new Callback() {
protected void completed() {
textView.setText("Completed!");
}
});
但其实,解决回调崩溃的问题是知易行难,真实的项目中,情况会非常隐匿,线上崩溃总是由于 "不小心"、"没想到" 而引发的。所以,很多时候把保护放在服务内而不是调用者那里,是更安全可靠的方案。
要尽可能的取消异步操作
除了崩溃,和异步形影不离的还有内存泄露,它的隐蔽性很强,总是藏匿在 Activity 等具有 Context 的对象中。比如:
// 执行一个时间很长的操作...
requestManager.execute(aRequest(activity) {
protected void run() {
// wait long long time...
}
});
这个异步操作拿了 Activity 对象,如果它需要执行很长时间,那在这个阶段 Activity 和它上面的各种元素都是无法释放的,在用户离开页面很长时间后,内存依旧在占用,并且反复进出会持续累加直到 Out Of Memory(OOM),这其实也就是引发了内存泄露。而这样长时间的等待,有可能是定时引发、有可能是队列排队引发、有可能卡在了某个操作上(比如网络、IO ...),很多时候,甚至都难于提前判断。
因此,如果我们假设,大部分的异步操作都可能是慢的,需要执行很长时间,那如何来处理呢?使用弱引用是一个办法,把等待回调的界面对象(一般都持有 Context)用 WeakReference 包裹起来,如果用户离开页面,很快也会把它给收了,以此来避免内存泄露。但弱引用本身使用上有很多禁忌,稍有不慎就从一个坑到了另一个坑。比如,弱引用最好不要放到服务内弄成黑盒,而是要调用者自行使用,如果放在服务内调用者很可能会出现傻等回调等不来的状况;而一旦完全交由调用者,又难以保证实现是统一可靠的,依旧会由于 "不小心" 引发问题。
除此之外呢?支持干净的取消可以算是最漂亮的方案之一了,在异步回调未返回但界面要退出之前,把操作给回收了,而这个回收不只是设定一个标志位,而是要将回调中潜在泄露的对象彻底移除,这样才能是干净的、可以避免内存泄露的取消方案。
使用LeakCanary检测内存泄漏
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'
}
import android.content.Context;
import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.RefWatcher;
public class MyApplicationextends Application {
private RefWatcher refWatcher;
public static RefWatcher getRefWatcher(Context context) {
MyApplication application = (MyApplication) context.getApplicationContext();
return application.refWatcher;
}
@Override
public void onCreate() {
super.onCreate();
refWatcher = LeakCanary.install(this);
}
}
import android.content.Context;
public class SingletonSavesContext {
private Context context;
private static SingletonSavesContext instance;
public Context getContext() {
return context;
}
public void setContext(Contextcontext) {
this.context = context;
}
public
static SingletonSavesContext
getInstance() {
if (instance == null) {
instance = new SingletonSavesContext();
}
return instance;
}
}
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import com.squareup.leakcanary.RefWatcher;
import com.youshixiu.kuplaywawa.R;
public class LeakedActivity extends AppCompatActivity {
@Override
protected void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new MyAsyncTask().execute(this);
}
@Override
public void onDestroy() {
super.onDestroy();
RefWatcher refWatcher = MyApplication.getRefWatcher(this);
refWatcher.watch(this);
}
public class MyAsyncTask extends AsyncTask<Object, String, String> {
private Context context;
@Override
protected String doInBackground(Object... params) {
context = (Context) params[0];
// Invoke the leak!
SingletonSavesContext.getInstance().setContext(context);
// Simulate long running task
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
return "result";
}
@Override
protected void onPostExecute(Strings) {
super.onPostExecute(s);
Intent newActivity = new Intent(context, AnotherActivity.class);
startActivity(newActivity);
}
}
}
然后,在新的活动中,我们将调用System.gc强制垃圾收集器来加速分析。结果也可以从logcat中获取:
14211-16753/anaware.leakcanarysample D/LeakCanary: In anaware.leakcanarysample:1.0:1.
14211-16753/anaware.leakcanarysample D/LeakCanary: * anaware.leakcanarysample.LeakedActivity has leaked:
14211-16753/anaware.leakcanarysample D/LeakCanary: * GC ROOT static anaware.leakcanarysample.SingletonSavesContext.instance
14211-16753/anaware.leakcanarysample D/LeakCanary: * references anaware.leakcanarysample.SingletonSavesContext.context
14211-16753/anaware.leakcanarysample D/LeakCanary: * leaks anaware.leakcanarysample.LeakedActivity instance
14211-16753/anaware.leakcanarysample D/LeakCanary: * Reference Key: 226ffd5e-902c-4202-8508-2e268616e2ae
14211-16753/anaware.leakcanarysample D/LeakCanary: * Device: samsung samsung GT-I9305 m3xx
14211-16753/anaware.leakcanarysample D/LeakCanary: * Android Version: 4.4.4 API: 19 LeakCanary: 1.3.1
14211-16753/anaware.leakcanarysample D/LeakCanary: * Durations: watch=5007ms, gc=120ms, heap dump=617ms, analysis=13070ms
14211-16753/anaware.leakcanarysample D/LeakCanary: * Details:
14211-16753/anaware.leakcanarysample D/LeakCanary: * Class anaware.leakcanarysample.SingletonSavesContext
14211-16753/anaware.leakcanarysample D/LeakCanary: | static $staticOverhead = byte[] [id=0x42548e79;length=24;size=40]
14211-16753/anaware.leakcanarysample D/LeakCanary: | static instance = anaware.leakcanarysample.SingletonSavesContext [id=0x42548fb8]
14211-16753/anaware.leakcanarysample D/LeakCanary: * Instance of anaware.leakcanarysample.SingletonSavesContext
14211-16753/anaware.leakcanarysample D/LeakCanary: | static $staticOverhead = byte[] [id=0x42548e79;length=24;size=40]
14211-16753/anaware.leakcanarysample D/LeakCanary: | static instance = anaware.leakcanarysample.SingletonSavesContext [id=0x42548fb8]
14211-16753/anaware.leakcanarysample D/LeakCanary: | context = anaware.leakcanarysample.Leake
声明
欢迎转载,但请保留文章原始出处
作者:Jaiky_杰哥
出处:http://blog.csdn.net/jaikydota163/article/details/78915024
上一篇: String为什么要设置成不可改变的量
下一篇: String为什么要设计成不可变的