AsyncTask,即异步任务,是Android给我们提供的一个处理异步任务的类.通过此类,可以实现UI线程和后台线程进行通讯,后台线程执行异步任务,并把结果返回给UI线程.
1.为什么需要使用异步任务?
我们知道,Android中只有UI线程,也就是主线程才能进行对UI的更新操作,而其他线程是不能直接操作UI的.这样的好处是保证了UI的稳定性和准确性,避免多个线程同时对UI进行操作而造成UI的混乱.但Android是一个多线程的操作系统,我们总不能把所有的任务都放在主线程中进行实现,比如网络操作,文件读取等耗时操作,如果全部放到主线程去执行,就可能会造成后面任务的阻塞.Android会去检测这种阻塞,当阻塞时间太长的时候,就会抛出Application
Not Responsed(ANR)错误.所以我们需要将这些耗时操作放在非主线程中去执行.这样既避免了Android的单线程模型,又避免了ANR.
2.AsyncTask为何而生?
提到异步任务,我们能想到用线程,线程池去实现.确实,Android给我们提供了主线程与其他线程通讯的机制.但同时,Android也给我们提供了一个封装好的组件--AsyncTask.利用AsyncTask,我们可以很方便的实现异步任务处理.AsyncTask可以在子线程中更新UI,也封装简化了异步操作.使用线程,线程池处理异步任务涉及到了线程的同步,管理等问题.而且当线程结束的时候还需要使用Handler去通知主线程来更新UI.而AsyncTask封装了这一切,使得我们可以很方便的在子线程中更新UI.(封装、简化异步操作)
3.构建AsyncTask子类的泛型参数
AsyncTask<Params,Progress,Result>是一个抽象类,通常用于被继承.继承AsyncTask需要指定如下三个泛型参数:
Params:启动任务时输入的参数类型.(URL类型)
Progress:后台任务执行中返回进度值的类型.(进度值类型)
Result:后台任务执行完成后返回结果的类型.(返回值类型)
4.构建AsyncTask子类的回调方法
AsyncTask主要有如下几个方法:
onPreExecute:执行后台耗时操作前被调用,通常用于进行初始化操作.
doInBackground:必须重写,异步执行后台线程要完成的任务,耗时操作将在此方法中完成.
onProgressUpdate:当在doInBackground方法中调用publishProgress方法更新任务执行进度后,将调用此方法.通过此方法我们可以知晓任务的完成进度.
onPostExecute:当doInBackground方法完成后,系统将自动调用此方法,并将doInBackground方法返回的值传入此方法.通过此方法进行UI的更新.
下面通过代码演示一个典型的异步处理的实例--加载网络图片.网络操作作为一个不稳定的耗时操作,从4.0开始就被严禁放入主线程中.所以在显示一张网络图片时,我们需要在异步处理中下载图片,并在UI线程中设置图片.
onPreExecute:加载进度条
doInBackground:下载网络数据(耗时操作)
onPostExecute:显示图片
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="loadImage"
android:text="获取图片"/>
</RelativeLayout>
image.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ProgressBar
android:id="@+id/progressbar"
android:visibility="gone"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
MainActivity.java
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void loadImage(View view) {
startActivity(new Intent(this,ImageTest.class));
}
}
ImageTest.java
import android.app.Activity;
import android.app.ProgressDialog;
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; //导入URL包
import java.net.URLConnection;
public class ImageTest extends Activity {
private ImageView imageView;
private ProgressBar progressBar;
private static String URL = "http://pic3.zhongsou.com/image/38063b6d7defc892894.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image);
imageView = (ImageView) findViewById(R.id.image);
progressBar = (ProgressBar) findViewById(R.id.progressbar);
new MyAsyncTask().execute(URL);
}
class MyAsyncTask extends AsyncTask<String,Void,Bitmap>{
@Override
protected void onPreExecute() {
super.onPreExecute();
progressBar.setVisibility(View.VISIBLE); //显示进度条
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
//操作UI,设置图像
progressBar.setVisibility(View.GONE);
imageView.setImageBitmap(bitmap);
}
@Override
protected Bitmap doInBackground(String... params) {
String url = params[0]; //取出对应URL
Bitmap bitmap = null;
URLConnection connection; //定义网络连接对象
InputStream inputStream = null; //用于获取数据的输入流
BufferedInputStream bufferedInputStream = null; //定义缓冲处理流
//Thread.sleep(3000);
try {
connection = new URL(url).openConnection(); //获取网络连接对象
inputStream = connection.getInputStream(); //获取输入流
bufferedInputStream = new BufferedInputStream(inputStream); //将输入流进行包装
bitmap = BitmapFactory.decodeStream(bufferedInputStream); //将输入流解析成Bitmap
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedInputStream != null) {
try {
bufferedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return bitmap;
}
}
}
由于涉及到网络操作,需要在AndroidManifest.xml中添加网络操作权限:<uses-permission android:name="android.permission.INTERNET"/>下面再演示一个模拟更新进度条的实例.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="loadProgressBar"
android:text="加载进度条"/>
</LinearLayout>
progressbar.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/progressBar"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
ProgressBarActivity.java
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ProgressBar;
public class ProgressBarActivity extends Activity{
private ProgressBar progressBar;
private MyAsyncTask task;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.progressbar);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
task = new MyAsyncTask();
//启动异步任务的处理
task.execute();
}
//AsyncTask是基于线程池进行实现的,当一个线程没有结束时,后面的线程是不能执行的.
@Override
protected void onPause() {
super.onPause();
if (task != null && task.getStatus() == AsyncTask.Status.RUNNING) {
//cancel方法只是将对应的AsyncTask标记为cancel状态,并不是真正的取消线程的执行.
task.cancel(true);
}
}
class MyAsyncTask extends AsyncTask<Void,Integer,Void>{
@Override
protected Void doInBackground(Void... params) {
//使用for循环来模拟进度条的进度.
for (int i = 0; i < 100; i++) {
//如果task是cancel状态,则终止for循环,以进行下个task的执行.
if (isCancelled()){
break;
}
//调用publishProgress方法将自动触发onProgressUpdate方法来进行进度条的更新.
publishProgress(i);
try {
//通过线程休眠模拟耗时操作
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if (isCancelled()){
return;
}
//通过publishProgress方法传过来的值进行进度条的更新.
progressBar.setProgress(values[0]);
}
}
}
MainActivity.java
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void loadProgressBar(View view) {
startActivity(new Intent(this,ProgressBarActivity.class));
}
}
5.使用AsyncTask的注意事项
① 必须在UI线程中创建AsyncTask的实例.
② 只能在UI线程中调用AsyncTask的execute方法.
③ AsyncTask被重写的四个方法是系统自动调用的,不应手动调用.
④ 每个AsyncTask只能被执行(execute方法)一次,多次执行将会引发异常.
⑤ AsyncTask的四个方法,只有doInBackground方法是运行在其他线程中,其他三个方法都运行在UI线程中,也就说其他三个方法都可以进行UI的更新操作,doInBackground方法不能进行UI的更新操作.