重识 Handler

Posted ZhangQiang-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重识 Handler相关的知识,希望对你有一定的参考价值。

注:本文系统源码展示基于 API-27(部分删减) .

android 开发肯定离不开跟 Handler 打交道,Handler 作为 Android 中消息机制的重要一员 ,它通常被我们用来做主线程与子线程之间的通信工具

可以说只要有异步线程与主线程通信的地方就一定会有 Handler

所以搞懂 Handler 对理解Android以及开发非常有必要 那么,一起过一下Handler 用法,handler通信机制的原理,以及可能的错误规避

(handler机制; IPC Binder机制; IPC socket)

(Android的四剑客Activity, Service, Broadcast, ContentProvider的启动过程的交互,都离不开消息机制,Android某种意义上也可以说成是一个以消息驱动的系统。消息机制涉及MessageQueue/Message/Looper/Handler这4个类)

重识 Handler

定义:一套 Android 消息传递机制

作用: 在多线程的应用场景中,将工作线程中需更新UI的操作信息 传递到 UI主线程,从而实现 工作线程对UI的更新处理,最终实现异步消息的处理

为什么要用 Handler消息传递机制:  多个线程并发更新UI的同时 保证线程安全

1.1 Handler 的基本用法

android.os.Handler handler = new Handler()
  @Override
  public void handleMessage(final Message msg) 
    //这里接受并处理消息
  
;
//发送消息
handler.sendMessage(message);
handler.post(runnable);
handler.postDelayed(runnable);
实例化一个 Handler 重写 handleMessage 方法 ,然后在需要的时候调用它的 send 以及 post 系列方法就可以了,非常简单易用,并且支持延时消息。(更多方法可查询 API 文档)

当熟悉了Handler的原理之后我们知道,Handler不仅仅能将子线程的数据传递给主线程,它能实现任意两个线程的数据传递。
子线程中创建Handler注意:1.手动调用Looper.prepare(); 2.Looper.loop();

