我对Handler的学习和理解
今天花了点时间研究了一下Handler的细节,来做一下总结:
- Handler的基本用法
- Handler的大致实现机制
- 防止内存泄漏的优化
Handler的基本用法
当我们在初学安卓的时候,就听说过一句话:“耗时操作要放在子线程中,更改UI操作要放在主线程中”,为什么呢?因为如果在主线程中出现耗时操作,那么我们的应用便会出现无响应的情况,这样会造成用户的使用体验变差,而且应用如果出现那么我们如果遇到这样的情况:**当我们在加载资源的时候,这个加载的操作是耗时操作需要放在子线程中,我们加载完成后,需要告诉主界面,数据已经加载完成了,我们现在可以把资源显示在主界面上了,但是我们又不能在子线程中对界面进行修改,那么该怎么办呢?**这时我们就可以使用Handler来帮我们解决问题。
public class MainActivity extends AppCompatActivity {
private static final int DOWNLAND_OVER = 1;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case DOWNLAND_OVER:
Toast.makeText(MainActivity.this,"加载完成了",Toast.LENGTH_SHORT).show();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
try {
//耗时操作
Thread.sleep(3000);
Message message;
if(Message.obtain()!=null){
message = Message.obtain();
}else{
message = new Message();
}
message.what = DOWNLAND_OVER;
handler.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
这里我使用了Thread.sleep(3000);来模拟加载的耗时操作,用Toast来模拟改变界面的操作,这样便可以达到子线程中通知主线程修改UI的操作。
Handler的大致实现机制
讲Handler的实现机制,便要涉及到Looper,Handler,Message,MessageQueue这四个工具。
看上面的代码,首先,我们创建了一个Message的实例,我们可以把这个类理解成为一个消息,当我们在子线程中把耗时操作执行完以后,便创建一个消息,告诉主线程来对UI进行修改。那么,告诉的这个动作便由Handler来执行,handler.sendMessage(message),这个函数把消息发了出去,最后由handleMessage(Message msg)这个函数接收了消息,并对消息进行处理。大致就是这样的一个过程。
MessageQueue这个类是用来储存Message的集合,内部使用的是单链表的方式储存Message,Looper可以理解成为一个环,这个环里面是一个死循环,来不断地对MessageQueue里的内容进行提取,提取出来以后,再将message给handler进行处理。
handler的消息处理机制大致就是这样的。
##防止内存泄漏的优化
接下来讲一下Handler的内存泄漏的风险,看上面的代码,其实我写的是很有问题的。问题在哪里呢?
我们在创建内部类的时候,内部类会隐式地与外部类进行关联,持有外部类的引用,这样会导致一个问题,当我们Activity关闭时,handler理应也关闭,但是如果此时还有message未被处理,便会使得Activity无法被回收,就会出现内存泄漏的问题
那么我们如何来解决这个问题呢?
把handler定义成静态变量,并使用弱引用
们可以把handler定义成static类型,利用弱引用来对Activity1里的控件进行操作
static class MyHandler extends Handler{
WeakReference<Activity1> weakreference;
Activity1 mainactivity;
public MyHandler(Activity1activity){
weakreference = new WeakReference<Activity1>(activity);
mainactivity = weakreference.get();
}
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case DOWNLAND_OVER:
if(weakreference.get()!=null){
Toast.makeText(mainactivity,"加载完成了",Toast.LENGTH_SHORT).show();
Intent intent= new Intent(mainactivity,Activity2.class);}
}
}
}
MyHandler myhandler;
当GC碰到弱引用指向的实例时,一定会回收此实例,所以当Acivity被回收的时候,GC判断此时没有强引用指向Activity,只有弱引用,便会回收这个Activity实例。静态方法会让MyHandler类独立于外部的Activity,并且在处理Message时,判断弱引用是否被回收,如果被回收,就放弃执行代码。
这样我们就可以避免内存泄漏的问题了。
当然,我们的目的不仅仅是为了防止内存泄漏,更重要的是要逻辑业务的顺利执行,所以我们在买保险的同时,更重要的是对代码的审核和思考