最新在華為手機(jī)GEM=703L android6.0發(fā)現(xiàn)的問(wèn)題百炬,在
AsyncTask
執(zhí)行ProgressDialog
的顯示或隱藏褐隆,然后退出activity會(huì)發(fā)生泄漏。泄漏提示
GC ROOT com.android.internal.policy.HwPhoneWindow$1.this$0
開(kāi)始分析
首先上mat的分析圖
其中提示了兩個(gè)未清除的引用剖踊,都指向了HwPhoneWindow庶弃,貌似其內(nèi)部類引用了它,這是什么東東?
憑我有限的小腦分析德澈,涉及Window類在我的app只有兩種:Activity歇攻,Dialog。Activity我已經(jīng)完美的解決了泄漏問(wèn)題梆造,那先從Dialog下手缴守。
在Dialog類中可以看到可以看到mWindow
,mDecor
final Window w = new PhoneWindow(mContext);
mWindow = w;
...
mDecor = mWindow.getDecorView();
想到了什么,是不是跟Activity很像,原來(lái)Dialog自己擁有WIndow并維護(hù)屡穗,但是新建的時(shí)候使用Activity的上下文贴捡,在Activity銷毀的時(shí)候,Dialog不銷毀就會(huì)有泄漏風(fēng)險(xiǎn)村砂,而且Dialog的生命周期會(huì)跟Activity產(chǎn)生不同步烂斋。
嘗試1
原來(lái)寫法
progressDialog = new ProgressDialog(Activity.this);
progressDialog.setProgress(ProgressDialog.STYLE_SPINNER);
progressDialog.setTitle("加載通訊錄中...");
并在Activity的onDestory
執(zhí)行super
之前銷毀
@Override
protected void onDestroy() {
if(progressDialog != null ){
progressDialog.dismiss();
progressDialog = null;
}
super.onDestroy();
}
結(jié)果:
還是存在泄漏
嘗試2
修改原來(lái)的寫法,使用FragmentDialog改造箍镜,利用Fragment維護(hù)Dialog的生命周期
改造后:
public class ProgressFragmentDialog extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder progressDialog = new AlertDialog.Builder(this.getActivity());
progressDialog.setView(getActivity().getLayoutInflater().inflate(R.layout.dialog_progress,null));
progressDialog.setCancelable(false);
//取消返回鍵
progressDialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
@Override
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
return true;
}
});
AlertDialog dialog = progressDialog.create();
dialog.setCanceledOnTouchOutside(false); //取消點(diǎn)擊外關(guān)閉
return dialog;
}
}
//顯示Dialog
progressFragmentDialog = new ProgressFragmentDialog();
progressFragmentDialog.show(this.getFragmentManager(),"ProgressFragmentDialog");
結(jié)果:
還是存在泄漏
what fuck! 分析到這源祈,我已經(jīng)黔驢技窮了,干脆不用Dialog色迂,自己實(shí)現(xiàn)彈出框
嘗試3
思路就是拿到根節(jié)點(diǎn)decorView
,new一個(gè)View add
到decorView
上香缺,前提是在Activity的setContentView()
之后執(zhí)行
上代碼
public class ProgressViewDialog {
/**
* 上下文,存儲(chǔ)activity信息
*/
// private Context context;
private ViewGroup decorView; //decorView
// private ViewGroup activityRootView;//內(nèi)容區(qū)域的根視圖
private ViewGroup dialogView;//我的根視圖
/**
* 構(gòu)造函數(shù)
* @param context
*/
public ProgressViewDialog(Context context)
{
//獲得一個(gè)xml布局加載器
LayoutInflater layoutInflater = LayoutInflater.from(context);
//獲得decorView
decorView = (ViewGroup)((Activity)context).getWindow().getDecorView();
//Log.d("decorView count", decorView.getChildCount()+"") ;
//獲得內(nèi)容區(qū)域根視圖
//activityRootView = (ViewGroup)decorView.findViewById(android.R.id.content);
//Log.d("activityRootView count", activityRootView.getChildCount()+"") ;
//獲得我的根視圖
dialogView = (ViewGroup)layoutInflater.inflate(R.layout.dialog_progress,null);
//Log.d("rootView count", rootView.getChildCount() + "") ;
//屏蔽下層觸摸
dialogView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("lessonOneActivity","點(diǎn)擊了本層");
}
});
//屏幕返回鍵
dialogView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
return true;
}
});
}
public void show(){
if(dialogView.getParent() == null){
decorView.addView(dialogView);
dialogView.setVisibility(View.VISIBLE);
}
else{
//decorView.addView(rootView);
dialogView.setVisibility(View.VISIBLE);
}
}
public void dismiss(){
dialogView.setVisibility(View.GONE);
}
}
還是發(fā)現(xiàn)了上個(gè)泄漏問(wèn)題歇僧,分析到這里图张,我已經(jīng)沒(méi)轍了,然后換臺(tái)手機(jī)诈悍,魅族mx3,神奇的事情發(fā)生了祸轮,竟然沒(méi)有泄漏了。黑人問(wèn)號(hào)臉侥钳。
總結(jié)
繼續(xù)查資料适袜,發(fā)現(xiàn)很多人給出了討論或者解決方案,傾向于Android輸入法的漏洞舷夺,在15<=API<=23中都存在苦酱。
知乎用戶十三太飽給出的解釋是:
**
InputMethodManager的相關(guān)對(duì)象(mServedView等)沒(méi)有傳遞下去的話,通過(guò)工具的檢測(cè)的確會(huì)發(fā)現(xiàn)前一個(gè)Activity出現(xiàn)內(nèi)存泄漏给猾。但是實(shí)際上疫萤,InputMethodManager對(duì)象并不是完全歸前一個(gè)Activity持有,只是暫時(shí)性的指向了它敢伸,InputMethodManager的對(duì)象是被整個(gè)APP循環(huán)的使用扯饶。另外,InputMethodManager是通過(guò)單例實(shí)現(xiàn)的池颈,不會(huì)造成內(nèi)存的疊加尾序,所以個(gè)人覺(jué)得InputMethodManager并不會(huì)造成實(shí)質(zhì)的內(nèi)存泄漏。
**
個(gè)人選擇不再解決饶辙,下面列一些blog供大家研究蹲诀,有什么問(wèn)題可以隨時(shí)討論,以上弃揽。
Android InputMethodManager 導(dǎo)致的內(nèi)存泄露及解決方案
Leakcanary部分泄露警報(bào)無(wú)需修復(fù)
待研究參考資料:
Android非UI線程使用View.post()方法一處潛在的內(nèi)存泄漏
注意事項(xiàng):
- Dialog銷毀一定要在activity銷毀之前