咱们知道Thread线程是一次性消费品,当Thread线程执行完一个耗时的任务以后,线程就会被自动销毁了。若是此时我又有一html
个耗时任务须要执行,咱们不得不从新建立线程去执行该耗时任务。然而,这样就存在一个性能问题:屡次建立和销毁线程是很耗java
系统资源的。为了解这种问题,咱们能够本身构建一个循环线程Looper Thread,当有耗时任务投放到该循环线程中时,线程执行耗android
时任务,执行完以后循环线程处于等待状态,直到下一个新的耗时任务被投放进来。这样一来就避免了屡次建立Thread线程致使的安全
性能问题了。也许你能够本身去构建一个循环线程,但我能够告诉你一个好消息,Aandroid SDK中其实已经有一个循环线程的框架微信
了。此时你只须要掌握其怎么使用的就ok啦!固然就是咱们今天的主角HandlerThread啦!接下来请HandlerThread上场,鼓掌~~app
HandlerThread的父类是Thread,所以HandlerThread实际上是一个线程,只不过其内部帮你实现了一个Looper的循环而已。那么咱们框架
先来了解一下Handler是怎么使用的吧!异步
【转载请注明出处:Android HandlerThread 源码分析 废墟的树】ide
HandlerThread handlerThread = new HandlerThread("handlerThread");
以上参数能够任意字符串,参数的做用主要是标记当前线程的名字。oop
handlerThread.start();
到此,咱们就构建完一个循环线程了。那么你可能会怀疑,那我怎么将一个耗时的异步任务投放到HandlerThread线程中去执行呢?固然是有办法的,接下来看第三部。
Handler subHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() { @Override public boolean handleMessage(Message msg) { //实现本身的消息处理 return true; } });
第三步建立一个Handler对象,将上面HandlerThread中的looper对象最为Handler的参数,而后重写Handler的Callback接口类中的
handlerMessage方法来处理耗时任务。
总结:以上三步顺序不能乱,必须严格按照步骤来。到此,咱们就能够调用subHandler以发送消息的形式发送耗时任务到线程
HandlerThread中去执行。言外之意就是subHandler中Callback接口类中的handlerMessage方法实际上是在工做线程中执行的。
package com.example.handlerthread; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity { private Handler mSubHandler; private TextView textView; private Button button; private Handler.Callback mSubCallback = new Handler.Callback() { //该接口的实现就是处理异步耗时任务的,所以该方法执行在子线程中 @Override public boolean handleMessage(Message msg) { switch (msg.what) { case 0: Message msg1 = new Message(); msg1.what = 0; msg1.obj = java.lang.System.currentTimeMillis(); mUIHandler.sendMessage(msg1); break; default: break; } return false; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.textView); button = (Button) findViewById(R.id.button); HandlerThread workHandle = new HandlerThread("workHandleThread"); workHandle.start(); mSubHandler = new Handler(workHandle.getLooper(), mSubCallback); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //投放异步耗时任务到HandlerThread中 mSubHandler.sendEmptyMessage(0); } }); } }
/** * 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 { //线程优先级 int mPriority; //当前线程id int mTid = -1; //当前线程持有的Looper对象 Looper mLooper; //构造方法 public HandlerThread(String name) { //调用父类默认的方法建立线程 super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; } //带优先级参数的构造方法 public HandlerThread(String name, int priority) { super(name); mPriority = priority; } ............... }
分析:该类开头就给出了一个描述:该类用于建立一个带Looper循环的线程,Looper对象用于建立Handler对象,值得注意的是在建立Handler
对象以前须要调用start()方法启动线程。这里可能有些人会有疑问?为啥须要先调用start()方法以后才能建立Handler呢?后面咱们会解答。
上面的代码注释已经很清楚了,HandlerThread类有两个构造方法,不一样之处就是设置当前线程的优先级参数。你能够根据本身的状况来设置优先
级,也能够使用默认优先级。
public class HandlerThread extends Thread { /** * Call back method that can be explicitly overridden if needed to execute some * setup before Looper loops. */ protected void onLooperPrepared() { } @Override public void run() { //得到当前线程的id mTid = Process.myTid(); //准备循环条件 Looper.prepare(); //持有锁机制来得到当前线程的Looper对象 synchronized (this) { mLooper = Looper.myLooper(); //发出通知,当前线程已经建立mLooper对象成功,这里主要是通知getLooper方法中的wait notifyAll(); } //设置当前线程的优先级 Process.setThreadPriority(mPriority); //该方法实现体是空的,子类能够实现该方法,做用就是在线程循环以前作一些准备工做,固然子类也能够不实现。 onLooperPrepared(); //启动loop Looper.loop(); mTid = -1; } }
分析:以上代码中的注释已经写得很清楚了,以上run方法主要做用就是调用了Looper.prepare和Looper.loop构建了一个循环线程。值得一提的
是,run方法中在启动loop循环以前调用了onLooperPrepared方法,该方法的实现是一个空的,用户能够在子类中实现该方法。该方法的做用是
在线程loop以前作一些初始化工做,固然你也能够不实现该方法,具体看需求。由此也能够看出,Google工程师在编写代码时也考虑到代码的可扩展性。牛B!
/** * This method returns the Looper associated with this thread. If this thread not been started * or for any reason is 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. //若是线程已经启动,可是Looper还未建立的话,就等待,知道Looper建立成功 synchronized (this) { while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; }
分析:其实方法开头的英文注释已经解释的很清楚了:该方法主要做用是得到当前HandlerThread线程中的mLooper对象。
首先判断当前线程是否存活,若是不是存活的,这直接返回null。其次若是当前线程存活的,在判断线程的成员变量mLooper是否为null,若是为
null,说明当前线程已经建立成功,可是还没来得及建立Looper对象,所以,这里会调用wait方法去等待,当run方法中的notifyAll方法调用以后
通知当前线程的wait方法等待结束,跳出循环,得到mLooper对象的值。
总结:在得到mLooper对象的时候存在一个同步的问题,只有当线程建立成功而且Looper对象也建立成功以后才能得到mLooper的值。这里等待方法wait和run方法中的notifyAll方法共同完成同步问题。
/** * 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> * * @return True if the looper looper has been asked to quit or false if the * thread had not yet started running. * * @see #quitSafely */ public boolean quit() { Looper looper = getLooper(); if (looper != null) { looper.quit(); return true; } return false; } //安全退出循环 public boolean quitSafely() { Looper looper = getLooper(); if (looper != null) { looper.quitSafely(); return true; } return false; }
分析:以上有两种让当前线程退出循环的方法,一种是安全的,一中是不安全的。至于二者有什么区别? quitSafely方法效率比quit方法标率低一点,可是安全。具体选择哪一种就要看具体项目了。
1.HandlerThread适用于构建循环线程。
2.在建立Handler做为HandlerThread线程消息执行者的时候必须调用start方法以后,由于建立Handler须要的Looper参数是从HandlerThread类中得到,而Looper对象的赋值又是在HandlerThread的run方法中建立。
3.关于HandlerThread和Service的结合使用请参考另外一篇博客:Android IntentService 源码分析
【转载请注明出处:Android HandlerThread 源码分析 废墟的树】
扫码关注微信公众号“Android知识传播”,不定时传播经常使用Android基础知识。