问题背景
早上老铁跟我说,他们项目接入讯飞语音的时候重复进Activity导致了空指针异常,怀疑是第二次进入Activity的时候调用了onCreate()之后,上一次该Activity的onDestroy才开始调用,然后操作相同变量导致了这个crash;换句话来说就是调用了finish()方法之后延迟了很久(4-5s)才调用onDestroy(),然后5秒内再次打开该Activity导致崩溃,说来惭愧,在我的认知里面,finish的调用是会很快调用onDestroy()的,或者说我的代码里从来没出现过这种问题,也就少见多怪了,我首先怀疑这个问题可能是内部类持有了外部Activity的引用,导致其不能释放,所以我记录一下这个问题。
问题复现
首先是不是简单的对象持有就能引发这个问题呢,简单写一个工具类加入静态方法来保存Activity对象,然后延迟5秒操作这个Activity对象。
private static Context mContext;
/**
* 长时间持有Activity
* @param context
*/
public static void holdContext(Activity context){
mContext = context;
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Log.e("zkh",mContext.getPackageName());
}
},5000);
}
再finish()方法里面调用holdContext方法,这样Activity就被这个工具类持有,系统GC一时间应该是不会回收这个Activity的,那么这种情况下会怎么样呢,日志打印之后发现其实并不会有什么问题,那么要是起个线程在Activity的主线程干点事呢?这个情况好像和老铁反应的现象很相似。
public static void holdContext(Activity context){
mContext = context;
new Handler().post(new Runnable() {
@Override
public void run() {
mContext.runOnUiThread(new Runnable() {
@Override
public void run() {
for (int i = 0;i<80000;i++){
Log.e( "zkh" ,mContext.getLocalClassName());
}
}
});
}
});
}
这样在finish调用这个方法,但是因为post的原因执行会在finish()方法之后,所以复现了这个场景,应该也是这个原因导致了我朋友的这个情况发生。
finish和onDestroy的关系
finish()方法之后到底干了什么呢?我们看下源码:
Activity源码
/**
* Finishes the current activity and specifies whether to remove the task associated with this
* activity.
*/
@UnsupportedAppUsage
private void finish(int finishTask) {
i