Android中Handler、MessageQueue、Looper三者的关系然后手写一套自己的Handler

版权声明:本文出自阿钟的博客,转载请注明出处: https://blog.csdn.net/a_zhon/article/details/79049089

这里写图片描述

时间总是比我们想象的过的要快,我们来不及抓住,让那些重要的时光走了一个又一个。

Handler、Looper、MessgaeQueue三者的分工:

  • handler 负责发送消息
  • Looper 负责轮询MessageQueue中的消息,并把消息回传给handler
  • MessageQueue 负责存储消息(先进先出)

Handler、Looper、MessgaeQueue三者的引用关系

  • Handler 中有MessageQueue对象、Looper对象
  • Looper 中有MessageQueue对象(和Handler中的是同一个对象)
  • MessageQueue 中有 Message(而Message中有Handler(target属性))

图解三者的关系:

这里写图片描述

现在我们从创建Handler到handlerMessge收到一条消息,对整个过程进行讲解:

一:创建一个Handler,做了哪些操作?Handler handler = new Handler();

public Handler(Callback callback, boolean async) {

        //省略若干源码...

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
    }
  • 重要的一个操作Looper.myLooper()获取当前线程绑定的Looper对象,为什么这么说呢?那就要通过Looper的源码来分析了。
  • mLooper.mQueue获取消息队列

二:Looper.myLooper(),获取当前线程绑定的Looper对象

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

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
  • sThreadLocal:内部维护着一个 ThreadLocalMap(类似HashMap)以key value形式存储
  • key:当前创建Looper对象的线程
  • value:Looper对象
  • 这里获取到Looper对象可能会是个空对象,因为我们要先调用Looper.prepare()对它进行初始化

Looper.prepare():为当前线程创建一个Looper对象

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));
    }
  • sThreadLocal.set(new Looper(quitAllowed)):这里就为我们创建了Looper对象并绑定到当前线程
  • 重点:第一次使用时 必须先调用Looper.prepare()进行初始化

MessageQueue的初始化又在哪里呢?当然是在Looper的构造函数中啦

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

到这里 三个对象的创建都已经完成了,上面说了使用Handler的时候必须先调用Looper.prepare()方法,但是我们平常使用的时候并没有调用它那为什么也是可以的呢?答案当然是:系统为你调用好了、那系统又在哪里调用的呢?

系统在程序启动的时候默认创建了一个线程 也就是主线程(UI线程):通过查看ActivityThread.java(这个类并不是一个线程只是一个简单的java类)源码中的main()函数,可以看到如下代码:

public static void main(String[] args) {

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();
        CloseGuard.setEnabled(false);

        //省略若干代码....

        Process.setArgV0("<pre-initialized>");

         //初始化我们的主线程
        Looper.prepareMainLooper();

         //省略若干代码....

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

         //省略若干代码....

        //进行无限死循环,当然在loop内部都是通过 线程的等待唤醒机制进行死循环的(节约资源)
        Looper.loop();

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

讲到这里相信你已经理解到了这三者的相互关系,重要的操作都是在Looper中;那继续分析最后一步handler.sendMessage(msg)发送一条消息:

handler.sendMessage(msg)
//调用上面这行代码,我们继续跟踪源码,发现最终调用了如下代码
//将消息添加进MessageQueue中,并唤醒Looper.loop()中的等待 把消息取出。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

到这里Handler的整个工作的过程就一目了然了:

handler发送一条消息——>放入MessageQueue中——>Looper.loop()死循环 取出消息 回传给handler

下面我们在自己创建一个线程中使用Handler,你就可以很清楚的理解上面的Looper

new Thread(new Runnable() {
    @Override
    public void run() {
        Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
            }
        };
    }
}).start();

这里写图片描述

可以看到:系统抛出了一个RuntimeException 提示需要调用Looper.prepare()
那对代码改造一下:

new Thread(new Runnable() {
    @Override
    public void run() {
        Looper.prepare();
        Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
            }
        };
        Looper.loop();
    }
}).start();

