欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android之Handler消息机制——深入理解 Looper、Handler、Message、MessageQueue

程序员文章站 2022-03-08 14:08:57
...

序言

handler是我们日常编码中经常使用的一个类,通常用来由子线程转到主线程,或子线程与子线程之前的通信(消息传递),那么什么是Looper呢?什么是MessageQueuene呢?不要着急,我们一步一步看!

1.示例

一般我们编码最基本的常识就是,不能在主线程执行耗时操作(如网络请求、读取数据、数据库读写、io流操作等等),必须创建一个子线程去执行,如以下示例:

   //运行在子线程
        Thread twoThread = new Thread(new Runnable() {
            @Override
            public void run() {
                Message message = mHandler.obtainMessage();
                message.obj = "handler";
                mHandler.sendMessage(message);
                Log.i("TAG" , "当前线程twoThread:" + Thread.currentThread().getName());
            }
        });


        twoThread.setName("thread#2");
        twoThread.start();

耗时操作可以放在run方法中去执行,并且可以设定指定的线程名,但是我们执行完耗时操作拿到数据之后,需要回到主线程去更新UI,但是子线程不能更新UI,所以我们需要转到主线程去进行UI更新操作,就需要用到handler,如以下示例:

 Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

我们在子线程中获取到数据之后,通过sendMessage来发送数据,然后在handler中接收数据进行处理,基本的使用就完成了,但是我们仅仅知道使用是不够的,我们要了解其中的原理,并且能够在未来的编码中使用这种模式。

到这里我们就有一个问题了,为什么handler能够接收子线程发送的Message?我们可以通过源码解析来进行分析!

2.源码解析

1.Looper

一般looper有两个常用的方法Looper.prepare()和Looper.loop()

Looper.prepare():用来创建一个Looper对象,并把Looper对象存储到sThreadLocal对象中

Looper.loop():用来轮询,假如有新的消息,就将新的消息添加到消息队列中;假如没有消息,就阻塞线程

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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));
}
  • 在prepare()方法中,先创建了一个ThreadLocal对象,相当于一个数组或者一个HashMap,用来存储当前线程的消息,使用ThreadLocal的好处是可以保证当前拿到的消息是这个线程的消息,可以避免消息错乱!
  • 假如sThreadLocal已经有Looper对象了,将会报错!因为已经创建过的Looper对象不能重复添加,用来保证唯一性!这也说明了prepare()方法只能调用一次!
  • 将创建的looper对象添加到sThreadLocal中

接下来我们看一下创建Looper的方法

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mRun = true;
        mThread = Thread.currentThread();
}
  • mQueue:消息队列,用来保存发送的消息
  • mRun :是否运行
  • mThread:当前所在的线程

创建完Looper之后,我们会调用Looper.loop()方法,loop()方法重点代码如下:

public static void loop() {
    //获取Looper对象
    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;
    //开始进行轮询
    for (;;) {
        //遍历所有的消息
        Message msg = queue.next(); // might block
        //假如没有消息,就进行阻塞
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        /* 
         * 如果有消息了,就通过dispatchMessage()方法进行发送消息,消息将在dispatchMessage()中 
         * 进行处理
         */
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        //释放资源
        msg.recycleUnchecked();
    }
}

我们一步一步看,首先获取Looper对象,那么Looper对象从哪里来呢?我们还记得在调用Looper.prepare()方法时创建了一个Looper对象,并把它set到sThreadLocal中,那么我们的Looper对象也很有可能是在sThreadLocal中取的,我们看一下myLooper()方法;

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

果然如此!上面的注释已经写得很明白了,我们总结一下loop方法到底做了什么!

(1)获取Looper对象,并且从消息队列获取消息

(2)无限循环消息,如果没有新的消息,就阻塞线程;如果有新的消息,就通过dispatchMessage()方法处理

(3)释放资源

msg.target返回的是一个handler对象,这个时候,我们的handler和message已经开始产生关联了!接下来就到我们的主角Handler上场了!

2.Handler

回到最开始我们的示例代码

  Message message = mHandler.obtainMessage();
  message.obj = "handler";
  mHandler.sendMessage(message);

我们从子线程转到主线程时,是通过handler来传输消息,首先获取了Message的单例对象,通过sendMessage方法来发送,那么最重要的肯定是sendMessage方法了

   public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

    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);
    }

sendMessage()方法最后调用enqueueMessage()方法来解析数据

 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

我们为msg.target赋了值,在以上我们的Looper.loop()方法最后处理是调用msg.target.dispatchMessage(msg),在sendMessage时,msg.target被赋值!接下来我们看一下dispatchMessage()方法

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

我们可以看到很熟悉的一个方法handlerMessage()

public void handleMessage(Message msg) {
}

handlerMessage()竟然是一个空方法,仔细一想确实,我们看下面一串代码就知道了

 Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

handlerMessage()方法是用来给我们实现的,在handler中重写父类方法,拿到Message来进行接下来的操作!

我们再回头看一下handler的构造方法

public Handler() {
        this(null, false);
}
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来传递消息,当然,一定要调用Lopper.prepare()方法来创建Looper!完整的消息传递机制就显而易见了!

总结

(1)开启线程,调用Looper.prepare()方法创建一个Looper,Looper.prepare()只能调用一次,同时将创建一个MessageQueue(消息队列),用来存储消息

(2)调用Looper.loop()方法来获取Lopper对象和MessageQueue消息队列,并开启无限循环,读取消息队列的消息,如果消息队列里没有消息,则进行阻塞;如果有新的消息,则通过dispatchMessage传递给handler

(3)创建handler接收子线程发送过来的消息,通过dispatchMessage()方法拿到Message,通过重写父类方法handlerMessage()最终拿到数据