AsyncTask的优点是任务执行在UI线程之外,而回调方法是在UI线程中执行,能够有效避免主线程的阻塞(ANR)。官方解释:
AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
意思是:AsyncTask能够正确容易的使用UI线程。这个类进行后台操作和在不用操作线程和Handler的情况下,发布结果给UI线程。
1). 不要阻塞UI线程 ;
2). 确保只在UI线程中访问Android UI控件。 当一个程序第一次启动时,Android会同时启动一个对应的 主线程 ( Main Thread ),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做 UI线程 。
- Activity.runOnUiThread( Runnable )
- View.post( Runnable )
- View.postDelayed( Runnable, long )
- Handler消息传递机制
下面通过分析部分源码来了解AsyncTask的使用步骤:
①An asynchronous task is defined by 3 generic types, called Params
, Progress
and Result
, and 4 steps, called onPreExecute
, doInBackground
,onProgressUpdate
and onPostExecute
.
译:一个异步任务,定义了三个泛型:Params
, Progress
和 Result。重写四个方法:
onPreExecute
, doInBackground
,onProgressUpdate
and onPostExecute
.
- Params 启动任务执行的输入参数,比如HTTP请求的URL。 一般用String类型;
- Progress 后台任务执行的百分比。 一般用Integer类型;
- Result 后台执行任务最终返回的结果,一般用byte[]或者String。
需要实现的四个方法都是由应用程序自动调用,开发者需要做的就是实现这些方法:
- onPreExecute(), 该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。
- doInBackground(Params...), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
- onProgressUpdate(Progress...),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。
- onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.
②AsyncTask must be subclassed to be used。The subclass will override at least one method (doInBackground(Params...)
), and most often will override a second one (onPostExecute(Result)
.)
译:AsyncTask 要想被使用,必须要有子类来继承它。子类至少需要重写doInBackground(Params...)方法,通常还会重写onPostExecute(Result)方法。
下面通过一个实例来介绍使用AsyncTask下载网络图片:
1.布局文件中的代码:
<span style="font-size:18px;"><span style="font-size:18px;"><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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity"
android:layout_margin="20dp" >
<ImageView
android:id="@+id/iv"
android:layout_width="350dp"
android:layout_height="370dp"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:onClick="download"
android:text="下载"
android:textSize="28sp"/>
</RelativeLayout></span></span>
2.MainActivity的代码:
<span style="font-size:18px;"><span style="font-size:18px;">package com.example.day08_download;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.ImageView;
public class MainActivity extends Activity {
private ImageView iv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = (ImageView) findViewById(R.id.iv);
}
//点击“下载”按钮
public void download(View view){
//实例化异步任务的类
DownloadAsynctask task = new DownloadAsynctask(iv, this);
//execute方法执行后,会调用异步任务的doInBackground方法
task.execute("http://p2.so.qhmsg.com/t01fb3e43c8cd9ee917.jpg");
}
}</span></span>
3.异步任务AsyncTask类的代码:
<span style="font-size:18px;"><span style="font-size:18px;">package com.example.day08_download;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import android.app.ProgressDialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.widget.ImageView;
import android.widget.Toast;
public class DownloadAsynctask extends AsyncTask<String, Integer, byte[]>{
private ImageView iv;
private Context context; //上下文对象
private ProgressDialog dialog; //进度对话框
/*
* 通过构造方法,把MainActivity中的数据传递过来
*/
public DownloadAsynctask(ImageView iv, Context context) {
super();
this.iv = iv;
this.context = context;
}
/*
* 调用当前类的execute方法后,最先运行此方法,准备数据(加载进度条)
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
dialog = new ProgressDialog(context);
dialog.setIcon(R.drawable.s);
dialog.setTitle("正在加载中.........");
dialog.setMessage("请耐心等待");
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);//水平进度条
dialog.setCancelable(false); //屏蔽返回键
dialog.show();
}
/*
* 后台获取网络上的资源
*/
@Override
protected byte[] doInBackground(String... params) {
try {
URL url = new URL(params[0]);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
conn.setConnectTimeout(5000);
byte[] result = null;
if (conn.getResponseCode() ==200) {
InputStream is = conn.getInputStream();
//获取下载的总数据量
int totalSize = conn.getContentLength();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
//记录当前下载的数据量
int currentSize = 0;
while((len = is.read(buffer))!=-1){
baos.write(buffer, 0, len);
currentSize += len; //更新当前下载的数据量
//通过publishProgress方法来唤醒异步任务中的onProgressUpdate方法
publishProgress(totalSize, currentSize); //把数据传给onProgressUpdate进行更新UI
}
//返回的结果会被onPostExecute方法接收
result = baos.toByteArray();
}
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/*
* 获取doInBackground方法传递过来的参数(result),进行更新UI
*/
@Override
protected void onPostExecute(byte[] result) {
super.onPostExecute(result);
if (result != null) {
//把doInBackground方法传递过来的数据转换为Bitmap
Bitmap bm = BitmapFactory.decodeByteArray(result, 0, result.length);
iv.setImageBitmap(bm);
}else {
Toast.makeText(context, "网络错误", 0).show();
}
dialog.dismiss();
}
/*
* 调用publishProgress方法,会触发此方法的调用
*/
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
//更新进度条
dialog.setMax(values[0]);
dialog.setProgress(values[1]);
}
}</span></span>
4.最终的效果如下: