AsyncTask在Android十分常用,那为什么如此常用呢,不用行不行呢,内部又是怎么实现的呢,为什么Java的API中没有这个类呢,看完本文后,你将会知道答案。
这里说有设计思想是我根据查看Android源代码提炼出来的代码逻辑,所以不会跟Google工程师的原始设计思想100%符合(也有可能是0%),但是本文一定可以帮助你理解AsyncTask,也可能有一些你以前没有发现的内容。
大家都知道,Android的主线程(又叫UI线程,线程ID为1)有一些限制策略,使得主线程有些事做不了,比如访问网络就不允许,否则就是报,但在2.3之后的版本,你可以通过添加以下代码更改其限制策略,从而强制使得主线程可以访问网络:
1
2
3
4
|
if
(android.os.Build.VERSION.SDK_INT >
9
) {
StrictMode.ThreadPolicy policy =
new
StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
|
还是回到正题,就是因为主线程的这些限制使开发者不得不写多个线程,当然,你也可以不用AsyncTask,不过你不用也避免不了多线程,如果你不用,就是可能要用Handler和Thread了,我想很多人初学的时候就是那么干的,包括我,因为那时很有可能还没有发现有这个类,于是就经常写Handler和Thread的了,写着写着就发现有一些代码是相同的,你写的Handler和Thread匿名类肯定是重复代码,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
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的方式来复用这段代码,用的时候只要在省略号处写入你的代码就可以了,但更好的复用是将其用一个类封装起来,好吧,那我们就简单的封装一下吧,于是,就变成了这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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() {
// ...
}
};
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
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() {
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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类的代码就变成这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
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
}
...
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public
class
MainActivity
extends
Activity {
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
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"
);
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
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);
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
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);
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);
}
}
}
|
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设计思想,软件架构,特别是当你遇到技术问题无法解决,网上也搜索不到时,你可以通过查看源码来找到解决办法,我就经常这么干。
所以提议大家在开发时学会看源码利用源码,一定要耐心,一篇看不懂不要气馁,多看几篇一定会看懂的。