Android 后台任务(三)AsyncTask
翻译自:http://blog.stylingandroid.com/archives/833
转载请注明:http://blog.csdn.net/liaoqianchuan00/article/details/23949649
前面我们使用了线程来让耗时操作脱离UI线程执行,也介绍了一些在工作线程中如何再去更新主线程的方法。但是当我们为了频繁的在UI线程和工作线程之间切换加了很多Runnables时,我们的代码变得越来越难看。这篇文章,我们来看看AsyncTask怎么来提供一个更清晰的更新UI的机制。
AsyncTask为我们提供了开一个后台线程的框架。我们实现一个AsyncTask的子类,重写他的不同life-cycle点的一些方法,一些方法运行在UI线程,一些运行在后台工作线程。
你必须重写的一个方法是doInBackground方法,这个方法运行在后台工作线程,这个地方你需要添加你需要做得一些耗时操作的代码。其他3个可选的UI线程的方法:
1. onPreExecute:在doInBackground之前执行,你可以在这里写上一些出事后的代码。
2. onProgressUpdate:在doInBackground执行的同时执行的一些方法,你的耗时操作在进行的时候,你可以在这里更新UI来显示进度。
3. onPostExecute:在doInBackground之后执行,你可以在这里写上耗时操作完成之后更新UI的代码。
有个评判的标准就是,当你只需要重写doInBackground方法的时候,你其实可以直接使用一个简单的Thread来实现。
AsyncTask是一个泛型类, 在定义一个AsyncTask的时候,你需要定义三个类型对象,分别是传入Task的对象类型(doInBackground传入的类型),代表进度的对象类型,工作线程返回的类型(doInBackground返回的类型。比如我们有一个任务是需要从网络上下载东西,同时基于一个整形数值来更新进度条,最后返回一个String值:
class MyAsyncTask extends AsyncTask<URL, Integer,String>
{
private final Activityactivity;
private ProgressBar progress;
private int count = 0;
public MyAsyncTask( Activityactivity )
{
this.activity= activity;
}
@Override
protected void onPreExecute()
{
progress= (ProgressBar)
activity.findViewById(R.id.progress );
}
@Override
protected StringdoInBackground( URL... params )
{
Stringret = null;
count= params.length;
for(int i = 0; i < count; i++ )
{
publishProgress(i );
//Do something which
//populates "ret"
}
returnret;
}
@Override
protected voidonProgressUpdate( Integer... values )
{
progress.setMax(count );
progress.setProgress(values[0] );
}
@Override
protected void onPostExecute(String result )
{
Toast.makeText(activity, result,
Toast.LENGTH_SHORT).show();
}
}
这样做我们就不需要考虑手动的在UI线程和工作线程之间切换来更新UI了,它都为我们自动的处理了。
要使用它,我们需要创建一个MyAsyncTask的实力,并且调用execute的时候传入一个或者多个URL参数:
public class MyActivity extends Activity
{
@Override
public void onCreate( BundlesavedInstanceState )
{
super.onCreate(savedInstanceState );
setContentView(R.layout.main );
//Get some urls
newMyAsyncTask( this ).execute( url1, url2, url3 );
}
使用AsyncTask的一个问题就是,除非开发人员很清楚每个方法在哪个线程上执行,会很容易发生问题。比如,我发现一些人在onPreExecute方法中放入了网络请求的代码,这看起来没有什么问题,因为网络请求已经在AsyncTask中实现了啊,但是其实这个网络请求仍然在UI线程中执行的。
上面例子中还有一个问题就是AsyncTask保存了一个Activity的实例,由于AsyncTask线程池的机制,他的工作线程的生命周期是不确定的,应用程序无法控制的,所以这个时候如果AsyncTask作为Activity的内部类的话很容易出现内存泄露。而我们直接使用线程的方式要好点,只有在run函数不结束时,才出现这种内存泄露问题。以后将会单独用一篇文章来分析。
Activityfinish掉了,但是AsyncTask还在执行,会有问题吗?(需要验证)
在Honeycomb上引入了Loaders,这是另外一个机制来处理耗时操作,他更清晰的区分了那些是在UI线程上执行的,那些是在工作线程上执行的。同时他不需要在任务结束后保持对Context的引用。下一章我们将介绍Loaders。