我们可以使用 Handler 发送并处理与一个线程关联的 Message 和 Runnable 。(注意:Runnable 会被封装进一个 Message,所以它本质上还是一个 Message 

每个 Handler 都会跟一个线程绑定,并与该线程的 MessageQueue 关联在一起,从而实现消息的管理以及线程间通信。

这里没有看到任何 MessageQueue 的身影,也没看到它与线程绑定的逻辑

2. Handler 原理解析

大家早就听说过了 Looper 以及 MessageQueue 了

不过在开始分析原理之前,先明确我们的问题

  1. Handler 是如何与线程关联的

  2. Handler 发出去的消息是谁管理的

  3. 消息又是怎么回到 handleMessage() 方法的

  4. 线程的切换是怎么回事

2.1 Handler 与 Looper 的关联

实际上我们在实例化 Handler 的时候 Handler 会去检查当前线程的 Looper 是否存在,如果不存在则会报异常,也就是说在创建 Handler 之前一定需要先创建 Looper 

代码如下

public Handler(Callback callback, boolean async) 
            //.....
        //检查当前的线程是否有 Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) 
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        
        //Looper 持有一个 MessageQueue
        mQueue = mLooper.mQueue;
            //....

这个异常相信很多人遇到过,而我们平时直接使用感受不到这个异常是因为主线程已经为我们创建好了 Looper,后面会讲。

一个完整的 Handler 使用例子其实是这样的:

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

private static void prepare(boolean quitAllowed) 
  if (sThreadLocal.get() != null) 
    throw new RuntimeException("Only one Looper may be created per thread");
  
//该方法在当前thread创建了一个Looper(), ThreadLocal主要用于维护线程的本地变量,
  sThreadLocal.set(new Looper(quitAllowed));



//looper构造方法  初始化MessageQueue ,以及当前线程mThread
private Looper(boolean quitAllowed) 
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();


//looper构造方法  初始化MessageQueue ,以及当前线程mThread
private Looper(boolean quitAllowed) 
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();

Looper 提供了 Looper.prepare() 方法来创建 Looper ,并且会借助 ThreadLocal 来实现与当前线程的绑定功能。Looper.loop() 则会开始不断尝试从 MessageQueue 中获取 Message , 并分发给对应的 Handler(Message 的分发与处理)

 

也就是说 Handler 跟线程的关联是靠 Looper 来实现的。

 

关于ThreadLocal

ThreadLocal: 线程本地存储区(Thread Local Storage,简称为TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。TLS常用的操作方法:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>()

可见sThreadLocal的get()和set()操作的类型都是Looper类型。

public void set(T value) 
    Thread currentThread = Thread.currentThread(); //获取当前线程
    Values values = values(currentThread); //查找当前线程的本地储存区
    if (values == null) 
        //当线程本地存储区,尚未存储该线程相关信息时,则创建Values对象
        values = initializeValues(currentThread);
    
    //保存数据value到当前线程this
    values.put(this, value);
public T get() 
    Thread currentThread = Thread.currentThread(); //获取当前线程
    Values values = values(currentThread); //查找当前线程的本地储存区
    if (values != null) 
        Object[] table = values.table;
        int index = hash & values.mask;
        if (this.reference == table[index]) 
            return (T) table[index + 1]; //返回当前线程储存区中的数据
        
     else 
        //创建Values对象
        values = initializeValues(currentThread);
    
    return (T) values.getAfterMiss(this); //从目标线程存储区没有查询是则返回null

2.2 Message 的存储与管理

Handler 提供了一些列的方法让我们来发送消息,如 send()系列 post()系列 。

不过不管我们调用什么方法,最终都会走到 Message.enqueueMessage(Message,long) 方法。

 sendEmptyMessage(int) 方法为例:

//Handler

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

查看源代码可以看出Message被存入MessageQueue时是将Message存到了上一个Message.next上, 形成了一个链式的列表,同时也保证了Message列表的时序性。

到了这里,消息的管理者 MessageQueue 也就露出了水面

MessageQueue 顾明思议就是个队列,负责消息的入队出队。

Message的发送实际是放入到了Handler对应线程的MessageQueue中

这里有两个问题

1. sendMessageDelayed是如何实现延时发送消息的? 

2. sendMessageDelayed是通过阻塞来达到了延时发送消息的结果,那么会不会阻塞新添加的Message?

boolean enqueueMessage(Message msg, long when) 
        ...//略过了部分抛出异常的代码
        synchronized (this) 
            ...
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            //p是当前MessageQueue队首Message
            if (p == null || when == 0 || when < p.when) 
                //如果当前队列没有其他需要发送的Message;或者当前新添加进来的的Message的时间点为0(即需要立即发送的消息);
                //或者当前新添加进来的的Message需要发送的时间点小与当前MessageQueue队列头部Message的时间点,
                //(即当前添加进来的Message需要在当前MessageQueue队列头部Message之前被发送)时,就会进入到if代码块里面。

                // New head, wake up the event queue if blocked. -->新的首部,然后如果阻塞了,需要唤醒线程。

                //此时做的事情是将当前新添加的Message插入到了MessageQueue的队首(Message 的存储是链表的形式,next相当于链表的尾指针)
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;

                //为什么会有线程的阻塞呢?其实MessageQueue内部的消息是按需要发送的时间点从小到大排列的(Handler发送消息并不是通过定时器发送的)
                //所以,当队首Message(最近需要发送的Message)未到达发送时间点时,线程被阻塞,所以这里需要根据线程是否阻塞看是否需要唤醒线程,这样才能使新加入的Message能及时发送出去,不会被阻塞。
                    线程的唤醒是通过native的方法来实现的。

             else 
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.

                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;

                //执行到了else语句里面,说明了当前添加进来的Message是在当前MessqgeQueue队首的Message之后才会被发送的
                //这里的for循环实际操作就是找到MessageQueue中比当前添加进来的Message需要发送的时间点大的位置,将Message插入到其前边(实际就是一个链表的插入操作)
                for (;;) 
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) 
                        break;
                    
                    if (needWake && p.isAsynchronous()) 
                        needWake = false;
                    
                
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) 
                nativeWake(mPtr);
            
        
        return true;
    

