子线程中:new Handler需要做哪些准备?消息队列中无消息的时候,Looper的处理方案是什么?

为什么主线程可以new Handler?

在ActivityThread.java里有一个main()函数,它是Android每一个应用最早执行的函数。

    public static void main(String[] args) {

        .....

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

它先prepare的,看这个prepare干了些什么事情?

    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。

然后又接着调用的Looper.loop()函数,这个loop()函数其实就是执行了那个for循环,它不断的调用next()函数,通过调用next()函数去轮询我们的MessageQueue。

如果不调用prepare(),Looper没有被初始化;如果不调用loop(),Looper的机制滚动不起来。所以,所有的执行必须先prepare(),然后再loop()。

回到问题,为什么主线程不需要做这些操作呢?

因为主线程一启动的时候,在main()函数中,由系统已经帮我们完成了,我们主线程中的所有代码,全都运行在这两个函数(prepare() 和 loop())之间。

 

在子线程中new Handler需要做哪些准备?

在上一题 为什么主线程可以new Handler?中其实已经解答,所有的线程都必须要prepare()和loop(),如果子线程中想要进行Handler操作,就必须在子线程中执行prepare() 和 loop()。

 

子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?

我们先来创建一个子线程,然后子线程中创建了一个Looper,并且发送了一个消息,消息处理完了,看一下会发生什么样的事情。

    Handler threadHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread(new Runnable() {
            @Override
            public void run() {
                if (threadHandler == null){
                    Looper.prepare();
                    threadHandler = new Handler(){
                        @Override
                        public void handleMessage(Message msg) {
                            super.handleMessage(msg);
                            Log.e("seas","handle Message");
                        }
                    };
                    Log.e("seas","Looper loop() UP");
                    Looper.loop();
                    Log.e("seas","Looper loop() DOWN");
                }
            }
        }).start();

    }

    public void click(View view){
        Log.e("seas","click");
        threadHandler.sendMessage(threadHandler.obtainMessage());

    }

我运行之后,再点击执行click方法,就出现了如下的log。

如果这个子线程结束,那么他应该是有调用到 Log.e("seas","Looper loop() DOWN"); 这个语句才对,但是它并没有,这就意味着这个Looper一直处于一个阻塞状态。

也就是说它一直卡在了这个地方:

        for (;;) {
            Message msg = queue.next(); // might block

            .....
        }

所以它就一直在等待,这个子线程也已经干不了其他的事情了,其实也被卡死了。

子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?

在Handler机制里面有一个Looper,在Looper机制里面有一个函数,叫做quitSafely()和quit()函数,这两个函数是调用的MessageQueue的quit()。

    /**
     * Quits the looper.
     * <p>
     * Causes the {@link #loop} method to terminate without processing any
     * more messages in the message queue.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p class="note">
     * Using this method may be unsafe because some messages may not be delivered
     * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
     * that all pending work is completed in an orderly manner.
     * </p>
     *
     * @see #quitSafely
     */
    public void quit() {
        mQueue.quit(false);
    }

    /**
     * Quits the looper safely.
     * <p>
     * Causes the {@link #loop} method to terminate as soon as all remaining messages
     * in the message queue that are already due to be delivered have been handled.
     * However pending delayed messages with due times in the future will not be
     * delivered before the loop terminates.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p>
     */
    public void quitSafely() {
        mQueue.quit(true);
    }

再进入到MessageQueue的quit()函数。

    void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("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);
        }
    }

它会remove消息,把消息队列中的全部消息给干掉。

把消息全部干掉,也就释放了内存

    private void removeAllFutureMessagesLocked() {
        final long now = SystemClock.uptimeMillis();
        Message p = mMessages;
        if (p != null) {
            if (p.when > now) {
                removeAllMessagesLocked();
            } else {
                Message n;
                for (;;) {
                    n = p.next;
                    if (n == null) {
                        return;
                    }
                    if (n.when > now) {
                        break;
                    }
                    p = n;
                }
                p.next = null;
                do {
                    p = n;
                    n = p.next;
                    p.recycleUnchecked();
                } while (n != null);
            }
        }
    }

而在quit()函数的最后一行,有一个nativeWake()函数。

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);

这个函数的调用,就会叫醒等待的地方,醒来之后,就接着往下执行。

            //native的方法,在没有消息的时候回阻塞管道读取端,只有nativePollOnce返回之后才能往下执行
            //阻塞操作,等待nextPollTimeoutMillis时长
            nativePollOnce(ptr, nextPollTimeoutMillis);

往下执行后,发现 Message msg = mMessages; 是空的,然后就执行了这个,就接着往下走。

                if (msg != null) {

                   ......

                } else {
                    // No more messages.
                	//没有消息,nextPollTimeoutMillis复位
                    nextPollTimeoutMillis = -1;
                }

然后又调用了这个方法,并且return了null。

                // Process the quit message now that all pending messages have been handled.
                //如果消息队列正在处于退出状态返回null,调用dispose();释放该消息队列
                if (mQuitting) {
                    dispose();
                    return null;
                }

所以说,这个时候Looper就结束了(跳出了死循环),则达成了第二个作用:释放线程

 

如果我在刚才的例子上加入退出操作。

    public void stopclick(View view){
        Log.e("seas","click");
        threadHandler.getLooper().quit();

    }

打印如下:

这个线程就真正的执行了,否则这个线程就一直在等待。

如果在子线程中创建了一个Handler,那么就必须做三个操作:

1. prepare();

2. loop();

3. quit();

 

主线程中能否调用quit()方法?

是不能的。它会抛出一个异常,让程序挂掉。

    void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }
        ......
    }

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值