今天学习了AsyncTask的基本用法,现在进行一个总结:
首先要知道为什么异步任务:
1、Android单线程模型
2、 耗时的操作要放在非主线程中进行,避免主线程堵塞
其次要知道AsyncTask的作用:
1、在子线程中更新UI
2、封装、简化异步操作
接下来看下AsyncTask的基本用法:
由于AsyncTask是一个抽象类,所以如果我们想使用它,就必须创建一个子类去继承它,在继承时我们必须为AsyncTask类指定三个泛型参数,如果传入Void,则表示不传入参数
1、Params:在执行时传入要传入的参数,可用于在后台任务中使用
2、Progress:后台任务执行时,如果需要在界面上显示当前的进度,则使用指定的泛型作为进度单位
3、当任务执行执行完毕后,如果需要对结果进行返回,使用这里指定的泛型作为返回值类型
然后我们需要构建AsyncTask子类的回调方法:
- onPrexecute()执行后台操作前调用,一般执行初始化操作,在doInBackground方法前执行
- doInBackground(Params…)必须重写,在这里执行异步任务;耗时操作写在这里,可调用publishProgress(Progress…)方法返回当前的执行进度
- onProgressUpdate(Progress…)当在doInBackground调用doInBackground()后很快被调用对UI操作,这个可以不写(没有在doInBackground中调用)
- onPostExecute(Result):当doInBackground方法结束执行,系统自动调用该方法,接收的值就是doInBackground最后返回的值;
说下使用AsyncTask的注意事项
1、必须在UI线程中创建AsyncTask实例
2、必须在UI线程中调用AsyncTask的execute()方法
3、重写的四个方法是系统自动调用的,不要手动去调用
4、每个AsyncTask只能被执行一次,多次调用将会引发异常
下面开始实例:使用AsyncTask加载图片
首选我们创建布局:image.xml用于显示图片IMageView和加载进度ProgressBar
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:padding="16dp"
android:layout_height="match_parent">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ProgressBar
android:id="@+id/progrssBar"
android:visibility="gone"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
新建活动ImageTest
package com.asynctask.css.asynctask;
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.net.URL;//手动导入
import java.io.InputStream;
import java.net.URLConnection;
/**
* Created by css on 2016/3/28.
*/
public class ImageTest extends Activity {
private ImageView mImageView;
private ProgressBar mProgressBar;
MyAsynvTask mtask;
//定义静态的String类型的URL用于存储要加载图片的地址
private static String URL = "http://pic.mmfile.net/2013/08/1315595G7-2.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image);
mImageView = (ImageView) findViewById(R.id.image);
mProgressBar = (ProgressBar) findViewById(R.id.progrssBar);
//当调用execute之后,进行异步操作
mtask = new MyAsynvTask();
mtask.execute(URL);
}
// 新建内部类MyAsynvTask继承AsyncTask,设置传递进去的参数,
// 1、第一个参数表示加载图片的地址,所以传入String,
// 2、第二个参数表示不需要显示返回进度,使用Void,
// 3、第三个参数表示最后返回Bitmap
class MyAsynvTask extends AsyncTask<String,Void,Bitmap>
{
// 1、系统调用该方法,对异步操作进行初始化操作,这里显示ProgressBar
@Override
protected void onPreExecute() {
super.onPreExecute();
mProgressBar.setVisibility(View.VISIBLE);
}
// 2、开始真正的异步操作处理,都在子线程中处理,放置任意的耗时操作,最后返回指定的类型
@Override
protected Bitmap doInBackground(String... params) {
String url = params[0];//可以传递不止一个参数进来,以数组的形式存在,取出对应的URL
Bitmap bitmap = null;
URLConnection connection ; //定义网络连接对象
InputStream is; //用于获取数据的输入流
//访问网络的费时操作
try {
connection = new URL(url).openConnection(); //此处需要手动导入URL包,获取网络连接对象
is = connection.getInputStream(); //获取输入流
BufferedInputStream bis = new BufferedInputStream(is);
//加入睡眠时间,晚点执行
// try {
// Thread.sleep(3000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//使用BitmapFactory的decodeStream方法可以很方便的把bis输入流转换为Bitmap
bitmap = BitmapFactory.decodeStream(bis);
is.close();//关闭输入流
bis.close();
}
catch (IOException e)
{
e.printStackTrace();
}
//将bitmap作为返回值传递到onPostExecute中,在onPostExecute进行UI更新
return bitmap;
}
// 3、运行在主线程,任意进行UI操作onPostExecute在本方法中操作UI,把图像设置到ImageView
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
mProgressBar.setVisibility(View.GONE); //隐藏ProgressBar
mImageView.setImageBitmap(bitmap); //显示图片
}
}
}
写完活动之后立马注册活动,养成好习惯;
<activity android:name=".ImageTest"></activity>
最后在主活动里面点击Button实现加载图片,剩下的点击Button就省略了,很简单。
看下运行效果:
1、MainActivity主界面
2、加载过程中ProgressBar显示界面
3、加载成功图片显示界面
最后说下AsyncTask的取消:
AsyncTaskc是通过线程池执行的,如果前面的线程没有执行完,再去点击开启一个新的线程的时候就会出现空白,即新线程并没有执行,而是等待上一个线程执行完之后才开始执行的;那么怎么解决这个问题呢?
需要通过让AsyncTask的声明周期和activity的声明周期保持一致,在activity的onpause方法中加入如下代码:
@Override
protected void onPause() {
super.onPause();
if (mTask != null && mTask.getStatus() == AsyncTask.Status.RUNNING)
{
//cancel方法只是将AsyncTask标记为cancel状态,并不是真正的取消线程的执行
mTask.cancel(true);
}
}
仅仅写这些事不够的,因为.cancel方法只是对该线程进行标示,并没有取消该线程,还要加上下面的代码
if(isCancelled())
{
break;
}
具体的参考代码,下载地址:AsyncTask小例子