深入理解Android异步线程

为什么要有异步线程?

因为有些操作是耗时的,比如大量计算啊,网络下载啊等,并且这些耗时操作的结果是需要反应在UI组件上的,比如进度条,计算结果等,如果这些耗时操作放在主线程里,就是阻碍主线程,从而导致用户交互被阻碍,出现假死,甚至ANR(Application not respond)。由此,异步线程的作用就是专门用户处理此类耗时操作,同时不阻碍主线程交互,当耗时操作过程中或者结束后,将结果反应在组件上。

Android上实现异步线程机制有两种方式:Handler 和 AsyncTask。
Handler模式是给每一个人物创建一个新的线程(Thread),任务完成后通过Handler实例向UI主线程发送消息,更新相关组件,完成界面的更新,这种方式比较精细,但是代码会相对比较臃肿,并且如果多个线程任务同时进行,无法对线程进行精确控制。
AsyncTask是Android为了更加方便地创建异步线程任务而提供的新的工具类,从Android1.5开始就有android.os.AsyncTask,实质上AsyncTask也是对Handler 和 Thread的更优秀的封装。

首先介绍一下AsyncTask的用法。
先来看看AsyncTask的定义:
Public abstract class AsyncTask<泛型1,泛型2,泛型3>{}

三种泛型类型分别代表“启动任务执行时需要的参数”,“后台任务执行过程中的进度”,“后台计算结果的类型”。有些场合并不需要参数,此时便可以使用java.lang.Void。
一个异步任务的执行一般包括以下几个步骤:
1. Execute(Params… params): 执行一个异步任务,需要我们再代码中调用此方法,启动异步任务;
2. onPreExecute() : 在execute被调用后则执行,实际它是存在于execute函数中,一般用来在执行后台耗时操作前,对即将更新的UI组件做一些标记;
3.doInBackGround(Params… params) : 调用Execute后,执行完OnPreExecute后,则开始执行耗时操作,此方法接收的是输入参数和返回计算结果,可以在此方法中实时更新计算进度,调用publishProgress(Progress… values);
4. onProgressUpdate(Progress… values) :在调用publishProgress(Progress… values)时执行,直接可以进行UI组件的更新;
5.onPostExecute(Result result) : 当后台操作结束后,此方法被调用,计算结果将作为参数传递到此方法中,直接将计算结果(即doInBackGround 函数返回结果)显示在UI组件上。

注意点:
1. AsyncTask的实例必须在主线程中创建;
2. 实例的执行execute函数必须在主线程中调用;
3. 一个AsyncTask的实例只能执行一次,否则会抛出异常;

接下来是一个简单地示例,每隔0.5s将进度增加1,当完成100%后,显示完成:

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends Activity {
    private static final String TAG = "ASYNC_TASK";

    private Button execute;
    private Button cancel;
    private ProgressBar progressBar;
    private TextView textView;

    private MyTask mTask;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        execute = (Button) findViewById(R.id.execute);
        execute.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //注意每次需new一个实例,新建的任务只能执行一次,否则会出现异常
                mTask = new MyTask();
                mTask.execute("http://www.baidu.com");

                execute.setEnabled(false);
                cancel.setEnabled(true);
            }
        });
        cancel = (Button) findViewById(R.id.cancel);
        cancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //取消一个正在执行的任务,onCancelled方法将会被调用
                mTask.cancel(true);
            }
        });
        progressBar = (ProgressBar) findViewById(R.id.progress_bar);
        textView = (TextView) findViewById(R.id.text_view);

    }

    private class MyTask extends AsyncTask<String, Integer, String> {


        //onPreExecute方法用于在执行后台任务前做一些UI操作
        @Override
        protected void onPreExecute() {
            Log.i(TAG, "onPreExecute() called");
            textView.setText("loading...");
        }


        int progress = 0;
        //doInBackground方法内部执行后台任务,不可在此方法内修改UI
        @Override
        protected String doInBackground(String... params) {
            Log.e(TAG, "" + progress);
            while(progress < 100){
            publishProgress(progress++);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            }
            return "finished";
        }

        //onProgressUpdate方法用于更新进度信息
        @Override
        protected void onProgressUpdate(Integer... progresses) {
            Log.i(TAG, "onProgressUpdate(Progress... progresses) called");
            progressBar.setProgress(progresses[0]);
            textView.setText("loading..." + progresses[0] + "%");
        }

        //onPostExecute方法用于在执行完后台任务后更新UI,显示结果
        @Override
        protected void onPostExecute(String result) {
            Log.i(TAG, "onPostExecute(Result result) called");
            textView.setText(result);

            execute.setEnabled(true);
            cancel.setEnabled(false);
        }

        //onCancelled方法用于在取消执行中的任务时更改UI
        @Override
        protected void onCancelled() {
            Log.i(TAG, "onCancelled() called");
            textView.setText("cancelled");
            progressBar.setProgress(0);

            execute.setEnabled(true);
            cancel.setEnabled(false);
        }
    }
}

