之所以需要异步任务,因为当进行一些耗时操作在主线程时,超过20秒会引起ANR(所谓application not respond)异常。
安卓中已经封装好了一个异步处理的方法,即AsyncTask。
AsyncTask <Params,Progress,Result> //是一个抽象类
//Params:启动任务输入参数的类型
//Progress:后台任务完成进度值的类型
//Result:后台任务返回的结果
AsynTask需要实现以下四个方法:
doInBackground:必须重写,异步执行后台线程要完成的任务,耗时操作将在此方法中完成.
onPreExecute:执行后台耗时操作前被调用,通常用于进行初始化操作.
onPostExecute:当doInBackground方法完成后,系统将自动调用此方法,并将doInBackground方法返回的值传入此方法.通过此方法进行UI的更新.
onProgressUpdate:当在doInBackground方法中调用publishProgress方法更新任务执行进度后,将调用此方法.通过此方法我们可以知晓任务的完成进度.
下面通过代码演示一个典型的异步处理的实例–加载网络图片.网络操作作为一个不稳定的耗时操作,从4.0开始就被严禁放入主线程中.所以在显示一张网络图片时,我们需要在异步处理中下载图片,并在UI线程中设置图片:
这里先准备一张自己拍的照片地址:
https://drscdn.500px.org/photo/149628981/q%3D80_m%3D1000/ab0758214aa0193240eb74e48bfb8aca
由于涉及到网络操作,需要在AndroidManifest.xml中添加网络操作权限:
<uses-permission android:name="android.permission.INTERNET"/>
在MainActivity布局中加入一个按钮,点击跳转到所要测试的Activity中去:
package com.chase.cn.myapplication;
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 imageTest;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageTest = (Button) findViewById(R.id.imageAsync);
imageTest.setOnClickListener(new View.OnClickListener() {//点进进入ImageTest Class进行测试
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this,ImageTest.class));
}
});
}
}
xml文件比较简单就不在这里贴出。
下面是跳转进入的ImageTest Activity:
package com.chase.cn.myapplication;
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;
/**
* Created by Chase on 2016/11/17.
*/
public class ImageTest extends Activity {
private ImageView mimageView;
private ProgressBar mprogressBar;
//准备图片的网址
private static String URL = "https://drscdn.500px.org/photo/149628981/q%3D80_m%3D1000/ab0758214aa0193240eb74e48bfb8aca";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image_test);
mimageView = (ImageView) findViewById(R.id.Myimage);
mprogressBar = (ProgressBar) findViewById(R.id.Image_Prograss);
//通过调用execute方法开始处理异步任务.相当于线程中的start方法.
new MyAsyncTask().execute(URL);
}
class MyAsyncTask extends AsyncTask<String, Void, Bitmap> {
//onPreExecute用于异步处理前的操作
@Override
protected void onPreExecute() {
super.onPreExecute();
//此处将progressBar设置为可见.
mprogressBar.setVisibility(View.VISIBLE);
}
@Override
protected Bitmap doInBackground(String... params) {
String url = params[0];//因为只传递进来一个参数,所以取出第0位
Bitmap bitmap = null;
URLConnection connection;
InputStream is;
try {
connection = new URL(url).openConnection();//打开connection连接对应流,在下面将它包装成BufferedReader
is = connection.getInputStream();
//为了更清楚的看到加载图片的等待操作,将线程休眠3秒钟.
Thread.sleep(3000);
BufferedInputStream bis = new BufferedInputStream(is);
//通过decodeStream方法解析输入流
bitmap = BitmapFactory.decodeStream(bis);
is.close();
bis.close();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return bitmap;//返回bitmap
}
// //此方法在doInBackground中调用publishPrograss(Prograss...values)方法更新执行进度
// @Override
// protected void onProgressUpdate(Void... values) {
// super.onProgressUpdate(values);
// }
//onPostExecute用于UI的更新.此方法的参数为doInBackground方法返回的值.
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
mprogressBar.setVisibility(View.GONE);
mimageView.setImageBitmap(bitmap);
}
}
}
布局文件为:
<?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"
android:padding="10dp"
android:gravity="center"
>
<ImageView
android:id="@+id/Myimage"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ProgressBar
android:visibility="gone"
android:id="@+id/Image_Prograss"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
进度条的模拟,进度条模拟用for循环。如果正在加载一半点退出,再进去就不会加载,为什么呢?
AsyncTask是基于线程池进行实现的,当一个线程没有结束时,后面的线程是不能执行的.所以必须等到第一个task的for循环结束后,才能执行第二个task.我们知道,当点击BACK键时会调用Activity的onPause()方法.为了解决这个问题,我们需要在Activity的onPause()方法中将正在执行的task标记为cancel状态,在doInBackground方法中进行异步处理时判断是否是cancel状态来决定是否取消之前的task.
同样在MainActivity中加一个按钮 到新的测试Activity中:
即PrograssTest
package com.chase.cn.myapplication;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ProgressBar;
/**
* Created by Chase on 2016/11/17.
*/
public class PrograssTest extends Activity{
private ProgressBar progressBar;
private MyAsyncTask myAsyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.prograsstest);
progressBar = (ProgressBar) findViewById(R.id.mprograssbar);
myAsyncTask = new MyAsyncTask();
//启动异步任务的处理
myAsyncTask.execute();
}
//AsyncTask是基于线程池进行实现的,当一个线程没有结束时,后面的线程是不能执行的.
@Override
protected void onPause() {
super.onPause();
if (myAsyncTask != null && myAsyncTask.getStatus() == AsyncTask.Status.RUNNING) {
//cancel方法只是将对应的AsyncTask标记为cancelt状态,并不是真正的取消线程的执行.
myAsyncTask.cancel(true);
}
}
class MyAsyncTask extends AsyncTask<Void,Integer,Void> {
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
//通过publishProgress方法传过来的值进行进度条的更新.
progressBar.setProgress(values[0]);
}
@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;
}
}
}
最后的注意事项:
1.必须在UI线程中创建AsyncTask的实例.
2.只能在UI线程中调用AsyncTask的execute方法.
3.AsyncTask被重写的四个方法是系统自动调用的,不应手动调用.
4.每个AsyncTask只能被执行(execute方法)一次,多次执行将会引发异常.