Android消息处理机制

原创 2013年12月03日 21:56:51
通常情况下使用Handler发送消息并且处理消息。

一:此时mHandler属于UI主线程。
private final Handler mHandler = new Handler() {
             @Override
             public void handleMessage(Message msg) {
                    //其他代码
      }
  }
二:此时mHandler属于子线程(工作者线程)
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();
      }
  }
new Handler()的时候做了如下工作:    public Handler() {
        if (FIND_POTENTIAL_LEAKS ) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier. STATIC) == 0) {
                Log. w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        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 ;
    }

至此:Looper = Thread + Handler 
                         ||
                         ||
                         ||

这种情况下,如果没有绑定对应的Looper对象,在此刷新Ui会抛出异常;
Can't create handler inside thread that has not called Looper.prepare()

为什么需要调用 Looper.prepare();Looper.loop();这两个方法:

先看看Looper类有些什么
 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();//保存Looper变量,保证多个looper拷贝

    final MessageQueue mQueue;//消息队列
    final Thread mThread;//当前线程
当调用prepare()方法时做了些什么?

   public static void prepare() {
        if (sThreadLocal .get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }//检查该线程Looper实例变量是否为空,保证一个线程只有一个looper实例变量。
        sThreadLocal.set(new Looper());//这样sThreadLocal 就保存了当前线程的looper对象实例。
    }
                         ||
                         ||
                         ||
   private Looper () {
        mQueue = new MessageQueue();//一个Looper操作的消息队列
        mRun = true ;
        mThread = Thread.currentThread();//绑定到当前线程
    }
                         ||
                         ||
                         ||

public static void loop() {
        Looper me = myLooper();//获取与当前线程绑定的looper实例,
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        MessageQueue queue = me. mQueue;//获取消息队列
       
        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder. clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity ();
       
//进入死循环,不断的循环消息队列,取出消息处理消息
        while (true ) {
            Message msg = queue.next(); // 取出消息
            if (msg != null) {

                    //msg.target 其实是一个Handler ,作用看后面
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }

                long wallStart = 0;
                long threadStart = 0;

                // This must be in a local variable, in case a UI event sets the logger
                Printer logging = me. mLogging;
                if (logging != null) {
                    logging.println( ">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what );
                    wallStart = SystemClock.currentTimeMicro();
                    threadStart = SystemClock.currentThreadTimeMicro();
                }________打印日志

                msg.target.dispatchMessage(msg);//将消息处理转到msg.target 而看Message源码Handler target; 

                if (logging != null) {
                    long wallTime = SystemClock.currentTimeMicro() - wallStart;
                    long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;

                    logging.println( "<<<<< Finished to " + msg.target + " " + msg.callback);
                    if (logging instanceof Profiler) {
                        ((Profiler) logging).profile(msg, wallStart, wallTime,
                                threadStart, threadTime);
                    }
                }

                // Make sure that during the course of dispatching the
                // identity of the thread wasn't corrupted.
                final long newIdent = Binder.clearCallingIdentity ();
                if (ident != newIdent) {
                    Log. wtf(TAG, "Thread identity changed from 0x"
                            + Long. toHexString(ident) + " to 0x"
                            + Long. toHexString(newIdent) + " while dispatching to "
                            + msg.target.getClass().getName() + " "
                            + msg.callback + " what=" + msg.what );
                }_______打印日志
               
                msg.recycle();//
            }
        }
    }
                         ||
                         ||
                         ||
先看看Messenger有些什么?
    /*package*/ Bundle data;
   
    /*package*/ Handler target;    
   
    /*package*/ Runnable callback;//用于回调,  

    public void dispatchMessage(Message msg) {
       //先判断msg.callback是否为空或者不为空的情况?
          //问题1:而msg.callback是个线程,什么时候被初始化?
     if (msg.callback != null) {
            handleCallback(msg);
        } else {

            if (mCallback != null) {
                if (mCallback .handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);//这里可以看出,最终消息发出后该消息会由Handler的handleMessage(msg);处理。
        }
    }
解答: //问题1:而msg.callback是个线程,什么时候被初始化?
    private final void handleCallback(Message message) {
        message.callback.run();//直接执行到传进来的线程的run方法中去了,即又回调回去了。
    }

Handler还有这样一中用法(即延迟处理消息):

new Handler().postDelayed( new Runnable() {
                         @Override
                         public void run() {
                             
                              //操作代码
                        }
                  }, 300);
------------>看postDelayed 方法:
    public final boolean postDelayed (Runnable r, long delayMillis)
    {
//传进来的线程被放进了getPostMessage(r)方法
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }
------------>getPostMessage(r)方法

    private final Message getPostMessage(Runnable r) {
        Message m = Message. obtain();
        m.callback = r;//callback即被初始化了。则该消息的callback不为空
        return m;
    }
一路跟踪postDelayed的具体代码,最后到了sendMessageAtTime这个方法中来了。

上面一点说明了:handleCallback(msg);与 handleMessage(msg);的区别
                         ||
                         ||
                         ||
因此先看看sendMessageAtTime 这个方法:

public boolean sendMessageAtTime (Message msg, long uptimeMillis)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;//问题1:这个是那里来的?也就是说这个是不是就是该线程绑定的looper的那个消息队列?
        if (queue != null) {
            msg.target = this;//问题2:当前Handler即为处理消息的目标handler
            sent = queue.enqueueMessage(msg, uptimeMillis);//处理消息
        }
        else {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue" );
            Log. w("Looper", e.getMessage(), e);
        }
        return sent;
    }

                         ||
                         ||
                         ||
