您的位置:首页 > 移动开发 > Android开发

有关Android线程的学习

2011-06-08 15:52 246 查看
1. Android
进程

在了解
Android
线程之前得先了解一下
Android
的进程。当一个程序第一次启动的时候,

Android

会启动一个
LINUX

进程和一个主线程。默认的情况下,所有该程序的组件都将在该进程和线程中运行。

同时,
Android
会为每个应用程序分配一个单独的
LINUX
用户。
Android
会尽量保留一个正在运行进程,只在内存资源出现不足时,
Android
会尝试停止一些进程从而释放足够的资源给其他新的进程使用,
也能保证用户正在访问的当前进程有足够的资源去及时地响应用户的事件。
Android
会根据进程中运行的组件类别以及组件的状态来判断该进程的重要性,
Android
会首先停止那些不重要的进程。按照重要性从高到低一共有五个级别:

§

前台进程


前台进程是用户当前正在使用的进程。只有一些前台进程可以在任何时候都存在。他们是最后一个被结束的,当内存低到根本连他们都不能运行的时候。一般来说,
在这种情况下,设备会进行内存调度,中止一些前台进程来保持对用户交互的响应。

§

可见进程


可见进程不包含前台的组件但是会在屏幕上显示一个可见的进程是的重要程度很高,除非前台进程需要获取它的资源,不然不会被中止。

§

服务进程


运行着一个通过
startService()

方法启动的
service
,这个
service
不属于上面提到的
2
种更高重要性的。
service
所在的进程虽然对用户不是直接可见的,但是他们执行了用户非常关注的任务(比如播放
mp3
,从网络下载数据)。只要前台进程和可见进程有足够的内存,系统不会回收他们。

§

后台进程


运行着一个对用户不可见的
activity
(调用过
onStop()
方法
).
这些进程对用户体验没有直接的影响,可以在服务进程、可见进程、前台进
程需要内存的时候回收。通常,系统中会有很多不可见进程在运行,他们被保存在
LRU
(least recently used)
列表中,以便内存不足的时候被第一时间回收。如果一个
activity

确的执行了它的生命周期,关闭这个进程对于用户体验没有太大的影响。

§

空进程


未运行任何程序组件。运行这些进程的唯一原因是作为一个缓存,缩短下次程序需要重新使用的启动时间。系统经常中止这些进程,这样可以调节程序缓存和系统缓存的平衡。

Android
对进程的重要性评级的时候,选取它最高的级别。另外,当被另外的一个进程依赖的时候,某个进程的级别可能会增高。一个为其他进程服务的进程永远不会比被服务的进程重要级低。因为服务进程比后台
activity
进程重要级高,因此一个要进行耗时工作的
activity
最好启动一个
service
来做这个工作,而不是开启一个子进程
――
特别是这个操作需要的时间比
activity
存在的时间还要长的时候。例如,在后台播放音乐,向网上上传摄像头拍到的图片,使用
service
可以使进程最少获取到

服务进程

级别的重要级,而不用考虑
activity
目前是什么状态。
broadcast receivers
做费时的工作的时候,也应该启用一个服务而不是开一个线程。

2.
单线程模型

当一个程序第一次启动时,
Android
会同时启动一个对应的主线程(
Main Thread
),主线程主要负责处理与
UI
相关的事件,如用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做

UI

线程

。在开发

Android

应用时必须遵守单线程模型的原则:
Android UI

操作并不是线程安全的并且这些操作必须在
UI

线程中执行。

2.1
子线程更新
UI

Android

UI
是单线程
(Single-threaded)
的。为了避免拖住
GUI
,一些较费时的对象应该交给独立的线程去执行。如果幕后的线程来执行
UI
对象,
Android
就会发出错误讯息
CalledFromWrongThreadException

。以后遇到这样的异常抛出时就要知道怎么回事了!

2.2
Message Queue

在单线程模型下,为了解决类似的问题,
Android
设计了一个
Message Queue(
消息队列
)