Xml布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <Button
        android:id="@+id/execute"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="execute"/>
    <Button
        android:id="@+id/cancel"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:enabled="false"
        android:text="cancel"/>
    <ProgressBar 
        android:id="@+id/progress_bar" 
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:progress="0"
        android:max="100"
        style="?android:attr/progressBarStyleHorizontal"/>
</LinearLayout>

注意加上网络访问权限:

<uses-permission android:name="android.permission.INTERNET"/>

软件运行界面如下:
更新进度
运行结束

AsyncTask的使用已经比较清晰,使用起来也十分方便,下面就要详细看一下AsyncTask究竟是个什么鬼。

AsyncTask源码片段分析。

从上图中可以看出,Asynctask中包含一个Status的枚举类,该枚举类中包含:PENDING(未执行),RUNNING(执行中),FINISHED(已完成)。

首先看一下AsyncTask中的成员变量:

private static final int CORE_POOL_SIZE =5;//5个核心工作线程
    private static final int MAXIMUM_POOL_SIZE = 128;//最多128个工作线程
    private static final int KEEP_ALIVE = 1;//空闲线程的超时时间为1秒

    private static final BlockingQueue<Runnable> sWorkQueue =
            new LinkedBlockingQueue<Runnable>(10);//等待队列

    private static final ThreadPoolExecutorsExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
            MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue,sThreadFactory);//线程池是静态变量,所有的异步任务都会放到这个线程池的工作线程内执行。

从execute(Params… params)入手,可以看到:

public final AsyncTask<Params,Progress, Result> execute(Params... params) {  
        if (mStatus != Status.PENDING) {  
            switch (mStatus) {  
                case RUNNING:  
                    throw newIllegalStateException("Cannot execute task:"  
                            + " the taskis already running.");  
                case FINISHED:  
                    throw newIllegalStateException("Cannot execute task:"  
                            + " the taskhas already been executed "  
                            + "(a task canbe executed only once)");  
            }  
        }  

        mStatus = Status.RUNNING;  

        onPreExecute();//运行在ui线程,在提交任务到线程池之前执行  

        mWorker.mParams = params;  
        sExecutor.execute(mFuture);//提交任务到线程池  

        return this;  
}  

上半部分是根据status状态值做出相应反应,然后改变状态,调用onPreExecute()函数,但是后面的mWorker , sExecutor 以及 mFuture有点摸不着头脑。

当我们创建AsyncTask时,其构造函数则完成了mWorker和mFuture的实例:

  public AsyncTask() {  
        mWorker = new WorkerRunnable<Params, Result>() {  
            public Result call() throws Exception {  
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
                return doInBackground(mParams);  
            }  
        };  

        mFuture = new FutureTask<Result>(mWorker) {  
            @Override  
            protected void done() {  
                Message message;  
                Result result = null;  

                try {  
                    result = get();  
                } catch (InterruptedException e) {  
                    android.util.Log.w(LOG_TAG, e);  
                } catch (ExecutionException e) {  
                    throw new RuntimeException("An error occured while executing doInBackground()",  
                            e.getCause());  
                } catch (CancellationException e) {  
                    message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,  
                            new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));  
                    message.sendToTarget();//取消任务,发送MESSAGE_POST_CANCEL消息  
                    return;  
                } catch (Throwable t) {  
                    throw new RuntimeException("An error occured while executing "  
                            + "doInBackground()", t);  
                }  

                message = sHandler.obtainMessage(MESSAGE_POST_RESULT,  
                        new AsyncTaskResult<Result>(AsyncTask.this, result));//完成任务,发送MESSAGE_POST_RESULT消息并传递result对象  
                message.sendToTarget();  
            }  
        };  
}

