Android异步处理特性之AsyncTask

2011-07-19 15:43:41|分类:android|字号订阅

虽然使用线程能适用大部分异步的场景,但是代码看起来还是不够简练,在android平台,还有另外一种解决方案----AsyncTask。

首先直接上代码:

private class getRemoteDataTask extends AsyncTask<String, Integer, ArrayList<Hashtable<String, String>>> {
protected void onPostExecute(ArrayList<Hashtable<String, String>> dataList) {

this.cancel(false);
}

@Override
protected ArrayList<Hashtable<String, String>> doInBackground(String... params) {
ArrayList<Hashtable<String, String>> dataList = new ArrayList<Hashtable<String, String>>();

return dataList;
}
}

下面来解释一下,doInBackground方法类似于在线程中的run方法,这个方法是另一个后台线程中执行,然后执行完会通过回调机制执行 onPostExecute,神奇之处就在于,onPostExecute这个方法的执行权限又交回给activity的主进程中执行,也就是说 onPostExecute这个方法里面可以操作UI。于是就能实现异步读取数据,并且操作UI,是不是比起自己写线程方便很多?

然后我们来剖析一下,这种方案的几个不太容易理解的地方。

一、AsyncTask<String, Integer, ArrayList<Hashtable<String, String>>> 注意这里几个泛型的定义和意义,第一个代表该方法被调用时传递的参数类型,注意是参数的类型,而不是参数的个数,也就是说如果定义为String,那么, 该方法被调用时所传递的参数只能是String类型;然后第二个Integer,资料上说是线程后台执行的百分比,不过我没彻底弄清楚这个的含义,欢迎大 家指教,然后泛型里的第三个ArrayList<Hashtable<String, String>,这个和该类的两个方法都有密切联系,该泛型是指后台执行返回的结果的类型。

二、doInBackground这个方法是在后台执行的,然后它的返回结果将提供给onPostExecute,所 以,doInBackground的返回值必须和AsyncTask类的第三个泛型定义一致,然后onPostExecute的参数类型必须和 doInBackground的返回类型一致。

三、前边我们都一直在解释AsyncTask这个类,但是怎么调用呢?其实很简单

newgetRemoteDataTask().execute(String...params);

注意,execute方法里传递的参数的类型必须和AsyncTask第一个泛型的类型一致,然后这个方法传递的参数将在

doInBackground(String...params)

这里面起作用。

第四,AsyncTask还有很多方法,我们仅仅分析了其中两个,它被继承后,必须要重写的一个方法是doInBackground,因为doInBackground的返回值将在onPostExecute中被用来更新UI,所以我们姑且认为这两个方法是最重要。

第五、AsyncTask的实例必须在UI线程中被调用、execute(String...params)必须在UI线程中调用、不要手动调用onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...)等方法、AsyncTask只执行一次。

一个小例子

有一个ListView的小例子,一开始List中没有内容,通过一个AsyncTask逐步在List中加入条目。

1)XML文件:简单的ListView布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout ... ...>

<ListView android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>

2)例子代码

public class Chapter15Test3 extends ListActivity{
//这里是List Item内容,在这个例子中,将在后台任务中逐个加入
private static String[] items={"lorem", "ipsum", "dolor","sit", "amet", "consectetuer","adipiscing", "elit", "morbi","vel", "ligula", "vitae","arcu", "aliquet", "mollis","etiam", "vel", "erat","placerat", "ante","porttitor", "sodales","pellentesque", "augue","purus"};

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chapter_8_test2);

//在这个例子中,我们一开始并没有导入items的数据,注意item数据为新建的ArrayList,即无内容
setListAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,new ArrayList<String>()));
//步骤5:创建后台任务的对象,并通过execute()启动后台线程,调用doInBackground()的代码,execute中的参数类型为参数1,这里我们不需要传递任何内容
new AddStringTask().execute();
}

