Android中AsyncTask使用具体解释

时间:2022-09-22 10:29:41

在Android中我们能够通过Thread+Handler实现多线程通信。一种经典的使用场景是:在新线程中进行耗时操作。当任务完毕后通过Handler向主线程发送Message。这样主线程的Handler在收到该Message之后就能够进行更新UI的操作。上述场景中须要分别在Thread和Handler中编写代码逻辑,为了使得代码更加统一,我们能够使用AsyncTask类。

AsyncTask是Android提供的一个助手类,它对Thread和Handler进行了封装,方便我们使用。

Android之所以提供AsyncTask这个类,就是为了方便我们在后台线程中运行操作。然后将结果发送给主线程,从而在主线程中进行UI更新等操作。在使用AsyncTask时,我们无需关注Thread和Handler,AsyncTask内部会对其进行管理,这样我们就仅仅须要关注于我们的业务逻辑就可以。

AsyncTask有四个重要的回调方法,各自是:onPreExecute、doInBackground, onProgressUpdate 和 onPostExecute。

这四个方法会在AsyncTask的不同一时候期进行自己主动调用。我们仅仅须要实现这几个方法的内部逻辑就可以。这四个方法的一些參数和返回值都是基于泛型的,并且泛型的类型还不一样,所以在AsyncTask的使用中会遇到三种泛型參数:Params, Progress 和 Result,例如以下图所看到的:

Android中AsyncTask使用具体解释

  • Params表示用于AsyncTask运行任务的參数的类型
  • Progress表示在后台线程处理的过程中,能够阶段性地公布结果的数据类型
  • Result表示任务所有完毕后所返回的数据类型

我们通过调用AsyncTask的execute()方法传入參数并运行任务。然后AsyncTask会依次调用以下四个方法:

  • onPreExecute

    该方法的签名例如以下所看到的:

    Android中AsyncTask使用具体解释

    该方法有MainThread注解。表示该方法是运行在主线程中的。在AsyncTask运行了execute()方法后就会在UI线程上运行onPreExecute()方法,该方法在task真正运行前运行,我们通常能够在该方法中显示一个进度条。从而告知用户后台任务即将開始。

  • doInBackground

    该方法的签名例如以下所看到的:

    Android中AsyncTask使用具体解释

    该方法有WorkerThread注解,表示该方法是运行在单独的工作线程中的。而不是运行在主线程中。doInBackground会在onPreExecute()方法运行完毕后马上运行,该方法用于在工作线程中运行耗时任务,我们能够在该方法中编写我们须要在后台线程中运行的逻辑代码,因为是运行在工作线程中,所以该方法不会堵塞UI线程。

    该方法接收Params泛型參数,參数params是Params类型的不定长数组,该方法的返回值是Result泛型。因为doInBackgroud是抽象方法。我们在使用AsyncTask时必须重写该方法。

    在doInBackground中运行的任务可能要分解为好多步骤。每完毕一步我们就能够通过调用AsyncTask的publishProgress(Progress…)将阶段性的处理结果公布出去。阶段性处理结果是Progress泛型类型。当调用了publishProgress方法后,处理结果会被传递到UI线程中,并在UI线程中回调onProgressUpdate方法,以下会具体介绍。

    依据我们的具体须要,我们能够在doInBackground中不调用publishProgress方法。当然也能够在该方法中多次调用publishProgress方法。

    doInBackgroud方法的返回值表示后台线程完毕任务之后的结果。

  • onProgressUpdate

    上面我们知道。当我们在doInBackground中调用publishProgress(Progress…)方法后。就会在UI线程上回调onProgressUpdate方法,该方法的方法签名例如以下所看到的:

    Android中AsyncTask使用具体解释

    该方法也具有MainThread注解,表示该方法是在主线程上被调用的。且传入的參数是Progress泛型定义的不定长数组。

    假设在doInBackground中多次调用了publishProgress方法,那么主线程就会多次回调onProgressUpdate方法。

  • onPostExecute

    该方法的签名例如以下所看到的:

    Android中AsyncTask使用具体解释

    该方法也具有MainThread注解,表示该方法是在主线程中被调用的。当doInBackgroud方法运行完毕后。就表示任务完毕了,doInBackgroud方法的返回值就会作为參数在主线程中传入到onPostExecute方法中。这样就能够在主线程中依据任务的运行结果更新UI。

以下我们就以下载多个文件的演示样例演示AsyncTask的使用过程。

