一、AsyncTask介绍
AsyncTask:异步任务,从字面上来说,就是在我们的UI主线程运行的时候,异步的完成一些操作。AsyncTask允许我们在后台执行一个耗时的异步的任务,并随时将任务执行的结果返回给我们的UI线程来更新我们的UI控件。
AsyncTask 背后的实现原理是基于异步消息处理机制的,只是Android 帮我们做了很好的封装而已。AsyncTask允许在UI线程上执行后台操作和发布结果,而无需操作线程或处理程序。AsyncTask被设计为一个围绕Thread和android.os.Handler的帮助类,相当于Android给我们提供了一个多线程编程的一个框架。 另外,AsyncTasks应该理想地用于短操作(最多几秒钟)。
二、AsyncTask的基本用法
AsyncTask 是一个抽象类:
public abstract class AsyncTask<Params, Progress, Result>;
如果我们想使用它,就必须要创建一个子类去继承它,在继承时我们可以为AsyncTask 类指定三个泛型参数,这三个参数的用途如下:
(1)Params:这个泛型指定的是我们传递给异步任务执行时的参数的类型;
(2)Progress:这个泛型指定的是我们的异步任务在执行的时候将执行的进度返回给UI线程的参数的类型;
(3)Result:这个泛型指定的异步任务执行完后返回给UI线程的结果的类型。
因此,一个最简单的自定义AsyncTask 就可以写成如下方式:
class MyAsyncTask extends AsyncTask<String, Integer, Bitmap>;
AsyncTask提供了4个核心方法,它们的含义如下所示:
(1) onPreExecute()
这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
(2)doInBackground(Params…params)
这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return 语句来将任务的执行结果返回,如果AsyncTask 的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI 操作的,如果需要更新UI 元素,比如说反馈当前任务的执行进度,可以调用publishProgress方法,publishProgress方法又会调用onProgressUpdate方法来完成。
(3)onProgressUpdate(Progress…progress)
当在后台任务中调用了publishProgress(Progress…progress)方法后,这个方法就会很快被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI 进行操作,利用参数中的数值就可以对界面元素进行相应地更新。
(4)onPostExecute(Result…result)
当后台任务执行完毕并通过return 语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI 操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。
除了上面四个方法,AsyncTask还提供了onCancelled()方法,它同样在主线程中执行,当异步任务被取消时,onCancelled()方法会被调用,这时onPostExecute方法则不会被调用。
简单来说,使用AsyncTask 的诀窍就是,在doInBackground()方法中去执行具体的耗时任务,在onProgressUpdate()方法中进行UI 操作,在onPostExecute()方法中执行一些任务的收尾工作。如果想要启动这个任务,只需编写以下代码即可:
new MyAsyncTask().execute();
以上就是AsyncTask 的基本用法,我们并不需要去考虑什么异步消息处理机制,也不需要专门使用一个Handler 来发送和接收消息,只需要调用一下publishProgress()方法就可以轻松地从子线程切换到UI 线程了。
三、AsyncTask 的使用限制
AsyncTask 在具体的使用过程中也有一些条件限制,主要由以下几点:
(1)AsyncTask 类必须在主线程中加载,这就意味着第一次访问AsyncTask 必须发生在主线程;
(2)AsyncTask 的对象必须在注线程中创建;
(3)execute方法必须在UI线程中调用;
(4)不要在程序中直接调用onPreExecute、doInBackground、onProgressUpdate和onPostExecute方法;
(5)一个AsyncTask 对象只能执行一次,即只能调用一次execute方法,否则会报运行时异常。
四、AsyncTask 使用实例
首先是布局文件,简单的一个点击下载图片的Button和一个显示图片的ImageView控件,activity_main.xml如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.wz.MainActivity">
<Button
android:id="@+id/button"
android:text="图片下载"
android:layout_centerHorizontal="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_marginTop="50dp"
android:id="@+id/imageView" />
</RelativeLayout>
然后是主类MainActivity:
package com.wz;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class MainActivity extends AppCompatActivity {
private ProgressDialog progressDialog;//下载进度条
private Button button;
private ImageView imageView;
//图片下载地址
private final String IMAGE_URL = "http://a.hiphotos.baidu.com/image/pic/item/fcfaaf51f3deb48fc4cedcdff21f3a292df5780f.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new MyAsyncTask().execute(IMAGE_URL);
}
});
imageView = (ImageView) findViewById(R.id.imageView);
progressDialog = new ProgressDialog(this);
progressDialog.setTitle("提示");
progressDialog.setMessage("正在下载,请稍后...");
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
}
class MyAsyncTask extends AsyncTask<String, Integer, Bitmap> {
@Override
protected void onPreExecute() {
// 显示进度对话框
progressDialog.show();
}
@Override
protected Bitmap doInBackground(String... params) {
/*
*2016-11-01注释:在Android 5.0之后,HttpClient被HttpURLConnecetion替代,后来在Android 6.0完全被舍弃,
*如需要HttpURLConnecetion的实现方式,移步
*到http://blog.c dn.net/wei_zhi/article/details/52997246
*/
Bitmap bitmap = null;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
InputStream inputStream = null;
try {
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(params[0]);
HttpResponse httpResponse = httpClient.execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() == 200) {
inputStream = httpResponse.getEntity().getContent();
long file_len = httpResponse.getEntity().getContentLength();
int len = 0;
byte[] data = new byte[1024];
int total_len = 0;
while ((len = inputStream.read(data)) != -1) {
total_len += len;
int value = (int) ((total_len / (float) file_len) * 100);
//反馈当前任务的执行进度
publishProgress(value);
outputStream.write(data, 0, len);
}
byte[] result = outputStream.toByteArray();
bitmap = BitmapFactory.decodeByteArray(result, 0, result.length);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return bitmap;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
//设置进度条刻度
progressDialog.setProgress(values[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
//关闭进度条
progressDialog.dismiss();
//显示图片
imageView.setImageBitmap(bitmap);
}
}
}
这里需要注意的是,Android6.0版本移除了对Appache的HTTP client的支持。如果你的app的目标版本是Android2.3(API level 9)或者更高,如果你希望继续使用Appache Http API,请修改你的build.gradle文件,增加如下:
android {
useLibrary 'org.apache.http.legacy'
}
最后在AndroidManifest.xml中添加网络访问权限:
<uses-permission android:name="android.permission.INTERNET"/>
运行结果: