Handler机制(异步消息处理机制)

Android的异步消息处理由四个部分组成:Message、Handler 、MessageQueue和Looper。

先抛出自己的几个问题供自己以后进行回顾复习:

1、为什么需要进行异步消息处理?

大量耗时的操作我们一般是另开一个子线程来进行处理,但是在子线程中是不允许对主线程即UI线程进行操作,否则程序将会崩溃,这个时候我们就可以用Handler来实现在子线程安全的切换到UI线程中实现更新UI的操作。

2、为什么叫异步消息处理?

因为当Hander发动消息和处理消息不在同一个时间,所以叫异步消息处理。

3、Handler的实现细节?

4、Looper如何实现轮询操作?

5、Looper和MessageQueue何时new出来的?

6、Handler、Looper、MessageQueue、Message各自的作用?

7.创建Handler实例的时候为什么要用Handler.obtain(),而不是直接new Handler()?

一、Message:在线程之间传递的消息,存放在MessageQueue中。Message类中有三个携带信息的属性 what、arg1、arg2、obj。obj用来携带一个Object对象,其余三个携带整型数据,Handler通过这些字段来判断其接下来的操作。

二、Handler

Handler类似一个处理者,用来发送或者处理Message。在sendMessage(Message msg)发送Message,经过一系列的方法调用处理后,在handleMessage(Message msg)方法中接受子线程传递过来的Message,然后判断Message并在主线程中执行相关操作。

异步消息处理的代码写法:

第一步,是先new一个Handler,并重写handleMessage()方法,用来处理子线程传来的Message

// 先是new一个主线程的Handler,并重写handleMessage()方法
    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {// 判断携带的数据
                case UPDATE_TEXT:
                    // 在这里进行UI操作
                    text.setText("Nice to meet you");
                    break;
                default:
                    break;
            }
        }
    };

第二步,在子线程中new一个Message,并设置相应的携带数据的字段如what字段,在《第一行代码》中写法是直接new一个Message实例,但是通过查阅网上资料,发现这种写法已经过时

 new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message = new Message();
                        message.what = UPDATE_TEXT;
                        handler.sendMessage(message);
                    }
                }).start();

现在的写法是调用Message.obtain()来返回一个Message实例

new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message = Message.obtain();
                        message.what = UPDATE_TEXT;
                        handler.sendMessage(message);
                    }
                }).start();