布局文件例如以下所看到的:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity"> <Button android:id="@+id/btnDownload"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="開始下载" /> <TextView android:id="@+id/textView"
android:layout_below="@id/btnDownload"
android:layout_width="match_parent"
android:layout_height="wrap_content" /> </RelativeLayout>

界面上有一个“開始下载”的button,点击该button就可以通过AsyncTask下载多个文件,相应的Java代码例如以下所看到的:

package com.ispring.asynctask;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL; public class MainActivity extends Activity implements Button.OnClickListener { TextView textView = null;
Button btnDownload = null; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView)findViewById(R.id.textView);
btnDownload = (Button)findViewById(R.id.btnDownload);
Log.i("iSpring", "MainActivity -> onCreate, Thread name: " + Thread.currentThread().getName());
} @Override
public void onClick(View v) {
//要下载的文件地址
String[] urls = {
"http://blog.csdn.net/iispring/article/details/47115879",
"http://blog.csdn.net/iispring/article/details/47180325",
"http://blog.csdn.net/iispring/article/details/47300819",
"http://blog.csdn.net/iispring/article/details/47320407",
"http://blog.csdn.net/iispring/article/details/47622705"
}; DownloadTask downloadTask = new DownloadTask();
downloadTask.execute(urls);
} //public abstract class AsyncTask<Params, Progress, Result>
//在此例中。Params泛型是String类型,Progress泛型是Object类型,Result泛型是Long类型
private class DownloadTask extends AsyncTask<String, Object, Long> {
@Override
protected void onPreExecute() {
Log.i("iSpring", "DownloadTask -> onPreExecute, Thread name: " + Thread.currentThread().getName());
super.onPreExecute();
btnDownload.setEnabled(false);
textView.setText("開始下载...");
} @Override
protected Long doInBackground(String... params) {
Log.i("iSpring", "DownloadTask -> doInBackground, Thread name: " + Thread.currentThread().getName());
//totalByte表示所有下载的文件的总字节数
long totalByte = 0;
//params是一个String数组
for(String url: params){
//遍历Url数组,依次下载相应的文件
Object[] result = downloadSingleFile(url);
int byteCount = (int)result[0];
totalByte += byteCount;
//在下载完一个文件之后,我们就把阶段性的处理结果公布出去
publishProgress(result);
//假设AsyncTask被调用了cancel()方法。那么任务取消,跳出for循环
if(isCancelled()){
break;
}
}
//将总共下载的字节数作为结果返回
return totalByte;
} //下载文件后返回一个Object数组:下载文件的字节数以及下载的博客的名字
private Object[] downloadSingleFile(String str){
Object[] result = new Object[2];
int byteCount = 0;
String blogName = "";
HttpURLConnection conn = null;
try{
URL url = new URL(str);
conn = (HttpURLConnection)url.openConnection();
InputStream is = conn.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int length = -1;
while ((length = is.read(buf)) != -1) {
baos.write(buf, 0, length);
byteCount += length;
}
String respone = new String(baos.toByteArray(), "utf-8");
int startIndex = respone.indexOf("<title>");
if(startIndex > 0){
startIndex += 7;
int endIndex = respone.indexOf("</title>");
if(endIndex > startIndex){
//解析出博客中的标题
blogName = respone.substring(startIndex, endIndex);
}
}
}catch(MalformedURLException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally {
if(conn != null){
conn.disconnect();
}
}
result[0] = byteCount;
result[1] = blogName;
return result;
} @Override
protected void onProgressUpdate(Object... values) {
Log.i("iSpring", "DownloadTask -> onProgressUpdate, Thread name: " + Thread.currentThread().getName());
super.onProgressUpdate(values);
int byteCount = (int)values[0];
String blogName = (String)values[1];
String text = textView.getText().toString();
text += "\n博客《" + blogName + "》下载完毕。共" + byteCount + "字节";
textView.setText(text);
} @Override
protected void onPostExecute(Long aLong) {
Log.i("iSpring", "DownloadTask -> onPostExecute, Thread name: " + Thread.currentThread().getName());
super.onPostExecute(aLong);
String text = textView.getText().toString();
text += "\n所有下载完毕,总共下载了" + aLong + "个字节";
textView.setText(text);
btnDownload.setEnabled(true);
} @Override
protected void onCancelled() {
Log.i("iSpring", "DownloadTask -> onCancelled, Thread name: " + Thread.currentThread().getName());
super.onCancelled();
textView.setText("取消下载");
btnDownload.setEnabled(true);
}
}
}

点击下载button后,界面例如以下所看到的:

Android中AsyncTask使用具体解释

控制台输出例如以下所看到的:

Android中AsyncTask使用具体解释

以下对以上代码进行一下说明。

  1. 我们在MainActivity中定义了内部类DownloadTask,DownloadTask继承自AsyncTask。在该例中,Params泛型是String类型,Progress泛型是Object类型,Result泛型是Long类型。

  2. 我们定义了一个Url字符串数组。将该数组传递给AsyncTask的execute方法,用于异步运行task。

  3. 在运行了downloadTask.execute(urls)之后。AsyncTask会自己主动回调onPreExecute方法,在该方法中我们将textView设置为“開始下载…”几个字,告知用户即将运行下载操作。通过控制台输出我们也能够看出该方法是在主线程中运行的。

  4. 在运行了onPreExecute方法之后。AsyncTask会回调doInBackground方法,该方法中的输入參数是String类型的不定长数组,此处的String就相应着Params泛型类型,我们在该方法中遍历Url数组。依次下载相应的文件,当我们下载完一个文件。就相当于我们阶段性地完毕了一部分任务,我们就通过调用publishProgress方法将阶段性处理结果公布出去。在此例中我们将阶段性的处理结果定义为Object类型,即Progress泛型类型。通过控制台输出我们能够看出doInBackground方法是运行在新的工作线程”AsyncTask #1”中的,AsyncTask的工作线程都是以”AsyncTask #”然后加上数字作为名字。

    当所有文件下载完毕后。我们就能够通过totalSize返回所有下载的字节数,返回值类型为Long。相应着AsyncTask中的Result泛型类型。

  5. 在doInBackground方法中,每当下载完一个文件。我们就会调用publishProgress方法公布阶段性结果,之后AsyncTask会回调onProgressUpdate方法。在此例中,onProgressUpdate的參数为Object类型,相应着AsyncTask中的Progress泛型类型。通过控制台输出我们能够发现。该方法是在主线程中调用的,在该方法中我们会通过textView更新UI。告知用户哪个文件下载完毕了,这样用户体验相对友好。

  6. 在整个doInBackground方法运行完毕后,AsyncTask就会回调onPostExecute方法,在该方法中我们再次通过textView更新UI告知用户所有下载任务完毕了。

  7. 在通过execute方法运行了异步任务之后,能够通过AsyncTask的cancel方法取消任务,取消任务后AsyncTask会回调onCancelled方法,这样不会再调用onPostExecute方法。

在使用Android的过程中,有以下几点须要注意:

  • AsyncTask的实例必须在主线程中创建。

  • AsyncTask的execute方法必须在主线程中调用。

  • onPreExecute()、onPostExecute(Result),、doInBackground(Params…) 和 onProgressUpdate(Progress…)这四个方法都是回调方法,Android会自己主动调用。我们不应自己调用。

  • 对于一个AsyncTack的实例。仅仅能运行一次execute方法,在该实例上第二次运行execute方法时就会抛出异常。

通过上面的演示样例,大家应该熟悉了AsyncTask的使用流程。我们上面提到,对于某个AsyncTask实例,仅仅能运行一次execute方法。假设我们想并行地运行多个任务怎么办呢?我们能够考虑实例化多个AsyncTask实例,然后分别调用各个实例的execute方法,为了探究效果,我们将代码更改例如以下所看到的:

public void onClick(View v) {
//要下载的文件地址
String[] urls = {
"http://blog.csdn.net/iispring/article/details/47115879",
"http://blog.csdn.net/iispring/article/details/47180325",
"http://blog.csdn.net/iispring/article/details/47300819",
"http://blog.csdn.net/iispring/article/details/47320407",
"http://blog.csdn.net/iispring/article/details/47622705"
}; DownloadTask downloadTask1 = new DownloadTask();
downloadTask1.execute(urls); DownloadTask downloadTask2 = new DownloadTask();
downloadTask2.execute(urls);
}

在单击了button之后,我们实例化了两个DownloadTask,并分别运行其execute方法。运行后界面例如以下所看到的:

Android中AsyncTask使用具体解释

控制台输出例如以下所看到的:

Android中AsyncTask使用具体解释

我们观察一下控制台的输出结果,能够发现对于downloadTask1,doInBackground方法是运行在线程“AsyncTask #1”中的;对于downloadTask2,doInBackground方法是运行在线程”AsyncTask #2”中的,此时我们可能会觉得太好了,两个AsyncTask实例分别在不同的线程中运行,实现了并行处理。此处真的是并行运行的吗?

