Android 中的 Handler 同步分割栏

Handler异步实现控制Message的处理的,异步线程可以在Handler中设置异步卡子,设置好了以后,当前Handler的同步Message都不再执行,直到异步线程将卡子去掉。

一、postSyncBarrier 设置同步卡

  • 方法是隐藏的需要反射获取
  • API版本<23方法位于Looper中,API版本>23位于
    MessageQueue中
  • 作用是为Handler设置卡子,过滤掉同步消息,同步消息等待执行

二、removeSyncBarrier 移除同步卡

  • 方法是隐藏的需要反射获取
  • API版本<23方法位于Looper中,API版本>23位于
    MessageQueue中
  • 作用是为Handler移除卡子,放开同步消息过滤,同步消息继续执行。
package qin.xue.imageviewscaletype.activity;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.MessageQueue;
import android.util.Log;

import java.lang.reflect.Method;

import qin.xue.imageviewscaletype.R;

/**
 * Created by qinxue on 2018/5/21.
 */

public class SyncBarrierActivity extends Activity {
    private static final String TAG = "SyncBarrierActivity";

    private Handler handler;
    private Handler workHanlder;
    private HandlerThread workHandlerThread;
    private Method methodPostSyncBarrier;
    private Looper looper;
    private int token;
    private int count = 0;
    private long start = 0;
    private long end = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.sync_barrier_activity_layout);
        workHandlerThread = new HandlerThread("work_thread");
        workHandlerThread.start();
        looper = workHandlerThread.getLooper();
        workHanlder = new Handler(looper);
        handler = new Handler();
        workHanlder.post(runnable);

        handler.post(new Runnable() {
            @Override
            public void run() {
                start = System.currentTimeMillis();
                Log.i(TAG, "设置同步卡 时间:" + start);
                postSyncBarrier();        //设置同步卡
            }
        });

        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                end = System.currentTimeMillis();
                Log.i(TAG, "移除同步卡 时间:" + end + "耗时:" + (end - start));
                removeSyncBarrier();    //5秒后移除同步卡
            }
        }, 5000);
    }

    //postSyncBarrier 和 removeSyncBarrier 方法是否在MessageQueue类中(api23及以上)
    private static boolean methodInQueue() {
        return android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M;
    }


    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            count++;
            Log.i(TAG, "count: " + count + "time: " + System.currentTimeMillis()); //同步打印数据
            workHanlder.postDelayed(runnable, 100);
        }
    };

    /**
     * 移除卡子
     */
    private void removeSyncBarrier() {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            removeSyncBarrier(looper.getQueue());
        } else {
            removeSyncBarrier(looper);
        }
    }

    private void removeSyncBarrier(Object obj) {
        try {
            Method method = MessageQueue.class.getMethod("removeSyncBarrier", int.class);
            method.invoke(obj, token);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 添加卡子
     */
    private void postSyncBarrier() {
        if (methodInQueue()) {
            postSyncBarrier(looper.getQueue());
        } else {
            postSyncBarrier(looper);
        }
    }

    private void postSyncBarrier(Object obj) {
        try {
            methodPostSyncBarrier = obj.getClass().getMethod("postSyncBarrier");
            token = (int) methodPostSyncBarrier.invoke(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

三、结果展示

05-21 17:44:22.926 12715-12732/qin.xue.imageviewscaletype I/SyncBarrierActivity: count: 1time: 1526895862926
05-21 17:44:22.947 12715-12715/qin.xue.imageviewscaletype I/SyncBarrierActivity: 设置同步卡 时间:1526895862947
05-21 17:44:27.931 12715-12715/qin.xue.imageviewscaletype I/SyncBarrierActivity: 移除同步卡 时间:1526895867930耗时:4983
05-21 17:44:27.932 12715-12732/qin.xue.imageviewscaletype I/SyncBarrierActivity: count: 2time: 1526895867932
05-21 17:44:28.033 12715-12732/qin.xue.imageviewscaletype I/SyncBarrierActivity: count: 3time: 1526895868033
05-21 17:44:28.134 12715-12732/qin.xue.imageviewscaletype I/SyncBarrierActivity: count: 4time: 1526895868134
05-21 17:44:28.236 12715-12732/qin.xue.imageviewscaletype I/SyncBarrierActivity: count: 5time: 1526895868235
05-21 17:44:28.336 12715-12732/qin.xue.imageviewscaletype I/SyncBarrierActivity: count: 6time: 1526895868336
05-21 17:44:28.437 12715-12732/qin.xue.imageviewscaletype I/SyncBarrierActivity: count: 7time: 1526895868437
05-21 17:44:28.538 12715-12732/qin.xue.imageviewscaletype I/SyncBarrierActivity: count: 8time: 1526895868538
05-21 17:44:28.639 12715-12732/qin.xue.imageviewscaletype I/SyncBarrierActivity: count: 9time: 1526895868639
05-21 17:44:28.739 12715-12732/qin.xue.imageviewscaletype I/SyncBarrierActivity: count: 10time: 1526895868739
05-21 17:44:28.841 12715-12732/qin.xue.imageviewscaletype I/SyncBarrierActivity: count: 11time: 1526895868841
05-21 17:44:28.941 12715-12732/qin.xue.imageviewscaletype I/SyncBarrierActivity: count: 12time: 1526895868941
05-21 17:44:29.043 12715-12732/qin.xue.imageviewscaletype I/SyncBarrierActivity: count: 13time: 1526895869043
05-21 17:44:29.144 12715-12732/qin.xue.imageviewscaletype I/SyncBarrierActivity: count: 14time: 1526895869144
05-21 17:44:29.244 12715-12732/qin.xue.imageviewscaletype I/SyncBarrierActivity: count: 15time: 1526895869244
05-21 17:44:29.345 12715-12732/qin.xue.imageviewscaletype I/SyncBarrierActivity: count: 16time: 1526895869345

四、代码解释

  • 代码中首先加了使用主线程设置异步线程workHandler中的异步卡子,导致workhandler中的Message不在执行,仅保留待执行。
  • 然后在5s之后在主线程移除了异步线程workHandler中的卡子,workhandler中的Message继续执行下去。

五、源码分析

我这里是6.0的源码
postSyncBarrier
 public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    private int postSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

-这里可以看到这个卡子msg,新建了一个msg并根据时间插入到消息队列中,而且这个msg没有设置target

removeSyncBarrier
    public void removeSyncBarrier(int token) {
        // Remove a sync barrier token from the queue.
        // If the queue is no longer stalled by a barrier then wake it.
        synchronized (this) {
            Message prev = null;
            Message p = mMessages;
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            if (p == null) {
                throw new IllegalStateException("The specified message queue synchronization "
                        + " barrier token has not been posted or has already been removed.");
            }
            final boolean needWake;
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            } else {
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            p.recycleUnchecked();

            // If the loop is quitting then it is already awake.
            // We can assume mPtr != 0 when mQuitting is false.
            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }

-(p.target != null || p.arg1 != token)这里如果arg1 == token或者target为null,那么这是一个卡子。需要移除掉 ,代码如下。 prev.next = p.next;

next()
  • 在MessageQueue中还有这个方法,获取消息队列中的下一个Message
      if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }

摘取部分代码,循环便利整个消息队列,如果target为null,那么直接利用isAsynchronous()找到下一个异步的message,跳过同步消息,设置执行时间。
大致就是这样。

Handler中还有阻塞机制

-如果发先下一条消息执行时间过长,会阻塞掉线程Looper循环的。在C++层,下次再研究下。

转载自原文

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值