可以看出,在AsyncTask的默认构造函数中,完成了对mWorker 和 mFuture的定义。
mWorker是WorkerRunnable这个实现了Callable接口中的call() 方法的抽象内部类的实例,在其中调用doInBackground()函数:

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {  
        Params[] mParams;  
    }   

而mFuture是FutureTask的实例,而FutureTask是实现了RunnableFuture接口的,RunnableFuture接口又继承于Runnable 和 Future接口。其中FutureTask是一个可以中途取消的用于异步计算的类。 可以看到,mFuture的实例对象的done()方法中,如果捕捉到了CancellationException,就会发送一条”MESSAGE_POST_CANCEL”消息,当然,如果顺利进行,就会发送“MESSAGE_POST_RESULT” 消息 。这消息是怎么发送的呢?这些消息又是如何创建的呢?首先关于发送,我们可以看到,这些消息都是与一个sHandler对象关联的,而这个sHandler对象是AsyncTask 内部类InternalHandler的实例,而InternalHandler正式继承于Handler:

  private static final int MESSAGE_POST_RESULT = 0x1; //显示结果  
    private static final int MESSAGE_POST_PROGRESS = 0x2;    //更新进度  
    private static final int MESSAGE_POST_CANCEL = 0x3;  //取消任务  

    private static final InternalHandler sHandler = new InternalHandler();  

 private static class InternalHandler extends Handler {  
        @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  
                 //调用AsyncTask.finish方法  
                    result.mTask.finish(result.mData[0]);  
                    break;  
                case MESSAGE_POST_PROGRESS:  
                    //调用AsyncTask.onProgressUpdate方法  
                 result.mTask.onProgressUpdate(result.mData);  
                    break;  
                case MESSAGE_POST_CANCEL:  
                 //调用AsyncTask.onCancelled方法  
                    result.mTask.onCancelled();  
                    break;  
            }  
        }  
    }  

由此看出,可以根据消息类型做不同处理:任务完成,任务进度更新,任务取消三类。当任务完成时,即MESSAGE_POST_RESULT,会调用AsyncTask的finish()方法,其中finish()方法的定义如下:

          private void finish(Result result) {  
        if (isCancelled()) result = null;  
        onPostExecute(result);  //调用onPostExecute显示结果  
        mStatus = Status.FINISHED;  //改变状态为FINISHED  
}

原来在finish()方法中是调用onPostExecute(Result result),这也就才解释了,为什么doInBackground(Params… params)的执行结果会“自动“传到onPostExecute(Result… result)中!当然,如果传过来的消息是”MESSAGE_POST_CANCEL“,那么就会调用AsyncTask 的onCanncelled()方法取消任务;
然后我们看看,在mFuture中这些MESSAGE是如何创建的呢?说完了上面的sHandler为何物,下面看看 MESSAGE的创建过程:

//发送取消任务的消息  
    message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,  
            new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));  
    message.sendToTarget();

  //发送显示结果的消息  
    message = sHandler.obtainMessage(MESSAGE_POST_RESULT,  
             new AsyncTaskResult<Result>(AsyncTask.this, result));  
message.sendToTarget();  

这个AsyncTaskResult又是个啥?

 private static class AsyncTaskResult<Data> {  
        final AsyncTask mTask;  
        final Data[] mData;  

        AsyncTaskResult(AsyncTask task, Data... data) {  
            mTask = task;  
            mData = data;  
        }  
    }

原来,AsyncTaskResult是一个封装了一个AsyncTask的实例和某种类型的数据集!

在处理消息的时候,我们首先获取到AsyncTaskResult的实例:

 AsyncTaskResult result = (AsyncTaskResult) msg.obj;

然后再使用 :

//完成任务
result.mTask.finish(result.mData[0]);
//更新任务进度
result.mTask.onProgressUpdate(result.mData);

至此,我们稍微深入地理解了AsyncTask部分源码片段。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值