返回
顶部

修改密码

浅析HandlerThread

+1

-1

收藏

+1

-1

点赞0

评论0

浅析HandlerThread1 背景首先呢?HandlerThread面试的时候有的会问,但是面试官不直接问你是否知道HandlerThread以及用途和实现?面试官会问你:面试必问的一个题目:handler的消息机制等一系列问题,如果你说的还算可以,那么问题来了?接下来会问你假如在一个子线程(工作线…

浅析HandlerThread

 

1
背景
首先呢?HandlerThread面试的时候有的会问,但是面试官不直接问你是否知道HandlerThread以及用途和实现?面试官会问你:面试必问的一个题目:
handler的消息机制等一系列问题,如果你说的还算可以,那么问题来了?
接下来会问你假如在一个子线程(工作线程)中怎么使用handler?嘻嘻 ,如果你知道handler的消息机制,那么这个问题很好回答了,代码如下:

 


private final class WorkThread extends Thread {      
 private Handler mHandler;    
   public Handler getHandler() {     
      return mHandler;
        }     
    public void quit() {
      mHandler.getLooper().quit();

        }    
   @Override   
    public void run() {     
      super.run();            //创建该线程对应的Looper,       
    // 内部实现       
    // 1。new Looper()        
   // 2。将1步中的lopper 放在ThreadLocal里,ThreadLocal是保存数据的,主要应用场景是:线程间数据互不影响的情况       
    // 3。在1步中的Looper的构造函数中new MessageQueue();   
  //其实就是创建了该线程对用的Looper,Looper里创建MessageQueue来实现消息机制       
    //对消息机制不懂得同学可以查阅资料,网上很多也讲的很不错。        
   Looper.prepare();
            mHandler = new Handler() {     
          @Override             
  public void handleMessage(Message msg) {    
               super.handleMessage(msg);
                Log.d("WorkThread", (Looper.getMainLooper() == Looper.myLooper()) + "," + msg.what);
                }
            };          
 //开启消息的死循环处理即:dispatchMessage          
 Looper.loop();         
  //注意这3个的顺序不能颠倒   
        Log.d("WorkThread", "end");
        }
    }
这里我们可以测试下在另外一个子线程里通过WorkThread的getHandler给它发消息看下结果:

public void send(View view) {   
    new SendThread(mWorkThread.getHandler()).start();
    }   
private final class SendThread extends Thread {    
   private Handler mHandler;    
   SendThread(Handler handler) {      
     this.mHandler = handler;
        }     
  @Override    
   public void run() {    
       super.run();     
      for (int i = 0; i < 3; i++) {
      mHandler.sendEmptyMessage(0x1);          
     SystemClock.sleep(1000);
            }
        }
    }
log:可以看到收到了消息并打印了是在非主线程

 

 

这里有同学问 小伙子,你这里不对, Log.d("WorkThread", "end");没打印。是的,Looper.loop();为阻塞函数,只有当调用mHandler.getLooper().quit()或者quitSafely()方法后,loop才会中止.那2个方法有毛区别呢?

当我们调用Looper的quit方法时,实际上执行了MessageQueue中的removeAllMessagesLocked方法,该方法的作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息。

当我们调用Looper的quitSafely方法时,实际上执行了MessageQueue中的removeAllFutureMessagesLocked方法,通过名字就可以看出,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。

无论是调用了quit方法还是quitSafely方法只会,Looper就不再接收新的消息。即在调用了Looper的quit或quitSafely方法之后,消息循环就终结了,这时候再通过Handler调用sendMessage或post等方法发送消息时均返回false,表示消息没有成功放入消息队列MessageQueue中,因为消息队列已经退出了。

需要注意的是Looper的quit方法从API Level 1就存在了,但是Looper的quitSafely方法从API Level 18才添加进来。

2

HandlerThread的用法
前面扯远了,但是你必须知道的东西。那就有同学问了好麻烦啊上面的,假入我以后在工作中用的话我还得注意 Looper.prepare(); new mHandler(); Looper.loop();的顺序。呵呵。。。
哈哈,同学,这个HandlerThread就是解决这个的。google工程师早知道你会这么问。
我们先看怎么用,我们把上面的测试例子用HandlerThread实现然后在分析内部原理。

//1.初始化,参数为名字,也就是线程的名字,后面我们会结合源码来看

 mHandlerThread = new HandlerThread("WorkThread"); //必须调用start方法,因为HandlerThread继承自Thread来启动线程

mHandlerThread.start(); //初始化Handler,只是传递了一个mHandlerThread内部的一个looper

mHandler = new Handler(mHandlerThread.getLooper()) {      

      @Override            public void handleMessage(Message msg) {        

        super.handleMessage(msg);
                Log.d("WorkThread", (Looper.getMainLooper() == Looper.myLooper()) + "," + msg.what);
            }
        };
//2.使用  public void send(View view) {   

     new SendThread(mHandler).start();
    }   

