實際問題
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(); }
理論是可以了。
以上內容只經過簡單試驗,有問題請看官自行研究。或與本人討論。