AsyncTask的基本使用及工作原理

public abstract class AsyncTask< Params, Progress, Result > 对于android中的处理异步线程的机制, 能够更加简单的处理异步任务. 如下图是该抽象类的大纲
AsyncTask的基本使用及工作原理_第1张图片

其中,doInBackground(Params… params)是一个抽象方法,我们继承AsyncTask时必须覆写此方法;onPreExecute()、onProgressUpdate(Progress… values)、onPostExecute(Result result)、onCancelled()这几个方法体都是空的,我们需要的时候可以选择性的覆写它们;execute((Params… params)是触发一个实例task任务时调用的;publishProgress(Progress… values)是final修饰的,不能覆写,只能去调用,我们一般会在doInBackground(Params… params)中调用此方法;另外,我们可以看到有一个Status的枚举类和getStatus()方法,Status枚举类中记录了该AsyncTask任务的三种状态, AsyncTask的初始状态为PENDING,代表待定状态,RUNNING代表执行状态,FINISHED代表结束状态,我们可以通过这几种状态,对AsyncTask进行启动,终止等操作。


第二部分 介绍其基本使用
以打开百度首页为例子,介绍task的使用步骤:
第一步: 继承抽象类AsyncTask构建子类,必须覆写doInBackground(String… params) 方法,其他方法根据需要调用或者覆写,在doInBackground(String… params)方法中,触发异步请求,也就是请求 http://www.baidu.com , 并通过覆写onPreExecute(), 将异步请求执行之前的UI界面进行处理.

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

        //doInBackground方法内部执行后台任务,不可在此方法内修改UI  
        @Override  
        protected String doInBackground(String... params) {  
            Log.i(TAG, "doInBackground(Params... params) called");  
            try {  
                HttpClient client = new DefaultHttpClient();  
                HttpGet get = new HttpGet(params[0]);  
                HttpResponse response = client.execute(get);  
                if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {  
                    HttpEntity entity = response.getEntity();  
                    InputStream is = entity.getContent();  
                    long total = entity.getContentLength();  
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();  
                    byte[] buf = new byte[1024];  
                    int count = 0;  
                    int length = -1;  
                    while ((length = is.read(buf)) != -1) {  
                        baos.write(buf, 0, length);  
                        count += length;  
                        //调用publishProgress公布进度,最后onProgressUpdate方法将被执行  
                        publishProgress((int) ((count / (float) total) * 100));  
                        //为了演示进度,休眠500毫秒  
                        Thread.sleep(500);  
                    }  
                    return new String(baos.toByteArray(), "gb2312");  
                }  
            } catch (Exception e) {  
                Log.e(TAG, e.getMessage());  
            }  
            return null;  
        }  

        //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);  
        }  

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

第二步:在UI线程中创建task实例 并触发
mTask = new MyTask();
mTask.execute(“http://www.baidu.com“);

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.main);  
    execute = (Button) findViewById(R.id.execute);
    cancel = (Button) findViewById(R.id.cancel); 
    progressBar = (ProgressBar) findViewById(R.id.progress_bar);  
    textView = (TextView) findViewById(R.id.text_view);   
    execute.setOnClickListener(new View.OnClickListener() {  
        @Override  
        public void onClick(View v) {  
            //注意每次需new一个实例,新建的任务只能执行一次,否则会出现异常  
            mTask = new MyTask();  
            mTask.execute("http://www.baidu.com");   
        }  
    });  
    cancel.setOnClickListener(new View.OnClickListener() {  
        @Override  
        public void onClick(View v) {  
            //取消一个正在执行的任务,onCancelled方法将会被调用 ,该方法只是将对应的task标记为cancel状态 并不是真正的取消线程的执行,线程在后台仍然继续执行 另 java中不能直接见到粗暴的结束某一个线程
            mTask.cancel(true);  
        }  
    });  
}     

第三步: 与UI通信 通过onPostExecute(String result) 方法将异步请求的结果传递给UI线程,更新UI界面.

 //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);  
        }  

以该例子对该抽象类public abstract class AsyncTask< Params, Progress, Result > 的三种泛型参数做如下介绍:
Params 是“启动任务执行的输入参数” 触发异步任务执行的时候会调用execute(Params… params),去执行一个异步任务, 该方法将参数传递给后台真正异步线程,onPreExecute(),在execute(Params… params)被调用后立即执行,一般用来在执行后台任务前对UI做一些标记。
Progress 是“后台任务执行的进度”,在进行doInBackground的过程中,可以通过调用publishProgress(Progress… values)将异步执行的进度告诉UI线程,UI线程通过onProgressUpdate(Progress… values)将后台执行的进度公布在UI界面中
Result 是”后台计算结果的类型”当doInBackground执行完毕或者出现异常时,会通过onPostExecute(Result result)方法对加载的结果传递给UI线程,并更新UI界面.

