Android的Handler部分原理的解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qqchenjian318/article/details/54601670

前言

Handler是Android应用开发中最常用的线程间通信类,在Android应用开发甚至整个应用的运行中都有非常重要的作用。所以理解Handler的实现原理,是很有必要的一件事情。本文,会从Handler、Looper、MessageQueue等关键类的源码,分析Handler的主要原理。


一、Android的消息机制Handler

Android的消息机制主要是指Handler运行机制,Hnadler的运行,底层需要Looper和MessageQueue进行支撑。我们需要大致了解一下几个关键类。
Handler:消息机制的核心类,开发者调用的类,能够实现线程间通信
MeesageQueue:消息队列,用于消息的存取,是Handler机制的另一个核心类, ,虽然叫队列, 但是其实他底层实现其实是通过Message的next,形成一个单链表的数据结构,当前message对象的next变量,指向下一个message对象。
Looper:维持一个无限循环,从消息队列中取出消息
我们知道,当我们在不同的线程中创建Handler对象的时候,Handler会去取到当前线程的Looper来构建消息循环系统,那么如何取到当前线程的Looper呢,这就需要另一个概念ThreadLocal,
public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
handler的构造函数中,通过Looper.myLooper()方法去,取了looper对象。myLooper方法的具体实现如下

而这个mThreadLocal,其实就是一个ThreadLocal的对象



ThreadLocal:该类是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据。存储数据之后也只有在指定线程中才能获取到存储的数据,其他线程是无法获取当该线程存储的数据的。这样就实现了在不同线程中存储的Looper对象不同

二、ThreadLocal的工作原理

上面说到了ThreadLocal这个类,同一个对象,在不同线程中保存的数据不相同,这样就保证了每一个线程里面只有一个Looper对象。其实它在不同的线程中,都各自维护了一个数组,是set的时候,就将值存储在了对应线程的数组中,在get的时候,就根据线程,从不同线程维护的数组中,根据索引值取出存储的数据。所以同一个ThreadLocal对象,在不同的线程中,所存取的值也是不同的。

三、MessageQueue的工作原理

消息队列虽然称为队列,但是其实他底层是维护的一个单链表的数据结构,来维护消息列表。他的重点方法有两个一个是enqueueMessage(Message msg,long when),另一个是next()

enqueueMessage( Message msg,long when ):它的主要操作,就是往单链表中插入消息
boolean enqueueMessage(Message msg, long when) {
        ...//省略的代码
        synchronized (this) {
          
	    ...
           
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;//如果当前保存的mMessages是null的,意思就是当前队列里面没有message,就将该message作为链表的第一个
                mMessages = msg;
                needWake = mBlocked;
            } else {           
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;//说明当前链表中有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;
    }

Message  next():该方法实现了一个无限循环,如果消息队列中没有消息,那么next方法就会一直阻塞在这里,当有新的消息到来的时候,它会返回这条消息,并且将其从单链表中移除

四、Looper的工作原理

1、Looper的初始化
在它的构造方法中,会创建一个MessageQueue,然后将当前线程的对象保存起来,它的构造方法是私有的,所以我们只能通过它提供的Looper.prepare()方法,去创建它。如果我们在子线程中创建了一个Looper,要记住在所有事情处理完成之后,一定要调用Looper的quit方法,来终止消息循环,否则的话这个子线程会一直处于等待状态

2、Looper的loop方法
该方法是Looper的最重要的方法,它实现了不断的从MessageQueue中取出消息,它内部是一个死循环,会通过调用MessageQueue的next方法,去取消息,然后通过
    msg.target.dispatchMessage(msg);

方法,拿到这个msg的发送对象,然后msg就又回到了发送msg的handler的dispathMessage方法来处理了,不同的是sendMsg的方法是可以在子线程中执行的,然而dispathMessage方法,是运行在创建Handler的Looper中执行的。也就是说,如果是主线程的Handler,那么sendMessage方法在子线程中调用,往Handler的MessageQueue中插入了一条消息,然后Looper的loop方法从消息队列中取到了这条消息,然后又通过这个msg,获取到他的发送对象的handler,将msg传递给这个handler的运行在主线程的dispathMessage方法。这样就是实现了子线程发送消息,主线程处理消息的机制

五、Handler的工作流程

首先,我们在使用Handler前,需要注意一点就是,初始化Handler的时候,会调用Looper.myLooper()方法来获取到当前线程的Looper对象,这个对象是通过ThreadLocal来存储的,在不同的线程中都存储了不同的Looper对象。如果当前线程没有Looper对象的话,就需要先调用Looper.prepare()方法,在当前先中创建一个Looper对象。
1、通过sendMessage或者post发送消息
post系列方法其实底层也是调用的sendMessage的方法来实现的,
public final boolean post(Runnable r)
 {
       return  sendMessageDelayed(getPostMessage(r), 0);
  }
底层将post方法传入的Runnable对象,通过getPostMessage方法,封装成了一个Message对象,将Runnable对象设置给了msg的callback变量
 private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
 }