线程间可以通过该
Message Queue
并结合
Handler

Looper
组件进行信息交换。下面将对它们进行分别介绍:

1.
Message


Message
消息,理解为线程间交流的信息,处理数据后台线程需要更新
UI
,则发送
Message
内含一些数据给
UI
线程。

2. Handler

Handler
处理者,是
Message
的主要处理者,负责
Message
的发送,
Message
内容的执行处理。后台线程就是通过传进来的
Handler
对象引用来
sendMessage(Message)
。而使用
Handler
,需要
implement
该类的

handleMessage(Message)
方法,它是处理这些
Message
的操作内容,例如
Update UI
。通常需要子类化
Handler
来实现
handleMessage
方法。

3. Message Queue

Message Queue
消息队列,用来存放通过
Handler
发布的消息,按照先进先出执行。

每个
message queue
都会有一个对应的
Handler

Handler
会向
message queue
通过两种方法发送消息:
sendMessage

post
。这两种消息都会插在
message queue
队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过
sendMessage
发送的是一个
message
对象
,
会被
Handler

handleMessage()
函数处理;而通过
post
方法发送的是一个
runnable
对象,则会自己执行。

4. Looper

Looper
是每条线程里的
Message Queue
的管家。
Android
没有
Global

Message Queue
,而
Android
会自动替主线程
(UI
线程
)
建立
Message Queue
,但在子线程里并没有建立
Message Queue
。所以调用
Looper.getMainLooper()
得到的主线程的
Looper
不为
NULL
,但调用
Looper.myLooper()
得到当前线程的
Looper
就有可能为
NULL


对于子线程使用
Looper

API Doc
提供了正确的使用方法:

1

class LooperThread
extends Thread {

2

public Handler mHandler;

3

4

public void run() {

5

Looper.prepare(); //
创建本线程的
Looper
并创建一个
MessageQueue

6

7

mHandler = new Handler() {

8

public void handleMessage(Message
msg) {

9

// process incoming messages
here

10

}

11

};

12

13

Looper.loop(); //
开始运行
Looper,
监听
Message
Queue

14

}

15

}

这个
Message
机制的大概流程:

1.

Looper.loop()
方法运行开始后,循环地按照接收顺序取出
Message Queue
里面的非
NULL

Message


2.
一开始
Message Queue
里面的
Message
都是
NULL
的。当
Handler.sendMessage(Message)

Message Queue
,该函数里面设置了那个
Message
对象的
target
属性是当前的
Handler
对象。随后
Looper
取出了那个
Message
,则调用该
Message

target
指向的
Hander

dispatchMessage
函数对
Message
进行处理。


dispatchMessage
方法里,如何处理
Message
则由用户指定,三个判断,优先级从高到低:

1) Message
里面的
Callback
,一个实现了
Runnable
接口的对象,其中
run
函数做处理工作;

2) Handler
里面的
mCallback
指向的一个实现了
Callback
接口的对象,由其
handleMessage
进行处理;

3)
处理消息
Handler
对象对应的类继承并实现了其中
handleMessage
函数,通过这个实现的
handleMessage
函数处理消息。

由此可见,我们实现的
handleMessage
方法是优先级最低的!

3. Handler
处理完该
Message (update UI)
后,
Looper
则设置该
Message

NULL
,以便回收!

在网上有很多文章讲述主线程和其他子线程如何交互,传送信息,最终谁来执行处理信息之类的,个人理解是最简单的方法
——

判断
Handler

对象里面的
Looper

对象是属于哪条线程的,则由该线程来执行!



1.


Handler

对象的构造函数的参数为空,则为当前所在线程的
Looper





2. Looper.getMainLooper()

得到的是主线程的
Looper

对象,
Looper.myLooper()

得到的是当前线程的
Looper

对象。


现在来看一个例子,模拟从网络获取数据,加载到
ListView
的过程:

16

