上一篇博客中我在解释Handler的过程中提到了HandlerThread
,我们先回顾一下上一篇博客中对于Handler的定义:Handler会关联一个单独的线程和消息队列。Handler默认关联主线程,虽然要提供Runnable参数,但默认是直接调用Runnable中的run()方法。也就是默认下会在主线程执行,如果在这里面的操作会有阻塞,界面也会卡住。
也就是说如果我们使用Handler提供的方法handler.post(new Runnable())时候,如果使用不当的话一样会出现界面卡死,因为其默认管线的还是主线程,也就是说其还是会在主线程中简单地调用Runnable中的run()方法,这样耗时任务虽然在run()方法中,但是还是运行在主线程,所以同样会阻塞UI,接下来我们来看两个例子,就会更明白了,第一个例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33package cn.picksomething.testhandler;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
public class MainActivity extends Activity {
private String TAG = "TAG";
private Handler handler = new Handler();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler.post(new myRunnable());
Log.d(TAG, "onCreate-----The thread id is:" + Thread.currentThread().getId());
setContentView(R.layout.main);
}
class myRunnable implements Runnable{
public void run() {
Log.d(TAG, "run method is calling----");
Log.d(TAG, "The thread id is:" + Thread.currentThread().getId());
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
运行上述程序,程序启动6秒之后,才看到布局被加载,显示出TextView上的Hello World!字样
分析一下程序执行的过程,首先程序启动,就把myRunnable加入到当前线程的消息队列中,也就是handler默认关联的主线程消息队列,android的handler确实是异步机制,所以在handler.post(new myRunnable());之后,程序会继续执行,所以以后的语句会继续执行,会输出onCreate中的当前线程ID。但同时myRunnable()的run方法也在运行,一样输出run方法中的当前线程ID,然后让当前线程休眠6秒
通过输出结果和分析,我们可以得出handler和主线程是同一个线程,handler也是运行在主线程中的,这个时候若是执行耗时任务,显然会阻塞UI。当然肯定有解决办法的:
Android给我们提供了Looper这样一个类,上一遍博客中已经介绍过了,Android中每一个Thread都跟着一个Looper,Looper可以帮助Thread维护一个消息队列,每个Thread都只有一个消息队列。我们看看另一个例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38package cn.picksomething.testlooper;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.app.Activity;
import android.util.Log;
public class MainActivity extends Activity {
private String TAG = "TAG";
private Handler handler = null;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
HandlerThread mThread = new HandlerThread("handlerThread");
mThread.start();
handler = new Handler(mThread.getLooper());
handler.post(new myRunnable());
Log.d(TAG, "onCreate-----The Thread id is: " + Thread.currentThread().getId());
setContentView(R.layout.main);
}
class myRunnable implements Runnable{
public void run() {
Log.d(TAG, "myRunnable is running----");
Log.d(TAG, "The thread id is: " + Thread.currentThread().getId());
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
运行上述程序,过滤打印信息,我们看到LogCat
输出如下结果:1
2
3onCreate-----The Thread id is: xxx
myRunnable is running----
The thread id is: xxx
同时我们看到程序已启动,就立即输出了布局文件中的Hello world!
信息。
在这个例子中,用到了HandlerThread,在HandlerThread对象中可以通过getLooper方法获取一个Looper对象控制句柄,我们可以将这个Looper对象映射到一个Handler中来实现一个线程同步机制,所以就会有上面的结果了。这样就达到了多线程的效果
补充:HandlerThread
这个类,HandlerThread继承于Thread,所以它本质就是个Thread。HandlerThread类用于方便的创建一个含有looper
的线程类,looper用来创建handler类。我们一般不创建looper对象,直接调用HandlerThread即可,HandlerThread本身实现了循环处理消息的功能,不用再直接调用Looper.prepare()
方法,与普通Thread的差别就在于它有个Looper成员变量,这个Looper其实就是对消息队列以及队列处理逻辑的封装,简单说就是消息队列+消息循环