前言
涉及知识点:
- 消息机制:Handler、Looper 和 MessageQueue
- AsyncTask 基本使用
- 实现一个简单的 SimpleAsyncTask
消息机制
Android 中的消息机制由三大部分组成:Handler、Looper 和 MessageQueue.
Looper 就是创建一个 MessageQueue,然后进入一个死循环里面不断地读取 MessageQueue 里面的消息,Handler 就是消息的创建者,处理者。
消息机制由图我们可以看出,消息队列被封装在了 MessageQueue 中,通过 Looper 和线程 Thread 关联起来。而 Handler 又通过 Looper 关联,因而 Handler 最终和线程、线程的消息队列关联上来了。这也就是为什么我们常说更新 UI 的 Handler 必须要在主线程中创建,因为只有在主线程中创建,Handler 才能和主线程的消息队列关联上,这样 handleMessage 才会执行在 UI 线程,这时候更新 UI 才是线程安全的。
题外话:为什么常说只能在 UI 线程更新 UI ?
子线程可以有好多个,但如果每个子线程都直接对UI元素进行操作,界面会混乱不堪,线程会面临安全问题,虽然可以通过加锁机制来解决线程的安全问题,但是加锁会降低运行效率, 所以主线程(UI线程)并没进行加锁限制多线程访问, 可能这就是“出于性能优化考虑”。
既然没有对多线程访问进行限制,而且子线程依然有进行UI操作的需求,那么该如何解决呢?
所以Android规定只能在主线程中进行UI元素的更改,你们一帮菜鸡子线程如果还执意要来修改我管辖的用户界面 就必须先通知我(主线程),我来帮你们完成 :)
——知乎用户:大大大大头啊
创建自己的消息队列
我们知道了基本的消息机制。但是,要注意的是,Android 中除了 UI 线程,创建的工作线程默认是没有消息循环和消息队列的。
在非主线程直接 new Handler(); 会报错
Uncaught handler: thread Thread-8 exiting due to uncaught exception
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
所以,如果想让自己创建的工作线程 有消息循环和消息队列,并具有消息处理机制,就需要在线程中先调用 Looper.prepare() 来创建消息队列,然后调用 Loop.loop() 进入消息循环
public class MyloopThread extends Thread{
Handler mHandler;
public void run(){
Loop.prepare();
mHandler = new Handler(){
public void handleMessage(Message msg){
// process incoming message here
}
};
Loop.loop();
}
}
AsyncTask 基本使用
我们往往使用 Thread 创建子线程进行耗时操作,但是由于不能在子线程更新 UI,一般就会使用 Handler 发送消息给 UI 线程然后再更新。这个操作起来有点麻烦,在多个任务同时执行的时候,不易于对线程进行精细控制。于是 AsyncTask 应运而生。
public abstract class AsyncTask< Param, Progress, Result > ( ) //三个泛型类型: < 参数类型,后台执行任务的进度类型,返回的结果类型 > 如果不需要某个类型可以设置为 void
一个异步任务一般包含以下步骤:
AsyncTask实现一个简单的 AsyncTask
下面我们来实现一个简单的 AsyncTask,类名为 SimpleAsyncTask。与 AsyncTask 类似,提供了三个函数:onPreExecute( )、 doInBackground( )、onPostExecute( )。泛型参数为了方便只有一个 doInBackgroud( ) 函数返回值类型的泛型参数。SimpleAsyncTask 执行起来和 AsyncTask 基本一样。首先是 onPreExecute 函数在任务运行之前执行,而且运行在 UI 线程之中。doInBackgroud 运行在后台执行耗时操作,并且将结果返回。onPostExecute 含有一个参数,这个参数就是 doInBaskgroud 的返回结果,onPostExecute 执行在 UI 线程。
public abstract class SimpleAsyncTask<Result> {
private static final HandlerThread HT = new HandlerThread("SimpleAsyncTask",
Process.THREAD_PRIORITY_BACKGROUND);
static {
HT.start();
}
final Handler mUIHandler = new Handler(Looper.getMainLooper());
final Handler mAsyncHandler = new Handler(HT.getLooper());
/**
* @功能描述:onPreExecute 任务执行之前的初始化操作等
*/
protected void onPreExecute(){}
/**
* 后台执行任务
* @return 返回执行结果
*/
protected abstract Result doInBackground();
/**
* 返回结果传递给执行在 UI 线程的 onPostExecute
* @param result 执行结果
*/
protected void onPostExecuted(Result result){ }
public final SimpleAsyncTask<Result> excute () {
onPreExecute();
mAsyncHandler.post(new Runnable() {
@Override
public void run() {
postResult(doInBackground());
}
});
return this;
}
private void postResult(final Result result){
mUIHandler.post(new Runnable() {
@Override
public void run() {
onPostExecuted(result);
}
});
}
}
在 SimpleAsyncTask 里面首先创建了一个 HandlerThread(自带消息队列的 Thread),当线程启动之后就会构建它的消息队列,所以构建完成后,直接在静态代码块里面启动了该线程。然后创建了两个 Handler,分别关联 UI 线程和 HandlerThread 的子线程 mAsyncHandler。剩下三个函数已经解释过了,有需要的时候我们可以重写这三个方法。
execute 是执行的函数,里面先调用 onPreExecute,然后 doInBackground 函数被一个 Runnbale 包装通过 mAsyncTask 提交给了 HandlerThread 线程执行,当得到结果的时候又通过 mUIHandler 将结果提交到一个 Runnable 里面,这个 Runnbale 中执行了 onPostExecute。
下面是调用的示例代码:
new SimpleAsyncTask<String>() {
private void makeToast(String msg){
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
@Override
protected void onPreExecute() {
makeToast("onPreExecute");
}
@Override
protected String doInBackground() {
try{
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "doInBackground finish!";
}
@Override
protected void onPostExecuted(String s) {
makeToast("onPostExecuted"+s);
}
}.excute();
执行结果就是先 Toast:onPreExecute,延时 6 秒之后 Toast: onPostExecuted doInBackground finish!
执行结果喜欢就点个赞,有问题就留个言,你不说话我怎么知道你来过
网友评论