2.3 Message 的分发与处理

了解清楚 Message 的发送与存储管理后,接下来看分发与处理。

前面说到了 Looper.loop() 负责对消息的分发, Message的发送实际是放入到了Handler对应线程的MessageQueue中,那么,Message又是如何被取出来的呢?

假如在子线程中创建handler,如果不调用Looper.loop()方法,Handler是接受不到消息的,所以我们可以大胆的猜测,消息的获取肯定和它脱不了关系

先来看看所涉及到的方法:

//Looper
public static void loop() 
//可以看到,在调用Looper.prepare()之前是不能调用该方法的,不然又得抛出异常了
    final Looper me = myLooper();
    if (me == null) 
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    
    //创建loop时初始化的mQueue -->mQueue = new MessageQueue(quitAllowed);
    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 就是发送该消息的 Handler
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
         finally 
            //...
        
        //...
        //回收 message, 见【3.5 创建 Message 实例的最佳方式】
        msg.recycleUnchecked();
    


这里我们看到,mLooper()方法里我们取出了,当前线程的looper对象,然后从looper对象开启了一个死循环
不断地从looper内的MessageQueue中取出Message,只要有Message对象,就会通过Message的target调用dispatchMessage去分发消息

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) 
//当消息Handler为空时,查询MessageQueue中的下一条异步消息msg,则退出循环。
                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. 
        //...
    

还调用了 msg.target.dispatchMessage(msg) ,msg.target 就是发送该消息的 Handler,这样就回调到了 Handler 那边去了:

//Handler
public void dispatchMessage(Message msg) 
  //msg.callback 是 Runnable ,如果是 post方法则会走这个 if  ; 
  if (msg.callback != null) 
    handleCallback(msg);
   else 
    //callback 见【3.4 Handler 里藏着的 Callback 能干什么】
    if (mCallback != null) 
      if (mCallback.handleMessage(msg)) 
        return;
      
    
    //回调到 Handler 的 handleMessage 方法
    handleMessage(msg);
  

 

注:dispatchMessage() 方法针对 Runnable 的方法做了特殊处理,如果是 post() ,则会直接执行 Runnable.run() 

分析:Looper.loop() 是个死循环,会不断调用 MessageQueue.next() 获取 Message ,并调用 msg.target.dispatchMessage(msg) 回到了 Handler 来分发消息,以此来完成消息的回调(注:loop()方法并不会卡死主线程,[Android中为什么主线程不会卡死] )

那么线程的切换又是怎么回事呢?

其实非常简单,我们将所涉及的方法调用栈画出来,如下:

Thread.foo()
      Looper.loop()
        -> MessageQueue.next()
           -> Message.target.dispatchMessage()
               -> Handler.handleMessage()

显而易见,Handler.handleMessage() 所在的线程最终由调用 Looper.loop() 的线程所决定。

平时我们用的时候从异步线程发送消息到 Handler,这个 Handler 的 handleMessage() 方法是在主线程调用的,所以消息就从异步线程切换到了主线程。

 

线程(Thread)、循环器(Looper)、处理者(Handler)之间的对应关系如下:

  • 1个线程(Thread)只能绑定 1个循环器(Looper),但可以有多个处理者(Handler)

  • 1个循环器(Looper) 可绑定多个处理者(Handler)

  • 1个处理者(Handler) 只能绑定1个1个循环器(Looper)

 

