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<String, Void, StringBuffer> {

        //在方法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,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_data"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="12dp"
        android:text="Medium Text" />

    <Button
        android:id="@+id/btn_start_async"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="获取数据" />
</LinearLayout>

在上面的例子中,我们先创建继承自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的作用)

方法executeOnExecutor
  • 3. SerialExecutor的execute方法

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

类SerialExecutor
  • 4. 线程池

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

mWorker的call()方法
  • 5. postResult()

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

postResult()方法
  • 6. postResult()

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

InternalHandler

四、拓展

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

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

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

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

THREAD_POOL_EXECUTOR

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,030评论 5 464
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,198评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 144,995评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,973评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,869评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,766评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,967评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,599评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,886评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,901评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,728评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,504评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,967评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,128评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,445评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,018评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,224评论 2 339

推荐阅读更多精彩内容

  • Android Handler机制系列文章整体内容如下: Android Handler机制1之ThreadAnd...
    隔壁老李头阅读 3,155评论 1 15
  • 简介 1. 线程分类 主线程(UI线程) : 处理和界面相关的事情. 子线程 : 处理耗时操作. Android中...
    王世军Steven阅读 905评论 0 2
  • Android中的线程 线程,在Android中是非常重要的,主线程处理UI界面,子线程处理耗时操作。如果在主线程...
    shenhuniurou阅读 747评论 0 3
  • Android开发者:你真的会用AsyncTask吗? 导读.1 在Android应用开发中,我们需要时刻注意保证...
    cxm11阅读 2,689评论 0 29
  • ”从24楼阳台看出去,天气,雾转多云,同住在这城市里,现在你又在哪里”,时间是件奇妙的武器,总能让人在不经意...
    若即若离you阅读 266评论 2 0