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_Handler消息处理机制

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

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

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

Android应用程序消息处理机制

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

Android多线程消息处理机制(三) Handler部分源码分析

此文一起来看下Handler的部分源码,不用怕很简单,有英文的地方我都翻译了。 把里面相关的方法过一眼,你瞬间会明白很多。高手就不用看了,去过你的荣华富贵花天酒地。 我一致认为,想学好某一技术,首先是...

Android消息处理机制之Message 二

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

Android消息处理机制--Handler 超详细通俗的解读

Handler是什么每个Handler在创建的时候,都会与一个Looper绑定,UI线程有一个默认的Looper,所以在Activity类的字段中中直接创建Handler,也就是在UI线程中创建Han...

Android 异步消息处理机制:Looper、Handler、Message

我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序会发生崩溃。解决方法相信大家都用过无数次了:创建一个Message对象,然后借助Handler发送出去,之后在Han...

Android多线程----异步消息处理机制之Handler详解

感觉此篇文章很全面,很有价值,特此转载: 【声明】  欢迎转载,但请保留文章原始出处→_→  生命壹号:http://www.cnblogs.com/smyhvae/ ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android消息处理机制
举报原因:
原因补充:

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