图解原理

  • Handler通过sendMessage()发送Message到MessageQueue队列;

  • Looper通过loop(),不断提取出达到触发条件的Message,并将Message交给target来处理;

  • 经过dispatchMessage()后,交回给Handler的handleMessage()来进行相应地处理。

  • 将Message加入MessageQueue时,处往管道写入字符,可以会唤醒loop线程;如果MessageQueue中没有Message,并处于Idle状态,则会执行IdelHandler接口中的方法,往往用于做一些清理性地工作。

  • 消息缓存:为了提供效率,提供了一个大小为50的Message缓存队列,减少对象不断创建与销毁的过程。

2.4 小结

Handler 的背后有着 Looper 以及 MessageQueue 的协助,三者通力合作,分工明确。

尝试小结一下它们的职责,如下:

  • Looper :负责关联线程以及消息的分发,会与创建它的线程绑定,并负责在该线程下从 MessageQueue 获取 Message,分发给 Handler ;

  • MessageQueue :是个队列,负责消息的存储与管理,负责管理由 Handler 发送过来的 Message ;

  • Handler : 负责发送并处理消息,面向开发者,提供 API,并隐藏背后实现的细节。

对开头提出的问题用一句话总结:

1. Handler 是如何与线程关联的

2. Handler 发出去的消息是谁管理的

3. 消息又是怎么回到 handleMessage() 方法的

4. 线程的切换是怎么回事

Handler 发送的消息由 MessageQueue 存储管理,并由 Loopler 负责回调消息到 handleMessage()。

线程的转换由 Looper 完成,handleMessage() 所在线程由 Looper.loop() 调用者所在线程决定。

3. Handler 的延伸

由于 Handler 的特性,它在 Android 里的应用非常广泛,比如: AsyncTask、HandlerThread、Messenger、IdleHandler 和 IntentService 等等。没讲到的可以自行搜索相关内容进行了解。

3.1 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 并不一定执行

3.2 为什么我们能在主线程直接使用 Handler,而不需要创建 Looper ?

前面我们提到了每个Handler 的线程都有一个 Looper ,主线程当然也不例外,但是我们不曾准备过主线程的 Looper 而可以直接使用,这是为何?

注意:通常我们认为 ActivityThread 就是主线程。事实上它并不是一个线程,而是主线程操作的管理者,通常 ActivityThread 认为就是主线程,另外主线程也可以说成 UI 线程。

App初始化的时候 ActivityThread mMainThread 在Activity中的attach()方法中被初始化

在 ActivityThread.main() 方法中有如下代码:

//android.app.ActivityThread
public static void main(String[] args) 
  //...
  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");


----------------------------------------------------------
//Looper.prepareMainLooper(); 代码如下:
public static void prepareMainLooper() 
    prepare(false);
    synchronized (Looper.class) 
        if (sMainLooper != null) 
            throw new IllegalStateException("The main Looper has already been prepared.");
        
        sMainLooper = myLooper();
    

可以看到在 ActivityThread 里 调用了 Looper.prepareMainLooper() 方法创建了 主线程的 Looper ,并且调用了 loop() 方法,所以我们就可以直接使用 Handler 了。

注:Looper.loop() 是个死循环,后面的代码正常情况不会执行。

3.3 主线程的 Looper 不允许退出

如果你尝试退出 Looper ,你会得到以下错误信息:Caused by: java.lang.IllegalStateException: Main thread not allowed to quit.

主线程不允许退出,退出就意味 APP 要挂。

3.4 Handler 里藏着的 Callback 能干什么?

在 Handler 的构造方法中有几个 要求传入 Callback ,那它是什么,又能做什么呢?

来看看 Handler.dispatchMessage(msg) 方法:

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 处理并拦截(返回 true),那么 Handler 的 handleMessage(msg) 方法就不会被调用了;如果 Callback 处理了消息,但是并没有拦截,那么就意味着一个消息可以同时被 Callback 以及 Handler 处理

我们可以利用 Callback 这个拦截机制来拦截 Handler 的消息  ; 

