转:android异步任务设计思详解(AsyncTask)
这里说有设计思想是我根据查看Android源代码提炼出来的代码逻辑,所以不会跟Google工程师的原始设计思想100%符合(也有可能是0%),但是本文一定可以帮助你理解AsyncTask,也可能有一些你以前没有发现的内容。
大家都知道,Android的主线程(又叫UI线程,线程ID为1)有一些限制策略,使得主线程有些事做不了,比如访问网络就不允许,否则就是报,但在2.3之后的版本,你可以通过添加以下代码更改其限制策略,从而强制使得主线程可以访问网络:
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
不过StrictMode是一个开发工具主要用于侦测主线程中的磁盘和网络访问,而不是让你做这“坏”事,其实Android这样限制是有好处的,强制让开发者重视用户体验,一个反面例子是Windows,主线程里什么都可以做,一些很懒的开发者就把所有任务放到主线程里,使得线程经常好卡,比如编辑器UE或Notepad++打开了一个网络上(如samba服务器)的文件,如果突然网络中断了,那你的整个编辑器都卡住,要等好久才会有反应,不过我不确定那是不是因为主线程里访问了网络,不过Windows经常因为这个原因卡。还有一个正面例子是iOS,极其注意响应速度,所以当有用户输入事件时,其内核都有相应的调度,从而优先响应用户操作。
还是回到正题,就是因为主线程的这些限制使开发者不得不写多个线程,当然,你也可以不用AsyncTask,不过你不用也避免不了多线程,如果你不用,就是可能要用Handler和Thread了,我想很多人初学的时候就是那么干的,包括我,因为那时很有可能还没有发现有这个类,于是就经常写Handler和Thread的了,写着写着就发现有一些代码是相同的,你写的Handler和Thread匿名类肯定是重复代码,如下:
public void handleMessage(Message msg) {
System.out.println("The hard have done!");
// ... front end code
}
};
new Thread() {
public void run() {
doHardWork();
handler.sendEmptyMessage(0);
}
private void doHardWork() {
// ... back end code
}
}.start();
你可能想到要复用这些代码,当然,你可以通过Copy的方式来复用这段代码,用的时候只要在省略号处写入你的代码就可以了,但更好的复用是将其用一个类封装起来,好吧,那我们就简单的封装一下吧,于是,就变成了这样:
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
System.out.println("The hard have done!");
//...
}
};
public void doInBackground() {
new Thread() {
public void run() {
doHardWork();
handler.sendEmptyMessage(0);
}
private void doHardWork() {
// ...
}
};
}
}
这样好像还不行,因为无法告诉后台线程做什么事,做完了也不知道通知,要复用还是得Copy代码,我们可以加两个方法,一个在前台调用一个在后台调用,只要定义一个新类就可以实现复用,于是代码变成这样:
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
System.out.println("The hard have done!");
runInFrontend(); // added
}
};
public void doInBackground() {
new Thread() {
public void run() {
doHardWork();
handler.sendEmptyMessage(0);
}
private void doHardWork() {
runInBackend(); //added
}
};
}
//added
protected void runInBackend() {
}
//added
protected void runInFrontend() {
}
}
一个可复用的类就出炉了,我们写一个子类,并用一个Activity来调用一下吧:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new SubHandlerAndThread().doInBackground();
}
class SubHandlerAndThread extends HandlerAndThread {
protected void runInBackend() {
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
protected void runInFrontend() {
System.out.println("Task has been done");
}
}
}
这样是不是比直接写Thread和Handler简洁了许多呢,这里我是用sleep来模似长时间事务的,如果在真实的环境中,我们可能是要下载,如果是下载,我们可能希望传入个下载地址的参数到后台线程,来让他按我们的需要下载,我们给加doInBackground方法加一个参数,于是HandlerAndThread类的代码就变成这样:
...
public void doInBackground(final String url) { // added url
new Thread() {
public void run() {
doHardWork();
handler.sendEmptyMessage(0);
}
private void doHardWork() {
runInBackend(url); // added url
}
};
}
protected void runInBackend(String url) { // added url
}
...
}
而调用类的代码变成这样:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String url = "http://path/to/file";
new SubHandlerAndThread().doInBackground(url); //added url
}
class SubHandlerAndThread extends HandlerAndThread {
@Override
protected void runInBackend(String url) { // added url
System.out.println("Start download from url:" + url);
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
protected void runInFrontend() {
System.out.println("finish download");
}
}
}
假如是下一个文件呢,我们是不是加一个进度更新的方法呢,于是又变成这样:
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) { // added
case 0:
runInFrontend();
break;
case 1:
runInFrontendProgress(msg.arg1);
break;
}
}
};
...
final protected void publishProgress(int progress) { // added
handler.obtainMessage(1, progress, 0);
}
protected void runInFrontendProgress(int progress) { // added
}
}
public class MainActivity extends Activity {
...
class SubHandlerAndThread extends HandlerAndThread {
@Override
protected void runInBackend(String url) {
System.out.println("Start download from url:" + url);
for (int i = 0; i < 10; ++i) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrack();
}
publishProgress(i*10); // added
}
}
...
@Override
protected void runInFrontendProgress(int progress) { // added
System.out.println("Progress: " + progress);
}
}
}
你可能已经没有耐心一版一版的进化了,那我就跳跃一下吧,一次多加几条需要:一、我们下载完了可能要得到文件的路径,所以我们给runInFrontend方法加一个输入参数filePath表示路径;二、把子类必须实现的方法改成抽象方法,类也改成抽象方法;我把代码中的一些方法名改一下,使其更好理解,把doInBackground改为execute,把runInFrontend改为onPostExecute,把runInFrontendProgress改为onProgressUpdate。最终版如下:
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
onPostExecute((String) msg.obj);
break;
case 1:
onProgressUpdate(msg.arg1);
break;
}
}
};
public void doInBackground(final String url) {
new Thread() {
public void run() {
String result = runInBackend(url);
handler.obtainMessage(0, result);
}
};
}
final protected void publishProgress(int progress) {
handler.obtainMessage(1, progress, 0);
}
abstract protected String runInBackend(String url);
protected void onPostExecute(String filePath) { }
protected void onProgressUpdate(int progress) { }
}
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String url = "http://path/to/file";
new SubHandlerAndThread().doInBackground(url);
}
class SubHandlerAndThread extends HandlerAndThread {
@Override
protected String runInBackend(String url) {
System.out.println("Start download from url:" + url);
for (int i = 0; i < 10; ++i) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
publishProgress(i*10);
}
return "/path/to/file";
}
@Override
protected void onPostExecute(String filePath) {
System.out.println("Download finished");
}
@Override
protected void onProgressUpdate(int progress) {
System.out.println("Progress: " + progress);
}
}
}
这是不是跟Android的AsyncTask很像呢,我想Google就是由于这种需求做出这个类的,Android官网是这样描述AsyncTask的:
This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
意思是这个类使得:不使用Thread和Handler,就可以在后台执行操作然后在发布结果到UI线程。其实他内部的实现就是封装了Thread和Handler,所以你就不必要直接用这两个低层类了,但他的目的也是代码复用,他的实现跟我们上面写的类也差不多。主要有这几点不同:一、AsyncTask使用了线程池而不是单个线程去执行后台任务,该线程池是整个进程共用的,是因为他的线程池对象是一个静态成员变量,这一点很多人搞错,误以为AsyncTask越来创建的线程越多,这不是绝对正确的,因为线程池会根据负载动态调整的,而且有最大值和空闲超时,AsyncTask的配置是最小5,最大128,空闲超时1秒,当然你也可以配置成线程数根据任务数线程递增,关于线程池,可参考这里,后续我会在博客写文章讨论Java线程池;二、AsyncTask的输入和输出参数使用了泛型;三、AsyncTask支持中断当前任务。
现在知道了AsyncTask的设计思想了吧,是不是很简单呢,所以建议童鞋们去看一下它的源码,反正我写代码时有查看源码的习惯,因为我会好奇它是如何实现的,看源码有很多好处,如可以学到好的API设计思想,软件架构。
转:android异步任务设计思详解(AsyncTask)的更多相关文章
- Drawable实战解析:Android XML shape 标签使用详解(apk瘦身,减少内存好帮手)
Android XML shape 标签使用详解 一个android开发者肯定懂得使用 xml 定义一个 Drawable,比如定义一个 rect 或者 circle 作为一个 View 的背景. ...
- Android Design Support Library使用详解
Android Design Support Library使用详解 Google在2015的IO大会上,给我们带来了更加详细的Material Design设计规范,同时,也给我们带来了全新的And ...
- Android 之窗口小部件详解--App Widget
Android 之窗口小部件详解--App Widget 版本号 说明 作者 日期 1.0 添加App Widge介绍和示例 Sky Wang 2013/06/27 1 App ...
- [Android新手区] SQLite 操作详解--SQL语法
该文章完全摘自转自:北大青鸟[Android新手区] SQLite 操作详解--SQL语法 :http://home.bdqn.cn/thread-49363-1-1.html SQLite库可以解 ...
- 《Android群英传》读书笔记 (5) 第十一章 搭建云端服务器 + 第十二章 Android 5.X新特性详解 + 第十三章 Android实例提高
第十一章 搭建云端服务器 该章主要介绍了移动后端服务的概念以及Bmob的使用,比较简单,所以略过不总结. 第十三章 Android实例提高 该章主要介绍了拼图游戏和2048的小项目实例,主要是代码,所 ...
- Android 之窗口小部件详解(三) 部分转载
原文地址:http://blog.csdn.net/iefreer/article/details/4626274. (一) 应用程序窗口小部件App Widgets 应用程序窗口小部件(Widget ...
- Android中Canvas绘图基础详解(附源码下载) (转)
Android中Canvas绘图基础详解(附源码下载) 原文链接 http://blog.csdn.net/iispring/article/details/49770651 AndroidCa ...
- Android高效率编码-第三方SDK详解系列(一)——百度地图,绘制,覆盖物,导航,定位,细腻分解!
Android高效率编码-第三方SDK详解系列(一)--百度地图,绘制,覆盖物,导航,定位,细腻分解! 这是一个系列,但是我也不确定具体会更新多少期,最近很忙,主要还是效率的问题,所以一些有效的东西还 ...
- Android XML shape 标签使用详解(apk瘦身,减少内存好帮手)
Android XML shape 标签使用详解 一个android开发者肯定懂得使用 xml 定义一个 Drawable,比如定义一个 rect 或者 circle 作为一个 View 的背景. ...
随机推荐
- TCP、UDP、RTP(RTCP)异同与区别
OSI七层模型OSI 中的层 功能 TCP/IP协议族 应 用层 ...
- curl方法post一个数组
$r = $this->curl_post($url, $data);$list = json_decode($r,true); function curl_post($url = '', ...
- NPOI 2.0 创建Excel文件
如果只是简单的处理的话,只需要引用下载压缩包里的 NPOI.dll (office 2003)或 NPOI.OOXML.dll (office 2007) 文件而已. using System; us ...
- 分享SQL Server 2012/2014内存数据库,AlwaysOn,参考教材与网上总结
Sql Server 2012 高可用性的几种方案的比较,AlwaysOn优势何在 对Sql Server 2012 高可用性与灾难恢复的几种方案的比较,复制,集群,镜像优劣何在,新生技术Always ...
- C# 里的if/switch
今天又重新翻了翻C# Step by Step if 语句 if(bool 表达式) { 语句块: } else { 语句块: } switch(day) { case 0: dayName=&quo ...
- css 笔记
外边距合并 当一个元素出现在另一个元素的上面时,第一个元素的下外边距和第二个元素的上外边距会产生合并,两个盒子之间的上下间距为大的数值. 当一个子元素包含在另外一个父元素(假设没有内边距 没有边框), ...
- Hibernate 学习笔记一
Hibernate 学习笔记一 今天学习了hibernate的一点入门知识,主要是配置domain对象和表的关系映射,hibernate的一些常用的配置,以及对应的一个向数据库插入数据的小例子.期间碰 ...
- Android Studio下SlidingMenu的导入与基础使用
一.关于这个控件,其实我们现在很多app都在用,最简单的,你打开QQ,当看资料卡的时候,首先要侧拉一下,那个就是SlidingMenu 这几天查了很多资料,各种方法都试了,但是一直都没有成功,最后在一 ...
- array_filter函数
利用array_filter函数轻松去掉多维空值,而数组的下标没有改变, 如果自定义过滤函数返回 true,则被操作的数组的当前值就会被包含在返回的结果数组中, 并将结果组成一个新的数组.如果原数组是 ...
- 理解python的with语句
Python’s with statement provides a very convenient way of dealing with the situation where you have ...