更新时间:2025 09 06 19:36:37 作者 :庆美网 围观 : 58次
这篇文章给大家聊聊关于looper.prepare looper.prepare looper.loop,以及对应的知识点,希望对各位有所帮助,不要忘了收藏本站哦。
这些动作都涉及到一个概念:进程间通信;Android中的每个应用都是独立的进程,都有自己虚拟内存,两个进程之间不能互相访问数据;所以在Android中,应用进程间互相访问数据,我们最为常用的通信方式就是 Binder
然而Binder 通信实际上是同步而不是异步,但是在实际使用时,是设计成客户端同步而服务端异步
相信大家有看过Framework 层的各service 类、java 源码便会知道:在客户端调用服务端的各种方法时,通常会传递一个 Binder 过来,该Binder 对象用于服务端做异步回调,而服务端本身会使用Handler或队列的方式做成异步处理
在 Android 中,系统service是作为\”管理者\”的身份存在的:像Ams(ActivityManagerService),它并不创建 Activity,创建 Activity 的是客户端的 ActivityThread,但是每当 ActivityThread 想创建一个Acitivty,就必须告诉Ams,Ams 会进行调度该 Acitivty 的创建
更简单的一个例子:Toast 和 NotificationManagerService,Toast 负责弹出窗口的创建显示和隐藏,而何时显示跟何时隐藏却是由 NotificationManagerService 决定的
我们知道从Android 8.0开始,Binder 机制,被拆分成了Binder(System 分区 进程间通信)、HwBinder(支持 System/Vendor 分区进程间通信)、VndBinder(Vendor 分区进程间通信)
服务收到同步请求后,会将该同步任务添加至 proc->todo
这样做的目的是为了防止过多同步任务导致异步任务被饿死(若优先从 proc->todo 中取任务,则 thread->todo 中的异步任务必须等到 proc->todo 中的所有任务执行完成,那在大量同步任务的情况下,该异步任务就没有机会处理而被饿死)
首先 Android 使用得最多也最被认可的还是 Binder机制;为什么会选择 Binder 来作为进程之间的通信机制呢?是因为Binder更加简洁和快速,消耗的内存资源更小吗?不错,这些也正是 Binder 的优点
当然,也还有很多其他原因,比如传统的进程间通信可能会增加进程的开销,而且有进程过载和安全漏洞等方面的风险,Binder正好能解决和避免这些问题
Binder模型的4类角色:
Binder 机制的本质上是为了实现 IPC(Inter-Process Communication),即Client 和 Server 之间的通信
其中 Server,Client,ServiceManager 运行于用户空间,Binder 驱动运行于内核空间
这四个角色的关系和互联网类似:
创建一个 Thread 是最简单直接的方式,在 Thread 内部去执行耗时的操作,实现方式如下:
上面仅仅是在 Thread 内部执行耗时的操作,如果在执行玩耗时操作后,需要 UI 来进行更新,那应该如何操作呢?接下来继续看:
Android 提供了Handler机制来进行线程之间的通信,可以使用异步方式:Thread + handler 来进行异步任务执行及UI更新,实现方式如下:
从以上可以看到,在 Thread 内部执行耗时操作后,然后调用 Handler 来通知主线程更新 UI
这样的话,每次执行耗时请求,都需要new一个Thread,会导致开销过大,可以通过以下方式来进行改进:
通过以下改善,在 Thread 内部创建 Looper,然后创建 Handler,这样的话 Thread 就不会退出,就不需要频繁创建 Thread,此时 Handler 使用的是子线程创建的 looper,从而 Handler 消息处理(耗时操作)就在子线程里面,执行完耗时操作,通知主线程来更新 UI;
这样的话,Thread一直不退出,是不是会造成内存泄露呢?
如果不再使用的话,直接通过 mNoUIHandler.getLooper().quitSafely()来让 Looper.loop()结束循环, Thread 也就退出了
Thread + Handler深入具有实现简单的优点,但代码规范性较差,不易维护,而且每次操作都会开启一个匿名线程,系统开销较大
本文主要讲解了Binder 通信实际上是同步而不是异步的主要原理,想要往向更深入学习 Binder 难免需要寻找很多的学习资料辅助,我在这里推荐网上整合的一套《 Android Binder 学习手册》;鉴于出自大佬之手,可以帮助到大家,能够少走些弯路
篇幅原因,就不在这里为大家赘述了,有需要的小伙伴:可以私信发送\”进阶\”即可领取这份《Android Binder 学习手册》,助你早日成为底层原理大师!
最后大家如果觉得手册内容有用的话,可以点赞分享一下哦~
Android消息机制大家都不陌生,想必大家也都看过Handler、Looper的源码(看过可以直接看末尾重点,一款监控APP卡顿情况的控件),下面,网易视频云技术专家就整合一下这方面的资料,加深对这方面的印象。
用法
private Handler mHandler = new Handler() { @Override
public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_TEXT_VIEW:
mTextView.setText(\”UI成功更新\”); default: super.handleMessage(msg);
}
}
};new Thread(new Runnable() { @Override
public void run() { try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message = new Message();
message.what = MESSAGE_TEXT_VIEW;
mHandler.sendMessage(message);
}
}).start();
Handler 机制架构
从上图可以看到,是围绕 Handler、Message、MessageQueue 和 Looper 进行的。先介绍相关的概念
从开发角度看, Handler 是 Android 消息系统机制的上层接口,这使得在开发过程中只需要和 Handler 交互即可。另外, Handler 并不是专门用来更新 UI 的,只是经常被开发者用来更新 UI 而已,但是不能忽略它的其他功能,例如进行耗时的 I/O 操作等。
疑问:为什么子线程不能更新 UI?
这是因为 ViewRootImpl 对 UI 操作进行了验证
void checkThread() { if (mThread != Thread.currentThread()) {//Thread.currentThread()是UI主线程
throw new CalledFromWrongThreadException( \”Only the original thread that created a view hierarchy can touch its views.\”);
}
另外, Android 的 UI 空间不是线程安全的,如果在多线程中并发访问可能会导致 UI 控件处于不可预期的状态。
疑问:为什么不对 UI 控件的访问加上锁机制呢?
这是因为加锁,会导致 UI 访问的逻辑变复杂;其次,锁机制会降低 UI 访问的效率。
这也是为啥会存在 Hanlder 的原因。
MessageQueue:消息队列,顾名思义,它的内部存储了一组消息,以队列的形式对外提供插入和删除的工作。但是其内部是采用单链表来存储消息列表。
Looper:循环(消息循环),以无限循环的形式去查找是否有新消息,有则处理,无则等待。
Handler 源码分析及其原理
Handler 的构造方法
Handler 的构造方法有很多,核心的构造方法如下
/**
* Use the {@link Looper} for the current thread with the specified callback interface
* and set whether the handler should be asynchronous.
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
* Asynchronous messages represent interrupts or events that do not require global ordering
* with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
* @param callback The callback interface in which to handle messages, or null.
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
* @hide
*/public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) {//默认是false,若为true,则会检测当前handler是否是静态类
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();//获得了 Looper 对象
if (mLooper == null) {//如果是工作线程,就为空
throw new RuntimeException( \”Can\’t create handler inside thread that has not called Looper.prepare()\”);//不能在未调用 Looper.prepare() 的线程创建 handler
}
mQueue = mLooper.mQueue;//mLooper对应的消息队列
mCallback = callback;
mAsynchronous = async;
}
一个构造方法,Android 消息机制的三个重要角色全部出现了,分别是 Handler 、Looper 以及 MessageQueue。
mLooper = Looper.myLooper();//获得了 Looper 对象
下面看看 Looper.myLooper() 方法是嘛
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/public static @Nullable Looper myLooper() { return sThreadLocal.get();
}
sThreadLocal 是个嘛
// sThreadLocal.get() will return null unless you\’ve called prepare().static final ThreadLocal
好温馨的提示,定义在 Looper 中,是一个 static final 类型的 ThreadLocal 对象(在 Java 中,一般情况下,通过 ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的,各个线程中访问的是不同的对象。)至于 ThreadLocal 是个嘛,参考这里
大概说一下, ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获得存储的数据,对于其他线程来说则无法获取到数据。
对于 Handler 来说,它需要获取当前线程的 Looper,很显然,Looper 的作用域就是线程并且不同线程具有不同的 Looper,这个时候通过 ThreadLocal 就可以轻松实现 Looper 在线程中的存取。
根据提示,看看 prepare() 方法
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/public static void prepare() {
prepare(true);
}private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) {//一个线程只会有一个 Looper
throw new RuntimeException(\”Only one Looper may be created per thread\”);
}
sThreadLocal.set(new Looper(quitAllowed));
}
这段代码首先判断 sThreadLocal 中是否已经存在 Looper 了,如果还没有则创建一个新的 Looper 设置进去。下面看看 Looper 的构造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
构造方法可以看出,创建了一个 MessageQueue,传入参数值为 true (子线程默认是true,why?后面有讲到);创建了一个当前 thread 的实例引用。很明显,one looper only one MessageQueue
到此,就有一个疑问了:在 UI Thread 中创建 Handler 时没有调用 Looper.prepare(),但是却能正常运行(但是,我们注意到,sThreadLocal.get() will return null unless you\’ve called prepare()),Why?
既然能正常运行,那么肯定是调用了 prepare 方法,但是,在哪里调用了呢,这就要看主线程 ActivityThread 。首次启动 Activity 时通过 Process.start 创建应用层程序的主线程,创建成功后进入到主线程 ActivityThread 的 main 方法中开始执行, main 方法有:
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\”));
}
Looper.loop();
很明显咯,秘密就在 prepareMainLooper() 里面(即使后面加了个 MainLooper,但也是个 prepare)
/**
* Initialize the current thread as a looper, marking it as an
* application\’s main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/public static void prepareMainLooper() {
prepare(false);//可以看出,UI thread传入的是false
synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException(\”The main Looper has already been prepared.\”);
}
sMainLooper = myLooper();
}
}
UI 线程中会始终存在一个 Looper 对象( sMainLooper 保存在 Looper 类中, UI 线程通过getMainLooper 方法获取 UI 线程的 Looper 对象),从而不需要再手动去调用 Looper.prepare() 方法了。如下 Looper 类提供的 get 方法:
/**
* Returns the application\’s main looper, which lives in the main thread of the application.
*/public static Looper getMainLooper() {
synchronized (Looper.class) { return sMainLooper;
}
}
到这里,上面疑问的答案就显而易见了。同时,如果在子线程实例化 Handler,就必须要先调用Looper.prepare() 方法才可以。
到此先初步总结下上面关于 Handler 实例化的一些关键信息,具体如下:
在主线程中可以直接创建 Handler 对象,而在子线程中需要先调用 Looper.prepare() 才能创建 Handler 对象,否则运行抛出 ”Can’t create handler inside thread that has not called Looper.prepare()” 异常信息。
每个线程中最多只能有一个 Looper 对象,否则抛出异常。
可以通过 Looper.myLooper() 获取当前线程的 Looper 实例,通过 Looper.getMainLooper() 获取主(UI)线程的 Looper 实例。
一个 Looper 只能对应了一个M essageQueue 。
一个线程中只有一个 Looper 实例,一个 MessageQueue 实例,可以有多个 Handler 实例。
Handler 对象也创建好了,接下来就该发送消息了 mHandler.sendMessage(message);
/**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in {@link #handleMessage},
* in the thread attached to this handler.
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0);
}
嗯,继续往下看咯
/**
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed — if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/public final boolean sendMessageDelayed(Message msg, long delayMillis){ if (delayMillis< 0) {
delayMillis = 0;
} return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
最终走到了 sendMessageAtTime 这个方法
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds)uptimeMillis.
*The time-base is {@link android.os.SystemClock#uptimeMillis}.
* Time spent in deep sleep will add an additional delay to execution.
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed — if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;//mQueue是在Handler实例化时构造函数中实例化的
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);
}
sendMessageAtTime() 方法接收两个参数,其中 msg 参数就是我们发送的 Message 对象,而uptimeMillis 参数则表示发送消息的时间,它的值等于自系统开机到当前时间的毫秒数再加上延迟时间,如果调用的不是 sendMessageDelayed() 方法,延迟时间就为0。
而 mQueue 是在 Handler 实例化时构造函数中实例化的,在 Handler 的构造函数中可以看见 mQueue = mLooper.mQueue ;而 Looper 的 mQueue 对象上面分析过了,是在 Looper 的构造函数中创建的一个MessageQueue。
最终的 MessageQueue 的 enqueueMessage() 方法是个嘛,下面看看
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; if (mAsynchronous) {
msg.setAsynchronous(true);
} return queue.enqueueMessage(msg, uptimeMillis);
}
这个方法首先将我们要发送的消息 Message 的 target 属性设置为当前 Handler 对象(进行关联);接着将 msg 与 uptimeMillis 这两个参数都传递到 MessageQueue (消息队列)的 enqueueMessage() 方法中,如下
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) {//上面的target已经是handler对象,not null
throw new IllegalArgumentException(\”Message must have a target.\”);
} if (msg.isInUse()) { throw new IllegalStateException(msg + \” This message is already in use.\”);
} 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的状态
msg.when = when;
Message p = mMessages; boolean needWake; if (p == null || when == 0 || when< p.when) {//检测当前头指针是否为空(队列为空)或者没有设置when 或者设置的when比头指针的when要前
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else { // Inserted within the middle of the queue. Usually we don\’t have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
//几种情况要唤醒线程处理消息:1)队列是堵塞的 2)barrier,头部结点无target 3)当前msg是异步的
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 将当前msg插入第一个比其when值大的结点前。
prev.next = msg;
} // We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
} return true;
}
MessageQueue 消息队列对于消息排队是通过类似 C 语言的链表来存储这些有序的消息的。其中的 mMessages 对象表示当前待处理的消息;消息插入队列的实质就是将所有的消息按时间( uptimeMillis 参数,也就是 when )进行排序。具体的操作方法就根据时间的顺序调用 msg.next ,从而为每一个消息指定它的下一个消息是什么。
当然如果你是通过 sendMessageAtFrontOfQueue() 方法来发送消息的,它也会调用 enqueueMessage() 来让消息入队,只不过时间为0,这时会把 mMessages 赋值为新入队的这条消息,然后将这条消息的 next 指定为刚才的 mMessages ,这样也就完成了添加消息到队列头部的操作。
到此,消息也通过 handler 发送了,并且存到了 MessageQueue 中,那么,系统怎么处理 message 呢?
我们知道 MessageQueue 的对象在 Looper 构造函数中实例化的;一个 Looper 对应一个 MessageQueue,所以说 Handler 发送消息是通过 Handler 构造函数里拿到的 Looper 对象的成员 MessageQueue 的enqueueMessage 方法将消息插入队列,也就是说出队列一定也与 Handler 和 Looper 和 MessageQueue有关系。
既然会涉及到出队,那么肯定就有出队的方法,那么找来找去,就在 loop() 方法里面(为啥 UI Thread 没有调用 loop,loop 也会执行呢?一看就没有好好的看刚才贴的代码,明明已经调用,却说没有调用
Looper.prepareMainLooper();
…Looper.loop();
下回看代码用点心 )
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/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; // 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 (;;) {
Message msg = queue.next(); // might block
if (msg == null) { // No message indicates that the message queue is quitting.
return;
} // This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging; if (logging != null) {
logging.println(\”>>>>>Dispatching to \” + msg.target + \” \” +
msg.callback + \”: \” + msg.what);
}
msg.target.dispatchMessage(msg); if (logging != null) {
logging.println(\”<<<<< Finished to \” + msg.target + \” \” + msg.callback);
} // Make sure that during the course of dispatching the
// identity of the thread wasn\’t corrupted.
final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) {
Log.wtf(TAG, \”Thread identity changed from 0x\”
+ Long.toHexString(ident) + \” to 0x\”
+ Long.toHexString(newIdent) + \” while dispatching to \”
+ msg.target.getClass().getName() + \” \”
+ msg.callback + \” what=\” + msg.what);
}
msg.recycleUnchecked();
}
}
可以看到 for (;;) {} 就是一个死循环,然后不断的调用 next 方法(出队的方法)
Message next() { // Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr; if (ptr == 0) { return null;
} int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
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) { // Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
} 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;
} // Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose(); return null;
} // If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount< 0
&& (mMessages == null || now< mMessages.when)) {
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.wtf(TAG, \”IdleHandler threw exception\”, t);
} 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;
}
}
同样,可以看到 for (;;) {},一个死循环
它的简单逻辑就是如果当前 MessageQueue 中存在 mMessages (即待处理消息),就将这个消息出队,然后让下一条消息成为 mMessages,否则就进入一个阻塞状态,一直等到有新的消息入队。
这里有个疑问,相信网友的回答应该能回答这个问题了。
另外,可以参考这里,里面有对 epoll_wait 的介绍。
继续 loop 方法,每当有一个消息出队就将它传递到 msg.target 的 dispatchMessage() 方法中。其中这个 msg.target 其实就是当前 Handler 对象
/**
* Handle system messages here.
*/public void dispatchMessage(Message msg) { if (msg.callback != null) {
handleCallback(msg);
} else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return;
}
}
handleMessage(msg);
}
}
可以看见 dispatchMessage 方法中的逻辑比较简单,具体就是检查 Message 的 callback 是否为空,不为空,就通过 handleCallback() 方法处理消息。 Message 的 callback 是一个 Runnable 对象,就是handler 的 post 方法所传递的 Runnable 参数)不为空,handleCallback() 方法,如下
private static void handleCallback(Message message) {
message.callback.run();
}
否则,就检查 mCallback 是否为空,不为空就调用 Callback 的 handleMessage() 方法处理消息。mCallback 是一个接口,如下
/**
* 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 handler = new Handler(callback)。对应的构造方法如下
/**
* Constructor associates this handler with the {@link Looper} for the
* current thread and takes a callback interface in which you can handle
* messages.
* If this thread does not have a looper, this handler won\’t be able to receive messages
* so an exception is thrown.
* @param callback The callback interface in which to handle messages, or null.
*/public Handler(Callback callback) { this(callback, false);
}
Callback 的意义如同注释一般:可以用来创一个 Handler 的实例但不需要派生出 Handler 的子类。
因为在日常开发过程中,创建 Handler 最常见的方式就是派生一个 Handler 的子类并重写其handleMessage 方法来处理具体的消息,而Callback给我们提供了另外一种使用 Handler 的方式,当我们不想派生子类时,就可以通过 Callback 实现。
最后,调用 Handler 的 handleMessage 方法来处理消息。
为什么handleMessage() 方法中可以获取到之前发送的消息,这就是原因。
因此,一个最标准的异步消息处理线程的写法应该是这样:
class LooperThread extends Thread { public Handler mHandler; public void run() {
Looper.prepare();
mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here
}
};
Looper.loop();
}
}
现在再看 handler 的架构图,是不是就更清晰了。
当我们在子线程调用 loop.prepare() 和 loop() 方法后,最好调用 loop.quit() 方法退出,终止消息循环,否则这个子线程就会一直处于等待状态。那么 quit 方法如下
/**
* Quits the looper.
*
* Causes the {@link #loop} method to terminate without processing any
* more messages in the message queue.
*
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
*
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
*
* @see #quitSafely
*/public void quit() {
mQueue.quit(false);
}
再找
void quit(boolean safe) { if (!mQuitAllowed) { throw new IllegalStateException(\”Main thread not allowed to quit.\”);
} synchronized (this) { if (mQuitting) { return;
}
mQuitting = true; if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
} // We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
我们知道,在子线程中调用 preare 时
public static void prepare() {
prepare(true);
}
默认的是 true,也是就是说,子线程是可以退出的,而在 UI Thread 中
public static void prepareMainLooper() {
prepare(false);//可以看出,UI thread传入的是false
synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException(\”The main Looper has already been prepared.\”);
}
sMainLooper = myLooper();
}
}
传的 false,就是提示 UI Thread 是不可以退出的
回到 quit 方法继续看,可以发现实质就是对 mQuitting 标记置位,这个 mQuitting 标记在MessageQueue 的阻塞等待 next 方法中用做了判断条件,所以可以通过 quit 方法退出整个当前线程的loop 循环。
到此整个 Android 的一次完整异步消息机制分析使用流程结束。
前面涉及到的几个主要的类 Handler、Looper、MessageQueue 和 Message 的关系如下所述:
Handler 负责将 Looper 绑定到线程,初始化 Looper 和提供对外 API。
Looper 负责消息循环和操作 MessageQueue 对象。
MessageQueue 实现了一个堵塞队列。
Message 是一次业务中所有参数的载体。
重点
如果您看到了这里,那么今天分析 Handler、Loop 和 MessageQueue,主要是为了引出下面的这个东西 BlockCanary, 一个 Android 平台的一个非侵入式的性能监控组件,项目中已经打算性能优化专项中引入并解决相关性能问题,为了了解其原理,故整理了一下整个 Handler 的原理。该控件的相关说明在这里。
如果应用滑动卡顿,可以使用该控件进行监控(作者实现这个控件的原理,大家看过就会了解。很佩服作者在消息机制中发现了这么一个方式能够监控性能,同样是看过源码分析,差距还是很明显的,脑子不够开窍哇)
looper.prepare looper.prepare looper.loop的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于、looper.prepare looper.prepare looper.loop的信息别忘了在本站进行查找哦。
我是小七,给大家分享电子元器件选型、电路项目等知识,干货满满。大家不要错过,建议收藏,错过就不一定找得到了,内容仅供参考,图片记得放大观看。如果有什么错误或者不
资料图:运营商开始网络减频。三大运营商2G、3G网络减频日前,有消息称,福州市无线电管理局给福州移动发函,“你单位关于申请注销TDSCDMA基站的报告收悉。经研
其次,对USB驱动器本身的物理损害也可能导致无法格式化。例如,USB闪存驱动器接口松动或破损,或者内部电路和芯片损坏,等等。在这种情况下,我们需要检查USB驱动
这些动作都涉及到一个概念:进程间通信;Android中的每个应用都是独立的进程,都有自己虚拟内存,两个进程之间不能互相访问数据;所以在Android中,应用进程
电脑没有声音解决方法:步骤1:检查耳机还是扬声器是首先损坏的,然后尝试将耳机或扬声器放在另一台计算机上。步骤2:耳机或扬声器很好,然后在控制面板中输入设备管理,
C是一种非常低级的语言,在许多方面类似于汇编语言。在《Intel32位汇编语言程序设计》的书中,它甚至引入了手动将简单C语言翻译成汇编的方法。对于编译器之类的系
用户评论
这篇文章真是太棒了!我对循环和准备工作一直很感兴趣,作者对这个主题的解释非常清晰易懂。特别是 "Looper" 这个概念让我印象深刻,现在我觉得自己可以更好地理解复杂的程序逻辑了。
有17位网友表示赞同!
我目前在学习编程,这篇博客对我来说简直就是救命稻草啊!虽然我没有学过 "Looper" ,但通过这篇文章,我已经开始对它的原理和应用有了初步的了解。
有15位网友表示赞同!
其实我对循环已经很熟练了,但这篇文章让我发现自己还不知道很多新知识。特别是关于 "Looper.prepare" 的用法,我以前从未听说过,今天学到了不少东西。
有8位网友表示赞同!
我觉得作者把 "Looper" 运用得很巧妙,能有效地提高代码效率。不过文章的例子有点简单,希望能看到更复杂实际应用场景的阐述。
有13位网友表示赞同!
文章语言很通俗易懂,适合像我初学者理解。"Looper.loop" 这个概念让我意识到循环并不是只有 "for" 和 "while" 这两种方式可以实现,真是开拓了我的视野!
有18位网友表示赞同!
这篇博文写的太浅薄了!对 "Looper" 的原理和缺陷分析都不够深入,感觉像是读了一篇简单的介绍文档。希望作者能够更加详细地讲解。
有15位网友表示赞同!
我一直觉得循环编程比较复杂,看这篇文章才知道原来我们可以用像 "Looper" 这样的方法简化程序逻辑。真想把这个方法应用到自己的项目中去!
有12位网友表示赞同!
"Looper.prepare looper.loop" 这些词真是太拗口了! 文章的讲解也没让我明白他们具体是什么含义,感觉有点看不懂。
有6位网友表示赞同!
对开发者来说优化代码效率非常重要,"Looper" 恰好能帮助我们实现这一目标。这篇博文讲解得很到位,让我对 "Looper" 有更深入的理解。(
有8位网友表示赞同!
其实我一直觉得循环编程就那样,没有什么特别的之处。这篇文章的例子有点儿无聊,没有让我感受到 "Looper" 的魅力。
有11位网友表示赞同!
"Looper" 这个概念听起来很有趣,但我不知道它真的能带来什么实际收益。文章里缺少具体的应用场景和用例,很难让人理解它的价值。
有12位网友表示赞同!
我认为作者将 "Looper" 讲解得非常生动有趣,很容易让人记住关键点。希望以后还能看到更多关于 "Looper" 的探讨和案例分享。
有14位网友表示赞同!
这篇文章打开了一个全新的编程思维方式!原来循环不只有传统的实现方法,还有像 "Looper" 这样高效智能的方式!让我对未来的代码编写充满了期待!
有14位网友表示赞同!
"Looper.prepare" 和 "Looper.loop" 的使用方法我理解不了,这些函数的参数设置也太复杂了。希望作者能解释一下它们的具体用途和原理。
有16位网友表示赞同!
文章的语言过于抽象,没有贴近实际操作,对于新手来说难以理解。"Looper" 是什么,怎么用都没讲清楚,有点失望.
有15位网友表示赞同!
这篇文章让我对 "Looper" 的实现方式有了更清晰的认知。作者不仅解释了原理,还提供了具体的代码实例,非常实用!感谢作者!
有17位网友表示赞同!