Android(安卓)消息传递机制分析
Android中采用Handle进行消息传递,本文从实例出发分析Handle中一个Message从创建到处理整个流程,以此阐明消息传递机制原理。
一、Handle使用示例
1、在Android studio 创建一个默认项目,在MainActivity中写下如下代码:
public class MainActivity extends AppCompatActivity { private TextView tv; private final static int MSGTYPE =1; //主线程中创建Handle,并重写Handle处理消息的方法handleMessage() //我这里只是简单处理更新一个textview显示 Handler mHandle = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case MSGTYPE: if(tv !=null){ tv.setText(String.valueOf(msg.obj)); Log.d("Handle",String.valueOf(msg.obj)); } break; } super.handleMessage(msg); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tv); new Thread(){ @Override public void run() { try { for(int i= 0 ; i<10; i++) { Thread.sleep(1000); //耗时操作在子线程完成,建立一个消息 //实体,并携带数据 Message msg = new Message(); msg.what = MSGTYPE; msg.obj = "Thread" + i; //通过Handle发送消息 mHandle.sendMessage(msg); } } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); }}
子线程通过handle发消息给主线程(UI线程)然后在主线程handle中处理消息,来达到更新UI目的。那么问题来了在这里我们为什么不直接在主线程更新UI或者直接在子线程调用tv.setText()?不在主线程更新是因为主线程不能进行耗时操作,假设一种场景tv控件的数据来源于网络,需要建立一个http获取数据,网络操作是一种极其耗时操作,要等几秒才能更新UI,这对用户体验来说极其不好,而且Android规定UI线程5s没反应,会报ANR错误,所以耗时操作都放子线程。而由于主线程不是线程安全的,Android规定子线程不能更新UI元素。
二、Handle原理分析
在这里我们从上述demo中mHandle.sendMessage(msg)这句代码作为切入口,分析这个msg经过哪些流程被送到mHandle.handleMessage(msg)中进行处理的。
进入Handle.java
//Handler构造函数public Handler() {//调用下面含有2个参数的构造函数 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()); } } /** *Handler类含有2个成员变量,一个looper对象,一个messageQueue对象,在实例化Handle时个2个成员初始化赋值 *Looper.myLooper()方法返回当前线程looper对象引用,在后面分析Looper对象时再来分析这个方法实现。 */ mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } //初始化消息队列,Looper类负责初始化消息队列 mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }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) { //Handle类持有MessageQueue引用 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) { //target类型是Handle,target保存有发送此消息handler引用,代表这个消息最终由哪个handler来处理 msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
可以发现,Handle发送的msg被添加进消息队列中,然而我们说这个队列是在Looper轮询器初始化的,我们先来看看Looper
进入Looper.java
Looper主要作用是从自己的消息队列中不断的拿出消息,并且分发给对应的handler处理
Looper对象的初始化
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类的set方法来保存looper引用 * 简单说下这个ThreadLocal类,这个类的主要线程的一个成员变量,用来存储线程的 * 私有数据,主要有2个方法,一个set(),一个get(); * 不懂同学可以看一看ThreadLocal原理 */ sThreadLocal.set(new Looper(quitAllowed)); } //实例化Looper时,会在构造方法实例化一个MessageQueue对象,用来存储消息 private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } /** * 这个方法是在主线程调用,用来在主线程中初始化looper * 其实我们看还是调用prepare()方法 */ 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 @Nullable Looper myLooper() { /** * 看到没,ThreadLocal.get()返回了当前线程looper对象 *这个和前面Handler调用myLooper()返回looper引用对应 */ return sThreadLocal.get(); }
我们再来看看APP主线程ActivityThread类,是如何初始化looper轮询器的,当我们启动一个APP时,主线程ActivityThread类会被执行,从mian()函数开始
public static void main(String[] args) { ...... /** * 主线程初始化looper轮询器,我们在主线程中实例化Handler对象就是 * 绑定这个looper,而为什么没有传递looper引用 * 是因为中间有个ThreadLocal */ Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // 开启loop死循环,不断轮训消息队列 Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; //开启死循环,不停到队列拿消息 for (;;) { //next()从队列拿消息,当next()返回为null时,则在这里阻塞,等待唤醒后继续执行 Message msg = queue.next(); if (msg == null) { // 如果没有消息,表明消息队列正在退出 return; } try { //消息不为空,调用target对象dispatchMessage(),在发送msg时,msg的 //target就保存了发送消息的handle对象,此时这个msg就送到处理消息的 //handle中 msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } ..... } }//消息分发public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } //handleMessage(msg)被我们重载实现 handleMessage(msg); } }
至此我们进入MessageQueue.java
MessageQueue有2个主要方法一个是消息入队enqueueMessage()。一个是消息出对next()
boolean enqueueMessage(Message msg, long when) { .... 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; boolean needWake; if (p == null || when == 0 || when < p.when) { //当入队消息是第一个时,如果loop()阻塞,则要唤醒loop线程 msg.next = p; mMessages = msg; needWake = mBlocked; } else { 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; } if (needWake) { //唤醒主线程,这里用到了jni编程,这这里不展开讲 nativeWake(mPtr); } } return true; }enqueueMessage()这个方法是把消息加入消息队列,中间有一些if-else判断,当队列为空,主线程空闲等待,有消息加入时则唤醒主线程。 Message next() { // 从队列中取消息 final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } //jni方法,功能是查看当前消息队列是否有消息,没有则等待,有则获取 nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { // 判断消息时间,没有到,则等待 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // 取消息 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 { //没有消息,则阻塞等待 nextPollTimeoutMillis = -1; }}
通过UML绘制Handle、Message、MessageQueue、Looper之间关系
至此,一个消息完成整个流程。
欢迎大家对文章提出评论。
更多相关文章
- android的线程特点
- Android架构分析之Android消息处理机制(二)
- Android消息处理机制实现同步效果
- Android(安卓)Activity总结
- Android中Message机制的灵活应用
- Android中Message机制的灵活应用(一)
- 学习 Android(安卓)Handler 消息机制需要注意这些问题!
- Android(安卓)Java层 Looper 机制
- Android中的Handler的机制与用法详解