zoukankan      html  css  js  c++  java
  • Handle源码分析,深入群内了解风骚的Handle机制

    Hanlder的使用方式一:

     1 private static Handler mHandler =  new Handler(){
     2         public void handleMessage(android.os.Message msg) {
     3             switch (msg.what) {
     4             case ONE:
     5                 
     6                 break;
     7 
     8             default:
     9                 break;
    10             }
    11         };
    12     };

    Handler的使用方式二:

     1     public void startHandlerThread(){
     2         HandlerThread mHandlerThread = new HandlerThread("TestHandler");
     3         mHandlerThread.start();
     4         Handler mHandler = new Handler(mHandlerThread.getLooper()){
     5             @Override
     6             public void handleMessage(Message msg) {
     7                 // TODO Auto-generated method stub
     8                 super.handleMessage(msg);
     9             }
    10         };
    11     }

    第二种比较通用,为什么这么说类,原因就是HandlerTrehad是个Thread,在run方法内部已经完成了Looper.prepare()的方法的调用,所以即使在子线程中也不会有任何问题

    基本的使用就暂时说这些!!!!!

    进入咱们今天的正题,深入裙内分析Handler源码:

     1 /**
     2      * Sends a Message containing only the what value.
     3      *  
     4      * @return Returns true if the message was successfully placed in to the 
     5      *         message queue.  Returns false on failure, usually because the
     6      *         looper processing the message queue is exiting.
     7      */
     8     public final boolean sendEmptyMessage(int what)
     9     {
    10         return sendEmptyMessageDelayed(what, 0);
    11     }

    关于sendMessage就暂时拿这个进入问题内部;

    Line10:说的很明确,调用delay方法,传入delay事件零毫秒,在跟进一步

     1  /**
     2      * Sends a Message containing only the what value, to be delivered
     3      * after the specified amount of time elapses.
     4      * @see #sendMessageDelayed(android.os.Message, long) 
     5      * 
     6      * @return Returns true if the message was successfully placed in to the 
     7      *         message queue.  Returns false on failure, usually because the
     8      *         looper processing the message queue is exiting.
     9      */
    10     public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    11         Message msg = Message.obtain();
    12         msg.what = what;
    13         return sendMessageDelayed(msg, delayMillis);
    14     }

      这个方法为每个Handler消息进行封装一个Message对象,然后继续调用delay方法,但是这个方法的参数和之前的不一样,不通点就是接受一个Mesage对象,和一个延迟的时间。

      Line11:从缓存中读取Mesage对象,如果没有则直接创建;

      Line12,绑定What,回头再handlerMessage()方法中要用到;充当Swith的case;

      继续深入:

    1 public final boolean sendMessageDelayed(Message msg, long delayMillis)
    2     {
    3         if (delayMillis < 0) {
    4             delayMillis = 0;
    5         }
    6         return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    7     }

      该出进行了容错处理防止你瞎传值;

      然后调用方法sendMessageAtTime这里的参数需要说明下:为什么SystemClock.uptimeMillis() + delayMillis这块的意思是加当当前系统时间,其实意思很明显如果你的延迟时间是一秒,那么这个一一秒的起始点,自然就是从当前算起,不然你仅仅说一秒,那么从什么时候开始那,这里就是这个意思了!!!

      来继续看方法sendMessageAtTime

     1 public boolean sendMessageAtTime(Message msg, long uptimeMillis)
     2     {
     3         boolean sent = false;
     4         MessageQueue queue = mQueue;
     5         if (queue != null) {
     6             msg.target = this;
     7             sent = queue.enqueueMessage(msg, uptimeMillis);
     8         }
     9         else {
    10             RuntimeException e = new RuntimeException(
    11                 this + " sendMessageAtTime() called with no mQueue");
    12             Log.w("Looper", e.getMessage(), e);
    13         }
    14         return sent;
    15     }

      这个是Message入队的操作,看代码,既然说到入队,那么自然就少不了需要队列来存储这个Message,Line4,得到这个消息队列。然后准备入队;

      Line6:这个操作很重要,为什么要交付这个Targe,因为这里在出队列的时候要用到,出队后要调用handlermessge方法,进行消息处理,如何调用handlemessge方法,在Android的世界里基本就是指对象,很显然这里targe指向了当前对象this;(不懂没关系,后边会在补充讲解)

      Line7:进行入队操作,返回入队的最终结果,如果是true则代表成功入队,否则为失败;

     1 final boolean enqueueMessage(Message msg, long when) {
     2         if (msg.when != 0) {
     3             throw new AndroidRuntimeException(msg
     4                     + " This message is already in use.");
     5         }
     6         if (msg.target == null && !mQuitAllowed) {
     7             throw new RuntimeException("Main thread not allowed to quit");
     8         }
     9         final boolean needWake;
    10         synchronized (this) {
    11             if (mQuiting) {
    12                 RuntimeException e = new RuntimeException(
    13                     msg.target + " sending message to a Handler on a dead thread");
    14                 Log.w("MessageQueue", e.getMessage(), e);
    15                 return false;
    16             } else if (msg.target == null) {
    17                 mQuiting = true;
    18             }
    19 
    20             msg.when = when;
    21             //Log.d("MessageQueue", "Enqueing: " + msg);
    22             Message p = mMessages;
    23             if (p == null || when == 0 || when < p.when) {
    24                 msg.next = p;
    25                 mMessages = msg;
    26                 needWake = mBlocked; // new head, might need to wake up
    27             } else {
    28                 Message prev = null;
    29                 while (p != null && p.when <= when) {
    30                     prev = p;
    31                     p = p.next;
    32                 }
    33                 msg.next = prev.next;
    34                 prev.next = msg;
    35                 needWake = false; // still waiting on head, no need to wake up
    36             }
    37         }
    38         if (needWake) {
    39             nativeWake(mPtr);
    40         }
    41         return true;
    42     }

      Line2 Line6很显然如果这两个对象为Null则直接就是throw exception;为什么这样那????因为这handler必须的两个字段。

      时间:标记何事处理这个Message;

      Target:应该由谁来处理;

      两者缺一不可,否则Handler就不是Handler了。。。。。。。

     核心代码Line20--36

     1 ................
     2          msg.when = when;
     3             //Log.d("MessageQueue", "Enqueing: " + msg);
     4             Message p = mMessages;
     5             if (p == null || when == 0 || when < p.when) {
     6                 msg.next = p;
     7                 mMessages = msg;
     8                 needWake = mBlocked; // new head, might need to wake up
     9             } else {
    10                 Message prev = null;
    11                 while (p != null && p.when <= when) {
    12                     prev = p;
    13                     p = p.next;
    14                 }
    15                 msg.next = prev.next;
    16                 prev.next = msg;
    17                 needWake = false; // still waiting on head, no need to wake up
    18             }
    19         ...................

      Line5的判断是拿当前即将进行出队的Message的when做对比,如果你的时间小(也就是你应该被优先出队),那么久交换当前的出队对象,很显然,现在Message就在事队列的最前端,等出队的时间到了就执行出队操作。

      Line9,这是入队操作不同点就是需要执行插入的操作,为什么要插入哪?因为Message的排序是按照When来排序的谁的When小谁在前边,这样出队就不会出错,保证出队的有序性。首先声明一个temp变量prev,然后从队列的首位置开始,根据时间when的大小进行比较,如果找到合适的就交换交换类似C中的指针方式。建议大家绘制个草图,可以很明显的看出交换过程!

      到了这里Message就完成了入队的操作,在整个入队的操作大家是否注意到一个问题,MessageQueu怎么出现的!!!!!

    <:>讲述Message的初始化代码:

      说先追根溯源,第一次使用的地方就是Handler中,很好咱们就从这里入手吧!!!

     1 public Handler() {
     2         if (FIND_POTENTIAL_LEAKS) {
     3             final Class<? extends Handler> klass = getClass();
     4             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
     5                     (klass.getModifiers() & Modifier.STATIC) == 0) {
     6                 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
     7                     klass.getCanonicalName());
     8             }
     9         }
    10 
    11         mLooper = Looper.myLooper();
    12         if (mLooper == null) {
    13             throw new RuntimeException(
    14                 "Can't create handler inside thread that has not called Looper.prepare()");
    15         }
    16         mQueue = mLooper.mQueue;
    17         mCallback = null;
    18     }

      Line 16进行赋值 的操作,很显然最初的创建位置并不是这里,这里只是通过looper对象得到的改队列的引用,而并不是在这里进行了Create.

      Line 11这里有说道搞Looper对象的获取,既然咱们是通过Looper对象获取的队列对象,那么是不是咱们可以进入这个类看下原因为几何???

    1  private Looper() {
    2         mQueue = new MessageQueue();
    3         mRun = true;
    4         mThread = Thread.currentThread();
    5     }

      Line2说明这个问题,Looper对象创建的时候,初始化了这个MessageQueue.

    <>><>到这里基本咱们说完了怎么入队,以及队列的由来,那么接下来就看怎么出队的操作!!!!

     说道出队,自然就说到咱们的《Looper,轮询器》

     既然Looper是轮询器,那么根据java面向对象的思想,这里自然就会偶轮训的方法loop;

      <<<<<果不其然>>>>>

     1    /**
     2      *  Run the message queue in this thread. Be sure to call
     3      * {@link #quit()} to end the loop.
     4      */
     5     public static final void loop() {
     6         Looper me = myLooper();
     7         MessageQueue queue = me.mQueue;
     8         while (true) {
     9             Message msg = queue.next(); // might block
    10             //if (!me.mRun) {
    11             //    break;
    12             //}
    13             if (msg != null) {
    14                 if (msg.target == null) {
    15                     // No target is a magic identifier for the quit message.
    16                     return;
    17                 }
    18                 if (me.mLogging!= null) me.mLogging.println(
    19                         ">>>>> Dispatching to " + msg.target + " "
    20                         + msg.callback + ": " + msg.what
    21                         );
    22                 msg.target.dispatchMessage(msg);
    23                 if (me.mLogging!= null) me.mLogging.println(
    24                         "<<<<< Finished to    " + msg.target + " "
    25                         + msg.callback);
    26                 msg.recycle();
    27             }
    28         }
    29     }

      代码484很少,哈哈哈哈,开森死人了、、、、、

      直奔主题走;Line22;这个Targe大家还记得不,(忘了去前边再去复习,真都比)上边说到这个是Handler对象,那么这里调用的方法dispatchMessage进行的Message的分发操作,自然分发最后还是交给了Handler,一起看下咱们说的队不对吧,去Handler找下这个方法????

     1  /**
     2      * Handle system messages here.
     3      */
     4     public void dispatchMessage(Message msg) {
     5         if (msg.callback != null) {
     6             handleCallback(msg);
     7         } else {
     8             if (mCallback != null) {
     9                 if (mCallback.handleMessage(msg)) {
    10                     return;
    11                 }
    12             }
    13             handleMessage(msg);
    14         }
    15     }

      机会就是给准备的人,麻痹我终于还是给我找到了。。。。

      看方法Line13,这里干嘛了,前边的对于咱们的简单实用的时候尽管放心,没用,因为你没指定callback,所以自然就会走handleMessage方法中来,这个方法熟悉不!!!!!

      那个谁》》》》》你说啥不熟悉,奶奶的脚丫子滚蛋,,,,初始化Handler的时候 ,大家重载的那个方法就是这个了。。。

      

      哟,貌似忘记了啥,哦对了,子线程new handler crash问题,,,,,走看看去,报错的位置以及log484这。。。。

      

    1  mLooper = Looper.myLooper();
    2         if (mLooper == null) {
    3             throw new RuntimeException(
    4                 "Can't create handler inside thread that has not called Looper.prepare()");
    5         }

      这里是要looper的时候,发现木有,,,,,Why,,,思考下咯,为啥没,那就看看哪里new Looper》???????

      查源码发现还是在Looper类中new类。。。

      

     1 /** Initialize the current thread as a looper.
     2       * This gives you a chance to create handlers that then reference
     3       * this looper, before actually starting the loop. Be sure to call
     4       * {@link #loop()} after calling this method, and end it by calling
     5       * {@link #quit()}.
     6       */
     7     public static final void prepare() {
     8         if (sThreadLocal.get() != null) {
     9             throw new RuntimeException("Only one Looper may be created per thread");
    10         }
    11         sThreadLocal.set(new Looper());
    12     }

    额,这就是prepare方法了,也是就是大家禅说的子线程初始化的时候为啥调用这个方法,原因就是这了????

    先这样,6666666666

  • 相关阅读:
    12
    11
    10
    9
    8
    6. iOS APP 设计规范大全
    4. iOS中常用演示方法以及利弊
    我要写一篇动态计算tableView-cell高度的随笔
    doclever 5.5.1 安装及升级【原创】
    SPARROW-JS 从0开始写 0依赖,原生JS框架
  • 原文地址:https://www.cnblogs.com/liemng/p/5253812.html
Copyright © 2011-2022 走看看