最近在做毕设,关于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提供了很多的构造函数,
-
public Handler ( ) {
-
mLooper = Looper. myLooper ( ) ;
-
if (mLooper == null ) {
-
throw new RuntimeException (
-
"Can't create handler inside thread that has not called Looper.prepare()" ) ;
-
}
-
mQueue = mLooper. mQueue ;
-
mCallback = null ;
-
}
-
-
public Handler (Looper looper ) {
-
mLooper = looper ;
-
mQueue = looper. mQueue ;
-
mCallback = null ;
-
}
从这两个构造函数中,我们发现当为提供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就需要如下写,
-
class LooperThread extends Thread {
-
public Handler mHandler ;
-
-
public void run ( ) {
-
Looper. prepare ( ) ;
-
-
mHandler = new Handler ( ) {
-
public void handleMessage (Message msg ) {
-
// process incoming messages here
-
}
-
} ;
-
-
Looper. loop ( ) ;
-
}
Looper源代码中有一个关键的方法prepare(),那他具体做什么事呢,代码如下
-
public static final void prepare ( ) {
-
if (sThreadLocal. get ( ) != null ) {
-
throw new RuntimeException ( "Only one Looper may be created per thread" ) ;
-
}
-
sThreadLocal. set ( new Looper ( ) ) ;
-
}
通过ThreadLocal实现了一个线程只有一个Looper
二、Handler的消息发送
总感觉Handler就像一个交警一样,负责message的创建,获得,处理。Handler的sendMessage()方法最终其实就是调用了queue.enqueueMessage(msg, uptimeMillis);把消息放入message queue中。Looper通过Looper.loop()检测到有消息并将消息广播后,Handler又负责接收到此消息并调用handleMessage进行处理接下来的事情。Message的创建一般都是通过如下代码建立的,把Handler传进去
-
public final Message obtainMessage ( )
-
{
-
return Message. obtain ( this ) ;
-
}
三、Message
Message在里面是一个链表的结构。在这个方法中,会首先去MessagePool(消息回收站)去找Message,如果有被回收的Message,则会将这个Message取出来进行再利用。如果没有被回收的,这时系统才会真正new一个Message对象出来当然MessagePool不是无限大的,它最多只能保存十条回收的消息,多出来的就直接删除了
更新UI例子
-
public class ReviewList extends ListActivity {
-
-
private static final String CLASSTAG = ReviewList. class. getSimpleName ( ) ;
-
private static final int MENU_CHANGE_CRITERIA = Menu. FIRST + 1 ;
-
private static final int MENU_GET_NEXT_PAGE = Menu. FIRST ;
-
private static final int NUM_RESULTS_PER_PAGE = 8 ;
-
-
private TextView empty ;
-
private ProgressDialog progressDialog ;
-
private ReviewAdapter reviewAdapter ;
-
private List <Review > reviews ;
-
-
private final Handler handler = new Handler ( ) {
-
@Override
-
public void handleMessage ( final Message msg ) {
-
Log. v (Constants. LOGTAG, " " + ReviewList. CLASSTAG + " worker thread done, setup ReviewAdapter" ) ;
-
progressDialog. dismiss ( ) ;
-
if ( (reviews == null ) || (reviews. size ( ) == 0 ) ) {
-
empty. setText ( "No Data" ) ;
-
} else {
-
reviewAdapter = new ReviewAdapter (ReviewList. this, reviews ) ;
-
setListAdapter (reviewAdapter ) ;
-
}
-
}
-
} ;
-
-
@Override
-
public void onCreate (Bundle savedInstanceState ) {
-
super. onCreate (savedInstanceState ) ;
-
Log. v (Constants. LOGTAG, " " + ReviewList. CLASSTAG + " onCreate" ) ;
-
-
// NOTE* This Activity MUST contain a ListView named "@android:id/list"
-
// (or "list" in code) in order to be customized
-
// http://code.google.com/android/reference/android/app/ListActivity.html
-
this. setContentView (R. layout. review_list ) ;
-
-
this. empty = (TextView ) findViewById (R. id. empty ) ;
-
-
// set list properties
-
final ListView listView = getListView ( ) ;
-
listView. setItemsCanFocus ( false ) ;
-
listView. setChoiceMode ( ListView. CHOICE_MODE_SINGLE ) ;
-
listView. setEmptyView ( this. empty ) ;
-
}
-
-
@Override
-
protected void onResume ( ) {
-
super. onResume ( ) ;
-
Log. v (Constants. LOGTAG, " " + ReviewList. CLASSTAG + " onResume" ) ;
-
// get the current review criteria from the Application (global state placed there)
-
RestaurantFinderApplication application = (RestaurantFinderApplication ) getApplication ( ) ;
-
String criteriaCuisine = application. getReviewCriteriaCuisine ( ) ;
-
String criteriaLocation = application. getReviewCriteriaLocation ( ) ;
-
-
// get start from, an int, from extras
-
int startFrom = getIntent ( ). getIntExtra (Constants. STARTFROM_EXTRA, 1 ) ;
-
-
loadReviews (criteriaLocation, criteriaCuisine, startFrom ) ;
-
}
-
-
@Override
-
public boolean onCreateOptionsMenu ( Menu menu ) {
-
super. onCreateOptionsMenu (menu ) ;
-
menu. add ( 0, ReviewList. MENU_GET_NEXT_PAGE, 0, R. string. menu_get_next_page ). setIcon (
-
android. R. drawable. ic_menu_more ) ;
-
menu. add ( 0, ReviewList. MENU_CHANGE_CRITERIA, 0, R. string. menu_change_criteria ). setIcon (
-
android. R. drawable. ic_menu_edit ) ;
-
return true ;
-
}
-
-
@Override
-
public boolean onMenuItemSelected ( int featureId, MenuItem item ) {
-
Intent intent = null ;
-
switch (item. getItemId ( ) ) {
-
case MENU_GET_NEXT_PAGE:
-
// increment the startFrom value and call this Activity again
-
intent = new Intent (Constants. INTENT_ACTION_VIEW_LIST ) ;
-
intent. putExtra (Constants. STARTFROM_EXTRA, getIntent ( ). getIntExtra (Constants. STARTFROM_EXTRA, 1 )
-
+ ReviewList. NUM_RESULTS_PER_PAGE ) ;
-
startActivity (intent ) ;
-
return true ;
-
case MENU_CHANGE_CRITERIA:
-
intent = new Intent ( this, ReviewCriteria. class ) ;
-
startActivity (intent ) ;
-
return true ;
-
}
-
return super. onMenuItemSelected (featureId, item ) ;
-
}
-
-
@Override
-
protected void onListItemClick ( ListView l, View v, int position, long id ) {
-
// set the current review to the Application (global state placed there)
-
RestaurantFinderApplication application = (RestaurantFinderApplication ) getApplication ( ) ;
-
application. setCurrentReview ( this. reviews. get (position ) ) ;
-
-
// startFrom page is not stored in application, for example purposes it's a simple "extra"
-
Intent intent = new Intent (Constants. INTENT_ACTION_VIEW_DETAIL ) ;
-
intent. putExtra (Constants. STARTFROM_EXTRA, getIntent ( ). getIntExtra (Constants. STARTFROM_EXTRA, 1 ) ) ;
-
startActivity (intent ) ;
-
}
-
-
private void loadReviews ( String location, String cuisine, int startFrom ) {
-
-
Log. v (Constants. LOGTAG, " " + ReviewList. CLASSTAG + " loadReviews" ) ;
-
-
final ReviewFetcher rf = new ReviewFetcher (location, cuisine, "ALL", startFrom,
-
ReviewList. NUM_RESULTS_PER_PAGE ) ;
-
-
this. progressDialog = ProgressDialog. show ( this, " Working…", " Retrieving reviews", true, false ) ;
-
-
// get reviews in a separate thread for ProgressDialog/Handler
-
// when complete send "empty" message to handler
-
new Thread ( ) {
-
@Override
-
public void run ( ) {
-
reviews = rf. getReviews ( ) ;
-
handler. sendEmptyMessage ( 0 ) ;
-
}
-
}. start ( ) ;
-
}
-
}
在android里,新产生一个线程并不会自动建立其Message Loop。Android里并没有Global 的Message Queue,所以不同的APK里不能透过Message Queue来交换信息