android学习--消息机制Handler Loop MesssageQueue

      学习消息机制之前,我们先来了解一下消息机制,为什么要使用消息机制?采用消息机制有以下好处:

  第一,android 子线程不能刷新界面,因为很多耗时操作,你必须子线程来做否则会严重影响用户体验。所以必须要用handler 来给主线程发消息,简单的说消息机制就是简化了线程之间的通信。

  第二,减少功能的重复,因为很多功能我们可以调用系统来做,消息的好处是让系统多承担一些工作。

  第三,采用消息机制使用具体异构性,使用activity之间可以传递更多的消息。

   总之,采用消息机制的好处还是很多的。下面我们再看一下下面几个问题:

  1、消息的组成:消息有什么格式,是否可以自定义消息来发送通知和传送数据?

  2、谁将收到消息?

  3、谁能发送消息?

  4、未处理的消息到那里去了?

  我们先看一下具体的类再分析上面的问题的  

Message:消息,理解为线程间通讯的数据单元,消息的构成类。

Message Queue:消息队列,用来存放通过Handler发布的消息,按照先进先出执行。

Handler:Handler是Message的主要处理者,负责将Message添加到消息队列以及对消息队列中的Message进行处理。

Looper:循环器,扮演Message Queue和Handler之间桥梁的角色,循环取出Message Queue里面的Message,并交付给相应的Handler进行处理。

消息机制采用的是谁发送,谁处理的原则。Handler是消息的发送者,也是消息的处理者。我们可以重写Handler的dispatchMessage方法来进行消息的过滤,

重写handleMessage方法来进行消息的处理。我们可以用sendMessage来发送消息,我们先来看一下消息Message类:

public final class Message implements Parcelable 实现的Parcelable接口,证明它是可以跨进程传输的。Message类里面有很多变量,what主要用记标识msg的类型,

  obj是指msg传输的对象。这里面还有一个重要的变量:target它代表消息的发送者,也就是Handler对象。

为了更好的理解消息去哪里了,我们来看一下sendMessage方法:

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);
    }
uptimeMillis代表消息发送是否延迟,第一获取当前的消息队列,MessageQueue,然后调用 enqueueMessage方法把消息发送出去,再看一下:

   private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
msg.target = this;标识发送送本身,然后调用MessageQueue方法enqueueMessage进行发送:我们再看消息队列的发送方法。

final boolean enqueueMessage(Message msg, long when) {
        if (msg.when != 0) {
            throw new AndroidRuntimeException(msg
                    + " This message is already in use.");
        }
        if (msg.target == null && !mQuitAllowed) {
            throw new RuntimeException("Main thread not allowed to quit");
        }
        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;
                this.notify();
            } else {
                Message prev = null;
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                this.notify();
            }
        }
        return true;
    }


 
如果消息队列里面没有任何消息,我们先创建头: 

 if (p == null || when == 0 || when < p.when) {                // New head, wake up the event queue if blocked.                msg.next = p;                mMessages = msg;                 needWake = mBlocked;  } 

如果已经有其他消息,我们就添加到尾部:

 msg.next = p; // invariant: p == prev.next prev.next = msg;这里最简单的链表操作,next指向下一条消息。到这里消息发送的整个过程已经讲完,是不是很简单。我们再回去到sendMessageAtTime方法里面看一下: MessageQueue queue = mQueue这里面的成员变量哪里来的?

 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;
    }
从构造函数里面我们可以看到mQueue是由Looper提供的,我们再看一下Looper里面的消息队列,

每个线程都可以并仅可以拥有一个Looper实例,消息队列MessageQueue在Looper的构造函数中被创建并且作为成员变量被保存,也就是说MessageQueue相对于线程也是唯一的,Looper对象有一个ThreadLocal<Looper> 变里来保证每个线程都只有一个looper对象。

 到此我们已经明白了当初提的四个问题:

1.消息组成就是Message。

2.消息由Handler发送,

3.消息也是由Handler处理。

4.未处理的消息会被添加到MessageQueue里面。

但我们还是有几个问题没有明白:

1.Looper创建了消息队列,那谁创建了Looper?

2.Looper里面添加了消息,这些消息什么时候被处理?

我们先来搞清楚第一个问题,谁创建了Looper ? Looper是一个进程只有一个,我们要更新界面必须在主线程里面更新,那会不会Looper是在主线程里面创建的?我们看一下主线程的创建,直接看activityThread里面的main方法:

Looper.prepareMainLooper();
 ·······
 ·······
 Looper.loop();
看一下:prepareMainLooper方法做了什么?

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
prepare方法主要创建Looper对象,同时初始化消息队列,接着再看一下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;

        // 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();

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // 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);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // 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();
        }
    }
无限for循环里面取出MessageQueue里面的消息,也就是启动一个 循环不断的遍历消息队列,有消息添加进来就会调用Handler去处理。可能你会觉的很消耗资源,毕竟在主线程里面有这样一个无限循环,我们再来看一下消息队列的next方法:
final Message next() {
        boolean tryIdle = true;

        while (true) {
            long now;
            Object[] idlers = null;
    
            // Try to retrieve the next message, returning if found.
            synchronized (this) {
                now = SystemClock.uptimeMillis();
                Message msg = pullNextLocked(now);
                if (msg != null) return msg;
                if (tryIdle && mIdleHandlers.size() > 0) {
                    idlers = mIdleHandlers.toArray();
                }
            }
    
            // There was no message so we are going to wait...  but first,
            // if there are any idle handlers let them know.
            boolean didIdle = false;
            if (idlers != null) {
                for (Object idler : idlers) {
                    boolean keep = false;
                    try {
                        didIdle = true;
                        keep = ((IdleHandler)idler).queueIdle();
                    } catch (Throwable t) {
                        Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                    }

                    if (!keep) {
                        synchronized (this) {
                            mIdleHandlers.remove(idler);
                        }
                    }
                }
            }
            
            // While calling an idle handler, a new message could have been
            // delivered...  so go back and look again for a pending message.
            if (didIdle) {
                tryIdle = false;
                continue;
            }

            synchronized (this) {
                // No messages, nobody to tell about it...  time to wait!
                try {
                    if (mMessages != null) {
                        if (mMessages.when-now > 0) {
                            Binder.flushPendingCommands();
                            this.wait(mMessages.when-now);
                        }
                    } else {
                        Binder.flushPendingCommands();
                        this.wait();
                    }
                }
                catch (InterruptedException e) {
                }
            }
        }
    }
这里有一个wait方法,我们可以看到消息队列里面如果有消息就会取出消息执行,如果没有就会进入阻塞状态,那么什么时候会唤醒呢?应该是在添加消息的时候会唤醒,我再回过头来看一下添加消息方法;里面有一个notify方法。

     至此,我们已经回答上面的两个问题:第一,Looper是在主线程中创建的,也就是ActivityThread里同创建,并且一创建就进入消息循环。如果消息队列里面没有消息就进入阻塞状态。

  第二,消息添加到消息队列里面马上会唤醒主线程来对消息进行处理。

我们先来总结一下消息机制:

   

 第一,android 在进程启动的时候,创建了Looper 和MessageQueue,同时looper进入循环检测消息队列有没有消息,

 第二。创建handler 用于发消息Message,也生写handleMessage对消息进行处理。

  也许到这里大家可能都没有问题了,但我还是有一个问题:多个handler可以共用一个MessageQueue吗?

我们一起来解答这个问题,答案是:肯定可以的,那个MessageQueue处理消息的时候如何查找相应的handler呢?

还是在源码中寻找答案,我们都知道android消息机制有一个最简单原则就是消息谁发送,谁处理?看一下封装Message的时候,我们可以没有注意到每个Message里面都有一个target对象,在发送的时候会把handler赋值给它,而在消息处理的时候,我们是这样的进行回调:msg.target.dispatchMessage(msg);用消息里面刚传进来的target对象进行回调,也就是当初的发送者,也就是后来的执行者。

因此:我们要记住二点:主进程里面只能有一个Looper和MessageQueue。

                                        可以有多个Handler。

在实际应用我们也可以自己创建 属于自己的Looper和MessageQueue, Handle。

 首先,我们创建一个线程然后在run检测MessageQueue

public class LooperThread  extends Thread{

    private Looper mMainLooper;
    
    public LooperThread() {
        
    }
    
    @Override
    public void run() {
       super.run();
       Looper.prepare();
       mMainLooper = Looper.myLooper();
       Looper.loop();
    }
    
    public Looper getLooper() {
        return mMainLooper;
    }
}
然后,我们实现消息处理类的Handler:  

 class MyHandlerHandler extends android.os.Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //消息处理。
        }
    }
使用创建handler 对象的时候我们指定使用消息队列。

Handler handler = new Handler(mLooperThread.getLooper());

再后,我们就可以用handler发送消息了。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值