【android】手写一套Java的Handler程序,深入理解Android消息机制
发现无论是Windows还是Android,它们都是利用消息机制来运行一个程序,使得程序能够持久的运行下去,那它们之间都有共同的特点,都属于CS端
那么为了更好的深入理解android的消息机制,我打算手写一个Java版的Handler来模拟Android程序运行
1.首先我创建了一些空白类,其中Main类是我们程序的入口
2.接着我们开始手写Looper
在Looper中我们知道有两个非常重要的方法prepare()和loop(),在sdk中找到相关源码,提取关键,形成一份精简的代码
/** * Created by ccwant on 2019-01-10. */public class Looper { private static final String TAG = "Looper"; private static Looper sMainLooper; // guarded by Looper.class static final ThreadLocal sThreadLocal = new ThreadLocal(); final MessageQueue mQueue; final Thread mThread; private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } /** * 将当前线程初始化为循环器,并将其标记为 * 应用程序的主环。应用程序的主循环器 * 由Android环境创建,因此您不应该需要 * 自行调用此函数。另请参见:@link 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 Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } } 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"); } Log.d(TAG,"创建了一个新的Looper"); sThreadLocal.set(new Looper(quitAllowed)); } 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 (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } msg.target.dispatchMessage(msg); msg.recycleUnchecked(); } } public static Looper myLooper() { return sThreadLocal.get(); }}
3.手写MeesageQueue
同样,在MeesageQueue中也有两个非常重要的方法next()和enqueueMessage(),分别用于读取消息和插入新消息
import java.util.ArrayList;/** * Created by ccwant on 2019-01-10. */public class MessageQueue { private static final String TAG = "MessageQueue"; // True if the message queue can be quit. private final boolean mQuitAllowed; Message mMessages; private final ArrayList mIdleHandlers = new ArrayList(); private IdleHandler[] mPendingIdleHandlers; private boolean mQuiting; // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout. private boolean mBlocked; MessageQueue(boolean quitAllowed) { Log.d(TAG,"初始化消息队列"); mQuitAllowed = quitAllowed; } Message next() { int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (; ; ) { synchronized (this) { // Try to retrieve the next message. Return if found. final long now = System.currentTimeMillis(); final Message msg = mMessages; if (msg != null) { final long when = msg.when; if (now >= when) { mBlocked = false; mMessages = msg.next; msg.next = null; Log.d(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } else { nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE); } } else { nextPollTimeoutMillis = -1; } // If first time, then get the number of idlers to run. if (pendingIdleHandlerCount < 0) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount == 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.d("MessageQueue", "IdleHandler threw exception"); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; } } boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } final boolean needWake; synchronized (this) { if (mQuiting) { RuntimeException e = new RuntimeException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage()); return false; } else if (msg.target == null) { mQuiting = true; } msg.when = when; Log.d("MessageQueue", "Enqueing: " + msg); Message p = mMessages; if (p == null || when == 0 || when < p.when) { msg.next = p; mMessages = msg; needWake = mBlocked; // new head, might need to wake up } else { Message prev = null; while (p != null && p.when <= when) { prev = p; p = p.next; } msg.next = prev.next; prev.next = msg; needWake = false; // still waiting on head, no need to wake up } } if (needWake) { //nativeWake(mPtr); } return true; } private void dispose() { } /** * 用于发现线程何时阻塞的回调接口 * 等待更多消息 */ public static interface IdleHandler { /** * 当消息队列中的消息用完时调用,现在将 * 等待更多信息。返回true以保持空闲处理程序处于活动状态,返回false * 将其移除。如果仍有消息,则可以调用此函数 * 在队列中挂起,但它们都计划调度 * 在当前时间之后。 */ boolean queueIdle(); }}
4.手写Message对象
这里Message是一个实体类,我们通常使用它来传递消息对象,在这个地方我们同样精简化
import java.io.Serializable;/** * Created by ccwant on 2019-01-10. */public class Message implements Serializable { public int what; public Object obj; public int sendingUid = -1; /** * 如果正在使用set message。 * 此标志在消息排队时设置,在消息排队时保持设置状态。 * 交付后再回收。该标志只被清除 * 当创建或获取新消息时,因为这是唯一一次 * 允许应用程序修改消息的内容。 * 试图排队或回收已在使用的邮件是一个错误。 */ static final int FLAG_IN_USE = 1 << 0; /** * 如果设置消息是异步的 */ static final int FLAG_ASYNCHRONOUS = 1 << 1; /** * 在CopyFrom方法中要清除的标志 */ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE; int flags; long when; Handler target; Runnable callback; Message next; private static final Object sPoolSync = new Object(); private static Message sPool; private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 50; private static boolean gCheckRecycle = true; /** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. */ public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); } public boolean isAsynchronous() { return (flags & FLAG_ASYNCHRONOUS) != 0; } void markInUse() { flags |= FLAG_IN_USE; } public void setAsynchronous(boolean async) { if (async) { flags |= FLAG_ASYNCHRONOUS; } else { flags &= ~FLAG_ASYNCHRONOUS; } } /*package*/ boolean isInUse() { return ((flags & FLAG_IN_USE) == FLAG_IN_USE); } public void recycle() { if (isInUse()) { if (gCheckRecycle) { throw new IllegalStateException("This message cannot be recycled because it " + "is still in use."); } return; } recycleUnchecked(); } void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; obj = null; //replyTo = null; sendingUid = -1; when = 0; target = null; callback = null; //data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } } @Override public String toString() { return "Message{" + "what=" + what + ", obj=" + obj + '}'; }}
5.手写Handler
精简Handler,提取关键方法sendMessage()
import java.lang.reflect.Modifier;/** * Created by ccwant on 2019-01-10. */public class Handler { private static final boolean FIND_POTENTIAL_LEAKS = false; private static final String TAG = "Handler"; private static Handler MAIN_THREAD_HANDLER = null; final Looper mLooper; final MessageQueue mQueue; final Callback mCallback; final boolean mAsynchronous; public Handler() { this(null, false); } public Handler(Callback callback) { this(callback, false); } public Handler(Looper looper) { this(looper, null, false); } public Handler(boolean async) { this(null, async); } 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) { System.out.println("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; } public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.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, System.currentTimeMillis() +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.e(TAG,e.getMessage()); return false; } return 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); } public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } public void handleMessage(Message msg) { } private static void handleCallback(Message message) { message.callback.run(); } public interface Callback { public boolean handleMessage(Message msg); }}
6.到这里基本框架已经完成,紧接着我们模拟一下Android程序的运行
import com.ccwant.sys.Handler;import com.ccwant.sys.Log;import com.ccwant.sys.Looper;import com.ccwant.sys.Message;public class Main { private static final String TAG = "Main"; public static void main(String[] args) { Log.d(TAG, "主线程:" + Thread.currentThread().getName()); Looper.prepareMainLooper(); new Application().main(); Looper.loop(); } private static class Application{ private static final String TAG = "Application"; public void main(){ Log.d(TAG, "Application create."); startActivity(); } private void startActivity(){ Activity activity = new Activity(); activity.onCreate(); } } private static class Activity{ private static final String TAG = "Activity"; Handler mHandler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { Log.d(TAG, "当前线程:" + Thread.currentThread().getName()); Log.d(TAG, "拦截消息:" + msg.obj); return false; } }); public void onCreate(){ Log.d(TAG, "onCreate"); sendTest(); } private void sendTest(){ new Thread(new Runnable() { @Override public void run() { //Looper.prepare(); for (int i = 0; i < 2; i++) { Log.d(TAG, "-----------"); Message msg = new Message(); msg.obj = "123"; msg.what = 1; Log.d(TAG, "准备发送:" +msg+" 当前线程:"+Thread.currentThread().getName()); mHandler.sendMessage(msg); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } }}
我们想一下,Android中我们知道有个主线程,UI的绘制全部是在主线程中运行,如果主线程结束,那么程序也就停止了。
这个主线程究竟是怎么运行起来的?
在这里,Looper起到了重大的作用,在Looper中的loop()方法,通过无限循环不断的读取消息,分发消息,从而保证程序的正常运行
讲到这里,我又翻了一下Android源码,在android.app.ActivityThread类中找到了入口main方法
估计说到这里,有人会问:loop()的无限循环岂不是特别耗CPU资源?
其实不然,这里就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源
还有人可能会问:Activity的生命周期是怎么实现在死循环体外能够执行起来的?
ActivityThread的内部类H继承于Handler,通过handler消息机制,简单说Handler机制用于同一个进程的线程间通信。
Activity的生命周期都是依靠主线程的Looper.loop,当收到不同Message时则采用相应措施:
在H.handleMessage(msg)方法中,根据接收到不同的msg,执行相应的生命周期。
比如收到msg=H.LAUNCH_ACTIVITY,则调用ActivityThread.handleLaunchActivity()方法,最终会通过反射机制,创建Activity实例,然后再执行Activity.onCreate()等方法; 再比如收到msg=H.PAUSE_ACTIVITY,则调用ActivityThread.handlePauseActivity()方法,最终会执行Activity.onPause()等方法。
上述过程,我只挑核心逻辑讲,真正该过程远比这复杂。主线程的消息又是哪来的呢?
当然是App进程中的其他线程通过Handler发送给主线程,请看接下来的内容:
最后,从进程与线程间通信的角度,通过一张图加深大家对App运行过程的理解:
system_server进程是系统进程,java framework框架的核心载体,里面运行了大量的系统服务,比如这里提供ApplicationThreadProxy(简称ATP),ActivityManagerService(简称AMS),这个两个服务都运行在system_server进程的不同线程中,由于ATP和AMS都是基于IBinder接口,都是binder线程,binder线程的创建与销毁都是由binder驱动来决定的。
App进程则是我们常说的应用程序,主线程主要负责Activity/Service等组件的生命周期以及UI相关操作都运行在这个线程; 另外,每个App进程中至少会有两个binder线程 ApplicationThread(简称AT)和ActivityManagerProxy(简称AMP),除了图中画的线程,其中还有很多线程,比如signal catcher线程等,这里就不一一列举。
Binder用于不同进程之间通信,由一个进程的Binder客户端向另一个进程的服务端发送事务,比如图中线程2向线程4发送事务;而handler用于同一个进程中不同线程的通信,比如图中线程4向主线程发送消息。
结合图说说Activity生命周期,比如暂停Activity,流程如下:
1.线程1的AMS中调用线程2的ATP;(由于同一个进程的线程间资源共享,可以相互直接调用,但需要注意多线程并发问题)
2.线程2通过binder传输到App进程的线程4;
3.线程4通过handler消息机制,将暂停Activity的消息发送给主线程;
4.主线程在looper.loop()中循环遍历消息,当收到暂停Activity的消息时,便将消息分发给ActivityThread.H.handleMessage()方法,再经过方法的调用,最后便会调用到Activity.onPause(),当onPause()处理完后,继续循环loop下去。
更多相关文章
- 第十一章、Android的线程和线程池
- Android中的线程模型,
- [转载]Android中的线程模型
- android异步线程利用Handler将消息发送至UI线程
- Android消息机制原理,仿写Handler Looper源码跨线程通信原理--之
- Android日志消息的生成详细步骤