Android手写Handler框架

       Handler消息机制几乎是现在android面试中出现概率很高的一个问题,当然不建议大家去把这个过程背下来,有经验的面试官,很可能随便一问就问出来,所以连接handler机制的原理是非常重要的,这篇文章从handler的源码分析开始,顺便着手写一个handler简易框架,也顺便复习下,因为公司搬迁,我也要准备出去找工作了。(强调一句,不要背面试题,一定要知道原理,这样你才能走的更远)


   我们先看一下经典的handler的使用方法,在子线程发送消息到主线程,在主线程更新UI。

//uiHandler在主线程中创建,所以自动绑定主线程
private Handler uiHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what){
            case 1:
                System.out.println("handleMessage thread id " + Thread.currentThread().getId());

                break;
        }
    }
};
发送消息

new Thread(new Runnable() {
    @Override
    public void run() {
        // 发送 Message1
        Message message1 = new Message();
        uiHandler.sendMessageDelayed(message1, 1000);
        // 发送 Message2
        Message message2 = new Message();
        uiHandler.sendMessageDelayed(message2,1000);
    }
}).start();

先过一遍简单的流程。从发送消息开始

uiHandler.sendEmptyMessage(1);
进去

public final boolean sendEmptyMessage(int what)
{
    return sendEmptyMessageDelayed(what, 0);
}
继续

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

继续

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
继续下一步

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (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);
}


最终都是走向了(从字面翻译就应该知道,发送的消息最终都入列了)
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);
}
这里我们就要进入了messageQueue里边,我们找到enqueueMessage方法

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {  //判断target
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {   //判断消息是否在使用,避免重复发送消息
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {
        if (mQuitting) {	//给消息加锁,判断是否被放弃
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }

        msg.markInUse();   //给消息加上标签,正在使用
        msg.when = when; 
        Message p = mMessages; //mMessages此时为空,因为之前是没有被赋值的
        boolean needWake;    
        if (p == null || when == 0 || when < p.when)
            //第一次添加消息进入队列,此时不会走下面的else里边方法
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } 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;
            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;
}

上面是真个的代码,我们一行行的分析

当发送第一条消息的时候,是把消息添加到队列中,到了这里我们先不管了,此时在消息队列中的消息是如下。


此时我们发送第二条消息,此时仍然走到了上面将消息添加到消息队列中


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;
            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;
        }
此时走的是这里里面,因为上一次的p不等于null,这段代码主要是利用链表结构,每一条消息指向下一条消息,便于快速的删除,这上面的when主要是发送消息的时间,是否延迟,这里是计算数据插入的地方,根据时间来判断。


此时如果继续发送消息,handler.sendMessage(),其实是将我们的message发送的消息,采用链表的方式,按照时间的方式(这个时间是根据延时还有系统时间来判断的),添加到了messageQueue中。


这里有个经常问的面试题:

为什么MessageQueue中的消息队列采用链表的模式去存储消息?为什么不用数组?

回答这个问题的时候,我们首先要回答链表跟数组的优缺点。

比如说,链表查询比较慢,但是增删比较快,数组查询快,增删慢,在android系统中使用消息机制,为了让消息能够按时发送,必须要保证消息能够快速的到达,由于我们的消息是不断的去轮寻取出的,是按照时间顺序一条条的读取,所以对于删除或者添加消息速度要求比较高,刚好链表的数据结构符合我们的要求,使用数组这种结构会导致所有的数据跟着改变,时效性太慢。这个语言组织起来还是需要自己去处理的


接下来,我从另外一条路开始分析,我们看下handler的初始化。

new Handler()
点进去

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构造方法中获取了一个

mLooper对象,然后根据mLooper去获取mQueue对象。

看下

mLooper = Looper.myLooper();
继续

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
这里我们用一个sThreadLocal对象去get一个什么东西,点进去看下

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}
1.上面获取当前线程。

2.根据线程去获取ThreadLocalMap

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
看下ThreadLocalMap是什么

/**
 * ThreadLocalMap is a customized hash map suitable only for
 * maintaining thread local values. No operations are exported
 * outside of the ThreadLocal class. The class is package private to
 * allow declaration of fields in class Thread.  To help deal with
 * very large and long-lived usages, the hash table entries use
 * WeakReferences for keys. However, since reference queues are not
 * used, stale entries are guaranteed to be removed only when
 * the table starts running out of space.
 */
