【Android日记】深入理解Handler消息处理机制

一、Handler的基本用法

我们可以使用一个Handler发送并处理一个线程关联的Message或者Runnable。(注意:Runnable也会被封装到Message.Callback)

基本用法

//自定义Handler创建
static class MHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        //消息处理
        super.handleMessage(msg);
    }
}
//消息发送
mHandler.sendMessage(Message.obtain());
mHandler.post(Runnable);

二、Handler原理解析

1.Handler与Looper的关联

实际上在我们实例化Handler之前,都会先检查当前线程的Looper是否存在,如果不存在则会抛出异常。也就是说在我们创建Handler之前一定要先调用Looper.prepare()

实例化Handler代码如下:

public Handler(Callback callback, boolean async) {
    //获取当前线程Looper,从线程的ThreadLocal中获取
    mLooper = Looper.myLooper();
    //如果当前线程不存在Looper抛出异常
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

Looper.prepare()

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

Looper提供prepare方法用来实例化Looper,借助ThreadLocal来实现和当前线程的绑定,Looper.loop()开始轮训消息队列,获取消息并调用Message.target 进行处理

2.Message的存储和管理

Handler提供了一些方法进行发送消息,包括sendMessage和post。
最终会调用Handler的MessageQueue.enqueueMessage()进行入队操作

MessageQueue的底层实现是单链表

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

3.Message 的分发与处理

Looper.loop()是一个死循环,负责从消息队列中取出消息,交给Handler进行处理

//Looper
public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;
    //...
    for (;;) {
       // 不断从 MessageQueue 获取 消息
        Message msg = queue.next(); // might block
        //退出 Looper 
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        //...
        try {
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            //...
        }
        //消息回收,放在Message的静态变量,消息池里面,进行消息重用,减少对象的创建开销
        msg.recycleUnchecked();
    }
}

Loop()方法调用MessageQueue.next()方法取出消息

//MessageQueue
Message next() {
    //...
    for (;;) {
        //...
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            //...
            if (msg != null) {
                if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }


            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }
        }


        // Run the idle handlers. 关于 IdleHandler 自行了解
        //...
    }
}

loop()从消息队列取出消息之后,调用msg.target.dispatchMessage() msg.target就是Handler

//Handler
public void dispatchMessage(Message msg) {
  //msg.callback 是 Runnable ,如果是 post方法则会走这个 if
  if (msg.callback != null) {
    //如果是Handler.post(runnable)发送的消息,不会进入handleMessage方法
    handleCallback(msg);
  } else {
    //callback 见【3.4】
    if (mCallback != null) {
      if (mCallback.handleMessage(msg)) {
        return;
      }
    }
    //回调到 Handler 的 handleMessage 方法
    handleMessage(msg);
  }
}

4.图解原理

在这里插入图片描述

5.小结

  • Handler负责消息发送与处理,消息发送就是将消息加入消息队列
  • Looper通过loop不停的从消息队列中取出消息,并交给Handler处理
  • MessageQueue负责消息存储,当消息队列为空的时候,线程会挂起,让出时间片,避免消耗CPU资源,消息队列有消息的时候,线程会唤醒
  • 消息队列的挂起和唤醒机制是利用Linux的pipe epoll 机制
  • Message是消息实体,通过Message.obtain()创建消息是从消息对象池获取消息,避免对象的重复创建

三、Handler的延伸

1.Handler引起的内存泄漏的解决方案

Handler允许我们发送延时消息,延时期间用户关闭Activity就会产生内存泄漏

这个泄漏是因为Handler持有Activity的引用,这是因为内存类持有外部类的引用,解决这个问题的办法就是将Handler定义成静态内部类

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();
}

2.为什么我们在主线程使用Handler不需要自己去调用Looper.prepare()

前面我们提到,在使用Handler之前必须先实例化Looper,主线程当然也不例外。
因为在应用程序启动的时候,也就是ActivityThread main()方法里面已经进行了初始化操作

//android.app.ActivityThread
public static void main(String[] args) {
  //初始化主线程Looper
  Looper.prepareMainLooper();


  ActivityThread thread = new ActivityThread();
  thread.attach(false);


  if (sMainThreadHandler == null) {
    sMainThreadHandler = thread.getHandler();
  }
  //主线程循环,如果循环退出,则应用程序也就退出了
  Looper.loop();


  throw new RuntimeException("Main thread loop unexpectedly exited");
}

3.Handler的Callback能做什么?

public void dispatchMessage(Message msg) {
  //这里的 callback 是 Runnable
  if (msg.callback != null) {
    handleCallback(msg);
  } else {
    //如果 callback 处理了该 msg 并且返回 true, 就不会再回调 handleMessage
    if (mCallback != null) {
      if (mCallback.handleMessage(msg)) {
        return;
      }
    }
    handleMessage(msg);
  }
}

Handler.Callback 有优先处理消息的权利,我们可以通过Callback机制进行消息拦截

4.通过Looper判断当前线程是否是主线程

public static boolean isMainThread() { 
	return Looper.myLooper() == Looper.getMainLooper(); 
}

5.为什么Looper.loop 是一个死循环却不会阻塞主线程?

Android中为什么主线程不会卡死
个人理解:
主线程的所有操作都是通过ActivityThread.H进行分发的,包括Activity Service BroadcastReceiver 屏幕点击事件等
我们所说的主线程阻塞其实就是在主线程进行耗时操作,产生ANR,主线程的操作其实就是一个message,如果这个message是耗时操作,阻塞的是loop()方法,导致其他消息无法正常处理,所以会阻塞。loop()方法保证主线程一直存活。
Looper可以比喻成一个传送带,Message比喻成一个物品,传送带将一个物品运送到端就是Handler处理消息的过程,处理完就开始处理下一个消息。

四、知识点汇总

  • 创建Handler之前一定要先创建Looper
  • Looper有退出功能,但是主线程的Looper不允许退出
  • 子线程Looper需要手动调用Looper.myLooper().quit()退出
  • Runnable会被封装成一个Message,可以说是一个特殊的Message
  • 创建Handler的线程,就是Looper所在的线程,也就是消息处理的线程
  • 使用Handler的时候用静态内部类的方式避免内存泄漏

五、引用

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值