Android消息机制——Handler、Looper、MessageQueue
最近在做毕设,关于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来交换信息
更多相关文章
- Android的线程模型
- Android 消息提示框:五种Toast详解
- Android 消息机制之Message
- Android的消息处理机制(深入源码)
- Android 多线程AsyncTask详解
- android 上 webkit js 扩展之全局本地对象实现步骤
- Android消息处理系统原理简要概述
- Android 多线程之 AsyncTask
- android主线程和子线程的区别