我們先來看一張Android Studio中的warning截圖
public class HandlerTestActivity extends Activity {
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ... do something
}
}
}
上面這段代碼會引起內(nèi)存泄漏(Memory Leak)煌寇。
-
為什么會引起內(nèi)存泄漏?
我們都知道逾雄,非static的內(nèi)部類會持有外部類的引用,舉個類子來說腻脏,我們經(jīng)常在一些內(nèi)部類中顯示跳轉(zhuǎn)activity的時候鸦泳,給Intent賦值的時候,第一個參數(shù)會寫 外部類名.this 永品,這就是持有外部類的引用的很好表現(xiàn)做鹰。 同樣,其他地方需要用到這個內(nèi)部類的時候鼎姐,也不能是直接new出來钾麸,因為為非static的更振,必須先通過new出外部類才能。
那么饭尝,現(xiàn)在的情況就是肯腕,這個非static的handler內(nèi)部類,無論是否是匿名的钥平,便會持有外部的activity的引用实撒。
若此時你的handler的消息隊列中有未處理的Message,在Activity finish之后涉瘾,Message仍然存在知态,那么Handler也仍然存在。由于Handler中有Context的引用立叛,那么Context也就存在也就存在负敏。而該Context就是我們的Activity,也就是Activity依然純在秘蛇,那么我們便是發(fā)生了內(nèi)存泄露其做。
-
那么為什么要寫成靜態(tài)內(nèi)部類呢?或者寫成其他單獨的類呢彤叉?
隱性匿名類Handler變成static的內(nèi)部類庶柿,由于static的內(nèi)部類,使用的使用不需要外部類的實例秽浇,所以static的內(nèi)部類和外部類是沒有聯(lián)系的浮庐,從而不持有外部類的引用,通過這種方法柬焕,我們可以避免該種情況的發(fā)生审残。
將隱性匿名類寫成一個單獨的類(top-level-class),這樣Handler和Context之間就沒有聯(lián)系了斑举。
-
如何寫搅轿?
大家都知道,寫成靜態(tài)類后富玷,由于其類似于單獨成為了一個類璧坟,便不能直接調(diào)用我們Activity中的一些控件了,難不成要把所有的控件都寫成static的么赎懦,當然不是
我們通過使Handler持有Activity的一個弱引用來解決這個問題雀鹃,直接持有Activity的話,我們便與之前的匿名內(nèi)部類直接持有外部類的引用沒區(qū)別了励两,而持有了弱引用黎茎,在Activity有用的情況下,其會被AMS持有強引用当悔,GC不會回收傅瞻,而當其finish了踢代,便沒有強引用對象持有了,此時GC時便會回收該Activity嗅骄,我們的Handler由于是持有的弱引用胳挎,也不會導致其回收不成功。
來看一個簡單的demo掸读,我們寫一個靜態(tài)handler串远,實現(xiàn)5秒后修改我們布局中的textview的text。
package applock.anderson.com.statichandler;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import java.lang.ref.WeakReference;
public class MainActivity extends AppCompatActivity {
private TextView mTextView;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.tv_1);
mHandler = new MyHandler(this);
mHandler.sendEmptyMessageDelayed(0,5000);
}
public void setTextViewText(String str) {
if(mTextView != null) {
mTextView.setText(str);
}
}
private static class MyHandler extends Handler {
WeakReference<MainActivity> mainActivityWeakReference;
public MyHandler(MainActivity activity) {
mainActivityWeakReference = new WeakReference<MainActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//若Avtivity被回收儿惫,此時activity便為空
MainActivity activity = mainActivityWeakReference.get();
if(activity != null) {
activity.setTextViewText("修改成功");
}
}
}
}
可以看到澡罚,我們的Activity中的TextView成功被修改了。
-
進階
其實我們?yōu)槭裁匆钟形覀兊腁ctivity的弱引用呢肾请,完全可以使用MVP大法留搔,持有我們Activity實現(xiàn)的接口對象的弱引用,也就是多態(tài)的方式持有我們的Activity弱引用铛铁,多么美好的事情隔显。
大家還有什么注意事項可以拿出來一講的歡迎評論
謝謝大家閱讀,如有幫助饵逐,來個喜歡或者關(guān)注吧括眠!
本文作者:Anderson/Jerey_Jobs
簡書地址 : Anderson大碼渣
CSDN地址 : Jerey_Jobs的專欄
github地址 : Jerey_Jobs