美文网首页Android
Android Handler

Android Handler

作者: 微风LE | 来源:发表于2019-02-26 17:53 被阅读0次

Handler 作用

Android 中的Handler 主要用于主线程与子线程之间的通信,也就是处理异步消息。
简单理解来说主线程无法处理耗时任务,不然就会阻塞线程,那么就会用子线程来异步处理耗时工作,而子线程无法直接更新UI界面,所以需要handler来传递消息给主线程,让主线程完成UI界面的更新工作。

概念:MessageQueue和Looper

MessageQueue用来保存子线程从Handler所发送未处理的消息,就是个消息队列,负责消息的入列出列。
Looper依次取出MessageQueue中的消息传递给主线程响应处理。

Handler原理是什么?

一、Handler封装了消息的发送 (主要包括消息发送给谁)
Looper
1、内部包含一个消息对列,也就是MessageQueue,所有的handler发送的消息都走向这个消息对列。
2、Loopler.looper方法,就是一个死循环,不断地从MessageQueue取消息,如果有消息就处理消息,没有消息就阻塞
二、MessageQueue,就是一个消息对列,可以添加消息,并处理消息
三、handler也很简单,内部会跟Looper进行关联,也就是说在handler的内部可以找到Looper,找到Looper也就找到了MessageQueue,在handler中发送消息,其实就是向MessageQueue队列中发送消息
总结:handler负责发送消息,Looper负责接收handler发送的消息,并直接把消息回传给handler自己。MessageQueue就是一个存储消息的容器

用法:Handler基本用法

android.os.Handler handler = new Handler(){
  @Override
  public void handleMessage(final Message msg) {
    //这里接受并处理消息
  }
};
//发送消息
handler.sendMessage(message);
handler.post(runnable);

实例化一个 Handler 重写 handleMessage 方法 ,然后在需要的时候调用它的 send 以及 post 等方法就可以了,非常简单易用,并且支持延时消息操作。

Handler 与 Looper 的关联
一般在实例化 Handler 的时候, Handler 会去检查当前线程的 Looper 是否存在,如果不存在则会报异常,在创建 Handler 之前一定需要先创建 Looper 。

class LooperThread extends Thread {
    public Handler mHandler;
    public void run() {
        Looper.prepare();
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };
        Looper.loop();
    }
}

Looper.prepare() :
也就是说 Handler 跟线程的关联是靠 Looper 来实现的。

Message 的对象

message.what=1;//what属性
message.arg1=i;//arg属性
message.obj="消息内容:";//obj属性
handler.sendMessage(message);
 if (msg.what==1)//识别判断消息
   {
        //todo something
    }

Message 的存储与管理
Handler 提供了一些列的方法让我们来发送消息,如 send()系列 post()系列 。
以 sendEmptyMessage(int) 方法为例:

//Handler
sendEmptyMessage(int)
  -> sendEmptyMessageDelayed(int,int)
    -> sendMessageAtTime(Message,long)
      -> enqueueMessage(MessageQueue,Message,long)
            -> queue.enqueueMessage(Message, long);

最终都会走到 Message.enqueueMessage(Message,long) 方法。
上面我们可以看到消息的管理者是 MessageQueue
MessageQueue用来保存子线程从Handler所发送未处理的消息,就是个消息队列,负责消息的入列出列。

Handler 引起的内存泄漏原因

Handler 允许我们发送延时消息,如果在延时期间用户关闭了 Activity,那么该 Activity 会泄漏。
这个泄漏是因为 Message 会持有 Handler,而又因为 Java 的特性,内部类会持有外部类,
使得 Activity 会被 Handler 持有,这样最终就导致 Activity 泄漏。

解决方案

将 Handler 定义成静态的内部类,在内部持有 Activity 的弱引用,并及时移除所有消息。
示例:

private static class SafeHandler extends Handler {

    private WeakReference<HandlerActivity> ref;

    public SafeHandler(HandlerActivity activity) {
        this.ref = new WeakReference(activity);
    }

    @Override
    public void handleMessage(final Message msg) {
        HandlerActivity activity = ref.get();
        if (activity != null) {
            activity.handleMessage(msg);
        }
    }
}

并且再在 Activity.onDestroy() 前移除消息,加一层保障:

@Override
protected void onDestroy() {
  safeHandler.removeCallbacksAndMessages(null);
  super.onDestroy();
}

这样双重处理,就能避免内存泄漏。
注意:单纯的在 onDestroy 移除消息并不保险,因为特定情况下 onDestroy 并不一定执行。

常见Toast crash

当我们尝试在子线程里直接去弹 Toast 的时候,会发生crash :

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

本质上是因为 Toast 的实现依赖于 Handler,按子线程使用 Handler 的要求修改即可,同理的还有 Dialog。
正确示例代码如下:

new Thread(new Runnable() {
  @Override
  public void run() {
    Looper.prepare();
    Toast.makeText(HandlerActivity.this, "正常Toast啦!", Toast.LENGTH_SHORT).show();
    Looper.loop();
  }
});

以上粗略总结也是日常常识以及在学习中参考大佬们的总结。不喜匆喷,欢迎指正。_
---by mars_chu 啥都不会的楚楚

相关文章

网友评论

    本文标题:Android Handler

    本文链接:https://www.haomeiwen.com/subject/tekfyqtx.html