Android AsyncTask基础

AsyncTask是Android为了简化异步操作而封装的异步任务操作抽象类。当我们需要在程序中执行耗时的异步操作时,我们可以考虑使用AsyncTask来实现。

AsyncTask的使用比较简单,由于AsyncTask是一个抽象类,我们需要写一个类来继承AsyncTask,继承AsyncTask需要指定三个泛型参数,参数分别表示为:
(1)Params:在执行AsyncTask时需要传入的参数,比如我们输入的String类型URL
(2)Progress:后台任务执行中返回的进度值类型,如果需要在界面上显示当前进度,则就用Progress指定的泛型作为进度单位。
(3)Result:耗时操作完成后返回的结果,如果需要对结果进行返回,那么就用这里指定的泛型作为返回的类型。

实现AsyncTask类还需要重写相应的回调方法,一般我们用到的有如下四个方法,他们都是自动被调用的,记得不要手动的调用:
(1)doInBackground(Void... params):必须重写,异步执行耗时操作
(2)onPreExecute():执行耗时操作前被调用,通常用来完成一些初始化操作
(3)onProgressUpdate(Void... values):将doInBackground方法返回的值传递给该方法,在doInBackground中调用publishProgroess()方法可以且来更新进度
(4)onPostExecute(Void result):耗时的异步任务完成后回调该方法

需要说明一点:这几个方法中,只有doInBackground方法是在子线程中执行的,其他的方法都是在主线程中执行的。

启动和取消这个异步任务的方法分别是:
(1)myAsyncTask.execute() 其中该任务
(2)myAsyncTask.cancel() 取消该任务

我们已经大概了解了AsyncTask的基础知识,接下来就以如下三个点做一些实例,加深一下印象。


这里写图片描述

(一)观察AsyncTask子类方法的执行顺序
首先写一个最简单的AsyncTask子类,传入的参数都是空值, 然后在每一个方法中都打印出一句话,主要是为了便于观察他们的执行顺序。

package com.adan.asynctaskdome;

import android.os.AsyncTask;
import android.util.Log;

/**
 * @author: xiaolijuan
 * @description:
 * @projectName: AsyncTaskDome
 * @date: 2016-03-04
 * @time: 12:47
 */
public class MyAsyncTask extends AsyncTask<Void, Void, Void> {
    @Override
    protected Void doInBackground(Void... params) {
        Log.d("TAG", "doInBackground");
        return null;
    }

    @Override
    protected void onPreExecute() {
        Log.d("TAG", "onPreExecute");
        super.onPreExecute();
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        Log.d("TAG", "onPostExecute");
        super.onPostExecute(aVoid);
    }

    @Override
    protected void onProgressUpdate(Void... values) {
        Log.d("TAG", "onProgressUpdate");
        super.onProgressUpdate(values);
    }
}

MainActivity的代码

package com.adan.asynctaskdome;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;


public class MainActivity extends Activity implements View.OnClickListener {
    private Button button1, button2, button3;

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

        new MyAsyncTask().execute();
    }

}

运行结果:


这里写图片描述

这里的 方法没有执行,是因为我们需要在doInBackground(Void... params)中提供publishProgress(Progress...)方法用于显示进度信息,如下所示:

package com.adan.asynctaskdome;

import android.os.AsyncTask;
import android.util.Log;

/**
 * @author: xiaolijuan
 * @description:
 * @projectName: AsyncTaskDome
 * @date: 2016-03-04
 * @time: 12:47
 */
public class MyAsyncTask extends AsyncTask<Void, Void, Void> {
    @Override
    protected Void doInBackground(Void... params) {
        Log.d("TAG", "doInBackground");
        publishProgress();
        return null;
    }

    @Override
    protected void onPreExecute() {
        Log.d("TAG", "onPreExecute");
        super.onPreExecute();
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        Log.d("TAG", "onPostExecute");
        super.onPostExecute(aVoid);
    }

    @Override
    protected void onProgressUpdate(Void... values) {
        Log.d("TAG", "onProgressUpdate");
        super.onProgressUpdate(values);
    }
}

运行效果:


这里写图片描述

从打印的结果很容易就看出方法的执行顺序了吧,如下:

onPreExecute——>onProgressUpdate(调用了publishProgress方法才会调用该方法)——>doInBackground——>onPostExecute
(二)异步加载网络图片
首先一点先别忘记了,我们既然是要访问网络,千万不能忘记了在配置文件中添加网络权限哦

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

LoadBitmapActivity,我们使用一个ImageView用来加载我们的这张网络图片的,然后再在中间放置一个进度条,用来显示进程
LoadBitmapActivity

package com.adan.asynctaskdome;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

/**
 * @author: xiaolijuan
 * @description: 加载图片
 * @projectName: AsyncTaskDome
 * @date: 2016-03-04
 * @time: 13:12
 */
public class LoadBitmapActivity extends Activity {
    private ImageView img;
    private ProgressBar progressBar;
    private static final String url = "https://www.baidu.com/img/bd_logo1.png";

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