例子中传入的url = “http://www.baidu.com“是String类型的,并使用(int) ((count / (float) total) * 100)来表示加载的进度,所以是Integer类型,最后将获取的结果以new String(baos.toByteArray(), “gb2312”)类型传递给UI界面的,
因此例子中的MyTask是以String为请求参数 以Integer为进度值,以String为结果的异步任务去执行的. 所以创建实力的时候是 private class MyTask extends AsyncTask< String, Integer, String >
在特定场合下,并不是所有类型都被使用,如果没有被使用,可以用java.lang.Void类型代替。

总结:

一个异步任务的执行一般包括以下几个步骤:
1.execute(Params… params),在主线程通过调用task的该方法执行一个异步任务
2.onPreExecute(),在execute(Params… params)被调用后立即执行,在执行后台任务前对UI做一些标记, 或者界面的改变。
3.doInBackground(Params… params),在onPreExecute()完成后立即执行,用于执行较为费时的操作,此方法将接收输入参数和返回计算结果。在执行过程中可以调用publishProgress(Progress… values)来更新进度信息。
4.onProgressUpdate(Progress… values),在调用publishProgress(Progress… values)时,此方法被执行,直接将进度信息更新到UI组件上。
5.onPostExecute(Result result),当后台操作结束时,此方法将会被调用,计算结果将做为参数传递到此方法中,直接将结果显示到UI组件上。

在使用的时候,有几点需要格外注意:
1.异步任务的实例必须在UI线程中创建。
2.execute(Params… params)方法必须在UI线程中调用。
3.onPreExecute(),doInBackground(Params… params),onProgressUpdate(Progress… values),onPostExecute(Result result)这几个方法。都是系统调用的,不能手动调用,只要执行execute即可触发一个异步任务的执行.
4.不能在doInBackground(Params… params)中更改UI组件的信息, 该方法实在其他线程中执行的,onPreExecute(),onProgressUpdate(Progress… values),onPostExecute(Result result)这几个方法承接了异步线程处理的结果,都是运行在UI线程中,可直接更新UI界面.
5.一个任务实例只能执行一次,如果执行第二次将会抛出RuntimeException异常,也就是例子中mTask.execute(“http://www.baidu.com“) 只能执行一次.


第三部分 介绍其工作原理

从第二部分可知,一个异步任务的触发是由execute开始的,从这里作为入口看看AsyncTask真正的工作流程

public final AsyncTask execute(Params... params) {  
    if (mStatus != Status.PENDING) {  
        switch (mStatus) {  
            case RUNNING:  
                //如果该任务正在被执行则抛出异常  
                //值得一提的是,在调用cancel取消任务后,状态仍未RUNNING  
                throw new IllegalStateException("Cannot execute task:"  
                        + " the task is already running.");  
            case FINISHED:  
                //如果该任务已经执行完成则抛出异常  
                throw new IllegalStateException("Cannot execute task:"  
                        + " the task has already been executed "  
                        + "(a task can be executed only once)");  
        }  
    }  
    //改变状态为RUNNING  
    mStatus = Status.RUNNING;  
    //调用onPreExecute方法  
    onPreExecute();  
    mWorker.mParams = params;  
    sExecutor.execute(mFuture);  
    return this;  
}

从execute的代码段中,可知,对Status.PENDING状态的task在会首先调用onPreExecute()方法,来标记UI界面的状态,之后将params传递给mWorker,并执行sExecutor.execute(mFuture);
其中 sExecutor是java.util.concurrent.ThreadPoolExecutor的实例,用于管理线程的执行, 也就是在线程池中创建一个新的线程,用于执行后台任务.

private static final int CORE_POOL_SIZE = 5;  
private static final int MAXIMUM_POOL_SIZE = 128;  
private static final int KEEP_ALIVE = 10;  

//新建一个队列用来存放线程  
private static final BlockingQueue sWorkQueue =  
        new LinkedBlockingQueue(10);  
//新建一个线程工厂  
private static final ThreadFactory sThreadFactory = new ThreadFactory() {  
    private final AtomicInteger mCount = new AtomicInteger(1);  
    //新建一个线程  
    public Thread newThread(Runnable r) {  
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());  
    }  
};  
//新建一个线程池执行器,用于管理线程的执行  
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,  
        MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory); 

mWorker实际上是AsyncTask的一个的抽象内部类,且实现对象Callable的实例,它实现了Callable< Result >接口中的call()方法,
mFuture实际上是java.util.concurrent.FutureTask的实例,下面是它的FutureTask类的相关信息:

