内容简介:本人只是 Android小菜一个,写技术文档只是为了总结自己在最近学习到的知识,从来不敢为人师,如果里面有些不正确的地方请大家尽情指出,谢谢!这段
本人只是 Android小菜一个,写技术文档只是为了总结自己在最近学习到的知识,从来不敢为人师,如果里面有些不正确的地方请大家尽情指出,谢谢!
1. 概述
HandlerThread
是 Android
提供用来创建含有 Looper
线程的,其实在之前分析 IntentService
的博文中已经看到了它的应用,再来回顾下 IntentService
的启动过程:
public void onCreate() { // TODO: It would be nice to have an option to hold a partial wakelock // during processing, and to have a static startService(Context, Intent) // method that would launch the service & hand off a wakelock. super.onCreate(); // 创建包含 Looper 的线程并启动之 HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); thread.start(); // 通过新线程的 Looper 创建 Handler 实例 mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); } 复制代码
这段 IntentService
的启动代码中直接使用到了 HandlerThread
,但当时只是一笔带过并没有仔细分析 HandlerThread
的使用方法和实现原理,本文将详细讲解如何在项目中使用 HandlerThread
和其内部的实现原理。
本文假设您对 Handler,Thread,Looper,Message 和 MessageQueue
相关知识有了一定的了解,所以涉及到它们的地方,只会稍作说明不再深入分析。
2. HandlerThread 使用方法
在讲解其具体使用方法前,还是先来看下对 HandlerThread
的声明:
/** * Handy class for starting a new thread that has a looper. The looper can then be * used to create handler classes. Note that start() must still be called. */ public class HandlerThread extends Thread { ... } 复制代码
从这段声明里可以看到: HandlerThread
能够很方便地启动一个带有 looper
的线程,而这个 looper
可以用来创建 handler
。这句话里隐含了几点重要知识:
-
HandlerThread
是一个Thread
线程,具有线程的特性。 -
Android
中默认线程没有looper
,如果想创建带有looper
的线程需要在创建的过程中主动创造looper
对象。 -
Handler
中必须要有looper
,它是整个消息查询、分发、处理的核心,在创建Handler
的过程中可以指定任意线程的looper
对象。
现在通过一个简单的示例演示下 HandlerThread
的使用方法:
public class MainActivity extends Activity { private static final String TAG = "Android_Test"; private Button mButton; private TextView mText; // 新线程和与之相关联的 Handler 对象 private HandlerThread mHanderThread; private Handler mThreadHandler; // 和主线程相关的 Handler 对象 private Handler mUiHandler; // 用于子线程和主线程中的消息分发 private static final int MESSAGE_CODE_GET = 1; private static final int MESSAGE_CODE_SET = 2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mButton = (Button) findViewById(R.id.main_button); mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 主线程通过子线程 Handler 分发消息,以达到在子线程中处理耗时任务的目的。 mThreadHandler.sendEmptyMessage(MESSAGE_CODE_GET); } }); mText = (TextView) findViewById(R.id.main_text); // 创建 HandlerThread 并启动新线程 mHanderThread = new HandlerThread("HandlerThread"); mHanderThread.start(); // 通过新线程中的 looper 创建相关的 Handler 对象 mThreadHandler = new Handler(mHanderThread.getLooper()) { @Override public void handleMessage(Message msg) { Log.i(TAG, "mThreadHandler's thread: " + Thread.currentThread().getName()); if (msg.what == MESSAGE_CODE_GET) { try { // 休眠 5 秒,模拟子线程处理耗时任务的过程。 Thread.sleep(5 * 1000); } catch (InterruptedException ie) { ie.printStackTrace(); } // 向主线程 Handler 发送处理结果 mUiHandler.sendEmptyMessage(MESSAGE_CODE_SET); } } }; mUiHandler = new Handler() { @Override public void handleMessage(Message msg) { Log.i(TAG, "mUiHandler's thread: " + Thread.currentThread().getName()); if (msg.what == MESSAGE_CODE_SET) { // 主线程接收来自子线程的消息就行后续处理,这里是显示当前时间信息。 mText.setText(String.valueOf(SystemClock.uptimeMillis())); } } }; } } 复制代码
这个示例的主要功能是主线程中发起任务,在子线程中处理这些耗时任务,处理完成后通知主线程并更新界面,并打印出运行过程,从下面的运行结果可以看到:耗时任务确实是在子线程中执行的。
03-01 10:04:57.311 30673 30723 I Android_Test: mThreadHandler's thread: HandlerThread 03-01 10:05:02.313 30673 30673 I Android_Test: mUiHandler's thread: main 复制代码
从上面的示例可以总结得到 HandlerThread
的使用方法:
-
首先创建
HandlerThread
对象并运行它,在创建过程中需要指定线程名字; -
获取
HandlerThread
对象中的looper
并通过它来构造一个子线程Handler
对象; -
主线程通过子线程
Handler
对象向子线程分发任务; - 子线程处理耗时任务并把处理结果分发到主线程,主线程进行后续的处理。
3. HandlerThread 原理分析
HandlerThread
和普通的 Thread
的区别就在于其内部是包含 Looper
的,所以我们分析的重点就是它是怎么创建使用 Looper
以及在使用后如何退出。首先来看下它的构造函数:
public class HandlerThread extends Thread { // 线程优先级 int mPriority; // 线程号 int mTid = -1; // 线程内部的 Looper 对象 Looper mLooper; private @Nullable Handler mHandler; // 只指定线程名字并使用默认的线程优先级来构造 HandlerThread 对象 public HandlerThread(String name) { super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; } /** * Constructs a HandlerThread. * @param name * @param priority The priority to run the thread at. The value supplied must be from * {@link android.os.Process} and not from java.lang.Thread. */ // 同时指定线程名字和优先级来构造 HandlerThread 对象 public HandlerThread(String name, int priority) { super(name); mPriority = priority; } // 省略其他内容 ... } 复制代码
由于 HandlerThread
是直接继承 Thread
的,所以在通过 start()
启动线程后,其中的 run()
就会启动,这也是线程内部的核心方法,来看下其实现:
@Override public void run() { mTid = Process.myTid(); // 创建一个和当前线程有关的 Looper 对象 Looper.prepare(); synchronized (this) { // 得到当前线程的 Looper 对象后唤醒等待 mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); // 调用回调方法,可以在开始消息轮询之前进行某些初始化设置,默认是空方法。 onLooperPrepared(); // 启动消息轮询,进行消息的查询分发和处理。 Looper.loop(); mTid = -1; } 复制代码
这段代码就是 HandlerThread
中创建 Looper
对象并启动消息循环的核心,我们来一步步分析其重要逻辑。
3.1 创建 Looper 对象
在核心代码 run()
中首先看到的是 Looper.prepare()
,其作用就是创建当前线程的 Looper
对象:
/** 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) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } 复制代码
在使用 Looper.prepare()
创建 Looper
对象的过程中利用 ThreadLocal
把这个对象和当前线程建立了关联。
ThreadLocal
是一个可以存储线程局部变量的类,如果大家感兴趣可以自行查阅相关资料,在这里就不对其进行详细讲述了。
3.2 获取 Looper 对象
创建完 Looper
对象后会在同步代码块里去唤醒等待,那这个等待会发生在什么时候呢?记得示例中是通过 getLooper()
得到 Looper
对象的,来看下它的内部实现:
/** * This method returns the Looper associated with this thread. If this thread not been started * or for any reason isAlive() returns false, this method will return null. If this thread * has been started, this method will block until the looper has been initialized. * @return The looper. */ public Looper getLooper() { // 线程没有启动或者已经死亡时返回 null if (!isAlive()) { return null; } // If the thread has been started, wait until the looper has been created. synchronized (this) { // 线程已经启动但是 Looper 对象还没有创建完成时等待 while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } // 等待结束说明此时 Looper 对象已经创建完成,返回之。 return mLooper; } 复制代码
在这里看到当“线程已经启动但是 Looper
对象还没有创建完成”时会进行等待,当创建完成时会唤醒等待,这时 getLooper()
就可以返回已经创建完成的 Looper
对象了。之所以需要这个“等待-唤醒”机制,因为获取 Looper
是在主线程中进行的,而创建 Looper
是在子线程中进行的,必须使用这个机制来完成两者的状态同步。
3.3 开启 Looper 循环
前面已经讲了 Looper
对象的创建以及如何在主线程中获取,那么如何通过 Looper.loop()
开启循环呢?
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() { // 获取Looper对象 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(); // Allow overriding a threshold with a system prop. e.g. // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start' final int thresholdOverride = SystemProperties.getInt("log.looper." + Process.myUid() + "." + Thread.currentThread().getName() + ".slow", 0); boolean slowDeliveryDetected = false; // 开启一个无限循环来从消息队列中获取消息 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 final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long traceTag = me.mTraceTag; long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs; if (thresholdOverride > 0) { slowDispatchThresholdMs = thresholdOverride; slowDeliveryThresholdMs = thresholdOverride; } final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0); final boolean logSlowDispatch = (slowDispatchThresholdMs > 0); final boolean needStartTime = logSlowDelivery || logSlowDispatch; final boolean needEndTime = logSlowDispatch; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0; final long dispatchEnd; try { // 获取到消息后,分发到 target 去处理。 msg.target.dispatchMessage(msg); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (logSlowDelivery) { if (slowDeliveryDetected) { if ((dispatchStart - msg.when) <= 10) { Slog.w(TAG, "Drained"); slowDeliveryDetected = false; } } else { if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery", msg)) { // Once we write a slow delivery log, suppress until the queue drains. slowDeliveryDetected = true; } } } if (logSlowDispatch) { showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", 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(); } } 复制代码
这段代码非常长,在分析的时候不需要弄懂每一行的意思,只需要了解其中关于消息的大致处理流程即可,大家如果不想去看这大段代码,只需关注添加注释的几行即可,其基本流程是:通过一个无限循环从消息队列中查询 Message
消息,如果查询不到就等待,如果查询到就交给其 target
来处理,最后要回收资源。
3.4 退出 Looper 循环
在使用 HandlerThread
+ Handler
在子线程处理耗时任务后并且不再需要时,必须要退出 Looper
的消息循环,可以通过 quit()
:
/** * Quits the handler thread's looper. * <p> * Causes the handler thread's looper to terminate without processing any * more messages in the message queue. * </p><p> * 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. * </p><p class="note"> * 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. * </p> */ public boolean quit() { Looper looper = getLooper(); if (looper != null) { looper.quit(); return true; } return false; } 复制代码
这份方法可以退出 Looper
循环同时会把当前消息队列中的所有消息都抛弃,也无法再向该消息队列中发送消息。但有时我们并不想直接清空消息队列,这时可以使用另外一种方式:
/** * Quits the handler thread's looper safely. * <p> * Causes the handler thread's looper to terminate as soon as all remaining messages * in the message queue that are already due to be delivered have been handled. * Pending delayed messages with due times in the future will not be delivered. * </p><p> * 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. * </p><p> * If the thread has not been started or has finished (that is if * {@link #getLooper} returns null), then false is returned. * Otherwise the looper is asked to quit and true is returned. * </p> * * @return True if the looper looper has been asked to quit or false if the * thread had not yet started running. */ public boolean quitSafely() { Looper looper = getLooper(); if (looper != null) { looper.quitSafely(); return true; } return false; } 复制代码
这个方法可以更安全地退出,它会让消息队列中的非延迟消息继续得到处理,是更推荐的退出方式。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Zen of CSS Design
Dave Shea、Molly E. Holzschlag / Peachpit Press / 2005-2-27 / USD 44.99
Proving once and for all that standards-compliant design does not equal dull design, this inspiring tome uses examples from the landmark CSS Zen Garden site as the foundation for discussions on how to......一起来看看 《The Zen of CSS Design》 这本书的介绍吧!