相信大家在Java中都已经接触过线程,就是像一条线一样一次执行相关的操作,特点就是同步的,顺序进行的。
但是,Android和Java的线程有一点区别在于就是在子线程中不能对UI组件进行相关操作,Android中的所有组件的操作要求在主线程(UI线程)中进行。如果我们在主线程中进行过多的耗时操作,有可能导致线程卡死,超过5s,程序就会被系统杀死。因此,就要使用多线程,将一些复杂的耗时操作移动到其他的线程中去执行,提高程序的运行效率。
子线程的创建
Android在一开始就会自动创建一个主线程。Android和Java一样,可以使用Thread类来开辟一个新的线程。
new Thread(new Runnable() {
@Override
public void run() {
//耗时操作
}
}).start();
主线程与子线程的通信方式
- Handler+Thread方式
以前的学习中, 有关于Handler的详细解释;
http://blog.csdn.net/bistusim/article/details/52152454
handler发送Message消息,然后在主线程中的handler的回调方法中接受并处理消息。 - runOnUiThread更新主线程
Activity.runOnUiThread(Runnable);这是一个Activity的一个方法。把需要更新的UI代码创建在Runnable中。
runOnUiThread(new Runnable() {
@Override
public void run() {
//更新UI操作
}
});
//源码
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
- View.post(Runnable)更新主线程
textview.post(new Runnable() {
@Override
public void run() {
//更新UI }
});
//源码
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Assume that post will succeed later
ViewRootImpl.getRunQueue().post(action);
return true;
}
//通过源码可以看出,以上两种方法实质上都是获得了UI线程的Handler,然后使用post(Runnable)进行相关的更新操作。
- AsyncTask
AsyncTask是Google提供的一个封装过的后台任务类,适用于简单的异步处理。
使用时需要继承AsyncTask类,并且至少要重写两个方法;
- doInBackground(Params…) 后台执行,比较耗时的操作都可以放在这里。注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。
- onPostExecute(Result) 相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回。
除了上面的两个必须重写的方法外,还有onPreExecute()方法,当任务执行之前开始调用此方法。
AsyncTask定义了三种泛型类型 Params,Progress和Result。
- Params 启动任务执行的输入参数,比如HTTP请求的URL,或者String,Integer等。
- Progress 后台任务执行的百分比。
- Result 后台执行任务最终返回的结果,比如String。会传递给onPostExecute()方法。
线程安全
如果代码所在的进程中有多个子线程在同时运行,而这些子线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全。
老师还提到一个比较特别的方法,ProgressBar.setProgress()可以在子线程中直接使用,这看似违背了不能在子线程中进行更新UI的操作,但是看了源码发现,它实际还是获得了当前的UI线程进行更新UI操作。