用于执行异步任务
AsyncTask<Params,Progress,Result>是一个抽象类,通常用于被继承
需要指定以下三个泛型参数:
Params:启动任务时输入参数的类型
Progress:后台任务执行中返回进度值的类型
Result:后台执行任务完成后返回结果的类型
耗时操作:如加载网络图片等需要等待的操作,通常不允许放进主线程中
回调方法:
doInBackground:必须重写,异步执行后台线程将要完成的任务,所有耗时操作都将要在这个方法内执行
onPreExecute:执行后台耗时操作前被调用,通常用于完成一些初始化操作
onPostExecute:当doInBackground()完成后系统会自动调用onPostExecute()方法,并将doInBackground()的值传给该方法
onProgressUpdate():在doInBackground()方法中调用publishProgress()方法更新任务执行进度后就会触发该方法
回调方法调用顺序:
onPreExecute()-->doInBackground()-->onProgressUpdate()-->onPostExecute()
通过一个例子也加深了对AsyncTask的理解:
加载一张网络图片
作为一个不稳定的耗时操作,是严禁被放入主线程中的,所以需要在异步处理中下载图像,在UI线程中设置图像
过程:
1.创建一个布局文件,并创建一个imageView组件和一个progressBar组件
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/imageview"
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>
注意progressBar 使用android:visibility="gone"设置其默认不显示出来
android:layout_centerInParent="true"设置其居中显示
2.创建ImageTest类,并创建内部类myAsyncTask
public class ImageTest extends Activity {
private ImageView mImageView;
private ProgressBar mProgressBar;
private static String URL="http://img1.gtimg.com/digi/pics/hv1/109/199/2039/132636829.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image);
mImageView=(ImageView)findViewById(R.id.imageview);
mProgressBar=(ProgressBar)findViewById(R.id.progressbar);
new myAsyncTask().execute(URL);
}
class myAsyncTask extends AsyncTask<String,Void,Bitmap>{
@Override
protected void onPreExecute() {
super.onPreExecute();
mProgressBar.setVisibility(View.VISIBLE);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap); //操作UI设置图像
mProgressBar.setVisibility(View.GONE);
mImageView.setImageBitmap(bitmap);
}
@Override //传入可变长数组
protected Bitmap doInBackground(String... params) {
//获取传递进来的参数
String url=params[0];
Bitmap bitmap=null;
URLConnection connection;
InputStream is;
try {
connection=new URL(url).openConnection();
is=connection.getInputStream();//获取输入流
BufferedInputStream bis=new BufferedInputStream(is);
Thread.sleep(3000); bitmap=
BitmapFactory.decodeStream(bis);//将输入流解析成bitmap
is.close();
bis.close();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return bitmap;
}
}
}
其中注意:
1.传入URL为String类型,表示需要加载的图片地址
protected Bitmap doInBackground(String... params) {
String url=params[0];
doInBackground()方法传递进来的数组是一个可变长数组,也就是说可以传递不只一个参数,由于本例子只传入一个参数,所以只需要取出第一位即可,即params[0]
connection=new URL(url).openConnection();
这里获取网络连接对象
is=connection.getInputStream();
这里获取输入流
BufferedInputStream bis=new BufferedInputStream(is);
//将输入流包装
bitmap= BitmapFactory.decodeStream(bis);
将输入流解析成bitmap
6.在线程开始之前需要先把progressBar设置成可见
protected void onPreExecute() {
super.onPreExecute();
mProgressBar.setVisibility(View.VISIBLE);}
protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); mProgressBar.setVisibility(View.GONE); mImageView.setImageBitmap(bitmap);}
doInBackground()方法执行后返回一个bitmap图像,获取到图像后就可以操作UI设置图像,同时将progressBar隐藏
new myAsyncTask().execute(URL);
设置传递进去的参数,首先调用onPreExecute()方法,接下来调用doInBackground()方法,最后调用onPostExecute()方法
9.最后补充Mainfest文件,并添加activity和网络权限
<uses-permission android:name="android.permission.INTERNET"/>
再在主线程中添加按钮事件
10.若觉得图片加载太快看不见progressBar可以添加Thread.sleep(3000)让其睡眠3秒再开始加载
onPreExecute()加载进度条
doInBackground()下载网络数据(耗时操作)
onPostExecute()显示图片
下面用ProgressBar来模拟进度条
首先创建布局文件并设置progressBar样式
progressbar.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/pg"
style="?android:attr/progressBarStyleHorizontal"
android:layout_gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:layout_alignParentStart="true" />
</RelativeLayout>
然后创建ProgressBarTest类
public class ProgressBarTest extends Activity {
ProgressBar mProgressBar;
private myAsyncTask mTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.progressbar);
mProgressBar=(ProgressBar)findViewById(R.id.pg);
mTask=new myAsyncTask();
mTask.execute();
}
class myAsyncTask extends AsyncTask<Void,Integer,Void>{
@Override
protected Void doInBackground(Void... params) {
for(int i=0;i<100;i++){
publishProgress(i);
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
mProgressBar.setProgress(values[0]);
}
}
}
其中
protected Void doInBackground(Void... params) {
for(int i=0;i<100;i++){
publishProgress(i);
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace(); } }
return null;}
publishProgress(i);用于调用更新进度的函数,所以将i传入
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
mProgressBar.setProgress(values[0]);
}
将values即传入的i取出并设置给progressBar
可是运行时,若退出再进入页面,progressBar会等一段时间再开始动,这是因为必须等到前面的task执行完成后才能执行后面的操作,所以要等到for循环执行完毕后才能执行下一次循环
解决方法:使Activity的生命周期和AsyncTask的生命周期保持一致即可
添加onPause()方法
@Override
//当mTask线程不为空而且状态是Running
protected void onPause() {
super.onPause();
if(mTask!=null&&
mTask.getStatus() == AsyncTask.Status.RUNNING){
mTask.cancel(true);
}}
判断当mTask线程不为空而且状态是Running时,取消线程
可是运行后还是没解决,这是因为cancel只是将对应的AsyncTask标记为Cancel状态,并不是真正的取消线程的执行
而且在java中也是不能直接停止线程的,必须等一个线程执行完毕后才能执行后面的操作
所以应该在具体的异步线程中去检测状态值的改变,一旦当前AsyncTask的状态改为cancel,就跳出循环来结束掉操作,从而结束掉整个线程的逻辑
在doInBackground()方法中加入判断
protected Void doInBackground(Void... params) {
for(int i=0;i<100;i++){
if(isCancelled()){
break;
}
publishProgress(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace(); } }
return null;}
同理在更新操作中也要加入判断
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if (isCancelled()){
return;
}
mProgressBar.setProgress(values[0]); }}
总结
AsyncTask使用注意事项:
-必须在UI线程(onCreate())中创建AsyncTask实例
-必须在UI线程(onCreate())中调用AsyncTask的execute()方法
-重写的四个方法时系统自动调用的,不能手动调用
-每个AsyncTask只能被执行一次,多次调用会引起异常
-在AsyncTask中只有doInBackground()运行在其他线程,其他方法都运行在主线程,也就是说在其他方法中可以更新UI,而唯一在这个方法中需要做异步处理,不能再此方法中更新UI,安卓提供了onPreExecute()和onPostExecute()去承接异步处理中的操作去更新UI