内容简介:本人只是 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; } 复制代码
这个方法可以更安全地退出,它会让消息队列中的非延迟消息继续得到处理,是更推荐的退出方式。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
C Primer Plus
Stephen Prata、云巅工作室 / 云巅工作室 / 人民邮电出版社 / 2005-2-1 / 60.00元
《C Primer Plus(第5版)(中文版)》共17章。第1、2章学习C语言编程所需的预备知识。第3到15章介绍了C语言的相关知识,包括数据类型、格式化输入输出、运算符、表达式、流程控制语句、函数、数组和指针、字符串操作、内存管理、位操作等等,知识内容都针对C99标准;另外,第10章强化了对指针的讨论,第12章引入了动态内存分配的概念,这些内容更加适合读者的需求。第16章和第17章讨论了C预处......一起来看看 《C Primer Plus》 这本书的介绍吧!