private final class SendThread extends Thread {    

    private Handler mHandler;      

  SendThread(Handler handler) {     

       this.mHandler = handler;
        }     

   @Override     

   public void run() {    

        super.run();          

 for (int i = 0; i < 3; i++) {
              

  mHandler.sendEmptyMessage(0x1);               

 SystemClock.sleep(1000);
            }


        }
    }


//3.别忘记释放,停止Looper消息循环,内部还是调用了looper的quit,及时释放防止内存泄漏哦!!   mHandlerThread.quit();
 

因此

如果我们有耗时的任务处理可以通过HandlerThread获取looper,looper进而构造Handler,然后通过Handler的post(Runnable r)在handleMessage里进行处理耗时处理。

很方便进行线程间的通信。

3

HandlerThread的源码分析
原理:HandlerThread其实是extends Thread,内部的run方法调用了Looper.prepare()方法和 Looper.loop();哈哈,和我们之前写的第一种方法一样。

public class HandlerThread extends Thread {
    int mPriority;//线程优先级  
 int mTid = -1;//线程id   
Looper mLooper;//Looper对象 
  public HandlerThread(String name) {  
     super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
对于线程,我们还是主要看run方法,源码很短,直接全部贴了。我们清楚的看到了Looper.prepare和Looper.loop的创建,这里和最开始说的一样。

   @Override    public void run() {
        mTid = Process.myTid();
        Looper.prepare();        //后面会分析   
    synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }     
  Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
但是这多了一个onLooperPrepared()方法,这里可以根据名字看出来,当Looper.prepare()创建完后,我们可以做一些初始化的东西,这当然是在子线程里,我们看下这个方法的实现。空实现,如果你要做些初始化的准备工作可以extends HandlerThread重写onLooperPrepared方法即可。

/**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */    protected void onLooperPrepared() {
    }
退出/结束方法:quit()方法或quitSafely()方法。实际上是调用了looper.quit方法和looper.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;
    }
最后还有一点,就是在上面的例子中我们构造Handler的时候传入了Looper,通过HandlerThread的getLooper(),源码如下。这里如果线程没有start活着一些其它原因,该线程没有处于一种存活的状态会返回null,

   /**
     * 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() {      
 if (!isAlive()) {    
       return null;
        }        // If the thread has been started, wait until the looper has been created. 
      synchronized (this) {       
    while (isAlive() && mLooper == null) {    
           try {
                    wait();

                }
 catch (InterruptedException e) {
                }
            }
        }        return mLooper;
    }
该函数有可能阻塞,目的是为了防止获取的looper对象为空,使用了wait方法,并且使用了局部synchronized,锁住了当前的对象,那什么时候唤醒等待呢?当然是初始化完该线程关联looper对象的地方,也就是run方法。也就是你构造完HandlerThread必须调用start方法。对于synchronized和notifyAll和wait一些线程同步处理的操作,可以参考一些资料。

synchronized (this) {
            mLooper = Looper.myLooper(); 
          notifyAll();    
   }
4

结束
这里我们学习了HandlerThread类的简单用法以及内部源码以及实现原理。
同时也介绍了一些面试必问handler的消息机制。so,当再有面试官问你文章最开始的在一个子线程(工作线程)中怎么使用handler的问题,你可以说下怎么使用,同时最后说一个android提供的类-HandlerThread也很不错哦!!

扫一扫在手机打开

评论
已有0条评论
0/150
提交
热门评论
相关推荐
今日要闻
换一批
热点排行