static class ThreadLocalMap {。。。}
百度翻译:
threadlocalmap是一个定制的哈希映射只适合维护线程局部变量的值。没有操作出口的ThreadLocal类之外。类是包私有的,允许在类线程中声明字段。协助处理非常大的和长期的使用,哈希表的条目使用弱引用使用钥匙。但是,由于不使用引用队列,所以只有在表开始耗尽空间时才保证删除过期条目。

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
看下threadLocals

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
这个线程ThreadLocal值有关。这个集合是由ThreadLocal类保持

上面的翻译看的还不是很明白这个东西到底是什么,实际上翻译起来就是线程本地集合,这个东西到底有设么用呢?

我们必须要从整个的ThreadLocal看起,我们看下set的方法

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

这里看出来,在这个ThreadLocalMap集合中存储的是ThreadLocal与value(其实是looper)

ThreadLocal提供了set和get访问器用来访问与当前线程相关联的线程局部变量。

可以从ThreadLocal的get函数中看出来,其中getmap函数是用t作为参数,这里t就是当前执行的线程

从而得知,get函数就是从当前线程threadlocalmap中取出当前线程对应的变量的副本【注意,变量是保存在线程中的,而不是保存在ThreadLocal变量中】。当前线程中,有一个变量引用名字是threadLocals,这个引用是在ThreadLocal类中createmap函数内初始化的。每个线程都有一个这样的threadLocals引用的ThreadLocalMap,以ThreadLocal和ThreadLocal对象声明的变量类型作为参数。这样,我们所使用的ThreadLocal变量的实际数据,通过get函数取值的时候,就是通过取出Thread中threadLocals引用的map,然后从这个map中根据当前threadLocal作为参数,取出数据。

其实也就是说,我们使用ThreadLocal保证一个线程的数据,还有对应的Looper,当我们需要使用这个线程的时候,可以直接获取到这个线程对应的数据。这样能够保证一个线程数据一一对应,往前看,因为一个线程的数据对应一个Looper,因为我们是在主线程中创建的handler,所以我们在handler中获取的这个looper也是我们在主线程中存储的Looper,因为mQueue是Looper中的一个引用,在Loop方法里面

public static void loop() {;;;}
不断的去轮寻队列里边的消息,使用

msg.target.dispatchMessage(msg);
去分发消息,我们创建handler重写的的那个handlerMessage方法就能收到消息了



===================================分割线=============================================

上面就是简单的分析handler的大概过程,接下来,我们自己去试下简单的handler机制


首先说下需求:

我们要自己实现handler,使用自己的handler能够处理消息机制,并且在子线程不能更新UI。

我们使用HandlerText来作为程序的入口,代码只是模拟这部分功能。

需要达到如下效果:


上面的代码能够看到,在子线程更新UI会报错,但是在主线程中更新UI是可以的,如果想要在子线程中更新UI就必须使用android提供的消息机制,下面代码是使用我们自定义的消息机制来实现在子线程中更新UI。






代码,就不做过多的注释,我们只是截取了部分功能,比如实际消息机制中消息的插入是需要计算时间的,在我们的demo中是没有的,在很多地方需要进行判断,在代码中我们也是省略了,下面的代码只是为你了解handler机制提供了参考。

程序入口:

 *  @项目名:  Zhifubao 
 *  @包名:    com.huhai.zhifubao
 *  @文件名:   HandlerText
 *  @创建者:   huhai
 *  @创建时间:  2017/11/10 14:59
 *  @描述:    详细代码可见ActivityThread
 */

import android.os.Looper;
import android.os.Trace;
import android.util.Log;
import android.util.LogPrinter;

public class HandlerText {


    public static void  main(String[] args){

        MyLooper.prepare();
        MyActivityThread thread = new MyActivityThread();
        thread.attach(false);
        MyLooper.loop();
  }
}

MyActivityThread

 *  @项目名:  Zhifubao 
 *  @包名:    com.huhai.zhifubao.HandlerText
 *  @文件名:   MyActivityThread
 *  @创建者:   huhai
 *  @创建时间:  2017/11/10 15:05
 *  @描述:    TODO
 */

import java.util.logging.Handler;

import static android.R.id.message;

public class MyActivityThread {


    public void attach(boolean b) {
        MyActivity myActivity=new MyActivity();
        myActivity.onCreat();
    }

}

MyActivity:(模拟了实际真是的activity,写了一个oncreat方法,一个onResume方法)

*  @项目名:  Zhifubao 
 *  @包名:    com.huhai.zhifubao.HandlerText
 *  @文件名:   MyActivity
 *  @创建者:   huhai
 *  @创建时间:  2017/11/10 15:05
 *  @描述:    TODO
 */

import android.util.Log;
import android.widget.TextView;


public class MyActivity {
    private static final String TAG = "MyActivity";

