必威体育Betway必威体育官网
当前位置:首页 > IT技术

HandlerThread 的使用及原理

时间:2019-11-08 02:13:27来源:IT技术作者:seo实验室小编阅读:78次「手机版」
 

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.

handlerthreadAndroid API提供的一个方便、便捷的类,使用它我们可以快速的创建一个带有Looper的线程。Looper可以用来创建Handler实例。注意:start()仍然必须被调用。

1.定义

一个Android 已封装好的轻量级异步类

2. 作用

实现多线程

工作线程中执行任务,如 耗时任务

异步通信、消息传递

实现工作线程 & 主线程(UI线程)之间的通信,即:将工作线程的执行结果传递给主线程,从而在主线程中执行相关的UI操作

3. 优点

方便实现异步通信,即不需使用 “任务线程(如继承Thread类) + Handler”的复杂组合

4.代码实例

原料:

Button 点击按钮去执行请求随机数的任务

Textview 在界面上更新随机数

HandlerThread 创建Looper 与 Handler 绑定

预期效果 : 点击Button 在界面更新 一次随机数

  public class MainActivity extends APPCompatActivity {
    TextView tv;
    Button btn;   
   //与Ui线程绑定的Handler实例。用来更新界面
    private Handler mUiHandler = new Handler(){
    public void handlemessage(Message msg) {
        switch (msg.what) {
            case 2:
                tv.setText((String)msg.obj);
                break;
            default:
                break;
        }
    };
};
    protected void onCreate(Bundle savedinstanceState) {
        super.onCreate(savedInstanceState);
        setcontentView(R.layout.activity_main);
        tv = (TextView) findViewById(R.id.tv);
        btn = (Button) findViewById(R.id.btn);
        MyhandlerThread = new HandlerThread("handlerThreadName"); // 创建HandlerThread实例
        //开始运行线程
        MyhandlerThread.start(); 
        Looper loop = MyhandlerThread.getLooper();
        //创建Handler与该线程绑定。
        final   Handler WorkerHandler = new Handler(loop) {
            @Override
            public void handleMessage(Message msg) {
                  Log.v("tag", Thread.currentThread().getName());
                super.handleMessage(msg);
                try {
                    Thread.sleep(1000);  //等于 DownLoadApkFile 
                     //模拟延时处理代表可以执行耗时操作 请求网络数据 ,IO 流
                           Message Mymsg = Message.obtain();
                                   Mymsg .what=2;
                                   Mymsg .obj=msg.obj;
                           mUiHandler.sendMessage(Mymsg);
                           //final String result=(String)msg1.obj; // 第二种方法 直接切换到handler的线程中更新
                            //mUiHandler.post(new Runnable() {
                            // @Override
                            //public void run() {
                            // tv.setText(result);
                                          // }
                                       // });
                } catch (InterruptedException e) {
                    e.printstacktrace();
                }
            
              
            }
        }
        
    @Override
    protected void onDestroy() {
        super.onDestroy();
        MyhandlerThread.quit();
    //  结束线程,即停止线程的消息循环
    }
    }

以下是对于上述代码的解释

MyhandlerThread : 继承自Thread 作用创建实现了 Looper 和 Messagequeue

WorkerHandler : 在工作线程 在里边执行耗时方法 执行完成后 向mUiHandler 发送Message 。

mUiHandler :在主线程中创建的方法 用做更新UI。

执行结果很简单,就是 每点一次button 就会更新 一次随机数

HandlerThread 原理

步骤一 构造方法

public class HandlerThread extends Thread {

int mpriority;

int mTid = -1;

Looper mLooper;

     // HandlerThread类有2个构造方法
     // 区别在于:设置当前线程的优先级参数,即可自定义设置 or 使用默认优先级
    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
   public HandlerThread(String name, int priority) {
    super(name);
    mPriority = priority;
}
}

HandlerThread(String name) 参数为线程名称,线程优先级Process.THREAD_PRIORITY_DEFAULT。

HandlerThread(String name, int priority),name为线程名称,priority为设置的线程优先级。

步骤二 Looper创建

HandlerThread其实还是一个线程,它跟普通线程有什么不同?

protected void onLooperPrepared() {
                }
 源码分析:此处调用的是父类(Thread类)的start(),最终回调HandlerThread的run()
@Override
public void run() {
    // 1. 获得当前线程的id
    mTid = Process.myTid();
     // 2. 创建1个Looper对象 & MessageQueue对象
    looper.prepare();
    synchronized (this) { 
       //创建Looper实例
        mLooper = Looper.myLooper();//生成Looper  =myLooper()是通过返回sThreadlocal.get() 获取线程里的looper对象
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();//空方法,在Looper创建完成后调用,可以自己重写逻辑
    Looper.loop();//死循环,不断从MessageQueue中取出消息并且交给Handler处理
    mTid = -1;
}

调用的 Looper.prepare();

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)); //在threadLocal新建一个looper
}

