使用軟引用解決Handler記憶體洩露和顯示Popupwindow、Dialog時提示"Unable to add Window-token is null"的問題

王世暉發表於2016-04-22

原文連結:http://zmywly8866.github.io/2015/09/22/softreferences-in-android.html

通過軟引用解決Handler記憶體洩露的問題

  下面對軟引用使用的方式適用於任何內部類,嚴格來說是通過軟引用解決靜態內部類無法呼叫當前類中的物件和方法的問題,真正解決記憶體洩露是需要將內部類改成靜態內部類。

  當在一個類中按照如下方式建立一個Handler內部類時,使用Lint工具檢測時會給出“This Handler class should be static or leaks might occur”的警告,原因是Handler內部類可能持有當前類的引用,導致即使該類不再被使用時系統仍無法回收這給類持有的物件,Android中記憶體洩露很多都是由於持有類中的物件時間太長導致,如果很多地方出現類似的程式碼會導致應用佔用的記憶體不斷上漲,最終導致程式崩潰。

private TextView mUserNameTxt = null;

class LoadDataHandler extends Handler{
	@Override
	public void handleMessage(Message msg) {
		super.handleMessage(msg);
		switch(msg.what){
		case 0:{
			mUserNameTxt.setText(msg.obj.toString());
		}
		break;
		default:{
			
		}
		break;
		}
	}
}

  按照Lint的建議將Handler內部類改成static靜態內部類後,由於不可能將當前類的所有全域性物件都宣告為static物件,所以會報“Cannot make a static reference to the non-static field”的錯誤,這時候可以使用軟引用來解決這個問題,具體程式碼如下:

private TextView mUserNameTxt = null;
static class LoadDataHandler extends Handler{
	private SoftReference<MainActivity> activitySRF = null;
	public LoadDataHandler(MainActivity activity){
		activitySRF = new SoftReference<MainActivity>(activity);
	}
	
	@Override
	public void handleMessage(Message msg) {
		super.handleMessage(msg);
		// 因為Handler是非同步的,存在退出當前類之後才接收到handler訊息的情況,並且軟引用持有的物件會在堆記憶體不足時存在被回收的可能,所以這裡需要判空處理
		if(null == activitySRF || null == activitySRF.get()){
			return;
		}
		
		switch(msg.what){
		case 0:{
			activitySRF.get().mUserNameTxt.setText(msg.obj.toString());
		}
		break;
		default:{
			
		}
		break;
		}
	}
}

通過軟引用解決銷燬某個Activity後,仍然在Activity顯示Popupwindow或者Dialog時提示”Unable to add Window-token is null”的問題

  在實際專案中踩過這個坑,特地分享出來,這個問題是由於在Activity中顯示PopupWindow或者Dialog時,PopupWindow、Dialog還沒顯示出來就銷燬了當前Activity導致(主要是快速切換)。因為PopupWindow和Dialog都是依附於當前Activity的某個View上的,當前Activity被銷燬後依附的view為空,此時顯示PopupWindow或者Dialog時會提示這個異常。

  這個問題也可以通過軟引用來解決(通過判斷軟引用中的activity物件是否為空或者是否已經finish即可判斷是否可以顯示PopupWindow或者Dialog),具體程式碼如下:

private SoftReference<Activity> activitySRF = null;
public void initDialog(Activity activity){
	activitySRF = new SoftReference<Activity>(activity);
	if(null != activitySRF&&null!=activitySRF.get()&&!activitySRF.get().isFinishing()){
		getWindow().requestFeature(Window.FEATURE_NO_TITLE);
		show();
		setContentView(R.layout.hanzi_medal_dialoglayout);
		setCanceledOnTouchOutside(true);
        setCancelable(false);
	}
}

public void show(Activity activity, String medalName){
	initDialog(activity);
	if(null != activitySRF&&null!=activitySRF.get()&&!activitySRF.get().isFinishing()){
		TextView medalTxt = (TextView) findViewById(R.id.medalTxtId);
		if (!TextUtils.isEmpty(medalName)) {
			medalTxt.setText("恭喜您獲得“" + medalName + "”勳章");
		}
		new Handler(activitySRF.get().getMainLooper()).postDelayed(new Runnable() {
			@Override
			public void run() {
				if(null != activitySRF&&null!=activitySRF.get()&&!activitySRF.get().isFinishing()){
					dismiss();
				}
			}
		}, 3000);
	}
}

相關文章