Android学习笔记16 多线程编程之AsyncTask剖析

在之前的博客中,已经详细地介绍了Android中异步消息处理机制的原理和整个流程,Handler、Message、MessageQueue、Looper各自的作用相信大家都已经很熟悉了。今天介绍的是多线程编程方面又一个重要的角色——AsyncTask。

一、概述
二、AsyncTask基本使用
三、AsyncTask源码
四、拓展

一、AsyncTask是什么

在Handler异步消息处理机制中,通过Handler发送消息处理消息,我们可以在非UI线程里对UI界面进行更新。但是,在实际使用中,因为要操作线程和Handler,过程可能会稍微有点复杂,重复的代码还是比较多的。

AsyncTask,翻译为异步任务,它封装了Thread和Handler,可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。通过AsyncTask,我们可以很方便地完成一些需要异步执行的任务。当然,需要注意的一点是,AsyncTask不适合特别耗时的后台任务,所以对于特别耗时的任务来说,建议使用线程池等。

二、AsyncTask怎么用

简介

  1. AsyncTask是一个抽象类,一般在使用过程中我们会创建一个任务类,继承自AsyncTask,AsyncTask有三个泛型参数,分别是Params, Progress,Result,其中Params是参数的类型,Progress是后台任务执行进度的类型,Result是后台任务返回结果的类型,如果没有具体的类型就用Void代替。

  2. AsyncTask提供了4个重要的方法,分别是onPreExecute(),doInBackground(),onProgressUpdate(),onPostExecute(),其中doInBackground主要是执行耗时操作,onPreExecute主要是做一些准备工作,它是在UI线程里执行的,onProgressUpdate用于进度更新,onPostExecute则是用于发布结果。

  3. 主线程里实例化AsyncTask并调用execute方法来启动即可。

实例

下面我们通过一个实例来了解AsyncTask的基本用法,实例主要是通过网络请求获取服务器的数据,并把数据展示在界面上。

/**
 * Created by JackalTsc on 2016/7/25.
 */
public class AsyncTaskActivity extends Activity {

    private TextView tvData;
    private String dataUrl = "http://115.159.149.87:8080/testssm/user/usertest";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_activity_asynctask);
        initView();
    }

    private void initView() {
        tvData = (TextView) findViewById(R.id.tv_data);
        Button btnStartAsync = (Button) findViewById(R.id.btn_start_async);
        btnStartAsync.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //执行异步任务
                new taskGetData().execute(dataUrl);
            }
        });
    }

    private class taskGetData extends AsyncTask {

        //在方法doInBackground()之前UI线程里调用.
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            tvData.setText("正在获取数据...");
        }

        //这里是执行耗时操作
        @Override
        protected StringBuffer doInBackground(String... params) {

            StringBuffer result = new StringBuffer("获取到的数据为:");

            //使用HttpURLConnection进行网络请求获取数据
            HttpURLConnection connection = null;

            try {

                URL mURL = new URL(params[0]);
                connection = (HttpURLConnection) mURL.openConnection();
                connection.setConnectTimeout(5000);

                if (connection.getResponseCode() == 200) {

                    InputStream is = connection.getInputStream();
                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
                    String data;
                    try {
                        while ((data = bufferedReader.readLine()) != null) {
                            result.append(data);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (connection != null) {
                    connection.disconnect();
                }
            }
            return result;
        }

        //获取进度更新
        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
        }

        //doInBackground方法获取到数据后调用
        @Override
        protected void onPostExecute(StringBuffer result) {
            super.onPostExecute(result);
            tvData.setText(result);
        }
    }
}

布局文件很简单,layout_activity_asynctask.xml,代码如下:




    

    

在上面的例子中,我们先创建继承自AsyncTask的异步任务类taskGetData,因为是直接根据url来获取数据,这里我们传入参数类型为String,返回类型为StringBuffer,任务类里重写了方法onPreExecute,让TextView先显示文字 “ 正在获取数据... ”,重写doInBackground方法进行网络请求获取数据,返回result,最后result会传递到onPostExecute方法里,我们在这里更新UI界面,显示数据。当然,创建好异步任务类后,我们通过new taskGetData().execute(dataUrl)来执行异步任务,这样就可以了。

三、AsyncTask源码

这部分我们主要是看下AsyncTask的源码,了解AsyncTask的实现原理。这里展示的只是关键的几步代码,要详细了解的话还是自己去看源码比较好。

  • 1. execute()方法

当我们传入参数启动异步任务时,调用的是方法execute()。execute方法内部实现是调用executeOnExecutor方法,传入一个串行的线程池sDefaultExecutor和之前我们传入的参数。

AsyncTask的execute()
  • 2. executeOnExecutor()方法

接着看方法executeOnExecutor,先执行onPreExecute(),也就是我们可以重写来在执行前做初始化工作的地方,然后传入参数mFuture给SerialExecutor的execute方法,线程池开始执行。(mFuture是FutureTask实例,FutureTask是个并发类,充当Runnable的作用)

Android学习笔记16 多线程编程之AsyncTask剖析_第1张图片
方法executeOnExecutor
  • 3. SerialExecutor的execute方法

查看SerialExecutor的execute方法。可以看到,方法中先是调用offer()方法,把传入的FutureTask实例插入到任务队列中,之后如果没有正在活动的任务,就调用scheduleNext()执行下个任务。同时当一个AsyncTask完成后,AsyncTask会继续执行其它任务直到都执行完。FutureTask的run方法会调用mWorker的call()方法。

Android学习笔记16 多线程编程之AsyncTask剖析_第2张图片
类SerialExecutor
  • 4. 线程池

AsyncTask中有两个线程池和一个Handler,其中线程池SerialExecutor用于任务排队,线程池THREAD_POOL_EXECUTOR用于任务执行。在AsyncTask的构造方法中,有下面这段代码。可以看到,在mWorker的call()方法中会调用doInBackground方法,之后把得到的结果传递给postResult。

Android学习笔记16 多线程编程之AsyncTask剖析_第3张图片
mWorker的call()方法
  • 5. postResult()

再看postResult()方法,不难发现,这里先获取Handler,然后发送消息。

Android学习笔记16 多线程编程之AsyncTask剖析_第4张图片
postResult()方法
  • 6. postResult()

最后,我们查看AsyncTask内部的Handler,不难发现,接收到消息后,如果是MESSAGE_POST_RESULT标识,那么继续调用onPostExecute,这样整个过程就结束了。

Android学习笔记16 多线程编程之AsyncTask剖析_第5张图片
InternalHandler

四、拓展

在查看AsyncTask的源码时,我们可以看到其中用到了两个线程池。这里简单地对线程池作个介绍,算是初步接触,以后再作详细学习。

进程是一个“执行中的程序”。通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度。

线程的引入可以减小开销,但是创建和销毁时难免有开销。引入线程池的好处的优点有三个,一是重用线程池中的线程,可以避免因为线程的创建和销毁而带来的性能开销。二是有效控制线程池的最大并发数,避免大量线程间因互相抢占系统资源而导致的阻塞现象。三是能够对线程进行简单的管理,提供定时执行及指定间隔循环执行的功能。

在AsyncTask类里,我们可以看到其中有创建线程池THREAD_POOL_EXECUTOR:

Android学习笔记16 多线程编程之AsyncTask剖析_第6张图片
THREAD_POOL_EXECUTOR

点击查看更多关于多线程编程的内容

你可能感兴趣的:(Android学习笔记16 多线程编程之AsyncTask剖析)