彻底搞明白Android消息机制

简介:

Android中的消息机制主要是指Handler的运行机制,而Handler的运行又离不开Looper和MessageQueue。

MessageQueue

  • MessageQueue用来接收Hnadler发送过来的消息Message,其内部是用单链表的数据结构来存储消息列表的。
//MessageQueue.class
Message next() {
    ...
    for (;;) {
        ...
        synchronized (this) {
            ...
            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.
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }
            ...
        }
        ...
    }
}
  • MessageQueue在调用Looper的prepare方法时创建。
//Looper.class
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");
    }
    //将Looper存到当前线程的ThreadLocal中
    sThreadLocal.set(new Looper(quitAllowed));
}

private Looper(boolean quitAllowed) {
    //创建MessageQueue
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
  • MessageQueue的next方法是一个无限循环,除非调用quit方法才会停止
Message next() {
    ...
    for (;;) {
        ...
        synchronized (this) {
            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                //返回null,退出循环
                return null;
            }
        }
        ...
    }
    ...
}

void quit(boolean safe) {
    ...
    synchronized (this) {
        ...
        //这样MessageQueue就退出循环啦
        mQuitting = true;
        ...
    }
}

Looper

  • Looper用于从MessageQueue中取出待处理的消息,会调用MessageQueue的next方法来取消息。如果MessageQueue中没有消息,Looper就会一直阻塞等待,除非MessageQueue返回null才退出消息的循环监听。
  • 调用Looper的prepare方法后会创建MessageQueue和Looper。但需要调用Looper的loop方法才会开启消息循环,Looper才会开始循环监听MessageQueue中的消息
  • Looper取到消息后,会调用消息Message所对应的Handler的dispatchMessage方法来处理消息。这样Handler的dispatchMessage方法就会在Looper所在的线程中执行了。
public static void loop() {
    final Looper me = myLooper();
    //调用loop方法前,必须先通过prepare方法创建looper和消息队列MessageQueue
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {
        //调用MessageQueue的next方法取消息,因为next是个无限循环,所以loop方法也会阻塞
        Message msg = queue.next(); // might block
        //只有在调用MessageQueue的quit方法后MessageQueue才会返回null,才会退出循环监听
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        ...
        
        try {
            //msg.target就是Handler,这里就是调用Handler的dispatchMessage方法来处理消息。
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

        ...

    }
}

Handler

  • Looper获取到消息后,最后会交给Handler的dispatchMessage方法处理
//Handler.class
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
  • 首先看看Message的callback是否为null,不为null就通过handleCallback方法来处理消息。
private static void handleCallback(Message message) {
    message.callback.run();
}
  • Message的callback是个Runnable对象,是Handler的post方法传递的Runnable参数。
public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}
  • 如果callback参数为null,就检查mCallback参数是否为null。不为null就调用mCallback的handleMessage方法处理消息。mCallback是一个Callback接口
/**
 * Callback interface you can use when instantiating a Handler to avoid
 * having to implement your own subclass of Handler.
 *
 * @param msg A {@link android.os.Message Message} object
 * @return True if no further handling is desired
 */
public interface Callback {
    public boolean handleMessage(Message msg);
}
  • 如果我们在使用Handler时不想派生Handler的子类,就可以用Callback的方式
private Handler callbackHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {

        return false;
    }
});
  • 如果mCallback参数也为null,那就调用handleMessage方法处理消息,也就是我们用派生Handler子类的方式创建Handler时重写的handleMessage方法
//Handler.class
/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(Message msg) {
}
  • 用派生Handler子类的方式创建Handler
private static class MyHandler extends Handler {
    @Override
    public void handleMessage(Message message) {
        switch (message.what) {
            case MESSAGE_TAG:
                Log.d(Thread.currentThread().getName(),"receive message");
                break;
            default:

                break;
        }
    }
}

Handler为什么能切换线程?

一句话总结就是:Handler是线程间共享的一个对象,而Looper则是每个线程私有的。

怎么理解上面的说法呢?
  • Handler是共享的意思是,在需要切换的2个线程中都能访问我们创建的Handler对象,比如能调用Handler的sendMessage方法。
  • 比如下面的代码中myHandler变量是在HandlerActivity的onCreate方法中创建的,也就是在UI线程中。myHandler即可以在UI线程中调用,也可以在子线程中调用。
