HandlerThread完全解析

为什么需要HandlerThread?

HandlerThread本质上就是一个普通Thread,只不过内部建立了Looper,handlerthread实质上建立了一个模型,简化了像下面这种子线程与子线程之间的通信。
子线程和子线程之间的通信:

public class MainActivity extends AppCompatActivity {
    private Handler handler1;
    private Handler handler2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new MyThread1().start();
        new MyThread2().start();
    }
    class MyThread1 extends Thread {
        @Override
        public void run() {
            super.run();

            Looper.prepare();

            handler1 = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    System.out.println("threadName--" + Thread.currentThread().getName() + "messageWhat-" + msg.what);
                }
            };
            try {
                sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            handler2.sendEmptyMessage(2);
            Looper.loop();
        }
    }

    class MyThread2 extends Thread {
        @Override
        public void run() {
            super.run();
            Looper.prepare();
            handler2 = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    System.out.println("threadName--" + Thread.currentThread().getName() + "messageWhat-" + msg.what);
                }
            };
            try {
                sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            handler1.sendEmptyMessage(5);
            Looper.loop();
        }
    }
}
  1. 调用Looper类的 prepare() 方法可以为当前线程创建一个消息循环,调用loop() 方法使之处理信息,直到循环结束。
  1. Handler有几个构造重载,如果构造时不提供Looper类对象参数,会获取当前线程的Looper对象,即将当前线程的消息循环作为Handler关联的消息循环。
  2. 消息处理机制中,消息存放在一个消息队列中,而线程围绕这个队列进入一个无限循环,直到程序退出。如果队列中有消息,线程就会把消息取出来,并分发给相应的Handler进行处理;如果队列中没有消息,线程就会进入空闲等待状态,等待下一个消息的到来。

HandlerThread的用法

public class MainActivity extends AppCompatActivity {

    private HandlerThread myHandlerThread ;
    private Handler handler ;

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

        //创建一个线程,线程名字:handler-thread
        myHandlerThread = new HandlerThread( "handler-thread") ;
        //开启一个线程
        myHandlerThread.start();
        //在这个线程中创建一个handler对象
        handler = new Handler( myHandlerThread.getLooper() ){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //这个方法是运行在 handler-thread 线程中的 ,可以执行耗时操作
                Log.d( "handler " , "消息: " + msg.what + "  线程: " + Thread.currentThread().getName()  ) ;

            }
        };

        //在主线程给handler发送消息
        handler.sendEmptyMessage( 1 ) ;

        new Thread(new Runnable() {
            @Override
            public void run() {
             //在子线程给handler发送数据
             handler.sendEmptyMessage( 2 ) ;
            }
        }).start() ;

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        //释放资源
        myHandlerThread.quit() ;
    }
}

运行效果:

/com.app D/handler: 消息: 1  线程: handler-thread
/com.app D/handler: 消息: 2  线程: handler-thread

HandlerThread退出循环的方法
Looper是通过调用loop方法驱动着消息循环的进行: 从MessageQueue中阻塞式地取出一个消息,然后让Handler处理该消息,周而复始,loop方法是个死循环方法。那如何终止消息循环呢?

handlerThread.quit()
或
handlerThread.quitSafely()
  • 相同点:
    将不在接受新的事件加入消息队列。无论是调用了quit方法还是quitSafely方法只会,Looper就不再接收新的消息。即在调用了Looper的quit或quitSafely方法之后,消息循环就终结了,这时候再通过Handler调用sendMessage或post等方法发送消息时均返回false,表示消息没有成功放入消息队列MessageQueue中,因为消息队列已经退出了。
  • 不同点
  • 当我们调用Looper的quit方法时,实际上执行了MessageQueue中的removeAllMessagesLocked方法,该方法的作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息。
  • 当我们调用Looper的quitSafely方法时,实际上执行了MessageQueue中的removeAllFutureMessagesLocked方法,通过名字就可以看出,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。

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

HandlerThread要点

  1. HandlerThread将loop转到子线程中处理,说白了就是将分担MainLooper的工作量,降低了主线程的压力,使主界面更流畅,不阻塞UI线程。
  2. 处理任务是串行执行,按消息发送顺序进行处理。HandlerThread本质是一个线程,在线程内部,代码是串行处理的。
  3. 对于网络IO操作,HandlerThread并不适合,因为它只有一个线程,每一个任务都将以队列的方式逐个被执行到,一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。
  4. HandlerThread在不需要使用的时候需要手动的回收掉;

HandlerThread源码解析

我们通过

//创建一个线程,线程名字:handler-thread
        myHandlerThread = new HandlerThread( "handler-thread") ;
        //开启一个线程
        myHandlerThread.start();

创建和启动HandlerThread.
对应源码为:

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

其实我们就是初始化和启动了一个线程;然后我们看run()方法,可以看到该方法中调用了Looper.prepare(),Loop.loop();prepare()中创建了一个Looper对象,并且把该对象放到了该线程范围内的变量中(sThreadLocal),在Looper对象的构造过程中,初始化了一个MessageQueue,作为该Looper对象成员变量。
loop()就开启了,不断的循环从MessageQueue中取消息处理了,当没有消息的时候会阻塞,有消息的到来的时候会唤醒。
接下来,我们创建了一个handler

 handler = new Handler( myHandlerThread.getLooper() )

对应源码为:

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;
    }

myHandlerThread.getLooper()返回的就是我们在run方法中创建的mLooper。
如果你够细心你会发现,run方法里面当mLooper创建完成后有个notifyAll(),getLooper()中有个wait(),这是为什么呢?因为的mLooper在一个线程中执行,而我们的handler是在UI线程初始化的,也就是说,我们必须等到mLooper创建完成,才能正确的返回getLooper()。wait(),notify()就是为了解决这两个线程的同步问题。

参考

Android Handler、Loop 的简单使用

    原文作者:一只好奇的茂
    原文地址: https://www.jianshu.com/p/94bdecd38079
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