然后,不管是sendMessage方法还是post方法,其实都是往MessageQueue里面插入了一条msg

2、插入消息之后,就需要通过Looper的loop方法,来将MessageQueue中的消息取出,再交给Hnadler的dispathMessage(Message msg)方法
Looper的loop方法实现大概如下伪代码
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; //从当前的looper对象中拿到MessageQueue对象
        ....
        for (;;) {//开启一个死循环,无限从消息队列中读取消息
            Message msg = queue.next(); 
            if (msg == null) {
              //如果消息是null的,就return
                return;
            }
            //消息不为空,就获取到这条消息的发送者handler,然后调用他的dispatchMessage(msg)方法
            msg.target.dispatchMessage(msg)
         ....
        }
    }

3、当looper从消息队列中取到消息之后,msg又回到了handler的dispathMessage这个方法中
其实这个方法做的事情很简单
public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);//如果是通过post方法发送的消息,就会调用handleCallback方法
        } else {
            if (mCallback != null) {//如果有给这个Handler设置监听回调的话,就调用监听的handleMessage方法
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);//说明没有给handler设置监听回调,那么就调用handler的handleMessage方法,
        }
   }

通过msg的一个分发过程,msg就又回到了我们的代码中,不同的是,现在的dispatchMessage是运行在主线程中的了

六、Handler使用的注意事项

通过上面的解析我们已经清楚的知道了整个Handler的运行机制,但是还是有几点需要注意的
1、Handler使用前,必须有一个Looper对象,那为什么我们在主线程中使用没有调用Looper.prepare()方法呢?这是因为应用在入口方法main方法中,系统就已经调用了Looper.prepareMainLooper()来创建了主线程的Looper对象

2、在Handler有了Looper对象之后,我们还一定要记得调用Looper.loop方法来开启Looper的轮询,这一步主线程同样帮我们做了。也就是在一个子线程中,我们想创建这个子线程的Handler,我们需要先调用Looper.prepare()方法创建一个当前线程的Looper对象,然后new这个Handler的对象,再调用Looper.loop()方法,开启Looper的循环过程

3、在子线程中使用Looper.loop方法之后,一定要记得处理完所有事情之后,调用looper对象的quit方法,退出循环,不然这个子线程会一直阻塞

七、主线程中为什么不会因为Looper.loop()方法的死循环导致阻塞

在应用程序的启动类,ActivityThread的main方法中,我们创建了主线程了Looper,并且调用了loop方法,那么为什么不会出现阻塞呢?这是因为其实ActivityThread这个应用的启动类的main方法,其实就是做了消息循环,它存在一个内部类H,继承了Handler,负责处理其他地方发送给主线程的消息,比如Activity的生命周期方法、你的所有点击事件、每16ms一次的UI刷新消息等等,所以主线程虽然有等待的过程,但是并不会因为Looper一直阻塞。当没有消息处理的时候,就会处于休眠状态。


最后,因为时间和个人水平有限,所以并没有深入太多Handler机制的细节之处,从源码处对大致流程和实现原理进行了一些解析。必定存在许多不足之处,请各位指正。

猜你喜欢

转载自blog.csdn.net/qqchenjian318/article/details/54601670
今日推荐