步骤3:创建工作线程Handler & 复写handleMessage()

  • 源码分析:handlerThread.getLooper()

  • 作用:获得当前HandlerThread线程中的Looper对象

    public Looper getLooper() {
        // 若线程不是存活的,则直接返回null
        if (!isAlive()) {
            return null;
        } 
        // 若当前线程存活,再判断线程的成员变量mLooper是否为null
        // 直到线程创建完Looper对象后才能获得Looper对象,若Looper对象未创建成功,则阻塞
        synchronized (this) {
    
    
        while (isAlive() && mLooper == null) {
            try {
                // 此处会调用wait方法去等待
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    // 上述步骤run()使用 持有锁机制 + notifyAll()  获得Looper对象后
    // 则通知当前线程的wait()结束等待 & 跳出循环
    // 最终getLooper()返回的是在run()中创建的mLooper对象
    return mLooper;   }
    

步骤四 退出方法 (quit 和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;
    }

quit和quitSafely都是退出HandlerThread的消息循环。其分别调用Looper的quit和quitSafely方法。

quit方法会将消息队列中的所有消息移除(延迟消息和非延迟消息)。

quitSafely会将消息队列所有的延迟消息移除,非延迟消息派发出去让Handler去处理。quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息

HandlerThread适合处理本地IO读写操作(数据库,文件),因为本地IO操作大多数的耗时属于毫秒级别,对于单线程 + 异步队列的形式 不会产生较大的阻塞。而网络操作相对比较耗时,容易阻塞后面的请求,因此在这个HandlerThread中不适合加入网络操作。

文章最后发布于: 2018-12-30 11:09:26

相关阅读

手淘猜你喜欢的智能测图工具是如何使用的?

手淘中猜你喜欢流量占比非常高,所以商家不得不越来越重视推荐的流量。淘宝商家想要想要更多获取猜你喜欢流量,图片精细化的运营自然

Kafka的安装与使用

广告系统设计与实现(九)  9.1 Kafka 基础知识 9.1.1 消息系统 点对点消息系统:生产者发送一条消息到queue,一个queue可以有很

Keil N01:的软件逻辑分析仪( logic analyzer)使用

在keil MDK中软件逻辑分析仪很强的功能,可以分析数字信号,模拟化的信号,CPU的总线(UART、IIC等一切有输出的管脚),提供调试函数机制,用

日常小记之DBeaver简单使用操作

本人电脑为win10系统。 之前连接数据库都使用navicat(用于连接mysql数据库得一个图像化数据库连接工具,非常简易好上手)但是由于此

word中制表位的使用方法,用于编辑公式

点击段落旁边的小按钮,然后选择制表位,先设置居中的制表位:制表位位置写上多少多少字符,对齐方式选择居中, 然后同理设置右对齐的制表

分享到:

栏目导航

推荐阅读

热门阅读