Handler-HandlerThread

2019年04月12日 523点热度 0人点赞 1条评论

思考一下,假如我们需要同时下载A和B,下载A需要6s,下载B需要5s,在它们下载完成后Toast信息出来即可,此时HandlerThread便是一种解决方式之一。那么HandlerThread到底是什么?

  • HandlerThread就是一种线程。
  • HandlerThread和普通的Thread之间的区别就是HandlerThread在创建的时候会提供自己该线程的Looper对象

我们在Actvity创建时系统会自动帮我们初始化好主线程的Looper,然后这个Looper就会管理主线程的消息队列。但是在我们创建子线程时,系统并不会帮我们创建子线程的Looper,需要我们自己手动创建,如下:

    new Thread(){
        @Override
        public void run() {
            super.run();
            Looper.prepare();
            Handler mHandler = new Handler(Looper.myLooper());
            Looper.loop();
        }
    }.start();

所以HandlerThread就在内部帮我们封装了Looper的创建过程,从源码可以看到,HandlerThread集成于Thread,然后覆写run方法,进行Looper的创建,从而通过getLooper方法暴露出该线程的Looper对象

/* HandlerThread 源码 */
public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    ...
    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;//默认优先级
    }
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    @Override
    public void run() {
        mTid = Process.myTid(); //获取线程的tid
        Looper.prepare();// 创建Looper对象
        synchronized (this) {
            mLooper = Looper.myLooper();//获取looper对象
            notifyAll();//唤醒等待线程
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();// 该方法可通过覆写,实现自己的逻辑
        Looper.loop();//进入循环模式
        mTid = -1;
    }
    public Looper getLooper() {//获取HandlerThread线程中的Looper对象
        if (!isAlive()) {// 当线程没有启动或者已经结束时,则返回null
            return null;
        }
        // If the thread has been started, wait until the looper has been created.
        //当线程已经启动,则等待直到looper创建完成
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();//休眠等待
                } catch (InterruptedException e) {
                }
            }
        }
        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;
    }
    ...
}   

应用

很多时候,在HandlerThread线程中运行Loop()方法,在其他线程中通过Handler发送消息到HandlerThread线程。通过wait/notifyAll的方式,有效地解决了多线程的同步问题。
示例代码:

// Step 1: 创建并启动HandlerThread线程,内部包含Looper
HandlerThread handlerThread = new HandlerThread("gityuan.com");
handlerThread.start();
// Step 2: 创建Handler
Handler handler = new Handler(handlerThread.getLooper());
// Step 3: 发送消息
handler.post(new Runnable() {
        @Override
        public void run() {
            System.out.println("thread id="+Thread.currentThread().getId());
        }
    });

或者 handler.postDelayed(Runnable r, long delayMillis)用于延迟执行。

实例:使用HandlerThread同时下载A和B

public class HandlerThreadActivity extends AppCompatActivity {
    private TextView tv_A, tv_B;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_thread);
        tv_A = (TextView) findViewById(R.id.txt_dlA);
        tv_B = (TextView) findViewById(R.id.txt_dlB);
        final Handler mainHandler = new Handler();
        final HandlerThread downloadAThread = new HandlerThread("downloadAThread");
        downloadAThread.start();
        Handler downloadAHandler = new Handler(downloadAThread.getLooper());
        // 通过postDelayed模拟耗时操作
        downloadAHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getApplicationContext(), "下载A完成", Toast.LENGTH_SHORT).show();
                mainHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        tv_A.setText("A任务已经下载完成");
                    }
                });
            }
        }, 1000 * 5);
        final HandlerThread downloadBThread = new HandlerThread("downloadBThread");
        downloadBThread.start();
        Handler downloadBHandler = new Handler(downloadBThread.getLooper());
        // 通过postDelayed模拟耗时操作
        downloadBHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getApplicationContext(), "下载B完成", Toast.LENGTH_SHORT).show();
                mainHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        tv_B.setText("B任务已经下载完成");
                    }
                });
            }
        }, 1000 * 7);
    }
}

Horry

一个专门收集Android面试题的网站

文章评论

您需要 登录 之后才可以评论