Handler机制原理----全解

Handler机制,我们就会想到,他有四个重要的对象组成,分别是:Handler,Message,MessageQueue,Looper.

那Handler是如何工作的呢?

例如,你想刷新主界面的TextView,无奈你不在主线程,此时你就会包装好Message,然后声明一个Handler,让Handler将你的Message送往主线程(Looper),Handler将你的Message送到主线程后,还需要排队等待,等轮到你的时候,主线程就会告诉Handler,这个Message可以处理了,你负责分发一下,于是,Handler将该Message分发到相应的回调或者handleMessage( ) 方法中,于是,你就在该方法中更新了UI。

下面,我们就分别介绍一下,这四个对象,以及Handler机制的运行原理吧。

一、Message(消息)

Message.class位于android.os.包中。Message的构造函数为无参构造方法,且只有一个构造方法;

那么首先,我们想要用Message这个对象,我们就得知道,如何用他,他既然是一个对象,那秉承万物皆对象的原则,Message的创建也分为以下几种:

方式1、Message message = new Message();
方式2、Message message = Message.obtain();
方式3、Message message = new Handler().obtainMessage();

 像是第一种,自然不必多说,这也是我们最常用的方式,通过无参构造,创建一个对象。

第二种,其实他是有八种创建方式的,分别如下:

static Message obtain()
static Message obtain(Message orig)
static Message obtain(Handler h)
static Message obtain(Handler h, Runnable callback)
static Message obtain(Handler h, int what)
static Message obtain(Handler h, int what, Object obj)
static Message obtain(Handler h, int what, int arg1, int arg2)
static Message obtain(Handler h, int what, int arg1, int arg2, Object obj)

即通过静态构造方法来创建

当然,通过这种方式创建的,都会走第一个方法,下面,我们看一下通过obtain创建的方式的源码:

public static final Object sPoolSync = new Object();    //同步锁对象
    private static Message sPool;                           //全局池消息实例
    
	/**
     * 从全局池返回一个新的消息实例,允许我们在许多情况下避免分配新对象。
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                //......
                return m;
            }
        }
        return new Message();
    }

通过此方式创建,进行了一个空对象的判断,如果不为空,则获取第一个Message对象,通过此种方式创建,节省了很多内存资源

像是其他几种,基本上都是方法的重载。

第三种方法,通过其无参构造的源码我们发现:

public final Message obtainMessage(){
    return Message.obtain(this);
}

他也是调用了obtain方法来创建的,

public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;
    return m;
}

 此处大家注意一下,有一个Handler,即target,这个对象,我们后面会详细的讲述,此处,只需要记住一下几个

  • Message有8个静态方法可以创建Message实例
  • Message有两个重要的成员变量,分别为target 和callback,一个是Handler,一个是Runnable。
  • Message有4个公开变量what、arg1、arg2、obj 可以存储消息进行传递
  • Message还有一个包间成员变量next,它是Message类型,后面会使用到,知道有这个next就行

二、Handler

Handler.class 位于android.os包中,google给的解释就是消息的接收及处理者。

他的构造方法如下:

Handler()
Handler(Callback callback)
Handler(boolean async)
Handler(Callback callback, boolean async)
Handler(Looper looper)
Handler(Looper looper, Callback callback)
Handler(Looper looper, Callback callback, boolean async)

我们先来看第四个构造方法源码:

	public Handler(Callback callback, boolean async) {
        //......
        mLooper = Looper.myLooper();  //返回与当前线程关联的Looper对象,在后面Looper会讲到
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;  //返回Looper对象的消息队列,在后面MessageQueue会讲到
        mCallback = callback;  //接口回调
        mAsynchronous = async; //是否异步
    }
    
	public interface Callback {
        public boolean handleMessage(Message msg); //这个函数大家都很熟悉了,暂不细说,总之都知道是用来回调消息的
    }

此构造方法中做了三件事,

  • 获取当前线程的looper对象
  • 如果当前Looper不为空,则返回Looper对象的消息队列,并将消息队列复制给mQueue;
  • 利用CallBack来处理消息回调

我们结合实际的例子来解释一下Handler底层的工作原理

private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //... 更新UI
        }
    };

这就是我们经常使用的Handler最简化的案例,当然,这样创建,容易引起内存泄露,所以,最好还是使用弱引用的方式,对于内存泄露问题,我们后面专门一片博客再来讲述吧

先看这里

当你创建Hanlder时,底层做的事情如下

  • 拿到mHandler所在线程的Looper,当前mHandler是在Activity中创建的,很明显,当前的线程就是主线程,所以 mHandler的成员变量mLooper = Looper.myLooper(),此处就已经将当前的主线程Looper赋值过去了。
  • 紧接着,判断mLooper 是否为空,明显不为空,所以又会将主线程的消息队列赋值给mQueue。告诉Handler,你要是有消息,就送到这个消息队列中来,我(Looper)会一个个按顺序处理,处理完后我就会告诉你,你再处理。

很明显,Handler并不是最终的决策者,每一次的Handler的处理对象,必然需要经过Looper,所以Looper才是Handler的主体,也是最重要部分,并且由源码可以看出来,每一个Handler只能对应一个Looper

 

说到Handler,我们肯定会想到sendMessage()方法,那么现在,我们就来看看SendMessage他们之间的调用方式

Handler机制原理----全解_第1张图片

 

通过这张图,我们可以看出,不管是什么样的途径发送消息,最终都会通过sendMessageAtTime方法,通过enqueueMessage方法来发送消息,

	public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;  //获得当前的消息队列
        if (queue == null) {   //若是在创建Handler时没有指定Looper,就不会有对应的消息队列queue ,自然就会为null
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis); 
    }
    
	private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

此处,msg.target = this,由此声明,此处的target就是当前的Handler进行处理的,为了方便后面的Looper进行调用

那么发送消息之后,交给了Looper进行处理了,那Handler是如何接收到Looper通知进行处理消息的呢?这时候,我们看源码就知道他用了一个disPathMessage()方法进行处理消息

	/**
     * 在这里处理系统消息
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

	/**
     * 子类必须实现这个来接收消息
     */
    public void handleMessage(Message msg) {
    }