创建好了handler 最后肯定还是要调用Looper.loop()的,不然消息只是放进去了并没有取出。

疑问一:为什么在主线程中系统调用了Looper.loop()对主线程进行死循环,那Activity的生命周期或者其他的UI更新操作是怎么进行的呢?这里还是要继续查看ActivityThread.java这个类的main()函数源码进行解释:

public static void main(String[] args) {

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();
        CloseGuard.setEnabled(false);

        //省略若干代码....

        Process.setArgV0("<pre-initialized>");

         //初始化我们的主线程
        Looper.prepareMainLooper();

         //省略若干代码....
         //获取更新ui操作或者其他操作的handler
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

         //省略若干代码....

        //进行无限死循环,当然在loop内部都是通过 线程的等待唤醒机制进行死循环的(节约资源)
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
  1. 从上面源码中可以看到 首先初始化了一个sMainThreadHandlerHandler对象,那这个对象有什么用呢,下面在告诉你。
  2. 初始化完sMainThreadHandler后执行了Looper.loop();对主线程进行死循环
  3. 最后一行代码就比较骚了我的理解大概就是:如果Looper.loop() 退出了死循环,那么主线程就直接奔溃了、那么你的程序也就退出了。所以主线程是从应用启动一直运行到你程序退出的一个线程。
  4. 我们尝试调用如下代码:
 //将主线程退出
 Looper.getMainLooper().quit();

与我们预想的一模一样
这里写图片描述

  1. 我们都知道手动创建一个线程,当run函数中的代码执行完毕那么线程就会自动退出的,那我们的主线程是肯定是不可以退出的(上面已经演示过了),所以主线程最后进行了Looper.loop();让线程进入一个死循环这样线程就一直在运行了。

那么问题来了:Activity中的生命周期函数是怎么被调用的呢?

  1. 当然是使用在死循环之前创建的sMainThreadHandler对象了,它是ActivityThread中的一个内部类class H extends Handler{},Activity的一些生命周期回调函数都是通过这个来进行回调的。我们来查看一下这个H类里的handlerMessage(Message msg)函数
 public void handleMessage(Message msg) {

            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
                case RELAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
                    ActivityClientRecord r = (ActivityClientRecord)msg.obj;
                    handleRelaunchActivity(r);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
                case PAUSE_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                    SomeArgs args = (SomeArgs) msg.obj;
                    handlePauseActivity((IBinder) args.arg1, false,
                            (args.argi1 & USER_LEAVING) != 0, args.argi2,
                            (args.argi1 & DONT_REPORT) != 0, args.argi3);
                    maybeSnapshot();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
                case PAUSE_ACTIVITY_FINISHING: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                    SomeArgs args = (SomeArgs) msg.obj;
                    handlePauseActivity((IBinder) args.arg1, true, (args.argi1 & USER_LEAVING) != 0,
                            args.argi2, (args.argi1 & DONT_REPORT) != 0, args.argi3);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;

            // 截取了部分代码,感兴趣的可以查看下源码

    }

总结:主线程进行UI操作都是通过这个sMainThreadHandler 发送消息 通知界面进行相应的操作

疑问二:为什么在子线程发送一条消息,消息就在主线程中了呢?

原因:创建一个Handler的时候 同时也创建好了Looper 创建Looper的时候又创建好了MeessageQueue 这些都是在主线程中创建的(Handler一般都在主线程进行初始化),这样当你在其他任何线程中发送一条消息的时候,消息都是在MeessageQueue中,Looper循环从MeessageQueue中取出消息在调用msg.target.dispatchMessage(msg);这样消息就回到了创建Handler的地方 也就是主线程中。(如有错误还望指出)

接下来就根据我们上面分析的内容来自己实现一套Handler:

需要创建的主要类:

  1. MyHandler
  2. MyMessage
  3. MyLooper
  4. MyMessageQueue

MyHandler 这里只给出了主要的实现代码,源码中其他的一些代码都已省略

public class MyHandler {
    /**
     * 消息拦截
     */
    private MyCallback mCallback;
    /**
     * 轮询器
     */
    private MyLooper looper;
    /**
     * 消息队列
     */
    private MyMessageQueue queue;

    public MyHandler() {
        this(null);
    }

    /**
     * handler 进行初始化
     * 1.获取当前线程所绑定的Looper对象
     */
    public MyHandler(MyCallback mCallback) {
        looper = MyLooper.myLooper();
        if (looper == null) {
            throw new RuntimeException("如果当前线程未初始化Looper对象,则需要调用Looper.prepare();");
        }
        this.mCallback = mCallback;
        queue = looper.queue;
    }

    /**
     * 处理消息
     */
    public void handleMessage(MyMessage msg) {

    }

    /**
     * 对消息进行分发
     */
    public void dispatchMessage(MyMessage msg) {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }

    /**
     * 发送一条消息
     */
    public final boolean sendMessage(MyMessage msg) {

        //...中间一系列函数调用,最终执行如下重点代码
        msg.target = this;
        return queue.enqueueMessage(msg);
    }

    /**
     * 用来拦截handler发送的消息
     */
    public interface MyCallback {
        boolean handleMessage(MyMessage msg);
    }
}

MyMessage 消息实体类

public class MyMessage {

    public int what;
    public int arg1;
    public int arg2;
    public Object obj;
    public MyHandler target;

    public MyHandler getTarget() {
        return target;
    }

    public void setTarget(MyHandler target) {
        this.target = target;
    }

    public void sendToTarget() {
        target.sendMessage(this);
    }
}

MyLooper 循环取消息

public class MyLooper {
    /**
     * ThreadLocal 内部维护着一个 ThreadLocalMap(类似HashMap)以key value形式存储
     * key:当前创建Looper对象的线程
     * value: Looper对象
     */
    static final ThreadLocal<MyLooper> sThreadLocal = new ThreadLocal<>();
    /**
     * 当前所在线程
     */
    private final Thread mThread;
    /**
     * 消息队列
     */
    public final MyMessageQueue queue;

    /*
     * 初始化MyLooper对象并绑定他所在的线程
     * 一个线程内部维护着同一个(Handler Looper MessageQueue)
     */
    public static void prepare() {
        sThreadLocal.set(new MyLooper());
    }

    /**
     * 初始化消息队列
     */
    private MyLooper() {
        queue = new MyMessageQueue();
        mThread = Thread.currentThread();
    }

    /**
     * 返回与当前线程关联的Looper对象
     * 返回null 则线程中没有初始化一个Looper对象
     */
    public static MyLooper myLooper() {
        return sThreadLocal.get();
    }

    public static void loop() {
        /**
         * 在Looper 源码 loop 函数中
         * <p>使用将消息发送回handler
         * <p>msg.target.dispatchMessage(msg);
         */
        MyLooper looper = myLooper();
        MyMessageQueue queue = looper.queue;
        for (; ; ) {
            MyMessage msg = queue.next();
            if (msg != null) {
                msg.target.dispatchMessage(msg);
            }
        }
    }
}

MyMessageQueu 存储消息

public class MyMessageQueue {

    private int position = 0;
    /**
     * 存储消息
     */
    private List<MyMessage> list = new ArrayList<>();

    /**
     * 添加一条消息
     *
     * @return 是否添加成功
     */
    public boolean enqueueMessage(MyMessage msg) {
        synchronized (this) {
            //省略落干源码
            list.add(msg);
        }
        return true;
    }

    /**
     * 获取最新的条消息
     */
    public MyMessage next() {
        synchronized (this) {
            if (list.size() > 0) {
                if (position <= list.size() - 1) {
                    MyMessage message = list.get(position);
                    position++;
                    return message;
                } else {
                    position = 0;
                    list.clear();
                }
            }
            return null;
        }
    }
}

本篇文章Demo下载

阅读更多

扫码向博主提问

Code-Porter

博客专家

非学,无以致疑;非问,无以广识
  • 擅长领域:
  • Android
  • ReactNativ
  • 微信小程序
去开通我的Chat快问

没有更多推荐了,返回首页