那为什么要这样写呢?看obtain()方法的源码:

     /**
     * 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) {// 如果sPool不为空说明有现有的Message实例,不必再浪费开销创建一个新实例
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

在官方给的注释中写道“Return a new Message instance from the global pool. Allows us to avoid allocating new objects in many cases.”意思是在global pool中返回Message实例,避免在很多情况下分配新对象,通俗来讲就是如果global pool中有就直接返回现存的Message实例,节约空间开销。

第三步,就是调用Handler.sendMessage()后,经过中间MessageQueue和Looper的操作最后会调用Handler.handleMessage()方法,实现子线程切换到主线程中并在主线程中进行操作。那MessageQueue和Looper是什么时候创建的呢?在Handler的构造期间。

看Handler的构造器源码:

    public Handler() {
        this(null, false);
    }

继续看调用的父类构造器:

    public Handler(@Nullable 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();// 在这里返回了一个Looper
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;// 通过Looper又引入了一个MessageQueue
        mCallback = callback;
        mAsynchronous = async;
    }

mLooper = Looper.myLooper(); 在这里返回了一个Looper。

mQueue = mLooper.mQueue; 通过Looper又引入了一个MessageQueue

看Looper.myLooper()细节:

    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

这段代码注释的意思是返回了一个与当前线程相关的Looper。这里猜测一个线程只能有一个Looper,继续看sThreadLocal.get()源码:

       /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

上面代码的注释意思是返回当前线程的本地变量的一个副本,若是没有本地变量就返回first initialized的值。至此MessageQueue和Looper就有了,接下来看怎么把消息从Handler.sendMessage()方法辗转到Handler.handleMessage()的 ,理论是将Message添加到MessageQueue中,然后由主线程的Looper不断在一个死循环方法中轮询MessageQueue中的Message,然后再将Message传递给Handler。

接下来看Handler.sendMessage()方法源码:

①在sendMessage()方法中调用sendMessageDelayed(msg, 0)方法。

sendMessageDelayed(msg, 0)方法的定义:

②继续调用sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)方法

sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)定义:

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
690        MessageQueue queue = mQueue;// 消息队列queue
691        if (queue == null) {// 如果对列为空报异常
692            RuntimeException e = new RuntimeException(
693                    this + " sendMessageAtTime() called with no mQueue");
694            Log.w("Looper", e.getMessage(), e);
695            return false;
696        }
697        return enqueueMessage(queue, msg, uptimeMillis);
698    }

③继续调用enqueueMessage(queue, msg, uptimeMillis)方法

enqueueMessage(queue, msg, uptimeMillis)定义:

这段代码中的msg.target = this;this即Hanler,这段代码将Message和Handler捆绑在一起。

④调用queue.enqueueMessage(msg, uptimeMillis)方法

queue.enqueueMessage(msg, uptimeMillis)中将Message添加到消息队列中:

至此,Message已经被添加到MessageQueue中。那么Looper如何轮询MessageQueue的呢(Message是怎么从MessageQueue中被拿出来然后被传递到Handler的handleMessage()方法中去的)?

Looper又是什么时候被new的呢?MessageQueue又是什么时候被new的?

三、Looper

两个问题:

1、Looper如何轮询MessageQueue的呢(Message是怎么从MessageQueue中被拿出来然后被传递到Handler的handleMessage()方法中去的)?

2、Looper和MessageQueue是什么时候被new的呢?

看ActivityThread类的源码,在他的main方法中:

public static void main(String[] args) {
     
 
        //代码1
        Looper.prepareMainLooper();
 
        //代码2
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
 
        //代码3
        Looper.loop();
 
       
    }

在代码1中,看prepareMainLooper()方法:

108    /**
109     * Initialize the current thread as a looper, marking it as an
110     * application's main looper. The main looper for your application
111     * is created by the Android environment, so you should never need
112     * to call this function yourself.  See also: {@link #prepare()}
113     */
114    public static void prepareMainLooper() {
115        prepare(false);// 这段代码
116        synchronized (Looper.class) {
117            if (sMainLooper != null) {
118                throw new IllegalStateException("The main Looper has already been prepared.");
119            }
120            sMainLooper = myLooper();
121        }
122    }

上面这段代码的注释中说初始化当前线程作为一个Looper,并把它标记为app的主要的Looper,并强调这个Looper是由android环境自动创建的,实际上在创建主线程时,会自动的用ActivityThread中的静态main方法,因为会调用main方法中的 Looper.prepareMainLooper() 方法,对Looper进行初始化,但是还是没有看到何时new了,所以继续看Looper.prepareMainLooper()源码的prepare(false);

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

if (sThreadLocal.get() != null) {

throw new RuntimeException("Only one Looper may be created per thread");

}

这句话也证实了我之前的猜测,每一个线程只能有一个Looper。

new Looper(quitAllowed)跟进去:

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

到这里也终于明了,MessageQueue和Looper都是在主线程被创建时就被new了。

问题解决,下一个问题:Looper怎么进行轮询的(Message是怎么从MessageQueue中被拿出来然后被传递到Handler的handleMessage()方法中去的)?

看代码3 :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;

// 在死循环for中,不断取出MessageQueue中的元素,知道取到null停止,实现了轮询的操作
    for (;;) {
    Message msg = queue.next(); // might block
   if (msg == null) {
        // No message indicates that the message queue is quitting.
        return;
    }

// msg.target就是之前说Handler和Message绑定的操作,msg.target就是Hander,在这里又调回到Handler里面的
// dispatchMessage()方法
    msg.target.dispatchMessage(msg);
            
}

