最近在做毕设,关于android的,其中觉得android的消息机制很有意思,这里就写下自己的想法
和Windows一样android也是消息驱动的。Android通过Handler和looper实现消息循环机制。
一、Handler的创建
每个Handler都会和一个线程和线程的message queue关联起来,此时你可以传递messages 和 runnable对象到message queue中。后面可以从message queue中拿出该对象并且执行。
上面提到了Handler, Message queue这两个对象,那他们之间是怎么交互的呢?
这中间就涉及到了looper的概念。我们首先来看下handler和looper,message queue 这两个类的关系
Handler提供了很多的构造函数,
1. public Handler
(
)
{
2. myLooper
(
)
;
3. if
(mLooper ==
null
)
{
4. throw
new
RuntimeException
(
5. "Can't create handler inside thread that has not called Looper.prepare()"
)
;
6. }
7. mQueue
;
8. null
;
9. }
10.
11. public Handler
(Looper looper
)
{
12. ;
13. mQueue
;
14. null
;
15. }
从这两个构造函数中,我们发现当为提供looper的时候,android会通过Looper.myLooper()获得当前线程的looper。那通过myLooper()获得的当前线程的looper 可能会为 null 吗? 答案是肯定的,若在主线程下启动一个新的线程,那这个子线程是不会建立自己的Message Queue的。Android通过ActivityThread来启动一个activity,在activity的main()方法中有2句关键的代码
Looper. prepareMainLooper() //具体查看源代码
Looper.loop()
因为创建子线程的时候并没有运行前面这两句话,所以子线程中调用Looper.myLooper()返回的是null。所以就造成了在子线程中创default handler是错误的。而要在子线程中有自己的looper就需要如下写,
1. class LooperThread
extends
Thread
{
2. public Handler mHandler
;
3.
4. public
void run
(
)
{
5. prepare
(
)
;
6.
7. new Handler
(
)
{
8. public
void handleMessage
(Message msg
)
{
9. // process incoming messages here
10. }
11. }
;
12.
13. loop
(
)
;
14. }
Looper源代码中有一个关键的方法prepare(),那他具体做什么事呢,代码如下
1. public
static
final
void prepare
(
)
{
2. if
(sThreadLocal.
get
(
)
!=
null
)
{
3. throw
new
RuntimeException
(
"Only one Looper may be created per thread"
)
;
4. }
5. set
(
new Looper
(
)
)
;
6. }
通过ThreadLocal实现了一个线程只有一个Looper
二、Handler的消息发送
总感觉Handler就像一个交警一样,负责message的创建,获得,处理。Handler的sendMessage()方法最终其实就是调用了queue.enqueueMessage(msg, uptimeMillis);把消息放入message queue中。Looper通过Looper.loop()检测到有消息并将消息广播后,Handler又负责接收到此消息并调用handleMessage进行处理接下来的事情。Message的创建一般都是通过如下代码建立的,把Handler传进去
1. public
final Message obtainMessage
(
)
2. {
3. return Message.
obtain
(
this
)
;
4. }
三、Message
Message在里面是一个链表的结构。在这个方法中,会首先去MessagePool(消息回收站)去找Message,如果有被回收的Message,则会将这个Message取出来进行再利用。如果没有被回收的,这时系统才会真正new一个Message对象出来当然MessagePool不是无限大的,它最多只能保存十条回收的消息,多出来的就直接删除了
更新UI例子
1. public
class ReviewList
extends ListActivity
{
2.
3. private
static
final
String CLASSTAG = ReviewList.
class.
getSimpleName
(
)
;
4. private
static
final
int MENU_CHANGE_CRITERIA =
Menu.
FIRST +
1
;
5. private
static
final
int MENU_GET_NEXT_PAGE =
Menu.
FIRST
;
6. private
static
final
int NUM_RESULTS_PER_PAGE =
8
;
7.
8. private TextView empty
;
9. private ProgressDialog progressDialog
;
10. private ReviewAdapter reviewAdapter
;
11. private List
<Review
> reviews
;
12.
13. private
final Handler handler =
new Handler
(
)
{
14.
@Override
15. public
void handleMessage
(
final Message msg
)
{
16. v
(Constants.
LOGTAG,
" " + ReviewList.
CLASSTAG +
" worker thread done, setup ReviewAdapter"
)
;
17. dismiss
(
)
;
18. if
(
(reviews ==
null
) ||
(reviews.
size
(
) ==
0
)
)
{
19. setText
(
"No Data"
)
;
20. }
else
{
21. new ReviewAdapter
(ReviewList.
this, reviews
)
;
22. (reviewAdapter
)
;
23. }
24. }
25. }
;
26.
27.
@Override
28. public
void onCreate
(Bundle savedInstanceState
)
{
29. super.
onCreate
(savedInstanceState
)
;
30. v
(Constants.
LOGTAG,
" " + ReviewList.
CLASSTAG +
" onCreate"
)
;
31.
32. // NOTE* This Activity MUST contain a ListView named "@android:id/list"
33. // (or "list" in code) in order to be customized
34. // http://code.google.com/android/reference/android/app/ListActivity.html
35. this.
setContentView
(R.
layout.
review_list
)
;
36.
37. this.
empty =
(TextView
) findViewById
(R.
id.
empty
)
;
38.
39. // set list properties
40. final
ListView listView = getListView
(
)
;
41. setItemsCanFocus
(
false
)
;
42. setChoiceMode
(
ListView.
CHOICE_MODE_SINGLE
)
;
43. setEmptyView
(
this.
empty
)
;
44. }
45.
46.
@Override
47. protected
void onResume
(
)
{
48. super.
onResume
(
)
;
49. v
(Constants.
LOGTAG,
" " + ReviewList.
CLASSTAG +
" onResume"
)
;
50. // get the current review criteria from the Application (global state placed there)
51. (RestaurantFinderApplication
) getApplication
(
)
;
52. String criteriaCuisine = application.
getReviewCriteriaCuisine
(
)
;
53. String criteriaLocation = application.
getReviewCriteriaLocation
(
)
;
54.
55. // get start from, an int, from extras
56. int startFrom = getIntent
(
).
getIntExtra
(Constants.
STARTFROM_EXTRA,
1
)
;
57.
58. (criteriaLocation, criteriaCuisine, startFrom
)
;
59. }
60.
61.
@Override
62. public
boolean onCreateOptionsMenu
(
Menu menu
)
{
63. super.
onCreateOptionsMenu
(menu
)
;
64. add
(
0, ReviewList.
MENU_GET_NEXT_PAGE,
0, R.
string.
menu_get_next_page
).
setIcon
(
65. R.
drawable.
ic_menu_more
)
;
66. add
(
0, ReviewList.
MENU_CHANGE_CRITERIA,
0, R.
string.
menu_change_criteria
).
setIcon
(
67. R.
drawable.
ic_menu_edit
)
;
68. return
true
;
69. }
70.
71.
@Override
72. public
boolean onMenuItemSelected
(
int featureId,
MenuItem item
)
{
73. null
;
74. switch
(item.
getItemId
(
)
)
{
75. case
76. // increment the startFrom value and call this Activity again
77. new Intent
(Constants.
INTENT_ACTION_VIEW_LIST
)
;
78. putExtra
(Constants.
STARTFROM_EXTRA, getIntent
(
).
getIntExtra
(Constants.
STARTFROM_EXTRA,
1
)
79. NUM_RESULTS_PER_PAGE
)
;
80. (intent
)
;
81. return
true
;
82. case
83. new Intent
(
this, ReviewCriteria.
class
)
;
84. (intent
)
;
85. return
true
;
86. }
87. return
super.
onMenuItemSelected
(featureId, item
)
;
88. }
89.
90.
@Override
91. protected
void onListItemClick
(
ListView l,
View v,
int position,
long id
)
{
92. // set the current review to the Application (global state placed there)
93. (RestaurantFinderApplication
) getApplication
(
)
;
94. setCurrentReview
(
this.
reviews.
get
(position
)
)
;
95.
96. // startFrom page is not stored in application, for example purposes it's a simple "extra"
97. new Intent
(Constants.
INTENT_ACTION_VIEW_DETAIL
)
;
98. putExtra
(Constants.
STARTFROM_EXTRA, getIntent
(
).
getIntExtra
(Constants.
STARTFROM_EXTRA,
1
)
)
;
99. (intent
)
;
100. }
101.
102. private
void loadReviews
(
String location,
String cuisine,
int startFrom
)
{
103.
104. v
(Constants.
LOGTAG,
" " + ReviewList.
CLASSTAG +
" loadReviews"
)
;
105.
106. final ReviewFetcher rf =
new ReviewFetcher
(location, cuisine,
"ALL", startFrom,
107. NUM_RESULTS_PER_PAGE
)
;
108.
109. this.
progressDialog = ProgressDialog.
show
(
this,
" Working…",
" Retrieving reviews",
true,
false
)
;
110.
111. // get reviews in a separate thread for ProgressDialog/Handler
112. // when complete send "empty" message to handler
113. new
Thread
(
)
{
114.
@Override
115. public
void run
(
)
{
116. getReviews
(
)
;
117. sendEmptyMessage
(
0
)
;
118. }
119. }.
start
(
)
;
120. }
121. }
在android里,新产生一个线程并不会自动建立其Message Loop。Android里并没有Global 的Message Queue,所以不同的APK里不能透过Message Queue来交换信息