//步骤1:创建AsyncTask子类,参数1是Void的范式类型,参数2是String的范式类型,参数3是Void其中参数1:向后台任务的执行方法传递参数的类型;参数2:在后台任务执行过程中,要求主UI线程处理中间状态,通常是一些UI处理中传递的参数类型;参数3:后台任务执行完返回时的参数类型
private class AddStringTask extends AsyncTask<Void, String,Void>{
//我们加入一个检测信息的方法,打印当前在哪个线程执行的信息
private void printInfo(String info){
Log.d("WEI", info + " : Tread is " + Thread.currentThread().getName());
}


//步骤2:实现抽象方法doInBackground(),代码将在后台线程中执行,由execute()触发,由于这个例子并不需要传递参数,使用Void...,具体书写方式为范式书写
protected Void/*参数3*/ doInBackground(Void...params/*参数1*/) {
for(String item : items){
//步骤3:通知UI主线程执行相关的操作(在onProgressUpdate中定义)
publishProgress(item/*参数2*/);
printInfo("doInBackgound " + item);
SystemClock.sleep(200);
}
return null;

}

//步骤3:定义收到pushProgress()触发后,在UI主线程执行的内容,在本例,将item加入list中。方法中的参数为范式方式,实质为数组,由于我们只传递了item一个String,要获取,为values[0]
protected void onProgressUpdate(String... values/*参数2*/) {
printInfo("onProgressUpdate get param " + values[0]);
((ArrayAdapter<String>)getListAdapter()).add(values[0]);

}

//步骤4:定义后台进程执行完后的处理,本例,采用Toast

protected void onPostExecute(Void result/*参数3*/) {
printInfo("onPostExecute");
Toast.makeText(Chapter15Test3.this, "Done!", Toast.LENGTH_SHORT).show();
}
}
}

我们根据printInfo跟踪各部分代码在哪里执行:doInBackground在后台线程执行,onProgressUpdate()和onPostExecute()在UI主线程执行。main就是UI主线程,而AsyncTask #1为后台线程,名字不一样。

需要注意

虽然Android提供后台任务方便我们处理,是否使用后台任务,以及如何使用后台任务,我们要注意下面的内容:

可能在执行后台线程处理中,用户和UI之间存在交互,这些交换可能会对后台任务有重要的影响,因此需要通知后台线程,Android提供很多的类来处理,封装在java.util.concurrent包中,帮助与后台线程的安全通信。可能在执行后台线程处理中,我们的Activity就已经被kill了,例如有一个电话过来,然后发给短信,查看号码本。这时系统可能将你的activity踢走,接着后面我们会学习Activity的生命周期,了解相关的情况。在编程中,出现这种情况,只要有可能,需要将后台进程关闭。可能在执行后台线程处理中,出现某种错误,例如后台在下载URL,而网络连接中断了。这种情况下关闭后台进程可能是最好的处理。此外后台任务是消耗CPU和内存,是有代价的,我们应该确保它处理的时候更为有效。

更多相关文章

  1. android.os.NetworkOnMainThreadException
  2. android mediaStore
  3. Android发送数据到web服务器4种方式
  4. Android之Handler用法总结
  5. android animation中的参数interpolator详解
  6. Android(安卓)实现联网——在线程中联网
  7. 如何通过代码更改ANDROID的UI布局
  8. mybatisplus的坑 insert标签insert into select无参数问题的解决
  9. Python技巧匿名函数、回调函数和高阶函数

随机推荐

  1. 闲话Android(安卓)之 屏幕大小、pixel、
  2. 调查:Android统治美国 苹果停止增长 黑莓
  3. Android——TextView和EditText控件
  4. Android(安卓)手势(Gesture)
  5. 伦敦开发者构建了一个基于 Android(安卓)
  6. 巧用事件分发机制,和我一起hold住android
  7. android 可以精确到秒级的时间选择器
  8. 韩国监管机构撤销对Android反竞争指控
  9. 关于eclipse中关联各版本Android.jar对应
  10. 思考Android架構(一):What & Why《Android