zoukankan      html  css  js  c++  java
  • Android HandlerThread简介

    Android应用中的消息循环由Looper和Handler配合完成,Looper类用于封装消息循环,Handler类封装了消息投递和消息处理等功能,系统默认情况下只有主线程(即UI线程)绑定Looper对象,因此在主线程中可以直接创建Handler的实例,但是在子线程中就不能直接new出Handler的实例了,因为子线程默认并没有Looper对象,此时会抛出RuntimeException异常。如果需要在子线程中使用Handler类,首先需要创建Looper类实例,这时可以通过Looper.prepare()和Looper.loop()函数来实现。

    Handler与谁相关联不是看声明在什么地方,是看与哪个线程的Looper挂钩。默认是主线程的Looper,因为主线程中默认就有了Looper、消息循环队列。

    不管是主线程(一般是我们的UI线程)还是子线程,只要有Looper的线程,别的线程就可以向这个线程的消息队列中发送消息和计划任务,然后做相应的处理。

    Android为我们提供了一个HandlerThread类,该类继承Thread类,类中有个MessageQueue消息队列;与普通Thread的差别就在于,它有个Looper成员变量。这个Looper其实就是对消息队列以及队列处理逻辑的封装,简单说就是消息队列+消息循环。使用Looper.prepare()和Looper.loop()两个函数创建Looper对象,而且使用wait/notifyAll解决了多线程中子线程1获取子线程2的Looper对象为空的问题。HandlerThread源码解释:

    public class HandlerThread extends Thread{
              //线程的优先级
               private int mPriority;
              //线程的id
               private int mTid=-1;
               private Looper mLooper;
               public HandlerThread(String name){
                          super(name);
                         //设置优先级为默认线程
                         mPriority=Process.THREAD_PRIORITY_DEFAULT;
               }
    
               public HandlerThread(String name,int priority){
                          super(name);
                         mPriority=priority;
               }
    
               //这个如果有需要的话可以继承重写,例如可以在里面声明一个Handler关联此线程。
               protected void onLooperPrepared(){
               public void run(){
                   //得到当前线程的id
                    mTid=Process.myTid;
                   //通过此方法就在此线程中创建了Looper 对象。这就是为什么我们要在调用线程的start()方法后
                   //才能得到Looper 对象即当调用Looper.myLooper()时不为Null。
                    Looper.prepare();
                   //同步代码块,意思就是当获得mLooper对象后,唤醒所有线程。
                   synchronized(this){
                         mLooper=Looper.myLooper();
                         notifyAll();
                   }
    
                  //设置线程的优先级
                  Process.setThreadPriority(mPriority);
                  //调用上面的方法(需要用户重写)
                  onLooperPrepared();
                  //建立了消息循环
                  Looper.loop();
                  mTid=-1;
               }
    
               public Looper getLooper(){
                        //线程死了,那就只有NULL了。
                         if(!isAlive()){
                            return null;
                        }
                       //同步代码块,正好和上面的形成对应,就是说只要线程活着并且我的looper为NULL,那么我就让你一直等。。。
                       synchronized(this){
                              while(isAlive()&&mLooper==null){
                                   try{
                                             wait();
                                       }catch(InterruptedException e){}
                              }
                       }
                                 return mLooper;
               }
              public boolean quit(){
                  Looper looper =getLooper();
                   if(looper!=null){
                       //退出消息循环
                       looper.quit();
                       return true;
                  }
                  return false;
            }
             //返回线程ID
             public int getThreadId(){
                   return mTid;
            }
    }

    结合Handler使用:

    Handler handler = new Handler(Looper, Callback);

    // Looper--> HandlerThread.getLooper();传入Callback参数,使自定义的HandlerThread的handlerMessage替换掉Handler原生的handlerMessage;

    Android中的Looper类,是用来封装消息循环和消息队列的一个类,用于在android线程中进行消息处理。handler其实可以看做是一个工具类,用来向消息队列中插入消息的。 

    (1) Looper类用来为一个线程开启一个消息循环。 
        默认情况下android中新诞生的线程是没有开启消息循环的。(主线程除外,主线程系统会自动为其创建Looper对象,开启消息循环。) 
        Looper对象通过MessageQueue来存放消息和事件。一个线程只能有一个Looper,对应一个MessageQueue。

    (2) 通常是通过Handler对象来与Looper进行交互的。Handler可看做是Looper的一个接口,用来向指定的Looper发送消息及定义处理方法。 
        默认情况下Handler会与其被定义时所在线程的Looper绑定,比如,Handler在主线程中定义,那么它是与主线程的Looper绑定。 
    mainHandler = new Handler() 等价于new Handler(Looper.myLooper()). 
    Looper.myLooper():获取当前进程的looper对象,类似的 Looper.getMainLooper() 用于获取主线程的Looper对象。 

    (3) 在非主线程中直接new Handler() 会报如下的错误: 
    E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception 
    E/AndroidRuntime( 6173): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() 
    原因是非主线程中默认没有创建Looper对象,需要先调用Looper.prepare()启用Looper。 

    (4) Looper.loop(); 让Looper开始工作,从消息队列里取消息,处理消息。 

        注意:写在Looper.loop()之后的代码不会被执行,这个函数内部应该是一个循环,当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。 

    (5) 基于以上知识,可实现主线程给子线程(非主线程)发送消息。 

        把下面例子中的mHandler声明成类成员,在主线程通过mHandler发送消息即可。 
        
        Android官方文档中Looper的介绍: 
    Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped. 

    Most interaction with a message loop is through the Handler class. 

    This is a typical example of the implementation of a Looper thread, using the separation of prepare() and loop() to create an initial Handler to communicate with the Looper. 

    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();  
          }  
    }  
  • 相关阅读:
    15. DML, DDL, LOGON 触发器
    5. 跟踪标记 (Trace Flag) 834, 845 对内存页行为的影响
    4. 跟踪标记 (Trace Flag) 610 对索引组织表(IOT)最小化日志
    14. 类似正则表达式的字符处理问题
    01. SELECT显示和PRINT打印超长的字符
    3. 跟踪标记 (Trace Flag) 1204, 1222 抓取死锁信息
    2. 跟踪标记 (Trace Flag) 3604, 3605 输出DBCC命令结果
    1. 跟踪标记 (Trace Flag) 1117, 1118 文件增长及空间分配方式
    0. 跟踪标记 (Trace Flag) 简介
    SpringBoot + Redis + Shiro 实现权限管理(转)
  • 原文地址:https://www.cnblogs.com/a284628487/p/3031714.html
Copyright © 2011-2022 走看看