    private MyTextView mytextview=new MyTextView();
    private H mH=new H();

    public void onCreat(){
        //主线程更新UI
        System.out.println("执行onCreat");
        mytextview.setText();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                    //子线程更新UI
                   // mytextview.setText();
                    MyMessage message=new MyMessage();
                    message.obj="消息";
                    mH.sendMessage(message);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }

    public void onResume(){
        System.out.println("onResume");
    }

    private class  H extends MyHandler {

        public  void     handlerMessage(MyMessage message){
            String data= (String) message.obj;
            System.out.println("handler中更新了"+data);
            mytextview.setText();
        }
    }
}
MyTextView:

import android.os.Looper;

import static android.os.Looper.getMainLooper;


public class MyTextView  {
    private static final String TAG = "MyTextView";

    private  Thread mThread;

    public MyTextView() {

        mThread = Thread.currentThread();
    }

    public void  setText(){
        //先检查是否在主线程
        try {
            checkThread();
            System.out.println("更新了UI");
        } catch (Exception e) {
            e.printStackTrace();

        }
    }

    private void checkThread() throws Exception{
        if (mThread!=Thread.currentThread()){
            throw  new Exception("Only the original thread that created a view hierarchy can touch its views.");
        }
    }
    
}

MyLooper:

*  @项目名:  Zhifubao 
 *  @包名:    com.huhai.zhifubao.HandlerText
 *  @文件名:   MyLooper
 *  @创建者:   huhai
 *  @创建时间:  2017/11/10 15:04
 *  @描述:    TODO
 */


public class MyLooper {
    static final ThreadLocal<MyLooper> sThreadLocal = new ThreadLocal<MyLooper>();
    public MyMessageQueue mQueue;

    public MyLooper() {
        mQueue = new MyMessageQueue();
    }

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

    public static void loop() {
        MyLooper myLooper=getLooper();
        for (;;){
            MyMessageQueue queue= myLooper.mQueue;
            MyMessage message= queue.next();
            if (message==null){
                return;
            }
            message.target.handlerMessage(message);
        }
    }

    private static MyLooper getLooper() {
        return sThreadLocal.get();
    }

    public static MyLooper myLooper() {
        return sThreadLocal.get();
    }
}
MyHandler:

import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.os.SystemClock;
import android.util.Log;

import static android.R.attr.y;

public class MyHandler {

    public  MyMessageQueue mQueue;

    public MyHandler()  {
        MyLooper looper=MyLooper.myLooper();

        mQueue=looper.mQueue;
    }

    public void handlerMessage(MyMessage message){

    };

    public void sendMessage(MyMessage message) {

        sendMessageDelayed(message,0);

    }


    public final boolean sendMessageDelayed(MyMessage msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
       return sendMessageAtTime(msg, delayMillis);
    }

    public boolean sendMessageAtTime(MyMessage msg, long uptimeMillis) {
        MyMessageQueue queue = mQueue;
        if (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(MyMessageQueue queue, MyMessage msg, long uptimeMillis) {
        msg.target = this;

        return queue.enqueueMessage(msg, uptimeMillis);
    }
}

MyMessageQueue:(在消息队列里边消息的插入及后面的取出,都是直接拿的实际的源码,截取了关键的部分,省略了)

*  @项目名:  Zhifubao 
 *  @包名:    com.huhai.zhifubao.HandlerText
 *  @文件名:   MyMessageQueue
 *  @创建者:   huhai
 *  @创建时间:  2017/11/10 16:34
 *  @描述:    TODO
 */

import android.os.Binder;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;

import static android.content.ContentValues.TAG;

public class MyMessageQueue {

    private MyMessage mMessages;

    boolean enqueueMessage(MyMessage msg, long when) {
        synchronized (this) {
            msg.when = when;
            MyMessage 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 = msg;
            } 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.
                MyMessage prev;
                for (; ; ) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            return true;
        }
    }

    public MyMessage next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = 0;
                MyMessage prevMsg = null;
                MyMessage msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null);
                }
                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.

                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;

                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
            }
            nextPollTimeoutMillis = 0;
        }
    }




MyMessage:

 *  @项目名:  Zhifubao 
 *  @包名:    com.huhai.zhifubao.HandlerText
 *  @文件名:   MyMessage
 *  @创建者:   huhai
 *  @创建时间:  2017/11/10 15:04
 *  @描述:    TODO
 */

public class MyMessage {

    public  Object obj;

    public MyHandler target;
    public long      when;
    public MyMessage next;
}
大概消息机制就这么一个流程,大家想要了解其过程,把代码复制进去,跑一边,走一遍,消息机制其实就差不多了。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值