Android开发实践:线程与异步任务
基于移动客户端的软件特别强调实时性,Android程序更是如此,任何一个程序超过5s没有响应,都会被系统强制杀掉。而且Android也不允许在UI线程中进行任何网络操作,否则就会产生NetworkOnMainThreadException 异常。因此,凡是耗时的操作,都不应该直接出现在UI线程中。今天,我通过最简单直观地示例总结下Android开发中最常用的两种处理耗时操作的方法:一个是线程,另一个是异步任务。
首先,看看示例效果,点击Download后,进度条每1秒中增加1%,直到增加到100%。
我将分别用两种方式实现这个功能。
首先,给出 XML 的布局文件:
|
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
|
<
LinearLayout
xmlns:android
=
"http://schemas.android.com/apk/res/android"
xmlns:tools
=
"http://schemas.android.com/tools"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:orientation
=
"vertical"
>
<
Button
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:layout_margin
=
"10dp"
android:text
=
"Download"
android:onClick
=
"onClickDownLoad"
/>
<
TextView
android:id
=
"@+id/TextShow"
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:layout_marginLeft
=
"10dp"
android:text
=
"0%"
/>
<
ProgressBar
android:id
=
"@+id/ProgressBar"
android:layout_margin
=
"10dp"
android:layout_width
=
"match_parent"
android:layout_height
=
"30dp"
style
=
"@android:style/Widget.ProgressBar.Horizontal"
/>
</
LinearLayout
>
|
(1) 线程(Thread,Runnable)
第一种方法是通过线程的形式来实现,代码如下:
|
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
|
public
class
DownloadRunnable
implements
Runnable {
private
RunnableStateListener mStateListener;
private
String mURL;
public
static
interface
RunnableStateListener {
public
void
onRunnableUpdate(
int
progress);
public
void
onRunnableComplete(
boolean
isSuccess);
}
public
DownloadRunnable(RunnableStateListener listener, String url ) {
mStateListener = listener;
mURL = url;
}
@Override
public
void
run() {
Log.d(
"DownloadTask"
,
"Begin download, the URL is "
+ mURL );
for
(
int
i=
1
; i<=
100
; i++ ) {
mStateListener.onRunnableUpdate(i);
try
{
Thread.sleep(
500
);
}
catch
(InterruptedException e) {
e.printStackTrace();
mStateListener.onRunnableComplete(
false
);
return
;
}
}
mStateListener.onRunnableComplete(
true
);
}
}
|
Android/Java中,线程是通过 new Thread(Runnable runnable).start(); 来创建和执行的,所以可以先定义一个类实现 Runnalbe 接口,在 run 函数中完成耗时的操作。本类中,定义RunnableStateListener ,是为了方便线程与外界(调用者)交流,将线程中的任务运行状态传递到外界。
(2) 异步任务(AsyncTask)
另一种方法则是采用异步任务来实现,代码如下:
|
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
|
public
class
DownloadTask
extends
AsyncTask<String,Integer,Boolean> {
private
TaskStateListener mTaskStateListener;
public
static
interface
TaskStateListener {
public
void
onTaskUpdate(
int
progress);
public
void
onTaskComplete(
boolean
isSuccess);
}
public
DownloadTask(TaskStateListener listener) {
mTaskStateListener = listener;
}
@Override
protected
Boolean doInBackground(String ... params )
Log.d(
"DownloadTask"
,
"Begin download, the URL is "
+ params[
0
] );
for
(
int
i=
1
; i<=
100
; i++ ) {
//会回调onProgressUpdate
super
.publishProgress(i);
try
{
Thread.sleep(
500
);
}
catch
(InterruptedException e) {
e.printStackTrace();
return
Boolean.FALSE;
}
}
return
Boolean.TRUE;
}
@Override
protected
void
onProgressUpdate(Integer... values)
{
mTaskStateListener.onTaskUpdate(values[
0
]);
super
.onProgressUpdate(values);
}
@Override
protected
void
onPostExecute(Boolean result) {
mTaskStateListener.onTaskComplete(result);
}
}
|
异步任务与线程的实现方式有很大不同,异步任务主要通过实例化AsyncTask类来实现,该类有三个接口,doInBackground,该函数是任务的主体部分,将耗时的操作可以放在这里;onProgressUpdate是由系统回调的函数,当doInBackground主体中调用了publishProgress后,则会进入onProgressUpdate更新当前任务的状态,因此,在这里可以通过本类定义的TaskStateListener将状态传递给外界(调用者);而onPostExecute则是在doInBackground主体任务return(结束)后由系统回调。
AsyncTask的原型定义如下:AsyncTask<Params, Progress, Result>,有点像C++里的模板,子类可以实例化这三个参数,依次对应 doInBackground 的参数,onProgressUpdate的参数,onPostExecute的返回值。
AsyncTask 通过 new AsyncTask().execute() 来启动,其中 execute 的参数会被传递给 doInBackground 函数。AsyncTask可以通过 getStatus 来获取当前任务的执行状态,通过 cancel来取消。
(3) MainActivity 的实现
MainActivity 的代码如下,我把两种方式的代码都集成在里面了。
|
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
|
public
class
MainActivity
extends
Activity
implements
TaskStateListener,RunnableStateListener {
private
TextView mProgressShow;
private
ProgressBar mProgressBar;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mProgressShow = (TextView)findViewById(R.id.TextShow);
mProgressBar = (ProgressBar)findViewById(R.id.ProgressBar);
}
public
void
onClickDownLoad(View v) {
//new DownloadTask(this).execute("blog.ticktick.51cto.com");
new
Thread(
new
DownloadRunnable(
this
,
"blog.ticktick.51cto.com"
)).start();
}
@Override
public
void
onTaskUpdate(
int
progress) {
mProgressShow.setText(progress+
"%"
);
mProgressBar.setProgress(progress);
}
@Override
public
void
onTaskComplete(
boolean
isSuccess) {
if
( isSuccess ) {
Toast.makeText(
this
,
"Download Complete"
,Toast.LENGTH_LONG).show();
}
else
{
Toast.makeText(
this
,
"Download Failed"
,Toast.LENGTH_LONG).show();
}
}
@Override
public
void
onRunnableUpdate(
final
int
progress) {
this
.runOnUiThread(
new
Runnable() {
@Override
public
void
run() {
mProgressShow.setText(progress+
"%"
);
mProgressBar.setProgress(progress);
}
});
}
@Override
public
void
onRunnableComplete(
final
boolean
isSuccess) {
this
.runOnUiThread(
new
Runnable() {
@Override
public
void
run() {
if
( isSuccess ) {
Toast.makeText(MainActivity.
this
,
"Download Complete"
,Toast.LENGTH_LONG).show();
}
else
{
Toast.makeText(MainActivity.
this
,
"Download Failed"
,Toast.LENGTH_LONG).show();
}
}
});
}
}
|
这里注意,由线程类回调的onRunnableUpdate和onRunnableComplete函数中,通过this.runOnUiThread的方式在更新UI,而由AsynTask回调的则不需要采用这种方式,因为Android不允许非UI线程改变UI元素,所以必须通过runOnUiThread的方式来更新,而AsynTask则是在内部通过handle收发消息的方式自动切换到了UI线程,所以可以直接更新UI。
