• 转载:android笔记--android中的多线程--Handler, Looper, MessageQueue, Message类


    什么时候使用多线程:

    1. 耗时操作使用多线程, 耗时操作放在UI线程中会导致用户的操作无法得到响应.

    2. 阻塞操作使用多线程, 理由同上.

    3. 多核CUP的设备使用多线程, 可以有效提高CPU的利用率.

    4. 并行操作使用多线程.

    android中的多线程模型主要涉及的类有:Looper, Handler, MessageQueue, Message等. 

     一:Looper类:

     1 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
     2     private static Looper sMainLooper;  // guarded by Looper.class
     3 
     4     final MessageQueue mQueue;
     5     final Thread mThread;
     6 public static void loop() {
     7         final Looper me = myLooper();
     8         if (me == null) {
     9             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    10         }
    11         final MessageQueue queue = me.mQueue;
    12 
    13         // Make sure the identity of this thread is that of the local process,
    14         // and keep track of what that identity token actually is.
    15         Binder.clearCallingIdentity();
    16         final long ident = Binder.clearCallingIdentity();
    17 
    18         for (;;) {
    19             Message msg = queue.next(); // might block
    20             if (msg == null) {
    21                 // No message indicates that the message queue is quitting.
    22                 return;
    23             }
    24 
    25             // This must be in a local variable, in case a UI event sets the logger
    26             Printer logging = me.mLogging;
    27             if (logging != null) {
    28                 logging.println(">>>>> Dispatching to " + msg.target + " " +
    29                         msg.callback + ": " + msg.what);
    30             }
    31 
    32             msg.target.dispatchMessage(msg);//超级重要
    33 
    34             if (logging != null) {
    35                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
    36             }
    37 
    38             // Make sure that during the course of dispatching the
    39             // identity of the thread wasn't corrupted.
    40             final long newIdent = Binder.clearCallingIdentity();
    41             if (ident != newIdent) {
    42                 Log.wtf(TAG, "Thread identity changed from 0x"
    43                         + Long.toHexString(ident) + " to 0x"
    44                         + Long.toHexString(newIdent) + " while dispatching to "
    45                         + msg.target.getClass().getName() + " "
    46                         + msg.callback + " what=" + msg.what);
    47             }
    48 
    49             msg.recycle();
    50         }
    51     }
    52 public void quit() {
    53         mQueue.quit(false);
    54     }
    55 public void quitSafely() {//最好用此方法
    56         mQueue.quit(true);
    57     }

    Looper类用来创建消息队列. 每个线程最多只能有一个消息队列:Looper唯一的构造方法如下

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

    android中UI线程默认具有消息队列, 但非UI线程在默认情况下是不具备消息队列的. 如果需要在非UI线程中开启消息队列, 需要调用Looper.prepare()方法, 在该方法的执行过程中会创建一个Looper对象

     1  public static void prepare() {
     2         prepare(true);
     3     }
     4 
     5     private static void prepare(boolean quitAllowed) {
     6         if (sThreadLocal.get() != null) {
     7             throw new RuntimeException("Only one Looper may be created per thread");
     8         }
     9         sThreadLocal.set(new Looper(quitAllowed));
    10     }

    而Looper的构造函数中会创建一个MessageQueue instance(Looper的构造函数是私有的, 在Looper类之外无法创建其对象).  

    此后再为该线程绑定一个Handler instance, 然后调用Looper.loop()方法, 就可以不断的从消息队列中取出消息和处理消息了.

     1 public Handler() {
     2         this(null, false);
     3     }
     4 public Handler(Callback callback) {
     5         this(callback, false);
     6     }
     7 public Handler(Looper looper) {
     8         this(looper, null, false);
     9     }
    10 public Handler(Looper looper, Callback callback) {
    11         this(looper, callback, false);
    12     }
    13 public Handler(Callback callback, boolean async) {
    14         if (FIND_POTENTIAL_LEAKS) {
    15             final Class<? extends Handler> klass = getClass();
    16             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
    17                     (klass.getModifiers() & Modifier.STATIC) == 0) {
    18                 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
    19                     klass.getCanonicalName());
    20             }
    21         }
    22 
    23         mLooper = Looper.myLooper();
    24         if (mLooper == null) {
    25             throw new RuntimeException(
    26                 "Can't create handler inside thread that has not called Looper.prepare()");
    27         }
    28         mQueue = mLooper.mQueue;
    29         mCallback = callback;
    30         mAsynchronous = async;
    31     }
    32 public Handler(Looper looper, Callback callback, boolean async) {
    33         mLooper = looper;
    34         mQueue = looper.mQueue;
    35         mCallback = callback;
    36         mAsynchronous = async;
    37     }
    View Code

    (创建handler的时候必须关联一个looper)

    Looper.myLoop()方法可以得到线程的Looper对象, 如果为null, 说明此时该线程尚未开启消息队列.

    1 public static Looper myLooper() {
    2         return sThreadLocal.get();
    3     }

    二:Handler: 

    Handler类用于处理消息. 该类具有四个构造函数:(如上)

    1. public Handler(). 创建好的Handler instance将绑定在代码所在的线程的消息队列上, 因此一定要确定该线程开启了消息队列, 否则程序将发生错误. 使用这个构造函数创建Handler instance, 一般来说, 我们需要重写Hanler类的handleMessage()方法, 以便在之后的消息处理时调用.

    2. public Handler(Callback callback). Callback是Handler内部定义的一个接口, 因此想要使用这个构造函数创建Handler对象, 需要自定义一个类实现Callback接口, 并重写接口中定义的handleMessage()方法. 这个构造函数其实与无参的构造函数类似, 也要确保代码所在的线程开启了消息队列. 不同的是在之后处理消息时, 将优先调用callback的handleMessage()方法,返回true,则消息处理结束;返回false。则会调用Handler对象的handleMssage()方法.

    1  final MessageQueue mQueue;
    2  final Looper mLooper;
    3  final Callback mCallback;
    4  public interface Callback {
    5         public boolean handleMessage(Message msg);
    6     }
    7  private static void handleCallback(Message message) {
    8         message.callback.run();
    9     }
     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)) {//此处就是Handler的带Callback参数的构造方法中,赋值得到的mCallback
    10                     return;
    11                 }
    12             }
    13             handleMessage(msg);
    14         }
    15     }

    3. public Handler(Looper looper). 这个构造函数表示创建一个Handler instance, 并将其绑定在looper所在的线程上. 此时looper不能为null. 此时一般也需要重写Hanler类的handleMessage()方法

    4. public Handler(Looper looper, Callback callback). 可以结合2和3理解.

     1  public final Message obtainMessage()
     2     {
     3         return Message.obtain(this);
     4     }
     5 private static Message getPostMessage(Runnable r) {
     6         Message m = Message.obtain();
     7         m.callback = r;
     8         return m;
     9     }
    //以post开头的发送消息的方法,是在调用Message.obtain()之后,将postXX方法中的参数Runnable赋值给message对象.如上!
    10 public final boolean post(Runnable r) 11 { 12 return sendMessageDelayed(getPostMessage(r), 0); 13 } 14 public final boolean postAtTime(Runnable r, long uptimeMillis) 15 { 16 return sendMessageAtTime(getPostMessage(r), uptimeMillis); 17 } 18 public final boolean postDelayed(Runnable r, long delayMillis) 19 { 20 return sendMessageDelayed(getPostMessage(r), delayMillis); 21 } 22 public final boolean postAtFrontOfQueue(Runnable r) 23 { 24 return sendMessageAtFrontOfQueue(getPostMessage(r)); 25 } 26 public final boolean sendMessage(Message msg) 27 { 28 return sendMessageDelayed(msg, 0); 29 } 30 public final boolean sendEmptyMessage(int what) 31 { 32 return sendEmptyMessageDelayed(what, 0); 33 } 34 public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { 35 Message msg = Message.obtain(); 36 msg.what = what; 37 return sendMessageDelayed(msg, delayMillis); 38 } 39 public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) { 40 Message msg = Message.obtain(); 41 msg.what = what; 42 return sendMessageAtTime(msg, uptimeMillis); 43 } 44 //Handler所有post、send方法,最后调的都是此方法... 45 public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 46 MessageQueue queue = mQueue; 47 if (queue == null) { 48 RuntimeException e = new RuntimeException( 49 this + " sendMessageAtTime() called with no mQueue"); 50 Log.w("Looper", e.getMessage(), e); 51 return false; 52 } 53 return enqueueMessage(queue, msg, uptimeMillis); 54 } 55 //入队,其实调的是MessageQueue的入队方法 56 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { 57 msg.target = this; 58 if (mAsynchronous) { 59 msg.setAsynchronous(true); 60 } 61 return queue.enqueueMessage(msg, uptimeMillis); 62 } 63 //Handler的hasMessage、removeCallback是MessageQueue的方法 64 public final void removeMessages(int what) { 65 mQueue.removeMessages(this, what, null); 66 } 67 public final void removeCallbacksAndMessages(Object token) { 68 mQueue.removeCallbacksAndMessages(this, token); 69 } 70 public final boolean hasMessages(int what) { 71 return mQueue.hasMessages(this, what, null); 72 } 73 public final boolean hasCallbacks(Runnable r) { 74 return mQueue.hasMessages(this, r, null); 75 }

     三,MessageQueue:

    MessageQueue类用于表示消息队列. 队列中的每一个Message都有一个when字段, 这个字段用来决定Message应该何时出对处理. 消息队列中的每一个Message根据when字段的大小由小到大排列, 排在最前面的消息会首先得到处理, 因此可以说消息队列并不是一个严格的先进先出的队列.

     方法主要有:

    Message next() {...}
    void quit(boolean safe) {
            if (!mQuitAllowed) {
                throw new RuntimeException("Main thread not allowed to quit.");
            }
    
            synchronized (this) {
                if (mQuitting) {
                    return;
                }
                mQuitting = true;
    
                if (safe) {
                    removeAllFutureMessagesLocked();
                } else {
                    removeAllMessagesLocked();
                }
    
                // We can assume mPtr != 0 because mQuitting was previously false.
                nativeWake(mPtr);
            }
        }
    //入队
     boolean enqueueMessage(Message msg, long when) {...}
    //判断消息队列中是否包含某个消息
    boolean hasMessages(Handler h, int what, Object object) {...}
    //将某个消息从队列中移出去,该消息被回收到消息池中了
    void removeMessages(Handler h, int what, Object object) {...}

    四:Message:

    Message类用于表示消息. Message对象可以通过arg1, arg2, obj字段和setData()携带数据, 此外还具有很多字段. when字段决定Message应该何时出对处理, target字段用来表示将由哪个Handler对象处理这个消息, next字段表示在消息队列中排在这个Message之后的下一个Message, callback字段如果不为null表示这个Message包装了一个runnable对象, what字段表示code, 即这个消息具体是什么类型的消息. 每个what都在其handler的namespace中, 我们只需要确保将由同一个handler处理的消息的what属性不重复就可以.

     1     public int what;
     2     public int arg1; 
     3     public int arg2;
     4     public Object obj;
     5     public Messenger replyTo;
     6     /** If set message is in use */
     7     static final int FLAG_IN_USE = 1 << 0;
     8     /** If set message is asynchronous */
     9    static final int FLAG_ASYNCHRONOUS = 1 << 1;
    10     /** Flags to clear in the copyFrom method */
    11    static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
    12     int flags;
    13     long when;    
    14     Bundle data;    
    15      Handler target;        
    16      Runnable callback;       
    17     // sometimes we store linked lists of these things
    18     Message next;
    19 
    20     private static final Object sPoolSync = new Object();
    21     private static Message sPool;
    22     private static int sPoolSize = 0;
    23 
    24     private static final int MAX_POOL_SIZE = 50;
     1 public static Message obtain() {
     2         synchronized (sPoolSync) {
     3             if (sPool != null) {
     4                 Message m = sPool;
     5                 sPool = m.next;
     6                 m.next = null;
     7                 sPoolSize--;
     8                 return m;
     9             }
    10         }
    11         return new Message();
    12     }
    13 public static Message obtain(Message orig) {
    14         Message m = obtain();
    15         m.what = orig.what;
    16         m.arg1 = orig.arg1;
    17         m.arg2 = orig.arg2;
    18         m.obj = orig.obj;
    19         m.replyTo = orig.replyTo;
    20         if (orig.data != null) {
    21             m.data = new Bundle(orig.data);
    22         }
    23         m.target = orig.target;
    24         m.callback = orig.callback;
    25 
    26         return m;
    27     }
    28 public static Message obtain(Handler h) {
    29         Message m = obtain();
    30         m.target = h;
    31 
    32         return m;
    33     }
    34 ......
    35 void clearForRecycle() {
    36         flags = 0;
    37         what = 0;
    38         arg1 = 0;
    39         arg2 = 0;
    40         obj = null;
    41         replyTo = null;
    42         when = 0;
    43         target = null;
    44         callback = null;
    45         data = null;
    46     }
    47 public void recycle() {
    48         clearForRecycle();
    49 
    50         synchronized (sPoolSync) {
    51             if (sPoolSize < MAX_POOL_SIZE) {
    52                 next = sPool;
    53                 sPool = this;
    54                 sPoolSize++;
    55             }
    56         }
    57     }
    58 public void copyFrom(Message o)
    59 public long getWhen()
    60 public void setTarget(Handler target)
    61 public Handler getTarget()
    62 public Runnable getCallback()
    63 public Bundle getData() {
    64         if (data == null) {
    65             data = new Bundle();
    66         }
    68         return data;
    69     }
    70 public Bundle peekData() {
    71         return data;
    72     }
    73 public void setData(Bundle data)
    74  public void sendToTarget()

    将消息压入消息队列: Message对象的target字段关联了哪个线程的消息队列, 这个消息就会被压入哪个线程的消息队列中.

    1. 调用Handler类中以send开头的方法可以将Message对象压入消息队列中, 调用Handler类中以post开头的方法可以将一个runnable对象包装在一个Message对象中, 然后再压入消息队列, 此时入队的Message其callback字段不为null, 值就是这个runnable对象. 调用Handler对象的这些方法入队的Message, 其target属性会被赋值为这个handler对象.

    2. 调用Message对象的sendToTarget()方法可以将其本身压入与其target字段(即handler对象)所关联的消息队列中. 

    将未来得及处理的消息从消息队列中删除:

    调用Handler对象中以remove开头的方法就可以.

    从消息队列中取出消息并处理消息: 所有在消息队列中的消息, 都具有target字段. 消息是在target所关联的线程上被取出和处理的.

     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     }

    1. 如果取出的Message对象的callback字段不为null, 那么就调用callback字段的run()方法(callback字段的类型是runnable). 注意此时并不开启一个新的线程运行run()方法, 而是直接在handler对象(即Message的target字段)所关联的线程上运行.

    2. 如果取出的Message对象的callback字段为null, 且Handler对象中的callback字段也为null, 那么这个消息将由Handler对象的handleMessage(msg)方法处理. 注意Message对象的callback字段是Runnable类型的而Handler对象的callback字段是Callback类型的, Handler对象的callback字段是在创建Handler instance的时候指定的, 如果没有指定则这个字段为null, 详见Handler类的四个构造方法.

    3. 如果取出的Message对象的callback字段为null, 且Handler对象中的callback字段不为null, 那么这个消息将由Handler对象中的callback字段的handleMessage方法优先处理.

    线程间通信: 有了以上的叙述, 线程间的通信也就好理解了. 假如一个handler关联了A线程上的消息队列, 那么我们可以在B线程上调用handler的相关方法向A线程上的消息队列压入一个Message, 这个Message将在A线程上得到处理.

  • 相关阅读:
    mysql下载与安装
    Observable Flowable Test
    Linux 技巧:让进程在后台可靠运行的几种方法
    CallBack
    Linux内存分析
    Linux安装svn
    百度地图api
    安装Mysql官方的sakila数据库
    yii2 展示静态页面
    yii2 手动安装第三方扩展
  • 原文地址:https://www.cnblogs.com/wangziqiang/p/3823025.html
Copyright © 2020-2023  润新知