Handler的使用小结
程序员文章站
2022-07-14 17:07:54
...
Android UI的控件都不是线程安全的,如果允许并发访问,那控件的状态就是未知;所以Android只允许在 一个线程内对UI控件进行更新,这个线程就是创建View时的线程,默认状态下,这个线程就是主线程,这也就是为什么我们在对UI组件进行更新的时候,必须回到主线程去通过Handler操作。
在使用Handler的时候,有时候处理不当就会导致内存泄露, 当我们试图使用内部类或者匿名内部类重写Handler的handlerMessage()方法,一定要保证他们是静态,因为内部类和匿名内部类默认都会 持有外部类的引用,我们上面已经分析过,handler发送的每条Message都会持有一个对发送者的引用,即对handler的引用,如果我们发送了 延迟消息,直到我们关掉Activity的时候,消息还在消息队列里,就会发生内存泄露,引用链是这样的:
messageQueue->message->handler->activity
解决方案就是把内部类或者匿名内部类分别设置成静态内部类和静态成员,这样就没有没有了对外部activity的引用,如果需要对外部 Activity的引用可以给handler添加一个队外部类的弱引用。当然从上面的引用关系中,我们还可以有另外一种解决方案,那就是在退出的时候清空 消息队列,这样也可以达到我们的目的。
解决方案一: onDestroy时清除消息,mHandler.removeCallbacksAndMessages(null); // 参数为null时会清除所有消息。
解决方案二:声明Handler为static并持有Activity的弱引用。关键看程序中注释的地方
public class MainActivity extends Activity {
private static final String tag = "MainActivity";
private TextView mTvShow;
private MyHandler mHandler;
private static final int DELAY_TIME = 10000; // set to 5s and 10s to check result.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new MyHandler(MainActivity.this);
Log.e(tag, "onCreate");
mTvShow = (TextView)findViewById(R.id.tv_show);
new Thread() {
@Override
public void run() {
mHandler.sendEmptyMessageDelayed(1, DELAY_TIME);
Log.e(tag, "msg send");
}
}.start();
}
@Override
protected void onDestroy() {
Log.e(tag, "onDestroy");
super.onDestroy();
}
static class MyHandler extends Handler{
WeakReference<mainactivity> mWeakActivity;
public MyHandler(MainActivity activity) {
mWeakActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MainActivity activity = mWeakActivity.get();
Log.e(tag, "handleMessage," + msg.what);
// 5s is not null, 10s is null, tell that activity is recycled.
if(null == activity){
Log.e(tag, "null");
}else{
Log.e(tag, "not null");
activity.mTvShow.setText("delay show");
}
}
}
}//mainactivity