android异步任务设计思详解(AsyncTask)
这里说有设计思想是我根据查看android源代码提炼出来的代码逻辑,所以不会跟google工程师的原始设计思想100%符合(也有可能是0%),但是本文一定可以帮助你理解asynctask,也可能有一些你以前没有发现的内容。
大家都知道,android的主线程(又叫ui线程,线程id为1)有一些限制策略,使得主线程有些事做不了,比如访问网络就不允许,否则就是报,但在2.3之后的版本,你可以通过添加以下代码更改其限制策略,从而强制使得主线程可以访问网络:
if (android.os.build.version.sdk_int > 9) {
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匿名类肯定是重复代码,如下:
[final handler handler = new handler() {
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的方式来复用这段代码,用的时候只要在省略号处写入你的代码就可以了,但更好的复用是将其用一个类封装起来,好吧,那我们就简单的封装一下吧,于是,就变成了这样:
public class handlerandthread {
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代码,我们可以加两个方法,一个在前台调用一个在后台调用,只要定义一个新类就可以实现复用,于是代码变成这样:
public class handlerandthread {
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来调用一下吧:
public class mainactivity extends 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 class 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
}
...
}
而调用类的代码变成这样:
public class mainactivity extends activity {
@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");
}
}
}
假如是下一个文件呢,我们是不是加一个进度更新的方法呢,于是又变成这样:
public class handlerandthread {
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。最终版如下:
public abstract class handlerandthread {
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打开rar压缩文件
推荐阅读
-
android异步任务设计思详解(AsyncTask)
-
Android中实现异步任务机制的AsyncTask方式的使用讲解
-
Android带进度条的下载图片示例(AsyncTask异步任务)
-
Android AsyncTask实现异步处理任务的方法详解
-
Android的异步任务AsyncTask详解
-
Android 异步任务 - AsyncTask 理解及示例
-
Android的线程通信:消息机制原理(Message,Handler,MessageQueue,Looper),异步任务AsyncTask,使用JSON
-
android异步任务设计思详解(AsyncTask)
-
Android带进度条的下载图片示例(AsyncTask异步任务)
-
Android中实现异步任务机制的AsyncTask方式的使用讲解