我们自己观察控制台输出就能够发现,downloadTask1的doInBackground方法运行后,下载了五个文件,并五次触发了onProgressUpdate,在这之后才运行downloadTask2的doInBackground方法。我们对照上面的GIF图也能够发现。在downloadTask1依照顺序下载完五篇文章之后,downloadTask2才開始依照顺序下载五篇文章。综上所述,我们能够知道。默认情况下假设创建了AsyncTask创建了多个实例。并同一时候运行实例的各个execute方法。那么这些实例的execute方法并非并行运行的,是串行运行的,即在第一个实例的doInBackground完毕任务后,第二个实例的doInBackgroud方法才会開始运行,然后再运行第三个实例的doInBackground方法… 那么你可能会问,不正确啊,上面downloadTask1是运行在”AsyncTask #1”线程中的,downloadTask2是运行在”AsyncTask #2”线程中的。这明明是两个线程啊!

事实上AsyncTask为downloadTask1开辟了名为”AsyncTask #1”的工作线程,在其完毕了任务之后可能就销毁了,然后AsyncTask又为downloadTask2开辟了名为”AsyncTask #2”的工作线程。

AsyncTask在最早的版本号中用一个单一的后台线程串行运行多个AsyncTask实例的任务,从Android 1.6(DONUT)開始,AsyncTask用线程池并行运行异步任务,可是从Android 3.0(HONEYCOMB)開始为了避免并行运行导致的常见错误,AsyncTask又開始默认用单线程作为工作线程处理多个任务。

从Android 3.0開始AsyncTask添加了executeOnExecutor方法。用该方法能够让AsyncTask并行处理任务。该方法的方法签名例如以下所看到的:

public final AsyncTask<Params, Progress, Result> executeOnExecutor (Executor exec, Params... params)

第一个參数表示exec是一个Executor对象,为了让AsyncTask并行处理任务。通常情况下我们此处传入AsyncTask.THREAD_POOL_EXECUTOR就可以,AsyncTask.THREAD_POOL_EXECUTOR是AsyncTask中内置的一个线程池对象。当然我们也能够传入我们自己实例化的线程池对象。

第二个參数params表示的是要运行的任务的參数。

通过executeOnExecutor方法并行运行任务的演示样例代码例如以下所看到的:

public void onClick(View v) {
if(Build.VERSION.SDK_INT >= 11){
String[] urls = {
"http://blog.csdn.net/iispring/article/details/47115879",
"http://blog.csdn.net/iispring/article/details/47180325",
"http://blog.csdn.net/iispring/article/details/47300819",
"http://blog.csdn.net/iispring/article/details/47320407",
"http://blog.csdn.net/iispring/article/details/47622705"
}; DownloadTask downloadTask1 = new DownloadTask();
downloadTask1.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, urls); DownloadTask downloadTask2 = new DownloadTask();
downloadTask2.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, urls);
}
}

我们实例化了两个DownloadTask的实例。然后运行了这两个实例的executeOnExecutor方法,并将AsyncTask.THREAD_POOL_EXECUTOR作为Executor传入。二者都接收相同的Url数组作为任务运行的參数。

点击下载button后,运行完的界面例如以下所看到的:

Android中AsyncTask使用具体解释

控制台输出例如以下所看到的:

Android中AsyncTask使用具体解释

通过控制台的输出结果我们能够看到。在downloadTask1运行了doInBackground方法后,downloadTask2也马上运行了doInBackground方法。并且通过程序运行完的UI界面能够看到在一个DownloadTask实例下载了一篇文章之后。还有一个DownloadTask实例也马上下载了一篇文章,两个DownloadTask实例交叉按顺序下载文件,能够看出这两个AsyncTask的实例是并行运行的。

假设大家想了解AsyncTask的工作原理。可參见还有一篇博文《源代码解析Android中AsyncTask的工作原理》

希望本文对大家使用AsyncTask的使用有所帮助!

相关阅读:

我的Android博文整理汇总

源代码解析Android中AsyncTask的工作原理

Android中Handler的使用

Android新线程中更新主线程UI中的View方法汇总

