• Android正在使用Handler实现信息发布机制(一)


    上一篇文章,我们谈到了电话Handler的sendMessage方法,最后,我们将进入一个电话 sendMessageAtTime方法,例如下列:

        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
            MessageQueue queue = mQueue;
            if (queue == null) {
                RuntimeException e = new RuntimeException(
                        this + " sendMessageAtTime() called with no mQueue");
                Log.w("Looper", e.getMessage(), e);
                return false;
            }
            return enqueueMessage(queue, msg, uptimeMillis);
        }

    在这里。我们看到了MessageQueue和enqueueMessage等变量跟方法,我们能够想到,在Handler的实现的机制中,一定存在着一个消息队列。而它存放了我们创建的众多消息(Message)对象。

    从这里,我们就会開始去探寻隐藏在 Handler对象后面的那一些我们想知道的实现机制了。

    首先,我们还是从 Handler的创建開始说起,在上一篇文章,我们是通过 new Handler的方法来创建的,代码例如以下:

        private Handler mHandler = new Handler() {
    
            public void handleMessage(Message msg) {
                switch (msg.what) {
                case MSG_ID_1:
                    Log.v("Test", "Toast called from Handler.sendMessage()");
                    break;
                case MSG_ID_2:
                    String str = (String) msg.obj;
                    Log.v("Test", str);
                    break;
                }
                
            }
        };

    显然。我们要去看一下Handler的构造函数,例如以下:

        public Handler() {
            this(null, false);
        }
    
        public Handler(Callback callback) {
            this(callback, false);
        }
    
        public Handler(Looper looper) {
            this(looper, null, false);
        }
        
        public Handler(Looper looper, Callback callback) {
            this(looper, callback, false);
        }
    
       
        public Handler(boolean async) {
            this(null, async);
        }
    
        public Handler(Callback callback, boolean async) {
            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 = callback;
            mAsynchronous = async;
        }
        
        public Handler(Looper looper, Callback callback, boolean async) {
            mLooper = looper;
            mQueue = looper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }

    我们能够看到。真正实现的构造函数。事实上仅仅有以下两个。例如以下:

        public Handler(Callback callback, boolean async) {
            ....
        }
        
        public Handler(Looper looper, Callback callback, boolean async) {
            ...
        }

    这两个的区别就在于是否有參数Looper。而Looper是一个线程相关的对象。

    何谓线程相关的变量?就是线程间不能共享的对象,仅仅在本线程内有作用的对象。

    那么Looper对象的作用是什么?

    从我个人的理解,Looper类就是对MessageQueue的封装。它主要做的是两件事:

    1)构造Looper对象,初始化MessageQueue。我们能够从其构造函数看到:

        private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }


    显然。MessageQueue正是在创建Looper的时候初始化的。

    我们还注意到。这个构造函数是private的。而它则是被Looper.prepare方法调用的,例如以下:

        public static void prepare() {
            prepare(true);
        }
    
        private static void prepare(boolean quitAllowed) {
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            sThreadLocal.set(new Looper(quitAllowed));
        }

    能够看到,Loop对象被创建之后,会被放到ThreadLocal变量中,而ThreadLocal正是线程局部变量,这说明了关于Looper的一个特性:

    每个线程中都仅仅能有一个Looper对象。

    2)调用loop()方法。循环处理消息。详细代码例如以下:

        public static void loop() {
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            final MessageQueue queue = me.mQueue;
    
           ....
    
            for (;;) {
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
    
               ...
    
                msg.target.dispatchMessage(msg);
    
               ....
    
                msg.recycle();
            }
        }

    从上面的代码中。我们能够看到,在一个无限循环中,会从MessageQueue中获得消息,然后通过msg.target.dispatchMessage(msg)方法调用。处理消息。最后将消息进行回收。

    在这里。我们先不关心dispatchMessage方法。我们先跑一下题。看一下recycle方法里面做了什么事吧,例如以下:

        private static Message sPool;
        private static int sPoolSize = 0;
    
        private static final int MAX_POOL_SIZE = 50;
    
        /**
         * Return a new Message instance from the global pool. Allows us to
         * avoid allocating new objects in many cases.
         */
        public static Message obtain() {
            synchronized (sPoolSync) {
                if (sPool != null) {
                    Message m = sPool;
                    sPool = m.next;
                    m.next = null;
                    sPoolSize--;
                    return m;
                }
            }
            return new Message();
        }
        
        public void recycle() {
            clearForRecycle();
    
            synchronized (sPoolSync) {
                if (sPoolSize < MAX_POOL_SIZE) {
                    next = sPool;
                    sPool = this;
                    sPoolSize++;
                }
            }
        }

    从上面的代码中。我们能够看到,Message对象本身有一个next的字段指向另外一个Message,也就是说,能够通过链表的方式将众多的Message给串起来,变成一个链表的消息池sPool。

    而在这里,当调用recycle方法,就会将当前Message对象,先clearForRecycle之后。再加入到 sPool的头部中。而当我们通过Message的obtain方法的时候,我们事实上也是从sPool中拿 出一个空的Message对象。

    相信看到这里,大家就了解了上一篇文章中我说。为什么建议大家使用Message.obtain方法去获取消息对象了吧。

    接下来,我们再回到正题上。

    从上面关于Handler的创建和关于Looper的描写叙述中,我们能够得出这样一个结论:

    在每个线程中,假设我们要创建Handler,那么此线程中就必须有一个Looper对象,而这个Looper对象中又封装了一个MessageQueue对象来对Message进行管理。

    所以,假设我们要在一个新线程中使用handler的话,我们就必须通过调用Loop.prepare()方法,为此线程创建一个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();
          }
      }

    当然,仅仅有理论当然是不行的。我们还是得通过一个样例来看一下详细的效果,例如以下:

    public class MainActivity extends ActionBarActivity {
    
        private static final int MSG_ID_1 = 1;
        private static final int MSG_ID_2 = 2;
    
        class LooperThread extends Thread {
            public Handler mHandler;
    
            public void run() {
                Looper.prepare();
        
                mHandler = new Handler() {                
                    public void handleMessage(Message msg) {
                        Log.v("Test", "Id of LooperThread : " + Thread.currentThread().getId());
                        switch (msg.what) {
                        case MSG_ID_1:
                            Log.v("Test", "Toast called from Handler.sendMessage()");
                            break;
                        case MSG_ID_2:
                            String str = (String) msg.obj;
                            Log.v("Test", str);
                            break;
                        }
                    }
                };
                Looper.loop();
            }
        }
        
    
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            Log.v("Test", "Id of MainThread : " + Thread.currentThread().getId());
            
            LooperThread looperThread = new LooperThread();
            looperThread.start();
            
            while(looperThread.mHandler == null){                        
            }
            
            Message message = Message.obtain();
            message.what = MSG_ID_1;
            looperThread.mHandler.sendMessage(message);
    
            Message msg2 = Message.obtain();
            msg2.obj = "I'm String from Message 2";
            msg2.what = MSG_ID_2;
            looperThread.mHandler.sendMessage(msg2);
    
        }
    }

    相应的结果例如以下:

    10-27 16:48:44.519: V/Test(20837): Id of MainThread : 1
    10-27 16:48:44.529: V/Test(20837): Id of LooperThread : 68421
    10-27 16:48:44.529: V/Test(20837): Toast called from Handler.sendMessage()
    10-27 16:48:44.529: V/Test(20837): Id of LooperThread : 68421
    10-27 16:48:44.529: V/Test(20837): I'm String from Message 2
    

    那事实上在这里有一个问题,为什么我们寻常能够直接去创建Handler对象,而不须要去调用UI线程的Looper.prepare和loop等方法它?

    当然,这当然是需要。这个步骤是仅Android系统帮我们做,所以默认主线程不再需要做这些初始化。

    好,本文,我们了解到一个线程,Looper,Handler, MessageQueue 和 Message 等之间的一些相关,但主要用于Looper知识对象。

    结束。

  • 相关阅读:
    开发移动端项目如何利用Chrome浏览器连接真机测试
    有关浏览器开发者工具使用的技巧
    有关前端实时可视化工具的使用 ==实现边改代码边看效果
    vue的生命周期介绍beforeCreate(创建前)、created(创建后)、beforeMount(载入前)、mounted(载入后)、beforeUpdate(更新前)、updated(更新后)、beforeDestroy(销毁前)、destroyed(销毁后)
    echart中重新定义引导线的文字换行<br>不起作用
    使用mock数据填写表格同时带点击查看更多
    有关前后端分离前端如何使用mock数据
    echar图柱状图和折线图混合加双侧y轴坐
    【Oracle】DBMS_STATS.GATHER_TABLE_STATS
    【PostgreSQL-9.6.3】Red Hat 4.4.7下的安装
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/5041307.html
Copyright © 2020-2023  润新知