public class
ListProgressDemo extends ListActivity {

17

18

@Override

19

public void onCreate(Bundle
savedInstanceState) {

20

super.onCreate(savedInstanceState);

21

setContentView(R.layout.listprogress);

22

23

((Button)
findViewById(R.id.load_Handler)).setOnClickListener(new View.OnClickListener(){

24

25

@Override

26

public void onClick(View view) {

27

data = null;

28

data = new
ArrayList<String>();

29

30

adapter = null;

31

32

showDialog(PROGRESS_DIALOG);

33

new ProgressThread(handler,
data).start();

34

}

35

});

36

}

37

38

@Override

39

protected Dialog onCreateDialog(int id) {

40

switch(id) {

41

case PROGRESS_DIALOG:

42

return
ProgressDialog.show(this, "",

43

"Loading. Please wait...",
true);

44

45

default: return null;

46

}

47

}

48

49

private class ProgressThread extends Thread
{

50

51

private Handler handler;

52

private ArrayList<String> data;

53

54

public ProgressThread(Handler handler,
ArrayList<String> data) {

55

this.handler = handler;

56

this.data = data;

57

}

58

59

@Override

60

public void run() {

61

for (int i=0; i<8; i++) {

62

data.add("ListItem");
//
后台数据处理

63

try {

64

Thread.sleep(100);

65

}catch(InterruptedException e)
{

66

67

Message msg =
handler.obtainMessage();

68

Bundle b = new Bundle();

69

b.putInt("state",
STATE_ERROR);

70

msg.setData(b);

71

handler.sendMessage(msg);

72

73

}

74

}

75

Message msg =
handler.obtainMessage();

76

Bundle b = new Bundle();

77

b.putInt("state", STATE_FINISH);

78

msg.setData(b);

79

handler.sendMessage(msg);

80

}

81

82

}

83

84

//
此处甚至可以不需要设置
Looper
,因为
Handler
默认就使用当前线程的
Looper

85

private final Handler handler = new
Handler(Looper.getMainLooper()) {

86

87

public void handleMessage(Message msg)
{ //
处理
Message
,更新
ListView

88

int state =
msg.getData().getInt("state");

89

switch(state){

90

case STATE_FINISH:

91

dismissDialog(PROGRESS_DIALOG);

92

Toast.makeText(getApplicationContext(),

93

"
加载完成
!",

94

Toast.LENGTH_LONG)

95

.show();

96

97

adapter = new
ArrayAdapter<String>(getApplicationContext(),

98

android.R.layout.simple_list_item_1,

99

data );

100

101

setListAdapter(adapter);

102

103

break;

104

105

case STATE_ERROR:

106

dismissDialog(PROGRESS_DIALOG);

107

Toast.makeText(getApplicationContext(),

108

"
处理过程发生错误
!",

109

Toast.LENGTH_LONG)

110

.show();

111

112

adapter = new
ArrayAdapter<String>(getApplicationContext(),

113

android.R.layout.simple_list_item_1,

114

data );

115

116

setListAdapter(adapter);

117

118

break;

119

120

default:

121

122

}

123

}

124

};

125

126

127

private ArrayAdapter<String> adapter;

128

private ArrayList<String> data;

129

130

private static final int PROGRESS_DIALOG =
1;

131

private static final int STATE_FINISH = 1;

132

private static final int STATE_ERROR = -1;

133

}

这个例子,我自己写完后觉得还是有点乱,要稍微整理才能看明白线程间交互的过程以及数据的前后变化。随后了解到
AsyncTask
类,相应修改后就很容易明白了!

2.3
AsyncTask

AsyncTask
版:

134

((Button)
findViewById(R.id.load_AsyncTask)).setOnClickListener(new
View.OnClickListener(){

135

136

@Override

137

public void onClick(View view) {

138

data = null;

139

data = new ArrayList<String>();

140

141

adapter = null;

142

143

//
显示
ProgressDialog
放到
AsyncTask.onPreExecute()


144

//showDialog(PROGRESS_DIALOG);

145

new ProgressTask().execute(data);

146

}

147

});