场景:Hook ActivityThread.mH , 在 ActivityThread 中有个成员变量 mH ,它是个 Handler,又是个极其重要的类,几乎所有的插件化框架都使用了这个方法。(Xposed / Legend)

(关于hook)

  • 消息分发的优先级:

  • Message的回调方法:message.callback.run(),优先级最高;

  • Handler的回调方法:Handler.mCallback.handleMessage(msg),优先级仅次于1;

  • Handler的默认方法:Handler.handleMessage(msg),优先级最低。

3.5 创建 Message 实例的最佳方式

由于 Handler 极为常用,所以为了节省开销,Android 给 Message 设计了回收机制,所以我们在使用的时候尽量复用 Message ,减少内存消耗。

方法有二:

  1. 通过 Message 的静态方法 Message.obtain(); 获取;

  2. 通过 Handler 的公有方法 handler.obtainMessage(); 。

3.6 子线程里弹 Toast/DialogDialog 的正确姿势

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

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

本质上是因为 Toast 的实现依赖于 Handler,按子线程使用 Handler 的要求修改即可(见【2.1】),同理的还有 Dialog。

正确示例代码如下:

new Thread(new Runnable() 
  @Override
  public void run() 
    Looper.prepare();
    Toast.makeText(HandlerActivity.this, "不会崩溃啦!", Toast.LENGTH_SHORT).show();
    Looper.loop();
  
);

3.7 用 Looper 机制

我们可以利用 Looper 的机制来帮助我们做一些事情:

  1. 将 Runnable post 到主线程执行;

  2. 利用 Looper 判断当前线程是否是主线程。

完整示例代码如下:

public final class MainThread 

    private MainThread() 
    

    private static final Handler HANDLER = new Handler(Looper.getMainLooper());

    public static void run(@NonNull Runnable runnable) 
        if (isMainThread()) 
            runnable.run();
        else
            HANDLER.post(runnable);
        
    

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

 

4. Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

(1) Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

进程:每个app运行时前首先创建一个进程,该进程是由Zygote fork出来的,用于承载App上运行的各种Activity/Service等组件。进程对于上层应用来说是完全透明的, 大多数情况一个App就运行在一个进程中,除非在AndroidManifest.xml中配置Android:process属性,或通过native代码fork进程。

线程:线程对应用来说非常常见,比如每次new Thread().start都会创建一个新的线程。该线程与App所在进程之间资源共享,从Linux角度来说进程与线程除了是否共享资源外,并没有本质的区别,都是一个task_struct结构体,在CPU看来进程或线程无非就是一段可执行的代码,CPU采用CFS调度算法,保证每个task都尽可能公平的享有CPU时间片

再说说死循环问题:

对于线程既然是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于主线程,我们是绝不希望会被运行一段时间,自己就退出,那么如何保证能一直存活呢?简单做法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出,例如,binder线程也是采用死循环的方法,通过循环方式不同与Binder驱动进行读写操作,当然并非简单地死循环,无消息时会休眠。但这里可能又引发了另一个问题,既然是死循环又如何去处理其他事务呢?通过创建新线程的方式。

真正会卡死主线程的操作是在回调方法onCreate/onStart/onResume等操作时间过长,会导致掉帧,甚至发生ANR,looper.loop本身不会导致应用卡死。

(2) 没看见哪里有相关代码为这个死循环准备了一个新线程去运转?

事实上,会在进入死循环之前便创建了新binder线程,在代码ActivityThread.main()中:

public static void main(String[] args) 
    //....

//创建Looper和MessageQueue对象,用于处理主线程的消息
    Looper.prepareMainLooper();


//创建ActivityThread对象
    ActivityThread thread = new ActivityThread();
//建立Binder通道 (创建新线程)
    thread.attach(false);

    if (sMainThreadHandler == null) 
        sMainThreadHandler = thread.getHandler();
    
    //....
//消息循环运行
    Looper.loop();

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