Android中AsyncTask使用具体解释的更多相关文章

  1. Android中AsyncTask异步

    今天我们学习了 AsyncTack, 这是一个异步任务. 那么这个异步任务可以干什么呢? 因为只有UI线程,即主线程可以对控件进行更新操作.好处是保证UI稳定性,避免多线程对UI同时操作. 同时要把耗 ...

  2. Android中的动画具体解释系列【4】——Activity之间切换动画

    前面介绍了Android中的逐帧动画和补间动画,并实现了简单的自己定义动画.这一篇我们来看看怎样将Android中的动画运用到实际开发中的一个场景--Activity之间跳转动画. 一.定义动画资源 ...

  3. Android中AsyncTask的使用 (包含文件的下载与存储)

    今天看到大神写的相关详解Android中AsyncTask的使用,真的很是佩服,下面我将学习到的AsynTask知识运用到项目中,其中也涉及一些文件的下载与存储到本地 啥都不说了,直接上代码,我将对其 ...

  4. Android中时间戳的详细解释

    Android中时间戳的详细解释: (1).定义: 时间戳就是根据当前系统时间生成的一组随机数字. (2).作用: 作为对数据唯一性的一种判断依据.避免了重复修改数据所带来的错误! (3).应用: ( ...

  5. 具体解释Android中AsyncTask的使用

    在Android中实现异步任务机制有两种方式,Handler和AsyncTask. Handler模式须要为每个任务创建一个新的线程,任务完毕后通过Handler实例向UI线程发送消息,完毕界面的更新 ...

  6. 详解Android中AsyncTask的使用

    在Android中实现异步任务机制有两种方式,Handler和AsyncTask. Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更 ...

  7. Android中AsyncTask的简单用法 &period;

    在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行.在单线程模型中始终要记住两条法则: 1. 不要阻塞UI线程 2. 确保只 ...

  8. Android中AsyncTask的使用

    原文 https://blog.csdn.net/liuhe688/article/details/6532519 在Android中实现异步任务机制有两种方式,Handler和AsyncTask. ...

  9. android中asynctask的使用实例

    参考此blog写的非常的好http://www.cnblogs.com/devinzhang/archive/2012/02/13/2350070.html MainActivity.java imp ...

随机推荐

  1. Java使用正则表达式获取文本的章节名称

    获取文本的章节,首先要确定章节的开始标准,一般中文的章节都是以“第”开头,第一章.第二章等.所以使用“^”字符来确定首位,但是很多时候章节前面会有空白字符,所有以“第”作为章节的开始,进行以下的匹配 ...

  2. oracle体系结构详细示意图

  3. vs2010打包系统必备选择&period;net framework 3&period;5sp1编译错误的解决方法

    利用visual studio 2010进行打包程序,默认安装的是Framework 4.0,如果需要将3.5sp1打包到系统中一起安装(选择了"从与我的应用程序相同的位置下载系统必备组件& ...

  4. stm32f10x&period;h文件分析理解

    今天再看过半年前自己写的这篇发现自己当时理解有误,stm32f10x.h与库开发并未存在太大关系,只是一个最为重要的寄存器地址到寄存器结构体变量的映射. stm32f10x.h 这个头文件是STM32 ...

  5. 微软HoloLens虚拟现实可以开发了。

    1.microsoft-hololens-now-available-to-developers 2.http://www.microsoft.com/microsoft-hololens/en-us ...

  6. MT【277】华中科技大学理科实验班选拔之三次方程

    (2015华中科技大学理科实验班选拔)已知三次方程$x^3+ax^2+bx+x=0$有三个实数根.(1)若三个实根为$x_1,x_2,x_3$,且$x_1\le x_2\le x_3,a,b$为常数, ...

  7. day 5,格式化输出,for,while, break,continue,列表

    本节内容: 1,格式化输出 2,数据类型 3,for 循环 4,while 循环 5,列表 pycharm的简单使用,设置pycharm自动生成日期和计算机用户名 ctrl+d复制一行 1,格式化输出 ...

  8. 关于GitHub上传没有记录(小绿块不显示的问题)

    最近开始使用上github来上传保存自己在学习中所写过的代码,打算将自己每天的成果能有个保存,然后就利用上GitHub这么一个利器. 听说GitHub的那个绿块是用来记录每天的上传记录的,结果我将代码 ...

  9. Hadoop JobTracker和NameNode运行时参数查看

      1)JobTracker运行时参数: hadoop@ubuntu:/home/zhangchao3$ ps -ef | grep job hadoop 29563 1 0 11:34 pts/12 ...

  10. HTML 提高页面加载速度的方法

    (1)减少 HTTP 的请求.(合并资源文件 和 使用图片精灵 : (2)把CSS  放头部,把 JavaScript 放到 body 标签尾部: (3)定义图片的宽和高: (4)定义字符集: (5) ...