相信大家在Java中都已经接触过线程,就是像一条线一样一次执行相关的操作,特点就是同步的,顺序进行的。
但是,Android和Java的线程有一点区别在于就是在子线程中不能对UI组件进行相关操作,Android中的所有组件的操作要求在主线程(UI线程)中进行。如果我们在主线程中进行过多的耗时操作,有可能导致线程卡死,超过5s,程序就会被系统杀死。因此,就要使用多线程,将一些复杂的耗时操作移动到其他的线程中去执行,提高程序的运行效率。

子线程的创建

Android在一开始就会自动创建一个主线程。Android和Java一样,可以使用Thread类来开辟一个新的线程。

new Thread(new Runnable() {
            @Override
            public void run() {
            //耗时操作  
            }
        }).start();

主线程与子线程的通信方式

  • Handler+Thread方式
    以前的学习中, 有关于Handler的详细解释;

    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操作。