public class HandlerActivity extends Activity {

    public static final int MESSAGE_TAG = 0x01;
    private MyHandler myHandler;

    //通过派生Handler子类的方式创建我们的Handler
    private static class MyHandler extends Handler {
        @Override
        public void handleMessage(Message message) {
            switch (message.what) {
                case MESSAGE_TAG:
                    Log.d(Thread.currentThread().getName(),"receive message");
                    break;
                default:

                    break;
            }
        }
    }
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //创建Handler
        myHandler = new MyHandler();

        new Thread(new Runnable() {
            @Override
            public void run() {
                Message message = new Message();
                message.what = MESSAGE_TAG;
                //在子线程中调用UI线程创建的myHandler,发送消息
                myHandler.sendMessage(message);
            }
        }).start();
    }
}
  • 调用Handler发送消息以后怎么就把线程从子线程切换到UI线程了呢?这是因为每一个Handler在创建的时候都需要当前线程有Looper,而每个线程都会有自己的Looper
//Handler.class
public Handler() {
    this(null, false);
}

public Handler(Callback callback, boolean async) {
    ...
    //获取Looper,如果当前线程没有Looper就会抛异常
    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;
}

//Looper.class
//每个线程都有一个自己的ThreadLocal,用于存储当前线程中的数据
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static void prepare(boolean quitAllowed) {
    //每个线程只能有一个Looper
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //第一次调用Looper的prepare方法创建Looper时就会将Looper存入当前线程的ThreadLocal中
    sThreadLocal.set(new Looper(quitAllowed));
}
//从当前线程中的ThreadLocal中获取Looper
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
  • 从上面的源码中我们可以看到,每个Handler都对应一个Looper,而每个线程都有自己的Looper,而且每个线程只能有一个Looper。虽然每个线程我们可以创建多个Handler,但是实际上对应的都是一个Looper。
//Handler.class
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);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    //往MessageQueue中插入消息
    return queue.enqueueMessage(msg, uptimeMillis);
}
  • 从上面的源码中我们可以看出Handler的sendMessage方法所做的只是将Message插入到对应的MessageQueue当中。这样Looper调用MessageQueue的next方法就会取到添加的信息,Looper取到信息以后就会调用Handler的handleMessage方法处理信息。
  • 而因为上面我们分析过,Looper是每个线程独有的。那这个Looper所在的线程是哪个,上面调用Handler的handleMessage方法的逻辑就是在哪个线程中执行的。
  • 所以Handler的handleMessage方法是运行在创建Handler的线程中的。

总结

Handler机制

我们用上面派生Handler的子类的例子来总结下Handler机制

  • 首先我们在需要执行Handler的handleMessage方法的线程中创建Handler,比如在UI线程中,这样以来后面我们在子线程中执行完耗时的任务后就可以给Handler发送一个消息,最后在Handler的handleMessage方法中更新UI
  • 在子线程中执行完耗时任务,比如网络请求后,将结果封装在Message中通过Handler的sendMessage方法把消息插入到Handler的消息队列
  • Handler的Looper取到MessageQueue中的消息后交由Handler调用handleMessage方法执行。从而就把逻辑从子线程切换到了UI线程,并带回了子线程的执行结果

一句话就是:我们在UI线程中创建的Handler,里面用的Looper是UI线程的。当我们在子线程中调用sendMessage方法插入消息后,Handler的Looper会去取消息出来,然后调用Handler的handleMessage方法。所以handleMessage方法执行的线程就是Looper所在的线程,这样就从调用sendMessage方法的子线程切换到了调用handleMessage方法的Looper所在的线程了。

使用Handler的3种方式
  • 使用Handler有3种方式,分别是:派生Handler子类、Callback接口、post一个Runnable
  • 派生Handler子类、Callback接口上面的分析中都举过例子,post一个Runnable的方式并不常用,这里也贴个简单的例子
public class HandlerActivity extends Activity {
    private Handler myHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        myHandler = new Handler();

