zoukankan      html  css  js  c++  java
  • Android Handler机制 (一个Thead中可以建立多个Hander,通过msg.target保证MessageQueue中的每个msg交由发送message的handler进行处理 ,但是 每个线程中最多只有一个Looper,肯定也就一个MessageQuque)

    转载自http://blog.csdn.net/stonecao/article/details/6417364

    在android中提供了一种异步回调机制Handler,使用它,我们可以在完成一个很长时间的任务后做出相应的通知

        handler基本使用:

            在主线程中,使用handler很简单,new一个Handler对象实现其handleMessage方法,在handleMessage中
    提供收到消息后相应的处理方法即可,这里不对handler使用进行详细说明,在看本博文前,读者应该先掌握handler的基本使用,我这里主要深入描述handler的内部机制

       .现在我们首先就有一个问题,我们使用myThreadHandler.sendEmptyMessage(0);发送一个message对象,那么Handler是如何接收该message对象并处理的呢?我先画一个数据结构图:

       

    从这个图中我们很清楚可以看到调用sendEmptyMessage后,会把 Message对象放入一个MessageQueue队列,该队列属于某个Looper对象,每个Looper对象通过 ThreadLocal.set(new Looper())跟一个Thread绑定了,Looper对象所属的线程在Looper.Loop方法中循环执行从MessageQueue队列读取 Message对象,并把Message对象交由Handler处理,调用Handler的dispatchMessage方法。

         现在我们再来看一下使用Handler的基本实现代码:

                   // 主线程中新建一个handler
                    normalHandler = new Handler() {
                            public void handleMessage(android.os.Message msg) {
                                    btnSendMsg2NormalHandler.setText("normalHandler");
                                    Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]--normalHandler handleMessage run...", Thread.currentThread()
                                                    .getName()));
                            }
                    };

    ...
    //发送消息到hanlder
    myThreadHandler.sendEmptyMessage(0); 

    你现在已经很清楚了sendEmptyMessage到handleMessage的 过程,途中经过Looper.MessageQueue队列,转由Looper所在的线程去处理了,这是一个异步的过程,当然Looper所在的线程也可 以是sendEmptyMessage所在的线程。 

         看了上面你也许还是迷惑不解,那么什么要Looper了,跟我们要用的Handler又有啥鸟关系呢?

         我在前面一直强调在主线程中使用handler,为什么要这么说呢,因为你在自己new一个新线程中去像我前面那样简单建立一个Handler,程序执行是会报错的:

        java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
         at android.os.Handler.<init>(Handler.java:121)
         at com.cao.android.demos.handles.HandleTestActivity$MyThread$1.<init>(HandleTestActivity.java:86)
         at com.cao.android.demos.handles.HandleTestActivity$MyThread.run(HandleTestActivity.java:86)

        为什么在主线程中不会报错,而在自己新见的线程中就会报这个错误呢?很简单,因为主线程它已经建立了Looper,你可以打开ActivityThread的源码看一下:

        public static final void main(String[] args) {
            SamplingProfilerIntegration.start();

            Process.setArgV0("<pre-initialized>");

            Looper.prepareMainLooper();

            ActivityThread thread = new ActivityThread();
            thread.attach(false);

            Looper.loop();

            if (Process.supportsProcesses()) {
                throw new RuntimeException("Main thread loop unexpectedly exited");
            }

            thread.detach();
            String name = (thread.mInitialApplication != null)
                ? thread.mInitialApplication.getPackageName()
                : "<unknown>";
            Slog.i(TAG, "Main thread of " + name + " is now exiting");
        }

        在main函数中它已经做了这个事情了,为什么要调用 Looper.prepareMainLooper(); Looper.loop();我们可以进去看一下,在prepareMainLooper方法中新建了一个looper对象,并与当前进程进行了绑定,而 在Looper.loop方法中,线程建立消息循环机制,循环从MessageQueue获取Message对象,调用  msg.target.dispatchMessage(msg);进行处理msg.target在 myThreadHandler.sendEmptyMessage(0)设置进去的,因为一个Thead中可以建立多个Hander,通过 msg.target保证MessageQueue中的每个msg交由发送message的handler进行处理,那么Handler又是怎样与 Looper建立联系的呢,在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;

    在新建Handler时需要设置mLooper成员,Looper.myLooper是从当前线程中获取绑定的Looper对象:

    public static final Looper myLooper() {
            return (Looper)sThreadLocal.get();
        }

        若Looper对象没有创建,就会抛异常"Can't create handler inside thread that has not called Looper.prepare()"
    这跟我前面讲的是一致的。所以我们在一个新线程中要创建一个Handler就需要这样写:

            class MyThread extends Thread {

                    public void run() {               
                            Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]-- run...", Thread
                                            .currentThread().getName()));
                            // 其它线程中新建一个handler
                            Looper.prepare();// 创建该线程的Looper对象,用于接收消息,在非主线程中是没有looper的所以在创建handler前一定要使用prepare()创建一个Looper
                            myThreadHandler = new Handler() {
                                    public void handleMessage(android.os.Message msg) {
                                            Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]--myThreadHandler handleMessage run...", Thread
                                                            .currentThread().getName()));
                                    }
                            };
                            Looper.myLooper().loop();//建立一个消息循环,该线程不会退出
                    }
            }

       现在,你应该对Handler的机制有所了解了吧,若有什么疑问,欢迎在评论中提出

      在其它线程中Handler使用主线程的Looper

       前面我说了在新线程中要新建一个Handler需要调用Looper.prepare();也有另一种方法就是使用主线程中的Looper,那就不必新建Looper对象了:

                            threadMainLoopHandler =new Handler(Looper.getMainLooper()){
                                    public void handleMessage(android.os.Message msg) {
                                            Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]--threadMainLoopHandler handleMessage run...", Thread
                                                            .currentThread().getName()));                                       
                                    }
                                    //该handleMessage方法将在mainthread中执行
                            };

      这时候注意不要在handleMessage做太多的操作,因为它在主线程中执行,会影响主线程执行ui更新操作。

        使用Message.callback回调

       public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    从dispatchMessage定义可以看出,如果Message对象自带callback对象,handler不会执行handleMessage方 法而是执行message.callback中定义的run方法,当然callback还是在handler关联的looper所绑定的线程中执行的。实 际上Handler.post(Runnable r)方法就是把r添加到一个msg.callback的,也就是说,下面两种写法,没有什么区别:

    1.使用Message.callback

     

    1. Message msg = Message.obtain(myThreadHandler,new Runnable() {  
    2.       
    3.     @Override  
    4.     public void run() {  
    5.         Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]--myThreadHandler.Message.callback.run",  
    6.                 Thread.currentThread().getName()));   
    7.     }  
    8. });  
    9. myThreadHandler.sendMessage(msg);  

    2.使用Handler.post

     

    1. myThreadHandler.post(new Runnable() {  
    2.                       
    3.                     @Override  
    4.                     public void run() {  
    5.                         Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]--myThreadHandler.Message.callback.run",  
    6.                                 Thread.currentThread().getName()));   
    7.                     }  
    8.                 });  

    注:对于Handler机制相关测试,我写了一个测试类:

    http://download.csdn.net/source/3275970

    3.Handler对Activity finish影响。

    在开发的过程中碰到一个棘手的问题,调用Activity.finish函数 Acitivity没有执行生命周期的ondestory函数,后面查找半天是因为有一个handler成员,因为它有一个delay消息没有处理,调用 Activity.finish,Activity不会马上destory,所以记得在Ativity finish前清理一下handle中的未处理的消息,这样Activity才会顺利的destory

  • 相关阅读:
    164 Maximum Gap 最大间距
    162 Find Peak Element 寻找峰值
    160 Intersection of Two Linked Lists 相交链表
    155 Min Stack 最小栈
    154 Find Minimum in Rotated Sorted Array II
    153 Find Minimum in Rotated Sorted Array 旋转数组的最小值
    152 Maximum Product Subarray 乘积最大子序列
    151 Reverse Words in a String 翻转字符串里的单词
    bzoj3994: [SDOI2015]约数个数和
    bzoj 4590: [Shoi2015]自动刷题机
  • 原文地址:https://www.cnblogs.com/kobe8/p/3987620.html
Copyright © 2011-2022 走看看