public class FutureTask<V> implements RunnableFuture<V> {  
public interface RunnableFuture<V> extends Runnable, Future<V> {  
    /** 
     * Sets this Future to the result of its computation 
     * unless it has been cancelled. 
     */  
    void run();  
} 

所以从代码中可以看到 其实mFuture是Runnable的子类


mWorker和mFuture在AsyncTask中的体现如下, 在执行onPreExecute()方法,标记了主线程之后,将参数传递给mWorker,再通过ThreadPoolExecutor的实例sExecutor触发了以mWorker为参数的mFuture方法,mFuture实例中会调用mWorker做后台任务,而覆写call方法的mWorker实例是将该任务设置为后台级别,并调用doInBackground方法开始执行后台任务,后台任务完成后系统会调用done方法,将后台结果发送出去.

private final WorkerRunnable mWorker;  
private final FutureTask mFuture;  

public AsyncTask() {  
    mWorker = new WorkerRunnable() {  
        //call方法被调用后,将设置优先级为后台级别,然后调用AsyncTask的doInBackground方法  
        public Result call() throws Exception {  
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
            return doInBackground(mParams);  
        }  
    };  

    //在mFuture实例中,将会调用mWorker做后台任务,完成后会调用done方法  
    mFuture = new FutureTask(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(AsyncTask.this, (Result[]) null));  
                message.sendToTarget();  
                return;  
            } catch (Throwable t) {  
                throw new RuntimeException("An error occured while executing "  
                        + "doInBackground()", t);  
            }  

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

mFuture实例对象的done()方法中,通过不通的情况会发送不同的由AsyncTaskResult< Result>()构建的消息,将不同的结果发送出去.如果捕捉到了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;  
        }  
    }  
}

由sHandler的代码可知,在sHandler实例对象的handleMessage(Message msg)方法里,使用AsyncTaskResult result = (AsyncTaskResult) msg.obj方式取得消息中附带的AsyncTaskResult消息
而AsyncTaskResult其实也是AsyncTask的一个内部类,是用来包装执行结果的一个类,让我们来看一下它的代码结构:

@SuppressWarnings({"RawUseOfParameterizedType"})  
private static class AsyncTaskResult {  
    final AsyncTask mTask;  
    final Data[] mData;  
    AsyncTaskResult(AsyncTask task, Data... data) {  
        mTask = task;  
        mData = data;  
    }  
}

可知这个AsyncTaskResult封装了一个AsyncTask的实例和某种类型的数据集.所以在发送消息时是将AsyncTask实例和后台结果result发送出去.

sHandler在处理消息时是如何使用这个task对象和数据集呢? 举例如下
result.mTask.finish(result.mData[0]);
result.mTask.onProgressUpdate(result.mData);

分析一:当InternalHandler类型的实例sHandler收到MESSAGE_POST_RESULT类型的AsyncTaskResult消息时,是通过传进来的AsyncTask.this来调用它自己的finish方法,代码如下,从finish方法中可以看到,其实是调动AsyncTask的onPostExecute(result)方法 显示结果.

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

总结

在调用execute(Params… params)方法后,execute方法会调用onPreExecute()方法,然后由ThreadPoolExecutor实例sExecutor执行一个带WorkerRunnable参数的FutureTask任务,这个过程中doInBackground(Params… params)将被调用,当后台任务结束以后,会调用FutureTask的done方法,将后台结果通过InternalHandler实例sHandler发送出去.发送的消息由AsyncTaskResult构建,包含了该异步实例task和后台任务的结果result. 对不同类型的消息,使用task实例调用不同的方法来完成与UI线程的通信.
1 MESSAGE_POST_PROGRESS消息:result.mTask.onProgressUpdate(result.mData);
如果被开发者覆写的doInBackground(Params… params)方法中调用了publishProgress(Progress… values)方法,则通过InternalHandler实例sHandler发送一条MESSAGE_POST_PROGRESS消息,更新进度,sHandler处理消息时onProgressUpdate(Progress… values)方法将被调用;
2 MESSAGE_POST_CANCEL消息:result.mTask.onCancelled();
如果遇到异常,则发送一条MESSAGE_POST_CANCEL的消息,取消任务,sHandler处理消息时onCancelled()方法将被调用;
3 MESSAGE_POST_RESULT消息:result.mTask.finish(result.mData[0]);
如果执行成功,则发送一条MESSAGE_POST_RESULT的消息,显示结果,sHandler处理消息时onPostExecute(Result result)方法被调用。
可知AsyncTask的本质就是对Thread+Handler的良好封装,(sExecutor,mFuture,mWorker 均属于Thread类,sHandler属于Handler类)减少了开发者处理问题的复杂度,提高了开发效率。

你可能感兴趣的:(AsyncTask的基本使用及工作原理)