HandlerThread源码理解

一、概述

        本篇文章是对Android HandlerThread源码分析并应用,谈一下自己对HandlerThread的认知理解,并通过Demo来讲解它的使用方法。

二、HandlerThread是什么

2.1 概念

        从字面上来讲,它是 Handler + Thread 的组合,首先它是一个Thread线程,它是用来做耗时任务用的,其次在HandlerThread中已经实现好了Looper消息循环处理,我们在应用的时候只需要通过Handler发送具体的消息给HandlerThread执行对应的任务即可。

首先明确一点:Handler 是Android系统  同进程  不同线程间 通信用的。

为了理解上面这句话,我们引入进程和线程的概念

进程:进程是对运行时程序的封装,是系统进行资源调度和分配的的基本单位,实现了操作系统的并发。

线程:线程是进程的子任务,是CPU调度和分派的基本单位,用于保证程序的实时性,实现进程内部的并发,每个线程都独自占用一个虚拟处理器:独自的寄存器组,指令计数器和处理器状态。

两者的区别

1. 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程依赖于进程而存在。

2. 进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。(资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)

比如在某个Activity界面,默认有一个UI主线程,我们称为A线程,在里面创建一个Handler, 然后在创建一个线程B,在里面做耗时任务,待任务完成,在B中通过Handler发送消息,在A中更新UI显示,这样就相当于在把线程B中的消息分发到线程A中处理,从而完成了线程间通信。

为什么呢?因为线程A 和 线程B 可以共享数据段(Handler对象)。

2.2 特点

1. HandlerThread拥有自己的消息队列,它不会干扰或阻塞UI线程。

2. 优点是不会有堵塞,减少对性能的消耗,缺点是不能同时进行多任务的处理,需要排队,处理效率较低。

3. 与线程池注重并发不同,HandlerThread是一个窜行队列,因为HandlerThread背后只有一个线程。

三、源码解析

1. 两个构造方法

//构造方法,设置线程名称,默认线程优先级0
public HandlerThread(String name) {
    super(name);
    mPriority = Process.THREAD_PRIORITY_DEFAULT;
}


//构造方法,设置线程名称和自定义线程优先级
public HandlerThread(String name, int priority) {
    super(name);
    mPriority = priority;
}

2. onLooperPrepared,此方法是 Looper消息循环前的准备工作,可以重写。

/**
  * Call back method that can be explicitly overridden if needed to execute some
  * setup before Looper loops.
  * Looper消息循环前的准备工作,可以重写
  */
protected void onLooperPrepared() {

}

3. run方法:当线程调用start()方法时,回调此方法

@Override
public void run() {

    mTid = Process.myTid(); //线程id

    Looper.prepare(); // 创建Looper对象和消息队列MessageQueue

    synchronized (this) {  //通过同步锁机制获取当前线程的Looper对象

        mLooper = Looper.myLooper();

        notifyAll();  //通知getLooper方法looper已经创建成功,对应getLooper方法中的wait()
    }
    Process.setThreadPriority(mPriority); //设置线程优先级

    onLooperPrepared(); //重写此方法,作用是在消息循环之前进行一些准备工作

    Looper.loop(); //开启消息循环

    mTid = -1;
}

4. getLpoper()方法,与run()方法中有个同步锁机制,目的是为了准确且必定要获取当前线程的Looper对象

    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason 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.

此方法返回与此线程关联的Looper。如果此线程尚未启动或不处于活跃状态,
则此方法将返回null。如果此线程已启动,则此方法将阻塞,直到循环器已初始化。
     */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }

        boolean wasInterrupted = false;

        // If the thread has been started, wait until the looper has been created.
// 使用对象同步锁机制:如果线程已开启, 进入等待状态直到looper成功初始化.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait(); //等待Looper初始化完成,对应run方法中的notifyAll
                } catch (InterruptedException e) {
                    wasInterrupted = true;
                }
            }
        }

        /*
         * We may need to restore the thread's interrupted flag, because it may
         * have been cleared above since we eat InterruptedExceptions
         */
        if (wasInterrupted) {
            Thread.currentThread().interrupt();
        }

        return mLooper;
    }

为了理解上面synchronized同步锁机制,我们通过一个Demo来讲解

/**
 * author : me
 * date   : 22-11-14下午2:49
 * desc   : 线程测试类
 * version: 1.0
 */