先是判断当前线程有没有Looper,没有则抛出"No Looper; Looper.prepare() wasn't called on this thread."的异常,让你调用Looper.prepare()来new一个Looper。

然后在死循环for中,不断取出MessageQueue中的元素,知道取到null停止,实现了轮询的操作。

接着在msg.target.dispatchMessage(msg); 中msg.target就是之前说Handler和Message绑定的操作,msg.target就是Hander,在这里又调回到Handler里面的dispatchMessage()方法:

 /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

上面看到又调用了handleMessage()方法。

至此Message经过Handler发出,由MessageQueue接受,然后由主线程的Looper不断轮询MessageQueue,取出Message,然后回调方法 handleMessage(msg)再交由Handler进行处理的细节全部分析完,即Handler机制的实现。

问题总结:

1、为什么需要进行异步消息处理?

大量耗时的操作我们一般是另开一个子线程来进行处理,但是在子线程中是不允许对主线程即UI线程进行操作,否则程序将会崩溃,这个时候我们就可以用Handler来实现在子线程安全的切换到UI线程中实现更新UI的操作。

2、为什么叫异步消息处理?

因为当Hander发动消息和处理消息不在同一个时间,所以叫异步消息处理。

3、Handler机制的实现细节?

大致的步骤:先在主线程即UI线程创建一个Handler实例,同时需要重写handleMessager()方法,在自己创建的子线程中,通过的Message的obtain()方法得到一个Message实例,设置Message用来携带数据的字段,在子线程中调用Handler的sendMessage()来发送Message,在sendMessage()的实现中,经过一系列的方法调用最终会调用Queue.enqueueMessage(msg, uptimeMillis)方法,在这个方法中将Messag添加到消息队列中,在调用Queue.enqueueMessage(msg, uptimeMillis)之前,在enqueueMessage(queue, msg, uptimeMillis)方法中通过msg.traget = this将Message的target属性和Handler绑定,到这里为止消息已经被安全的添加到消息队列中,之后便是Looper对消息队列的轮询操作,在主线程被创建时会调用主线程ActivityThread中静态main方法中的Looper.prepareMainLooper()方法中的prepare()方法来new出一个Looper的实例(每个线程只能有一个Looper,这个会在prepareMainLooper()方法的prepare()方法中进行Looper是否存在的判断)并顺便new出一个MessageQueue实例,然后继续执行主线程中的Looper.loop()方法,在这个方法通过一个死循环for来不断的取出MessageQueue中的元素,直至MessageQueue的元素为空循环停止,最后会调用loop()方法中msg.target.dispatchMessage(Message msg)方法,在dispatchMessage()中调用Handler.handleMessage()方法。

4、Looper如何实现轮询操作?

在主线程创建是,会调用ActivityThread中main方法中的Looper.loop(),通过一个死循环for来不但取出MessageQueue中的元素,直至为空。然后通过调用msg.target.dispatchMessage()方法中的Handler.handleMessage()方法将Message传递给Handler

5、Looper和MessageQueue何时new出来的?

在主线程ActivityThread被创建时,会自动执行ActivityThread的main方法,main方法中Looper.prepareMainLooper()中的prepare()中对Looper和MessageQueue进行new;

6、Handler、Looper、MessageQueue、Message各自的作用?

Handler接受和发送Message; Looper轮询MessageQueue; MessageQueue存储Message; Message携带数据。

7.创建Message实例的时候为什么要用Message.obtain(),而不是直接new Message()?

节省空间开销。

new Message()的形式会在很多情况下浪费空间开销,Message.obtain()方法通过判断gobal pool中是否存在Message实例,如果有直接返回无需再浪费空间花销来创建Message对象。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值