当Looper处理完Message后,会使用到Message的target,即上面说到的target,即发送消息的那个Handler,Looper会调用Handler的dispatchMessage()方法分发消息,所以前面在enqueueMessage()发送消息的时候,为什么非得指明Message的target就是这个道理。

回到dispatchMessage()这个方法:
1、首先会判断Message的callback是否为空,此处的callback就是前面我们在Message中说到的,在静态方法创建Message时,可以指定的callback,若不为空,则将结果回调到callback中;
2、若Handler的mCallback 不为空,也一样的道理。
3、平时我们都没有传入这个callback,而是直接实现handleMessage()这个方法,在这个方法中处理更新UI任务。

以上就是Handler发送和接收消息的基本过程:把消息发送到队列—>然后喝茶等待—>接收消息—>分发消息—>在回调中处理。

三、MessageQueue

Handler发送消息会调用MessageQueue的enqueueMessage()方法

源码如下:

	boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {  //判断msg的所属Handler
            throw new IllegalArgumentException("Message must have a target.");
        }
        //......
        synchronized (this) {  //因为是队列,有先后之分,所以用了同步机制
            //......
            msg.when = when;
            Message p = mMessages;  //对列中排在最后的那个Message 
           //......
            if (p == null || when == 0 || when < p.when) {    
           	    //若队列为空,或者等待时间为0,或者比前面那位的等待时间要短,就插队
                msg.next = p;  //此处的next就是前面我们在Message提到的,指向队列的下一个结点
                mMessages = msg;
                //......
            } else { 
                //......
                Message prev;
                for (;;) {     
                    //此处for循环是为了取出一个空的或者when比当前Message长的一个消息,然后进行插入
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    //......
                }
                msg.next = p;       // 置换插入
                prev.next = msg;  // 置换插入
            }
		    //......
        }
        return true;
    }

以上就是消息队列插入消息的过程原理,通过单向链表的数据结构来存储消息。既然有了插入消息的方法供Handler插入消息,那么应该有对应的取出消息的方法,供Looper调用取出消息处理,它就是Message next()这个方法

 

四、Looper

Looper在Handler机制中扮演着关键的一环,他是循环处理消息的发动机,永不停息(永动机),它不断的从消息队列中取出的消息,处理,然后分发处理事件。每个线程都可以且只能绑定一个Looper。主线程之所以能处理消息,也是因为在APP启动时,在ActivityThread中的main()方法中就已经启动了Looper循环

我们先看一下Looper在ActivityThread().main()方法中的启动流程

main()方法源码如下:

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");//跟踪事件写入系统跟踪缓存区
        SamplingProfilerIntegration.start();//“抽样分析集成工具”,是集成Dalvik抽样分析器框架

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);//CloseGuard 是一种资源清理机制,资源应该被显式关闭清理。setEnabled()明显就是使其失效,不过会在debug builds的时候重现变得有效

        Environment.initForCurrentUser();//此处初始化用户使用环境

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());//给lib库设置事件打印记者

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);//确保可信证书存储在正确的地方以便于查找CA证书。

        Process.setArgV0("");//Process是管理系统流程的工具。

        Looper.prepareMainLooper();

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

        if (sMainThreadHandler == null) {//判断当前主线程的Handler是否为空。
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        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();
        }
    }

    public static void prepare() {
        prepare(true);
    }

    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只能有一个主线程。

Looper.loop();

最重要的一句来了,前面Looper.prepareMainLooper()开启主线程,而这里的loop()就是处理主线程程序的关键段,所有通过Handler传到主线程的Message都会缓存在Looper的消息队列Queue里面,loop()里有个for(;;)循环,不断的从消息队列里面拿出消息进行处理,然后就有了事件的信息交流啦。

下面来看Looper.loop()方法

	public static void loop() {
        final Looper me = myLooper();   //获得当前的Looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        
        final MessageQueue queue = me.mQueue;  //获取当前Looper的消息队列
        //......

        for (;;) {
            Message msg = queue.next();  //取出队头的消息
            if (msg == null) {
                // 如果消息为空,则跳过,继续执行下一个message
                return;
            }
            //......
            try {
                msg.target.dispatchMessage(msg);
                //......
            } finally {
               //......
            }
           //......
            msg.recycleUnchecked();  //回收可能正在使用的消息
        }
    }

由此可以看出,Looper的消息处理,无非就是循环的对消息进行取出,分发,处理,回收的过程

由此Handler机制可以简述为:

Handler将Message发送到Looper的消息队列中,即MessageQueue,等待Looper的循环读取Message,处理Message,然后调用Message的target,即附属的Handler的dispatchMessage()方法,将该消息回调到handleMessage()方法中,然后完成更新UI操作。

你可能感兴趣的:(Handler机制原理----全解)