如果我们要执行多个耗时任务,第一时间想到的肯定是创建多个线程来执行任务,如果学过线程池那我们也可以使用线程池,那有没有比线程池更轻量级的呢?Handler了解一下~
纳尼! Handler也可以执行耗时任务吗? 那么问题就来了
先来试试能不能用
new Thread(new Runnable() {
@Override
public void run() {
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("lkx", String.valueOf(msg));
}
};
Message message = Message.obtain();
message.obj = "Hello";
handler.sendMessage(message);
}
}).start();
执行结果:
E/AndroidRuntime: FATAL EXCEPTION: Thread-2
Process: com.example.hello, PID: 12612
java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:227)
at android.os.Handler.<init>(Handler.java:129)
at com.example.hello.HandlerThreadActivity$2$1.<init>(HandlerThreadActivity.java:42)
at com.example.hello.HandlerThreadActivity$2.run(HandlerThreadActivity.java:42)
at java.lang.Thread.run(Thread.java:923)
果然…报错了,在报错信息中有一个特别眼熟的方法 Looper.prepare()
学完上一篇的Handler源码浅析,我们应该已经对Handler的流程很熟悉了,我们知道主线程之所以可以直接使用Handler,是因为APP启动的时候已经在ActivityThread.main()
中初始化了Looper对象,那我们能不能在子线程中自己初始化呢?
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare(); //初始化当前线程Looper对象
Looper looper = Looper.myLooper(); //获取当前线程的Looper对象
Handler handler = new Handler(looper) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("lkx", (String) msg.obj);
}
};
Message message = Message.obtain();
message.obj = "Hello";
handler.sendMessage(message);
Looper.loop(); //注意: Looper.loop内部是死循环,会阻塞导致后续代码无法执行
}
}).start();
执行结果: Hello Thread-2
上面代码我们看到Handler可以在子线程中使用
,并且handlerMessage会在子线程中进行回调,也就是我们可以在handlerMessage中进行耗时操作了。
那每次这样写也太麻烦了,有没有更简单的方法呢? 当然有!Android早已经给我们提供好了类,没错就是HandlerThread
~
//创建一个HandlerThread并调用start()方法
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();
//创建一个Handler,并传入HandlerThread的Looper对象
Handler handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("lkx", (String) msg.obj);
}
};
//创建5个下载任务
for (int i = 0; i < 5; i++) {
Message message = Message.obtain();
message.obj = "下载任务" + i;
handler.sendMessage(message);
}
执行结果:
下载任务0
下载任务1
下载任务2
下载任务3
下载任务4
从第一眼来看,HandlerThread继承Thread,也就是说HandlerThread应该是个线程
。
public class HandlerThread extends Thread {}
name: 线程名
priority: 线程优先级
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
既然是个线程,那执行的入口应该是run()方法
run()方法跟我们上面自定义的子线程使用Handler好像很相似,先调用Looper.prepare()
初始化,然后调用Looper.loop()
开启循环
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
getLooper()这里非常设计的非常巧妙,为了保证可以正常拿到Looper对象,如果Looper对象为空会调用wait()
释放锁进入等待状态,同时等待run()
方法初始化,当run()
方法拿到Looper对象后会调用notifyAll()
方法通知,然后这里的wait()
会被唤醒,代码继续往下走,直到返回Looper对象。
public Looper getLooper() {
if (!isAlive()) {
return null;
}
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
当不使用的时候,我们需要关闭Looper的循环,这个时候我们可以调用HandlerThread.quit()
或者HandlerThread.quitSafely()
停止循环,这两个的区别就是前者直接停止,后者是等任务执行完在停止。
@Override
protected void onDestroy() {
super.onDestroy();
if (mHandlerThread!=null) {
mHandlerThread.quit();
}
}
HandlerThread本质上就是一个Thread
,只不过内部已经帮我们开启了一个Looper循环,我们可以多次通过Handler发消息到MessageQueue中,Handler会通过Looper的循环器将消息传递到handleMessage中,这个循环器将会始终开启着,所以我们不使用的时候一定要及时关闭。