148

149

private class
ProgressTask extends AsyncTask<ArrayList<String>, Void, Integer> {

150

151

/*
该方法将在执行实际的后台操作前被
UI thread
调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。
*/

152

@Override

153

protected void
onPreExecute() {

154

//
先显示
ProgressDialog

155

showDialog(PROGRESS_DIALOG);

156

}

157

158

/*
执行那些很耗时的后台计算工作。可以调用
publishProgress
方法来更新实时的任务进度。
*/

159

@Override

160

protected Integer
doInBackground(ArrayList<String>... datas) {

161

ArrayList<String> data = datas[0];

162

for (int i=0; i<8; i++) {

163

data.add("ListItem");

164

}

165

return STATE_FINISH;

166

}

167

168

/*

doInBackground
执行完成后,
onPostExecute
方法将被
UI
thread
调用,

169

*
后台的计算结果将通过该方法传递到
UI thread.

170

*/

171

@Override

172

protected void
onPostExecute(Integer result) {

173

int state = result.intValue();

174

switch(state){

175

case STATE_FINISH:

176

dismissDialog(PROGRESS_DIALOG);

177

Toast.makeText(getApplicationContext(),

178

"
加载完成
!",

179

Toast.LENGTH_LONG)

180

.show();

181

182

adapter = new
ArrayAdapter<String>(getApplicationContext(),

183

android.R.layout.simple_list_item_1,

184

data );

185

186

setListAdapter(adapter);

187

188

break;

189

190

case STATE_ERROR:

191

dismissDialog(PROGRESS_DIALOG);

192

Toast.makeText(getApplicationContext(),

193

"
处理过程发生错误
!",

194

Toast.LENGTH_LONG)

195

.show();

196

197

adapter = new
ArrayAdapter<String>(getApplicationContext(),

198

android.R.layout.simple_list_item_1,

199

data );

200

201

setListAdapter(adapter);

202

203

break;

204

205

default:

206

207

}

208

}

Android
另外提供了一个工具类:
AsyncTask
。它使得
UI thread
的使用变得异常简单。它使创建需要与用户界面交互的长时间运行的任务变得更简单,不需要借助线程和
Handler
即可实现。

1)

子类化
AsyncTask

2)

实现
AsyncTask
中定义的下面一个或几个方法

onPreExecute()
开始执行前的准备工作;

doInBackground(Params...)
开始执行后台处理,可以调用
publishProgress
方法来更新实时的任务进度;

onProgressUpdate(Progress...)


publishProgress
方法被调用后,
UI thread
将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。

onPostExecute(Result)

执行完成后的操作,传送结果给
UI

线程。


4
个方法都不能手动调用。而且除了
doInBackground(Params...)
方法,其余
3
个方法都是被
UI
线程所调用的,所以要求:

1) AsyncTask
的实例必须在
UI thread
中创建;

2) AsyncTask.execute
方法必须在
UI thread
中调用;

同时要注意:该
task
只能被执行一次,否则多次调用时将会出现异常。而且是不能手动停止的,这一点要注意,看是否符合你的需求!

在使用过程中,发现
AsyncTask
的构造函数的参数设置需要看明白:
AsyncTask<Params, Progress, Result>


Params
对应
doInBackground(Params...)
的参数类型。而
new
AsyncTask().execute(Params... params)
,就是传进来的
Params
数据,你可以
execute(data)
来传送一个数据,或者
execute(data1, data2,
data3)
这样多个数据。

Progress
对应
onProgressUpdate(Progress...)
的参数类型;

Result
对应
onPostExecute(Result)
的参数类型。

当以上的参数类型都不需要指明某个时,则使用
Void
,注意不是
void
。不明白的可以参考上面的例子,或者
API Doc
里面的例子。

----------------

本文的相当内容摘录于《
浅析

Android
线程模型一

---


》,但对于
Message
机制的流程理解则在参考《
android


Message
机制的灵活应用

》后修改了!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: