关于AsyncTask、HandlerThread的理解
在这之前 我们来先理解下消息循环机制
image.png
- 作用
实现多线程
在工作线程中执行任务,如 耗时任务
异步通信、消息传递
实现工作线程 & 主线程(UI线程)之间的通信,即:将工作线程的执行结果传递给主线程,从而在主线程中执行相关的UI操作
1、AsyncTask
1.21 基本使用
private class MyTask extends AsyncTask<String, Integer, String> {
// 方法1:onPreExecute()
// 作用:执行 线程任务前的操作
@Override
protected void onPreExecute() {
Log.w("TAG", "---開始加載---onPreExecute----");
// 执行前显示提示
}
// 方法3:onProgressUpdate()
// 作用:在主线程 显示线程任务执行的进度
@Override
protected void onProgressUpdate(Integer... progresses) {
Log.w("TAG", "---执行中---onProgressUpdate----"+ + progresses[0] + "%");
}
// 方法4:onPostExecute()
// 作用:接收线程任务执行结果、将执行结果显示到UI组件
@Override
protected void onPostExecute(String result) {
// 执行完毕后,则更新UI
Log.w("TAG", "---执行完成---onPostExecute----"+result);
}
// 方法5:onCancelled()
// 作用:将异步任务设置为:取消状态
@Override
protected void onCancelled() {
Log.w("TAG", "------onCancelled----");
}
@Override
protected String doInBackground(String... strings) {
try {
int count = 0;
int length = 1;
while (count<99) {
count += length;
// 可调用publishProgress()显示进度, 之后将执行onProgressUpdate()
publishProgress(count);
// 模拟耗时任务
Thread.sleep(50);
}
}catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
在需要的地方調用
/**
* 步骤:手动调用execute(Params... params) 从而执行异步线程任务
* 注:
* a. 必须在UI线程中调用
* b. 同一个AsyncTask实例对象只能执行1次,若执行第2次将会抛出异常
* c. 执行任务中,系统会自动调用AsyncTask的一系列方法:onPreExecute() 、doInBackground()、onProgressUpdate() 、onPostExecute()
* d. 不能手动调用上述方法
*/
task=new MyTask();
task.execute();
//执行结果
W/TAG: ---開始加載---onPreExecute----
W/TAG: ---执行中---onProgressUpdate----1%
W/TAG: ---执行中---onProgressUpdate----2%
W/TAG: ---执行中---onProgressUpdate----3%
W/TAG: ---执行中---onProgressUpdate----4%
W/TAG: ---执行中---onProgressUpdate----5%
....................
.....................
....................
W/TAG: ---执行完成---onPostExecute----null
1.2 具体原理介绍
-
AsyncTask
的实现原理 = 线程池 +Handler
其中:线程池用于线程调度、复用 & 执行任务;
Handler
用于异步通信
- 其内部封装了2个线程池 + 1个
Handler
,具体介绍如下:
执行任务前,通过 任务队列 线程池类(SerialExecutor)将任务按顺序放入到队列中;
通过同步锁 修饰execute()从而保证AsyncTask中的任务是串行执行的
SerialExecutor是使用ArrayDeque这个队列来管理Runnable对象的,ArrayDeque的offer()方法将传入的Runnable对象添加到队列的尾部,然后看mActive是不是空的,是空的就调用scheduleNext()在头部拿一个给它,也就是说每次当一个任务执行完毕后,下一个任务才会得到执行
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
//通过同步锁 修饰execute()从而保证AsyncTask中的任务是串行执行的
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
任務自行完成后通过
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
handleMessage中对消息的类型进行了判断,如果这是一条MESSAGE_POST_RESULT消息,就会去执行finish()方法,如果这是一条MESSAGE_POST_PROGRESS消息,就会去执行onProgressUpdate()方法。那么finish()方法的源码如下所示:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
总结
-
静态代码块 创建了THREAD_POOL_EXECUTOR 线程池,用来执行任务
-
任务队列 线程池 SerialExecutor,内部维护一个双向队列,任务调度,按需执行多个线程,按顺序排列
通过同步锁execute保证任务是串行执行,SerialExecutor是使用ArrayDeque这个队列来管理Runnable对象的,ArrayDeque的offer()方法将传入的Runnable对象添加到队列的尾部。然后看mActive是不是空的,是空的就调用scheduleNext()在头部拿一个给它,也就是说每次当一个任务执行完毕后,下一个任务才会得到执行。
- 任務自行完成后通过调用 postResult(Result result) ,通过handler发送消息,在handleMessage
对消息的类型进行了判断,如果这是一条MESSAGE_POST_RESULT消息,就会去执行finish()方法,如果这是一条MESSAGE_POST_PROGRESS消息,就会去执行onProgressUpdate()方法。
2、HandlerThread
线程间通信的时候,比如Android中常见的更新UI,涉及到的是子线程和主线程之间的通信,实现方式就是Handler+Looper,但是要自己手动操作Looper,不推荐,所以谷歌封装了HandlerThread类(类似于AsyncTask类)。
2、1 基本使用
//这个是主线程Handler 实例 还有一个知识点就是如果new Handler时候没有给她传递looper的话,他会默认的去当前运行线程的looper进行联系
MainHandler mainHandler = new MainHandler(this);
HandlerThread handlerThread = new HandlerThread("first");
handlerThread.start();
//这是子线程联系的handler 传入了handlerThread的looper
final Handler childHandler = new Handler(handlerThread.getLooper()) {
int count = 0;
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println("子线程 的handler 接受到数据 现在 将数据发送给 主线程 执行 "+Thread.currentThread().getName());
//发送消息给 主线程的handler 让她更新ui
Message.obtain(mainHandler, 1, ++count).sendToTarget();
}
};
// 开启一个子线程 调用childHandler 发送信息
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程发送数据 " + Thread.currentThread().getName());
//发送给handlerThread的handler
childHandler.sendEmptyMessage(1);
}
}).start();
}
});
//这个是主线程的Handler 定义成这种形式,可以防止内存泄露
static class MainHandler extends Handler{
private WeakReference<MainActivity> mainActivityWeakReference;
MainHandler(MainActivity mainActivity) {
this.mainActivityWeakReference =new WeakReference<>(mainActivity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
mainActivityWeakReference.get().textView.setText(msg.obj+"");
}
}
HandlerThread是不能更新UI的
正确使用是 我们仍然需要主线程的handler来更新ui,HandlerThread来接受其他子线程发送过来的消息,这些消息的处理不需要更新ui
在上面我们可以看见,我们开启了一个子线程,在子线程里面调用了 HandlerThread的childHandler发送信息,childHandler接受到消息,再调用主线程的mainHandler刷新ui。
通过上面的例子来看 ,大家肯定感觉 这不是多此一举么。一开始 我也是这么想的,后来 我仔细思考,这个HandlerThread 其实可以应用于这些情况下面:
比如,你同时有好几个子线程,你需要得到他们运算时候发出的信息,以前使用接口回调,还得实现的 ,如果 好几个子线程 岂不是要好几个接口。
比如 还有 一个子线程运算完 你想知道结果,也可以通过这个来实现。以前你使用callable,接口的话 也还是 比较复杂一点的。
2、1 工作原理
内部原理 = Thread类 + Handler类机制,即:
通过继承Thread类,快速地创建1个带有Looper对象的新工作线程
通过封装Handler类,快速创建Handler & 与其他线程进行通信
HandlerThread退出销毁
// 方式1:quit()
// 特点:效率高,但线程不安全
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
// 方式2:quitSafely()
// 特点:效率低,但线程安全
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
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;
}
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;
}
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
public int getThreadId() {
return mTid;
}
}
首先我们可以看到HandlerThread继承自Thread,因此在run()中的逻辑都是在子线程中运行的。
接下来就是两个关键的方法,run()和getLooper():
run()中可以看到是很简单的创建Looper以及让Looper工作的逻辑。
run()里面当mLooper创建完成后有个notifyAll(),getLooper()中有个wait(),这有什么用呢?因为的mLooper在一个线程中执行创建,而我们的handler是在UI线程中调用getLooper()初始化的。
也就是说,我们必须等到mLooper创建完成,才能正确的返回。getLooper();wait(),notify()就是为了解决这两个线程的同步问题。