        new Thread(new Runnable() {
            @Override
            public void run() {
                myHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        Log.d(Thread.currentThread().getName(),"receive message");
                    }
                });
            }
        }).start();
    }
}
  • 需要注意理解的是上面的run方法执行的线程也是在创建Handler的线程,在这个例子中就是UI线程。其实就是相当于把handleMessage方法中的代码直接放在run方法中了而已。
创建Message的正确方式
Message message = Message.obtain();
Message message = myHandler.obtainMessage();

创建Message时要用上述的obtain的方式,这样会利用Message的消息池,不会一直重复创建Message,可以提高性能。

注意点
  • 主线程,也就是UI线程中的Looper和消息队列,在我们启动应用的时候系统已经为我们创建好了,所以我们可以在Activity中直接就创建Handler而不会报错
  • 消息队列MessageQueue底层是单链表结构,插入删除有优势。通过Handler发送的多条消息会按发送的顺序排队执行。
  • 由于Looper的loop方法是个无限循环,所以子线程中创建的Handler在明确不需要再使用时需要调用Looper的quit方法来退出循环。

             今天你进步了嘛?欢迎关注我的微信公众号,和我一起每天进步一点点!

AntDream

目录
相关文章
|
6月前
|
前端开发 编译器 Android开发
构建高效Android应用:探究Kotlin协程的异步处理机制
【4月更文挑战第2天】在现代移动应用开发中,提供流畅且响应迅速的用户体验是至关重要的。随着Android平台的发展,Kotlin语言凭借其简洁性和功能性编程的特点成为了主流选择之一。特别地,Kotlin协程作为一种新型的轻量级线程管理机制,为开发者提供了强大的异步处理能力,从而显著提升了应用程序的性能和响应速度。本文将深入探讨Kotlin协程在Android中的应用,分析其原理、实现以及如何通过协程优化应用性能。
|
6月前
|
存储 Java Android开发
Android系统升级的机制概要
Android系统升级的机制概要
120 0
|
5天前
|
存储 安全 Android开发
探索Android与iOS的隐私保护机制
在数字化时代,移动设备已成为我们生活的一部分,而隐私安全是用户最为关注的问题之一。本文将深入探讨Android和iOS两大主流操作系统在隐私保护方面的策略和实现方式,分析它们各自的优势和不足,以及如何更好地保护用户的隐私。
|
1月前
|
消息中间件 存储 Java
Android消息处理机制(Handler+Looper+Message+MessageQueue)
Android消息处理机制(Handler+Looper+Message+MessageQueue)
38 2
|
24天前
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
|
28天前
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
48 1
|
29天前
|
存储 安全 数据安全/隐私保护
探索安卓与iOS的隐私保护机制####
【10月更文挑战第15天】 本文深入剖析了安卓和iOS两大操作系统在隐私保护方面的策略与技术实现,旨在揭示两者如何通过不同的技术手段来保障用户数据的安全与隐私。文章将逐一探讨各自的隐私控制功能、加密措施以及用户权限管理,为读者提供一个全面而深入的理解。 ####
51 1
|
1月前
|
消息中间件 存储 Java
Android消息处理机制(Handler+Looper+Message+MessageQueue)
Android消息处理机制(Handler+Looper+Message+MessageQueue)
45 2
|
2月前
|
存储 缓存 Android开发
Android RecyclerView 缓存机制深度解析与面试题
本文首发于公众号“AntDream”,详细解析了 `RecyclerView` 的缓存机制,包括多级缓存的原理与流程,并提供了常见面试题及答案。通过本文,你将深入了解 `RecyclerView` 的高性能秘诀,提升列表和网格的开发技能。
67 8
|
3月前
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android 消息处理机制估计都被写烂了,但是依然还是要写一下,因为Android应用程序是通过消息来驱动的,Android某种意义上也可以说成是一个以消息驱动的系统,UI、事件、生命周期都和消息处理机制息息相关,并且消息处理机制在整个Android知识体系中也是尤其重要,在太多的源码分析的文章讲得比较繁琐,很多人对整个消息处理机制依然是懵懵懂懂,这篇文章通过一些问答的模式结合Android主线程(UI线程)的工作原理来讲解,源码注释很全,还有结合流程图,如果你对Android 消息处理机制还不是很理解,我相信只要你静下心来耐心的看,肯定会有不少的收获的。
202 3
Android面试高频知识点(2) 详解Android消息处理机制(Handler)