1. 程式人生 > >防側漏之弱引用的使用

防側漏之弱引用的使用

本文依然基於github上的開源框架為基礎,看過之前發的 最新Retrofit + RxJava + MVP 那篇blog的講述,應該明白框架裡面的大概,一步步兌現之前的承諾,會寫上十篇左右的帖子來講解裡面的要點和難點,今天主要講述的是baseActivity裡面的WeakReference< BaseActivity >。

最初入行的時候,使用handler一般都是如下方式:

private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super
.handleMessage(msg); /** * 各種操作 */ } };

先說下此處的問題,首先,系統會有警告,This Handler class should be static or leaks might occur,大致意思就是說:Handler類應該定義成靜態類,否則可能導致記憶體洩露,因為非靜態內部類隱式自動持有外部類的強引用,而靜態內部類不會引用外部類物件,目前只需要記住這句話,說到根本原因就牽扯到jvm,這塊日後會單獨拿出來寫一個模組。

再者,這樣寫的話,如果有一個超生命週期的邏輯,則會出現記憶體洩漏,百說不如貼上程式碼:

    private void test() {
        handler.sendMessageDelayed(Message.obtain(), 60000);
        finish();
    }

當Android應用啟動的時候,會先建立一個UI主執行緒的Looper物件,同時會建立一個MessageQueue,Looper迴圈遍歷,把MessageQueue中的message一個一個取出來處理,處理結束後並不會銷燬(這和java中訊息機制不同,java中處理完便會自動銷燬,等待回收),而是等待後續傳來的message,只要Handler傳送的Message尚未被處理,則該Message及傳送它的Handler物件將被執行緒MessageQueue一直持有,上述程式碼是activity在finish一分鐘後才收到資訊,此時的activity中的handler還在被強引用,當這個activity退出時訊息佇列中還有未處理的訊息或者正在處理訊息,而訊息佇列中的message持有handler例項的引用,handler又持有activity的引用,所以導致該activity的記憶體資源無法及時回收,引發記憶體洩漏,關於handler這塊,之前一篇

android之handler的刨根問底 進行了詳細敘述,有興趣可以看下。

為何使用弱引用

對於強、軟、弱、虛四大引用,之前在簡述圖片載入框架有過講述並有程式碼例項,此處不重複貼程式碼,對於弱引用,指的是當垃圾回收器掃描到此處垃圾,無論記憶體是否充足,都會回收,功能上和軟引用如出一轍,和軟引用最大的不同就是軟引用是在記憶體不足的時候,gc掃描到才會回收此處記憶體。此處使用弱引用比軟引用更加合理,雖然軟引用同樣可以解決因為強引用導致記憶體無法回收的問題,但有個前提條件就是需要記憶體不足的時候才可以,這就沒有弱引用來得實在。

改進後的程式碼如下:

/**
 * Created by Zero on 2017/7/20.
 */
public abstract class BaseActivity<Pre extends BasePresenter> extends AppCompatActivity implements OnClickListener {

    private static final String DIALOG_LOADING = "DialogLoading";
    private boolean mVisible;
    private LoadingDialogFragment waitDialog = null;

    protected Pre presenter;
    protected final Handler mHandler = new MyHandler(this);
    private BroadcastReceiver receiver;
    private IntentFilter filter;

    /*****************省略一些不相關程式碼*****************/

    @Override
    protected void onDestroy() {
        super.onDestroy();
        /**
         * 移除mHandler
         */
        mHandler.removeCallbacksAndMessages(null);
        if (receiver != null) {
            LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
        }
    }
}
/**
 * Created by zero on 2017/7/24.
 */

public class MyHandler extends Handler {
    private final WeakReference<BaseActivity> mActivity;

    /**
     * 從BaseActivity中提取出來,原來是因為內部類會隱式強引用當前類,採用弱引用,避免長生命週期導致記憶體洩漏
     *
     * @param activity
     */
    public MyHandler(BaseActivity activity) {
        mActivity = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        if (mActivity.get() != null) {
            mActivity.get().requestOver(msg);
        }
    }
}

為了避免一些handler拖泥帶水,onDestroy方法中對mHandler進行removeCallbacksAndMessages(null)處理,便於mHandler和activity及時被回收。

專案已上傳,戳此進入github。