android 實例-弱引用示例 Handler正確使用方法


實際問題

android 習慣性問題:在使用handler的時候喜歡使用內部類形式。

private final Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            // ...
        }
    };

看一下問題代碼和現象:

package zol.com.zolcamera;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public class AActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_a);
        findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
        mHandler.sendEmptyMessageDelayed(0, 2000);
    }

    Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            mHandler.sendEmptyMessageDelayed(0, 2000);
        }
    };

    @Override
    protected void onResume() {

        super.onResume();
    }
}
package zol.com.zolcamera;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public class BActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_a);
        findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(BActivity.this, AActivity.class);
                startActivity(intent);
            }
        });
    }
}

上面兩個Activity B跳到A 再點button回A 。重復幾次。再看一下內存情況。

可以看到內存當中有好幾個AActivity,並沒有釋放。

如果你使用android studio 當你寫出這樣的代碼的時候,IDE會提示你這樣寫法會造成內存泄漏。

原因是:內部實例會持有外部類引用。

當Activity finish的時候如果handler沒有在工作,沒有延時消息,那么問題不大,否則是finish不掉的。

第一種解決辦法,聲明成靜態。內部靜態類不持有外部類引用。

第二種解決法,換一種寫法,不聲明成內部類。

package zol.com.zolcamera;

import android.os.Handler;
import android.os.Message;

/**
 * Created by Administrator on 2018/3/8.
 */

public class MyHandler extends Handler {

    AActivity mAActivity;

    public MyHandler(AActivity aActivity) {
        mAActivity = aActivity;
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        sendEmptyMessageDelayed(0, 2000);
    }

    public void clear() {
        mAActivity = null;
        removeCallbacksAndMessages(null);
    }
}

這里手動做了對Activity的置空。如果不放心那就用軟件引用。

 /** 
     * 聲明一個靜態的Handler內部類,並持有外部類的弱引用 
     */  
    private static class MyHandler extends Handler{  
  
        private final WeakReference<HandlerActivity> mActivty;  
  
        private MyHandler(HandlerActivity mActivty) {  
            this.mActivty = new WeakReference<HandlerActivity>(mActivty);  
        }  
  
        @Override  
        public void handleMessage(Message msg) {  
            super.handleMessage(msg);  
            HandlerActivity activity = mActivty.get();  
            if (activity != null){  
                // ....  
  
            }  
        }  
    }  
  

問題二:在主線程直接new Handler 使用的是主線程Looper.如果handler工作頻率很高,會影響主線程的效率。

所以某些UI操作 (對,ui操作)使用線程來執行。

大家都知道Thread + Handler 來實現。在子線程里new handler 必須先准備Looper.調用Looper.prepar()才能用。

google給大家其實已經提供了現成的,那就是HandlerThread ; 

  @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

這才是准備looper的正確姿勢。

下面聲明Handler 

    private HandlerThread handlerThread;
    private Handler mHandler;
    public void initHandler() {
        handlerThread = new HandlerThread("camera");
        handlerThread.start();
        mHandler = new Handler(handlerThread.getLooper(), mCallback);
    }

這個handler才是最好的。

 

package zol.com.zolcamera;

import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public class AActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_a);
        findViewById(R.id.bt).setOnClickListener(this);
        initHandler();
        mHandler.sendEmptyMessageDelayed(0, 2000);
    }


    private HandlerThread handlerThread;
    private Handler mHandler;

    public void initHandler() {
        handlerThread = new HandlerThread("camera");
        handlerThread.start();
        mHandler = new Handler(handlerThread.getLooper(), mCallback);
    }

    Handler.Callback mCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            return false;
        }
    };

    @Override
    public void finish() {
        mHandler.removeCallbacksAndMessages(null);
        super.finish();
    }

    @Override
    public void onClick(View v) {
        finish();
    }

    @Override
    protected void onResume() {

        super.onResume();
    }
}

退出時做如下操作。

   @Override
    public void finish() { mHandler.removeCallbacksAndMessages(null); super.finish(); }

理論是可以了。

以上內容只經過簡單試驗,有問題請看官自行研究。或與本人討論。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM