对于异步加载在平常的开发中使用的最多的就是Thread+Handler来完成的,但在处理单个后台异步任务的时候使用Handler进行处理会显得代码比较多,而且结构比较复杂,当然这是相对于AsyncTask来讲的,但是在进行多个异步操作以及也涉及到UI的更新操作的时候使用AsyncTask就会变得复杂起来,对于异步加载方案的选择还是需要以及业务逻辑的复杂程度来决定。
为了进行更简单的异步操作,Google为我们提供了AsyncTask进行异步操作,当然底层也是Thread和Handler的封装。
下面看一下使用AsyncTask进行异步加载的步骤:
1.继承AsyncTask抽象类,实现下面的方法:
class MyAsyncTask extends AsyncTask<Void,Void,Void>{
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Void doInBackground() {
}
@Override
protected void onProgressUpdate() {
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
}
}
在继承AsyncTask的时候需要传入三个泛型参数:
a: Params: 在执行AsyncTask时需要的参数,用于在后台任务中使用,作为doInBackground( )方法的参数传递进去
b: Progress: 后台任务执行的时候,如果需要在界面上显示进度,此处的反省执行的类型,为进度的单位,一般为Integer
c: Result: 当后台任务执行完毕后需要返回值的的类型
注: 如果不需要传递参数,或者不需要返回值,此处将泛型指定为Void即可
AsyncTask抽象类几个常用方法的介绍:
a: onPreExecute(): 此方法在后台任务开始之前调用,用于进行界面上的初始化操作,比如显示一个进度条的对话框
b: doInBackground(): 此方法中的代码在子线程执行,一般用于处理一些耗时操作。doInBackground中的参数在AsyncTask的实例调用execute( )方法的时候传入,当然传入参数的类型与Params的类型有关,如果Params的泛型设置为Void则不需要传递参数。还有一点需要注意的是,由于此方法是执行在子线程中的,因此不能在该方法中进行更新Ui的操作。
c: onProgressUpdate(): 在 doInBackground()方法中调用了 publishProgress()方法后,此方法将很快被调用,在这个方法 中可以进行更新UI的操作,根据 publishProgress()传入的参数进行Ui进度的更新。
d: onPostExecute(): 当doInBackground中的任务执行完毕并通过return 返回后,就会调用此方法,doInBackground()方法返回的数据会作为参数传递到onPostExecute()方法中,此时可以利用返回的结果进行Ui的操作,或者关闭对话框。
一个最简单的异步任务只需要实现 doInBackground() 和onPostExecute()即可。
2.创建AsyncTask实例,执行任务:
MyAsyncTask myAsyncTask = new MyAsyncTask();
myAsyncTask.execute();
当然这里的execute()方法需要传递参数的类型,是由Params的泛型决定,如果为Void则不需要传递参数。
需要注意的是,一个AsyncTask的实例,只能执行一次execute()方法,如果执行多次会出现下面的异常:
execute()方法的实现:
//定义的关于任务运行状态的几个枚举类型
public static enum Status {
FINISHED,
PENDING,
RUNNING;
private Status() {
}
}
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
//如果该任务处在运行状态则抛出异常
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
//如果该任务已经执行完成则抛出异常
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
sExecutor.execute(mFuture);
return this;
}
通过查看源码之后也就明白了为什么一个实例的execute()只能执行一次。
下面通过一个从网络加载图片的例子来使用一下AsyncTask:
运行效果:
示例代码:
/**
* 使用AsyncTask进行网络图片加载并显示到ImageView
*/
public class AsyncTaskActivity extends AppCompatActivity implements View.OnClickListener {
private ImageView imageView;
private EditText editText;
private Button btnSubmit;
private String mImageUrl;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ansy_task);
InitUI();
btnSubmit.setOnClickListener(this);
}
private void InitUI() {
imageView = (ImageView) findViewById(R.id.imageView_net);
editText = (EditText) findViewById(R.id.edt_img_url);
btnSubmit = (Button) findViewById(R.id.btn_show);
}
@Override
public void onClick(View view) {
MyAsyncTask myAsyncTask = new MyAsyncTask();
mImageUrl = editText.getText().toString();
myAsyncTask.execute(mImageUrl);
}
/**
* 继承 AsyncTask 抽象类三个泛型参数的作用
* 1.Params: 在执行AsyncTask时需要的参数,用于在后台任务中使用 (doInBackground)
* 2.Progress: 后台任务执行的时候,如果需要在界面上显示进度,此处的反省执行的类型,为进度的单位,一般为Integer
* 3.Result: 当后台任务执行完毕后需要返回值的的类型
* 如果不需要传递参数,或者不需要返回值,此处将泛型指定为Void即可
*/
class MyAsyncTask extends AsyncTask<String,Integer,Bitmap>{
/**
* 此方法在后台任务开始之前调用,用于进行界面上的初始化操作,比如显示一个进度条的对话框
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
Log.i("wk","onPreExecute()方法执行了,此方法用于进行初始化相关的工作");
}
/**
*此方法中的代码执行在子线程,用于处理一些耗时操作,
* doInBackground(String... strings)中的参数在 execute(str)方法中传入。
* 处理完后使用return返回处理结果
*
* 当需要反馈当前操作进度的时候,可以调用publishProgress()方法来完成,根据传递的参数,在onProgressUpdate()
* 进行显示进度的状态。
*
* 注:由于代码运行在子线程,不能在这个方法中进行更新UI的操作。
*
* String ...属于不定参数,可以传递对个String对象
* @param strings
* @return
*/
@Override
protected Bitmap doInBackground(String... strings) {
Log.i("wk","imageUrl:"+strings[0]);
//调用此方法来触发 onProgressUpdate()方法的执行,传入参数进行进度的更新
publishProgress(100);
InputStream is = getStreamFromUrl(strings[0]);
Bitmap bitmap = BitmapFactory.decodeStream(is);
return bitmap;
}
/**
* 在 doInBackground()方法中调用了 publishProgress()方法后,此方法将很快被调用,在这个方法
* 中可以进行更新UI的操作,根据 publishProgress();传入的参数进行进度的更新
* @param values
*/
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
Log.i("wk","当前进度:"+values[0]);
}
/**
* 当后台任务执行完毕并通过return 返回后,就会调用此方法,doInBackground()方法返回的
* 数据会作为参数传递到onPostExecute()方法,此时可以利用返回的结果进行Ui的操作,或者关闭对话框。
* @param bitmap
*/
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
imageView.setImageBitmap(bitmap);
}
}
/**
*
* 根据Image的URL获取流
* @param string
* @return
*/
private InputStream getStreamFromUrl(String string) {
try {
URL url = new URL(string);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setDoInput(true);
InputStream inputStream = conn.getInputStream();
if(inputStream!=null){
return inputStream;
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
return null;
}
}
^_^