一、Android Handler、Loop的简单使用
1、请看例子
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( 2000 );
} 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()
方法使之处理信息,直到循环结束。
(2)Handler有几个构造重载,如果构造时不提供Looper类对象参数,会获取当前线程的Looper对象,即将当前线程的消息循环作为Handler关联的消息循环。
(3)消息处理机制中,消息存放在一个消息队列中,而线程围绕这个队列进入一个无限循环,直到程序退出。
如果队列中有消息,线程就会把消息取出来,并分发给相应的Handler进行处理;
如果队列中没有消息,线程就会进入空闲等待状态,等待下一个消息的到来。
很明显的一点就是,我们要在子线程中调用Looper.prepare() 为一个线程开启一个消息循环,默认情况下Android中新诞生的线程是没有开启消息循环的。(主线程除外,主线程系统会自动为其创建Looper对象,开启消息循环。) Looper对象通过MessageQueue来存放消息和事件。一个线程只能有一个Looper,对应一个MessageQueue。 然后通过Looper.loop() 让Looper开始工作,从消息队列里取消息,处理消息。
值得注意的是:写在Looper.loop()之后的代码不会被执行,这个函数内部应该是一个循环,当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行
二、HandlerThread
1、上面这一切都可以用HandlerThread类来帮我们做这些逻辑操作,HandlerThread本质上就是一个普通Thread,只不过内部建立了Looper
2、创建HandlerThread
HandlerThread mThread = new HandlerThread("handler_thread");
3、启动HandlerThread
mThread.start();
4、退出循环
Looper是通过调用loop方法驱动着消息循环的进行: 从MessageQueue中阻塞式地取出一个消息,然后让Handler处理该消息,周而复始,loop方法是个死循环方法。
那如何终止消息循环呢?我们可以调用Looper的quit方法或quitSafely方法,二者稍有不同
HandlerThread mHandlerThread;
mHandlerThread.quit();
mHandlerThread.quitSafely();
相同点是quit()和quitSafely()调用之后都不再接受新的事件加入消息队列
当我们调用Looper的quit方法时,实际上执行了MessageQueue中的removeAllMessagesLocked方法,该方法的作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息。
当我们调用Looper的quitSafely方法时,实际上执行了MessageQueue中的removeAllFutureMessagesLocked方法,通过名字就可以看出,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。
无论是调用了quit方法还是quitSafely方法只会,Looper就不再接收新的消息。即在调用了Looper的quit或quitSafely方法之后,消息循环就终结了,这时候再通过Handler调用sendMessage或post等方法发送消息时均返回false,表示消息没有成功放入消息队列MessageQueue中,因为消息队列已经退出了。
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的特点
-
HandlerThread将loop转到子线程中处理,说白了就是将分担MainLooper的工作量,降低了主线程的压力,使主界面更流畅。
-
开启一个线程起到多个线程的作用。处理任务是串行执行,按消息发送顺序进行处理。HandlerThread本质是一个线程,在线程内部,代码是串行处理的。
-
但是由于每一个任务都将以队列的方式逐个被执行到,一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。
-
HandlerThread拥有自己的消息队列,它不会干扰或阻塞UI线程。
-
对于网络IO操作,HandlerThread并不适合,因为它只有一个线程,还得排队一个一个等着。