1. 前言
和很多其他的GUI库一样,Android的UI线程也是不安全的,也就是说,要想更新应用程序中的U元素,必须在主线程中进行,否则就会报错。一个应用程序启动之后,系统会默认的创建一个"main"的主线程,这个线程是应用于界面工具包(android.widgeth和android.view)交互的地方。在很多时候,进行开发工作的时候,会进行一些耗时的操作,如网络连接、数据库查询等,如果这些工作全部在主线程中进行,就有可能造成拥堵,导致界面进程就被阻塞,影响客户体验。耗时的任务需要在子线程,然后根据任务的结果来更新相应的UI控件。对于这种情况,Android提供了一套异步消息处理机制,完美地解决了在子线程中进行UI操作的问题。
2. 方法总结
这是我个人使用过的方法,如果有什么缺漏或者补充,可以联系我指出
1) Activity.runOnUIThread(Runnable)
2) Handler
3) AsyncTask
3. Activity.runOnUIThread(Runnable)
这一种方法是最简单的,只需要在子线程中需要更新子线程的地方直接调用就好,这会直接将子线程切回到主线程,由于不是新线程,所以尽量不在里面进行过多的耗时操作。下面是一个简单的例子
new Thread(new Runnable() {
@Override
public void run() {
//耗时操作
runOnUiThread(new Runnable() {
@Override
public void run() {
//进行UI操作
}
});
}
}).start();
4. Hander
Android中的异步消息处理主要由4个部分组成:Message、Handler、MessageQueue和Looper。
Message: 是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程中交换数据,what可以用来携带标记,arg1和arg2可以携带一些整型数据,使用onj字段可以携带一个Object队形对象
Hander: 主要用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()方法,而发送的消息经过一系列地辗转处理后,最终会传递到Handler的handlMessage()方法中
MessageQueue: 就是消息队列,它主要用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中直到i被处理。每个线程中只会有一个MessageQueue对象
Looper: Looper管理着每个线程中的MessageQueue,调用Looper的loop()方法后,会进入到一个无限循环中,不断取出MessageQueue中存在的消息,传递到Handler中的handleMessage()方法中进行处理。每个线程也只会有一个Looper
异步处理消息的基本过程为,首先在主线程中创建一个Handler对象,并重写handleMessage()方法。然后当子线程需要进行UI操作时,创建一个Message对象并通过Handler发送出去。之后这条消息会被添加到MessageQueue的队列中等待被处理,Looper会一直尝试从MessageQueue中取出待处理消息,分发到Handler的handleMessge()中进行处理。由于Handler对象是在主线程中进行,所以可以大胆地进行UI操作,下面是一个流程图
Handler.sendMessage例子
private static final int SUCESS = 1;
private static final int FAIL = 2;
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case SUCESS:
//UI操作
break;
case FAIL:
//UI操作
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
//耗时操作
//发送消息
Message message = handler.obtainMessage();
message.what = 1;
handler.sendMessage(message);
}
}).start();
}
Handler.postdelayed()方法可以用来实现一个循环操作,同样可以进行UI操作,下面是简单的示例代码
Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Runnable runnable = new Runnable() {
@Override
public void run() {
//UI操作
//循环执行
handler.postDelayed(this, 2000);
}
};
handler.postDelayed(runnable, 2000);
}
5. AsyncTask
AsyncTask是对异步消息处理机制很好的封装,使得即使对异步处理消息机制完全不了解的同学也可以简单地从子线程切换到主线程
AsyncTask是一个抽象类,需要创建一个子类去继承它,继承时可以为其指定3个泛型参数,Params、Progress、Result
Params: 在执行AsyncTask时需要传入的参数,可用于在后台任务中使用
Progress: 在后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位
Resutl: 当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型
下面对方法进行简单的介绍
onPreExecute(): 这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作
doInBackground(Params..): 这个方法中的所有代码都会在子线程中进行,所有的耗时操作都应该在这里处理。一旦任务完成可以通过return语句将任务的执行结果返回。请注意,这个方法中是不能进行UI操作的,如果需要更新UI状态,可以调用publishProgress(Progress...)方法来完成
onProgressUpdate(Progress..): 当后台任务中调用了publishProgress(Progress...)方法后,onProgressUpdate(Progress..)方法会快就被调用了,该方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的操作
onPostExecute(Result): 但后台任务执行完毕并通过return语句进行返回时,这个方法很快就被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据进行一些UI操作,比如提醒任务执行的结果等。
下面是简单的示例代码
public class ExampleTask extends AsyncTask{
@Override
protected void onPreExecute() {
//初始化UI
super.onPreExecute();
}
@Override
protected Boolean doInBackground(Void... params) {
//耗时操作,不能直接操作UI,可以通过publishProgress(Progress...)更新UI
return null;
}
@Override
protected void onPostExecute(Boolean aBoolean) {
//处理doInBackground(Params...)更新请求
super.onPostExecute(aBoolean);
}
@Override
protected void onProgressUpdate(Integer... values) {
//根据doInBackground(Params...)处理结果更新UI
super.onProgressUpdate(values);
}
}
6. 参考书籍
《
第一行代码
——Android》 郭霖