public class TestMain {
    public static void main(String[] args){

        Object object = new Object();

        MyThreadRunnable myThread1 = new MyThreadRunnable(object,"thread11111");

        Thread test1 = new Thread(myThread1);//通过public Thread(Runnable runnable)方法创建线程test1

        test1.start();//开始启动

        try {

            Thread.sleep(6000);

            System.out.println("6秒后调用notifiyAll方法唤醒线程,让线程继续执行任务");

            synchronized (object) {
                object.notifyAll();
            }

        } catch (InterruptedException  e) {
            e.printStackTrace();
        }
    }
}


/**
 * author : me
 * date   : 22-11-14下午2:49
 * desc   :
 * version: 1.0
 */
public class MyThreadRunnable implements Runnable {

    private Object object;
    private String threadName;

    public MyThreadRunnable(Object object, String threadName) {
        this.object = object;
        this.threadName = threadName;
    }

    @Override
    public void run() {

        try {
            for (int i=1; i<6; i++) {
                if (i == 3) {
                    synchronized (this.object) {
                        System.out.println("当i==3时,线程调用wait方法 等待");
                        this.object.wait();
                    }
                }
                System.out.println(this.threadName + " i的值: " + i );
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

打印如下:

thread11111 i的值: 1
thread11111 i的值: 2
当i==3时,线程调用wait方法 等待
6秒后调用notifiyAll方法唤醒线程,让线程继续执行任务
thread11111 i的值: 3
thread11111 i的值: 4
thread11111 i的值: 5

通过此例子,再来理解上面3和4中的代码,就可以明白了。

5. getThreadHandler()方法

/**
     * @return a shared {@link Handler} associated with this thread
     * @hide
// 返回与该线程关联的handler对象
//注意这是一个隐藏的方法,无法直接通过 HandlerThread对象调用  getThreadHandler() 获取Handler
*/
    @NonNull
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }

此方法可以忽略,是一个hide方法,无法直接调用获取Handler, 一般还是得通过

public Handler(@NonNull Looper looper)方法来 获取handler对象, 比如

Handler mThreadHandler = new Handler(myHandlerThread.getLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                //消息处理
            }

}

6. quit()和quitSafely()方法

// 直接退出线程的looper消息循环,强制退出循环
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }


//待消息队列中剩余消息全部处理完毕后,退出线程的looper消息循环,安全退出消息循环
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

四、HandlerThread使用步骤

// 步骤1:创建HandlerThread实例对象
   HandlerThread mHandlerThread = new HandlerThread("handlerThread");

// 步骤2:启动线程
   mHandlerThread.start();

//如果不调用start方法,会报错:
11-15 17:30:32.347  8763  8763 E AndroidRuntime: Caused by: java.lang.NullPointerException: Attempt to read from field 'android.os.MessageQueue android.os.Looper.mQueue' on a null object reference
11-15 17:30:32.347  8763  8763 E AndroidRuntime:    at android.os.Handler.<init>(Handler.java:257)
11-15 17:30:32.347  8763  8763 E AndroidRuntime:    at android.os.Handler.<init>(Handler.java:162)


//通过报错的堆栈log信息找到源码:Handler.java:257 
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;  //这行代码报错,空对象异常
        mCallback = callback;
        mAsynchronous = async;
}
//looper为空对象,说明looper对象没有创建成功。 这也间接地表明 源码中通过同步锁机制必须保证要成功创建Looper对象的缘由。



// 步骤3:创建工作线程Handler 并 复写handleMessage()
//关联HandlerThread的Looper对象、实现消息处理操作与其他线程进行通信
  Handler workHandler = new Handler( mHandlerThread.getLooper() ) {
            @Override
            //消息处理
            public boolean handleMessage(Message msg) {
                .......
                //当然这里可以做耗时任务,因为是处于子线程中,不会堵塞UI线程,不会引起ANR
                return true;
            }
  });



// 步骤4:使用工作线程Handler向工作线程的消息队列发送消息
  Message msg = Message.obtain();
  msg.what = 1;
  workHandler.sendMessage(msg);



// 步骤5:一般在Activity的OnDestory()方法中,结束线程,即停止线程的Looper消息循环
  mHandlerThread.quit();

五、实例演示

/**
 * author : me
 * date   : 22-11-15  下午3:46
 * desc   : 主界面
 * version: 1.0
 */

public class MainActivity extends AppCompatActivity {


    private Button mButton1;
    private Button mButton2;

    private MyHandlerThread myHandlerThread;

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mButton1 = findViewById(R.id.btn_one);
        mButton2 = findViewById(R.id.btn_two);

        //1. 创建HandlerThread实例对象
        myHandlerThread = new MyHandlerThread("handlerThread test");

        //2. 启动线程
        myHandlerThread.start();