处理消息放进来,拿出去的操作
 final boolean enqueueMessage(Message msg, long when) {
---------------开始-------------------------------------  
异常情况:消息正在使用;目标为空主线程不允许;   
   if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg
                    + " This message is already in use." );
        }
//mQuitAllowed消息队列 是否允许结束标志位
   他的使用在这里: public static void prepareMainLooper() {
        prepare();
        setMainLooper(myLooper ());
        myLooper().mQueue.mQuitAllowed = false;
    }而prepareMainLooper是在ActivityThread的时候调用,即初始化主线程的时候。说明他是与主线程挂钩的。

        if (msg.target == null && !mQuitAllowed) {
            throw new RuntimeException("Main thread not allowed to quit");
        }
        final boolean needWake;
        synchronized (this ) {
            if (mQuiting ) {
                RuntimeException e = new RuntimeException(
                    msg.target + " sending message to a Handler on a dead thread");
                Log. w("MessageQueue", e.getMessage(), e);
                return false ;
            } else if (msg.target == null) {
                mQuiting = true ;
            }
---------------结束------该方法的以上部分都是在处理一系列的异常情况----------------------------------

            msg.when = when;//延迟时间:从开机到当前时间的
            //Log.d("MessageQueue", "Enqueing: " + msg);
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked; // new head, might need to wake up
            } else {
                Message prev = null;
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                needWake = false; // still waiting on head, no need to wake up
            }
        }
        if (needWake) {
            nativeWake( mPtr);
        }
        return true ;
    }
重点方法:enqueueMessage()+next()
                         ||
                         ||
                         ||
从Handler的源码看,Handler的所有发送消息方法中,不管以何种方式发送最后都会转到sendMessageAtTime这个
方法中。


另外:刷新UI的另外几种方式:

     runOnUiThread( new Runnable() {
                               public void run() {
               //代码
                  }
               });
                       |
                       |
                       |
    public final void runOnUiThread(Runnable action) {
               //判断是否是主线程,如果是则直接到这个线程的run方法中
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }
                       |
                       |
                       |
    public final boolean post (Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }                       
后面的和上面一样了。最后还是回归到了r的run方法中


                   btn1.post(new Runnable () {

                         @Override
                         public void run() {
                               // TODO Auto-generated method stub

                        }

                  });


版权声明:本文为博主原创文章,未经博主允许不得转载。

Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自【张鸿洋的博客】很多人面试肯定都被问到过,请问Andro...
  • lmj623565791
  • lmj623565791
  • 2014年08月07日 09:17
  • 194442

Android异步消息处理机制完全解析,带你从源码的角度彻底理解

我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃。相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也是早已烂熟于心,即创建一个Messag...
  • sinyu890807
  • sinyu890807
  • 2013年08月22日 14:02
  • 85037

Android Handler消息处理机制详解

前言 从我们学习android开始,几乎每天都在和handler打交道.有了它,我们在子线程中处理好了耗时的操作,可以利用它来更新UI.它为我们在线程间的通信提供了很大的方便,而今天博客就来详细的介绍...
  • u011692041
  • u011692041
  • 2016年05月11日 10:48
  • 6943

Android的消息处理机制--Looper,Handler

  • 2012年09月02日 22:42
  • 479KB
  • 下载

Android_Handler消息处理机制

  • 2015年12月07日 00:12
  • 1.18MB
  • 下载

Android多线程及异步任务消息处理机制 一 Handler的使用

  • 2014年08月26日 12:39
  • 3.21MB
  • 下载

Android应用程序消息处理机制

  • 2013年10月23日 01:22
  • 716KB
  • 下载

Android消息处理机制之Message 二

  • 2013年11月29日 16:58
  • 1.14MB
  • 下载

Android异步消息处理机制解析

**一.概述** 由于Handler总是依附于创建时所在的线程,比如我们的Handler是在主线程中创建的,而在子线程中又无法直接对UI进行操作,于是我们就通过一系列的发送消息、入队、出队等环节,最...
  • Obama_Li
  • Obama_Li
  • 2015年08月12日 16:43
  • 250

android的消息处理机制(图+源码分析)——Looper,Handler,Message

作为一个大三的预备程序员,我学习android的一大乐趣是可以通过源码学习google大牛们的设计思想。android源码中包含了大量的设计模式,除此以外,android sdk还精心为我们设计了各种...
  • x1971481259
  • x1971481259
  • 2016年09月20日 13:47
  • 70
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android消息处理机制
举报原因:
原因补充:

(最多只允许输入30个字)