        img = (ImageView) findViewById(R.id.img);
        progressBar = (ProgressBar) findViewById(R.id.progressbar);
        new MyAsyncTask().execute(url);
    }

    class MyAsyncTask extends AsyncTask<String, Void, Bitmap> {
        /**
         * 将url所对应的图像解析成Bitmap
         *
         * @param params
         * @return
         */
        protected Bitmap doInBackground(String... params) {
            String url = params[0];// 获取传递进来的参数
            Bitmap bitmap = null;
            URLConnection connection;//定义网络连接对象
            InputStream is;//用于获取数据的输入流
            try {
                //访问网络操作,耗时操作
                connection = new URL(url).openConnection();//获取网络连接对象
                is = connection.getInputStream();//获取输入流
                BufferedInputStream bis = new BufferedInputStream(is);
                bitmap = BitmapFactory.decodeStream(bis);//通过decodeStream()方法解析输入流,从而转换成一张bitmap图片
                is.close();
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            //将bitmap作为返回对象
            return bitmap;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            //显示进度条
            progressBar.setVisibility(View.VISIBLE);
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            try {
                Thread.sleep(2000);//为了观看效果,休眠2秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            super.onPostExecute(bitmap);
            //关闭进度条和更新UI
            progressBar.setVisibility(View.GONE);
            img.setImageBitmap(bitmap);
        }
    }
}

activity_load_bitmap.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/img"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true" />

    <ProgressBar
        android:id="@+id/progressbar"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_centerInParent="true" />

</RelativeLayout>

效果图如下:


这里写图片描述

(三)显示进度
ShowProgressActivity,用来显示这个进度条,在这个类里面我们就使用AsyncTask让进度条动起来。

package com.adan.asynctaskdome;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ProgressBar;

/**
 * @author: xiaolijuan
 * @description: 显示进度
 * @projectName: AsyncTaskDome
 * @date: 2016-03-04
 * @time: 13:12
 */
public class ShowProgressActivity extends Activity {
    private ProgressBar progressBar;
    private MyAsyncTask mTask;

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

        progressBar = (ProgressBar) findViewById(R.id.progressBar);
        mTask = new MyAsyncTask();
        mTask.execute();
    }

    class MyAsyncTask extends AsyncTask<Void, Integer, Void> {

        protected Void doInBackground(Void... params) {
            for (int i = 0; i < 100; i++) {
                publishProgress(i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }

        //根据任务执行情况更新UI,其实就是更新进度条
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            // 进度条数值显示
            progressBar.setProgress(values[0]);
        }

    }
}

activity_show_progress,布局文件很简单,里面就放置一个横向的进度条而已

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="5dp" />

</LinearLayout>

效果图如下:


这里写图片描述

不知道大家发现一个问题没,当我第一次点击界面时候,进度条还没运行完,我就返回了,当再一次进去时候,等待了好久进度条才有开始启动起来。这是因为我们之前的doInBackground里的线程还在进行,循环还在继续,所以AsyncTask还在执行。因此此刻重新进入进度条界面时,就不可能立刻启动新的AsyncTask,而旧的呢又因为之前我们返回到按钮界面就没法重新绘制了。所以只有等旧的AsyncTask执行完毕,才会开启新的。就像队列一样,只有运行完之后才运行下一个。这时候你会想,那就麻烦了,如果我需要大量地处理一些耗时操作呢,这样给用户的体验性是不好的。我们能不能在我们点击返回的时候就取消了这个任务呢,答案当然是可以的,只不过我们需要将AsyncTask与Activity的生命周期相关联。
我把修改后的代码贴一下:

package com.adan.asynctaskdome;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ProgressBar;

/**
 * @author: xiaolijuan
 * @description: 显示进度
 * @projectName: AsyncTaskDome
 * @date: 2016-03-04
 * @time: 13:12
 */
public class ShowProgressActivity extends Activity {
    private ProgressBar progressBar;
    private MyAsyncTask mTask;

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

        progressBar = (ProgressBar) findViewById(R.id.progressBar);
        mTask = new MyAsyncTask();
        mTask.execute();
    }

    @Override
    protected void onPause() {
        // 判断当前异步任务是否在进行中
        if (mTask != null && mTask.getStatus() == AsyncTask.Status.RUNNING) {
            // cancel()方法:取消该任务,但只是将对应的AsyncTask状态标记为cancel状态,并没有真正取消异步操作
            mTask.cancel(true);
        }
        super.onPause();
    }

    class MyAsyncTask extends AsyncTask<Void, Integer, Void> {

        protected Void doInBackground(Void... params) {
            for (int i = 0; i < 100; i++) {
                if (isCancelled()) {//如果异步任务为取消状态,立刻break,跳出循环
                    break;
                }
                publishProgress(i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }

        //根据任务执行情况更新UI,其实就是更新进度条
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            // 进度条数值显示
            progressBar.setProgress(values[0]);
        }

    }
}

接下来我们再看看效果图:


这里写图片描述

最后我们来总结一下使用AsyncTask类需要注意的几点:
(1)AsyncTask实例必须在UI Thread中创建;
(2)execute()方法也必须在UI Thread中调用;
(3)cancel()方法:取消该任务,该方法只是将对应的AsyncTask状态标记为cancel状态,并没有真正取消异步操作;
(4)每个AsyncTask的execute()方法只能被执行一次,否则多次调用时将会出现异常;
(5)重写的四个方法是系统自动调用,不能手动更改;

如果存在理解偏差甚至错误的地方,请多多交流指正!谢谢各位!
点击下载源代码:Android AsyncTask基础

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

推荐阅读更多精彩内容