        //3. 创建工作线程Handler, 并复写handleMessage
        Handler mThreadHandler = new Handler(myHandlerThread.getLooper()) {

            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                if (msg.what == 1) {
                    try {
                        Log.e("test", "用线程睡眠6秒,模拟耗时任务,此任务是在子线程中处理的,所以不会引起ANR");
                        Thread.sleep(6000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.e("test", "====接收主线程中发来的消息  处理消息的线程名称为:" + Thread.currentThread().getName());
                } else if (msg.what == 2) {
                    try {
                        Log.e("test", "用线程睡眠12秒,模拟耗时任务,此任务是在子线程中处理的,所以不会引起ANR");
                        Thread.sleep(12000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.e("test", "====接收自定义线程中发来的消息  处理消息的线程名称为:" + Thread.currentThread().getName());
                }
            }
        };


        //点击此按钮: UI主线程 与 HandlerThread线程 进行通信
        mButton1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Message msg = Message.obtain();
                msg.what = 1;
                mThreadHandler.sendMessage(msg);
                Log.e("test", "调用线程的名称:" + Thread.currentThread().getName());
            }
        });

        //点击此按钮: 自定义线程 与 HandlerThread线程 进行通信
        mButton2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Thread thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message msg = Message.obtain();
                        msg.what = 2;
                        mThreadHandler.sendMessage(msg);
                        Log.e("test", "调用线程的名称:" + Thread.currentThread().getName());
                    }
                });
                thread.start();
            }
        });


        

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //退出app时,子线程中的looper要退出,终止消息循环
        myHandlerThread.quitSafely();
    }

}



/**
 * author : me
 * date   : 22-11-14下午6:09
 * desc   :
 * version: 1.0
 */
public class MyHandlerThread extends HandlerThread {
    public MyHandlerThread(String name) {
        super(name);
    }

    @Override
    protected void onLooperPrepared() {
        //如果需要执行某些Looper循环之前的设置,可以在该方法中处理
        super.onLooperPrepared();
        Log.e("test", "=====onLooperPrepared======");
    }

    @Override
    public boolean quitSafely() {
        Log.e("test", "=====quitSafely======");
        return super.quitSafely();
    }
}

当进入主界面,点击button1,然后退出app,打印log如下:

11-16 17:18:49.977 23277 23384 E test    : =====onLooperPrepared======
11-16 17:18:53.121 23277 23277 E test    : 调用线程的名称:main
11-16 17:18:53.121 23277 23384 E test    : 用线程睡眠6秒,模拟耗时任务,此任务是在子线程中处理的,所以不会引起ANR
11-16 17:18:59.122 23277 23384 E test    : ====接收主线程中发来的消息  处理消息的线程名称为:handlerThread test
11-16 17:21:33.309 23277 23277 E test    : =====quitSafely======

当进入主界面,点击button2,然后退出app,打印log如下:

11-16 17:22:43.080 23277 23620 E test    : =====onLooperPrepared======
11-16 17:22:44.758 23277 23622 E test    : 调用线程的名称:Thread-2
11-16 17:22:44.758 23277 23620 E test    : 用线程睡眠12秒,模拟耗时任务,此任务是在子线程中处理的,所以不会引起ANR
11-16 17:22:56.759 23277 23620 E test    : ====接收自定义线程中发来的消息  处理消息的线程名称为:handlerThread test
11-16 17:23:06.764 23277 23277 E test    : =====quitSafely======

好了,通过此Demo可以知道Handler可以在不同进程间通信,并且了解HandlerThread的使用方法啦。

总结

一. 关于Handler对象创建,取决于与哪个线程的Looper挂钩.

       1. Handler  uiHandler =  new Handler()  或 new  Handler(getMainLooper()) 或 new Handler(Looper.myLooper());   这3个方法都是创建主线程的Handler对象.

       2. Handler  workHandler = new Handler(new  HandlerThread("workthread").getLooper());  它是创建子线程中的Handler对象.

通过workHandler 这个来发送消息,然后在handleMessage方法中处理任务,因为是任务在子线程中处理,所以不会引起ANR现象.

二. 使用注意

        HandlerThread在线程内部创建Looper和消息队列,并且在线程启动之后开启消息循环。我们可以使用该Looper构建Handler对象从而绑定工作线程,然后通过Handler向消息队列发送消息,这样HandlerThread就可以取出消息并根据消息类型执行具体的后台任务,这里执行任务是在子线程中,它不会阻塞UI线程,所以不会引起ANR现象。

        由于HandlerThread的run方法是一个无限循环,因此当我们不再使用HandlerThread的时候应该调用它的quit或quitSafely方法来终止线程。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值