thread.attach(false);便会创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程( 具体过程可查看 startService流程分析)

另外,ActivityThread实际上并非线程,不像HandlerThread类,ActivityThread并没有真正继承Thread类,只是往往运行在主线程,该人以线程的感觉,其实承载ActivityThread的主线程就是由Zygote fork而创建的进程。

主线程的死循环一直运行是不是特别消耗CPU资源呢? 其实不然,这里就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里, 此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。 主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

(3) Activity的生命周期是怎么实现在死循环体外能够执行起来的?

ActivityThread的内部类H继承于Handler,通过handler消息机制,简单说Handler机制用于同一个进程的线程间通信。

Activity的生命周期都是依靠主线程的Looper.loop,当收到不同Message时则采用相应措施:

在H.handleMessage(msg)方法中,根据接收到不同的msg,执行相应的生命周期。

 

5. 知识点汇总

由前文可得出一些知识点,汇总一下,方便记忆。

  1. Handler 的背后有 Looper、MessageQueue 支撑,Looper 负责消息分发,MessageQueue 负责消息管理;

  2. 在创建 Handler 之前一定需要先创建 Looper;

  3. Looper 有退出的功能,但是主线程的 Looper 不允许退出;

  4. 异步线程的 Looper 需要自己调用 Looper.myLooper().quit(); 退出;

  5. Runnable 被封装进了 Message,可以说是一个特殊的 Message;

  6. Handler.handleMessage() 所在的线程是 Looper.loop() 方法被调用的线程,也可以说成 Looper 所在的线程,并不是创建 Handler 的线程;

  7. 使用内部类的方式使用 Handler 可能会导致内存泄露,即便在 Activity.onDestroy 里移除延时消息,必须要写成静态内部类;


 

Android中常用线程切换

1.开启线程 Thread与Runnable
两种方式均可实现多线程也都可以实现资源共享(如买票系统)
Runnable优势: 适合多个相同的程序代码的线程去处理同一个资源 ; 可以避免java中的单继承的限制  ; 增加程序的健壮性

2.在子线程中更新UI
view.post(Runnable action)

3.假如该方法是在子线程中
mActivity.runOnUiThread(Runnable action)

4.Handler机制
(1)假如该方法是在子线程中
首先在主线程中定义Handler,Handler mainHandler = new Handler();(必须要在主线程中定义才能操作主线程,如果想在其他地方定义声明时要这样写Handler mainHandler = new Handler(Looper.getMainLooper()),来获取主线程的 Looper 和 Queue )
Handler mainHandler = new Handler(Looper.getMainLooper());
    mainHandler.post(new Runnable() 
        @Override
        public void run() 
            //已在主线程中,更新UI
        
    );
(2): 假设在主线程中
Handler myHandler = new Handler() 
        @Override
        public void handleMessage(Message msg) 
            switch(msg.what) 
                case 0:
                    //更新UI等
  
5.使用AsyncTask
6.其他 :xUtils3,AsyncHttpClient,Okhttp,Volley,EventBus,rxjava等…

 


任何简单易用的知识点背后藏着工程师大量的智慧。

相关参考:

Handler

Android消息机制1-Handler(Java 层)  / 2Native层

Android Handler消息机制原理解读

了解Android 消息机制

Android Handler攻略集合

Android多线程:手把手教你使用HandlerThread

Android 消息机制——你真的了解Handler?

Android Handler消息机制原理最全解读

Handler进阶之sendMessage原理探索

Android中为什么主线程不会卡死

一文看穿 Handler blog

Android 中的“子线程”形态解析

Android UI线程和非UI线程

Android中多线程切换的几种方法(eventbus/rxjava)

以上是关于重识 Handler的主要内容,如果未能解决你的问题,请参考以下文章

重识Nginx - 系列导读

Handler机制详解

Object重识

jarvisoj web phpinfo

Bitset重识

重识Activity——生命周期详解