WCT系列(二):SyncTransactionQueue类详解

news2024/9/22 10:05:26

SyncTransactionQueue类:

接上一回的WindowContainerTransaction类讲解,上一篇博客根据TaskView.java中的updateTaskVisibility()方法分析了WindowContainerTransaction的功能及使用。本次继续上一篇的思路,主要拆解syncTransactionQueue类。

private void updateTaskVisibility() {
    WindowContainerTransaction wct = new WindowContainerTransaction();//(1)
wct.setHidden(mTaskToken, !mSurfaceCreated /* hidden */);//(2
    mSyncQueue.queue(wct);//(3)
   if (mListener == null) {//(4)
        return;
    }
    int taskId = mTaskInfo.taskId;//(5)
    mSyncQueue.runInSync((t) -> {
        mListenerExecutor.execute(() -> {
            mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated);
        });
    });//(6)
}

1、 WindowContainerTransaction的构建:

首先回顾下WindowContainerTransaction的使用,当应用侧需要修改WindowContainer的时候,需要发送WindowContainerTransaction消息给系统侧,然后在系统侧完成对WindowContainer的修改。 构建WindowContainerTransaction对象并设置其参数后,即上述代码执行完(1)和(2),就需要需要发送此消息了,就是需要执行(3)

2、 SyncTransactionQueue类:

在设置完成WindowContainerTransaction对象wct之后,将wct放入SyncTransactionQueue的对象mSyncQueue之中。从这里可以看出,SyncTransactionQueue中至少有一个Queue用来存储WindowContainerTransaction类的对象wct。根据推测,进入SyncTransactionQueue.java中查看该类的定义:

public final class SyncTransactionQueue {
    private static final boolean DEBUG = false;
    private static final String TAG = "SyncTransactionQueue";

    // Just a little longer than the sync-engine timeout of 5s
    private static final int REPLY_TIMEOUT = 5300;//(7)

    private final TransactionPool mTransactionPool;
    private final ShellExecutor mMainExecutor;

    // Sync Transactions currently don't support nesting or interleaving properly, so
    // queue up transactions to run them serially.
    private final ArrayList<SyncCallback> mQueue = new ArrayList<>();//(8)

    private SyncCallback mInFlight = null;//(9)
    private final ArrayList<TransactionRunnable> mRunnables = new ArrayList<>();//(10)

    private final Runnable mOnReplyTimeout = () -> {
        synchronized (mQueue) {
            if (mInFlight != null && mQueue.contains(mInFlight)) {
                Slog.w(TAG, "Sync Transaction timed-out: " + mInFlight.mWCT);
                mInFlight.onTransactionReady(mInFlight.mId, new SurfaceControl.Transaction());
            }
        }
    };//(11)

    public SyncTransactionQueue(TransactionPool pool, ShellExecutor mainExecutor) {//(12)
        mTransactionPool = pool;
        mMainExecutor = mainExecutor;
    }
           ……………………………………………………………
}

根据上面的代码发现没有可以存储WindowContainerTransaction类对象的容器,那是为什么呢?于是直接去查看SyncTransactionQueue类中的queue()方法的定义。

public void queue(WindowContainerTransaction wct) {
    if (wct.isEmpty()) {
        if (DEBUG) Slog.d(TAG, "Skip queue due to transaction change is empty");
        return;
    }
    SyncCallback cb = new SyncCallback(wct);//(13)
    synchronized (mQueue) {
        if (DEBUG) Slog.d(TAG, "Queueing up " + wct);
        mQueue.add(cb);//(14)
        if (mQueue.size() == 1) {
            cb.send();//(15)
        }
    }
}

在(13)这里可见,其实是将WindowContainerTransaction类(后续记作WCT类吧)的对象wct包装进了SyncCallback类的对象cb中。然后简单看一下SyncCallback吧:

private class SyncCallback extends WindowContainerTransactionCallback {
    int mId = -1;
    final WindowContainerTransaction mWCT;
    final LegacyTransitions.LegacyTransition mLegacyTransition;

    SyncCallback(WindowContainerTransaction wct) {
        mWCT = wct;
        mLegacyTransition = null;
    }
    ……………………………………………………………
}

这里就能看到SyncCallback类中有一个成员对象mWCT和另一个mLegacyTransition,同时也能通过wct一个单参数构造SyncCallback类对象。
所以在前面代码(3)处,将wct放入mSyncQueue中时,就是先将wct包装成cb,然后再将cb放入SyncTransactionQueue的mQueue中(如代码中(14))。
然后继续向下看,如果mQueue的大小为1,则调用cb.send(),这里send应该就是把包含wct消息的cb从应用侧发送到系统侧。暂时先不看其实现方式,继续看该类中调用mQueue的地方,其中还看到一个三参数的queue方法,其内容和本函数差不多,就是在构建cb的时候多加了几个参数,所以就省略了。 但是还有一个queueIfWaiting()方法,对这个方法进行解析:

public boolean queueIfWaiting(WindowContainerTransaction wct) {
    if (wct.isEmpty()) {
        if (DEBUG) Slog.d(TAG, "Skip queueIfWaiting due to transaction change is empty");
        return false;
    }
    synchronized (mQueue) {
        if (mQueue.isEmpty()) {
            if (DEBUG) Slog.d(TAG, "Nothing in queue, so skip queueing up " + wct);
            return false;
        }
        if (DEBUG) Slog.d(TAG, "Queue is non-empty, so queueing up " + wct);
        SyncCallback cb = new SyncCallback(wct);
        mQueue.add(cb);
        if (mQueue.size() == 1) {
            cb.send();
        }
    }
    return true;
}

对比而言,queueIfWaitting和queue方法的差异点就在如果mQueue为空,则直接返回,不进行排队,不过这里我暂时还不了解为什么会这么设计,不过也不影响代码的分析。这样可见,无论是queue还是queueIfWaiting方法,最终都会执行send()方法,通过名字也可以猜出来send方法是干啥的。

3、 SyncCallback.send():

看到send()方法的具体实现,这个方法中应该就会有跨进程的传输了。

void send() {
    if (mInFlight == this) {
        // This was probably queued up and sent during a sync runnable of the last callback.
        // Don't queue it again.
        return;
    }
    if (mInFlight != null) {
        throw new IllegalStateException("Sync Transactions must be serialized. In Flight: "
                + mInFlight.mId + " - " + mInFlight.mWCT);
    }
    mInFlight = this;//(16)
    if (DEBUG) Slog.d(TAG, "Sending sync transaction: " + mWCT);
    if (mLegacyTransition != null) {
        mId = new WindowOrganizer().startLegacyTransition(mLegacyTransition.getType(),
                mLegacyTransition.getAdapter(), this, mWCT);
    } else {
        mId = new WindowOrganizer().applySyncTransaction(mWCT, this);//(15)
    }
    if (DEBUG) Slog.d(TAG, " Sent sync transaction. Got id=" + mId);
    mMainExecutor.executeDelayed(mOnReplyTimeout, REPLY_TIMEOUT);//(16)
}

根据前面(13)处构建cb对象的时候可见,mLegacyTransition其实就是null,所以send()方法中很容易就判断出会进入(15)这里,于是这就到了通信的重点了,这里会通过WindowOrganizer类的applySyncTransaction方法,处理(3)处传过来的wct。
接下来代码(16)处还会开启一个超时处理线程,超时机制是5.3s,超时处理函数是mOnReplyTimeout。
这个地方的实现其实就是在前面的(11)处,通过一个lambda表达式构建的Runnable类的子类的对象,其run方法就是 -> 后的一大堆。简单的理解就是如果队列mQueue中包含mInFlight,就会调用这个mInFlight的onTransactionReady方法。而mInFlight就是一个SyncCallback类的对象,其赋值是在send方法中的(16)处。 所以这里就先推测,这个onTransactionReady方法其实就是在我们应用侧传输给系统的wct在应用后,应用侧会执行的一个逻辑,超时的时候也会执行一次,但是参数肯定和成功应用wct后执行的时候的参数不同罢了。

4、 SyncCallback.onTransactionReady():

从上面的send()方法中,大致猜到onTransactionReady方法的使用场景,但是这个函数具体是怎么实现的呢?

public void onTransactionReady(int id,
        @NonNull SurfaceControl.Transaction t) {
    mMainExecutor.execute(() -> {
        synchronized (mQueue) {
            if (mId != id) { //(17)
                Slog.e(TAG, "Got an unexpected onTransactionReady. Expected "
                        + mId + " but got " + id);
                return;
            }
            mInFlight = null;
            mMainExecutor.removeCallbacks(mOnReplyTimeout);//(18)
            if (DEBUG) Slog.d(TAG, "onTransactionReady id=" + mId);
            mQueue.remove(this);//(19)
            onTransactionReceived(t);//(20)
            if (mLegacyTransition != null) {
                try {
                    mLegacyTransition.getSyncCallback().onTransactionReady(mId, t);//(21)
                } catch (RemoteException e) {
                    Slog.e(TAG, "Error sending callback to legacy transition: " + mId, e);
                }
            } else {
                t.apply();
                t.close();//(22)
            }
            if (!mQueue.isEmpty()) {
                mQueue.get(0).send();//(23)
            }
        }
    });
}

首先(17)这里会先判断SyncCallback对象的id是否一致,即this.mId和mInFlight.mId是否一致。这个this指的就是(13)处构建的cb,只有在mId相同的时候才能进行下一步。
接下来就是清除mQueue中的SyncCallback对象cb了,首先需要对mInFlight进行置空,然后清除这个超时回调(如果是正常流程,这里应该是wct应用成功后触发的,此时超时机制还没触发,如果不移除,一会时间到了这个回调也没意义了)。
接下来就是(21)这块,主要是mLegacyTransition的区别,前面的流程都是知道的,这里就不会执行(21),直接走到else语句的(22)这里。在系统侧执行修改后,会返回SurfaceControl.Transaction的对象t回来,如果是超时机制触发的,那就是返回的默认构造的t。 这里看到t.apply()和t.close(),大概可以理解,其实也是应用什么修改了,不过暂时还没详细了解这个SurfaceControl.Transaction类,所以暂时不讨论,后面讲到这里的时候再分析吧。 目前根据这个流程的推测就是,应用侧将需要修改的内容wct发送给系统侧后,系统侧在他的维护数据库中更新了信息后,然后通过回调onTransactionReady方法,将修改的指令下达给应用侧,然后应用侧再执行修改。 这就像你想改名字,得先向派出所提出申请,然后派出所在户籍管理系统中,修改你的名字,然后再通知你,你名字修改成功了,你以后可以用新名字了,这时候你才能用新的名字。这个通知你的过程就可以理解为onTransactionReady方法,也就是说直到onTransactionReady方法被调用才能说明我们的修改真正的被应用了。这个流程可以参考下文末的配图。

5、 WindowOrganizer.applySyncTransaction:

在执行send方法的时候,调用了WindowOrganizer的applyTransaction方法,这里先大概讲一下,下一节再详细分析WindowOrganizer类和WindowOrganizerController类的详细内容。applySyncTransaction方法的详细实现如下:

public int applySyncTransaction(@NonNull WindowContainerTransaction t,
        @NonNull WindowContainerTransactionCallback callback) {
    try {
        return getWindowOrganizerController().applySyncTransaction(t, callback.mInterface);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

从代码中可见,这里其实还是调用了getWindowOrganizerController()的applyTransaction方法,于是再去看下getWindowOrganizerController()是干什么的:

static IWindowOrganizerController getWindowOrganizerController() {
    return IWindowOrganizerControllerSingleton.get();
}
private static final Singleton<IWindowOrganizerController> IWindowOrganizerControllerSingleton =
        new Singleton<IWindowOrganizerController>() {
            @Override
            protected IWindowOrganizerController create() {
                try {
                    return ActivityTaskManager.getService().getWindowOrganizerController();
                } catch (RemoteException e) {
                    return null;
                }
            }
        };

其实就是通过Aidl去获取atms中的WindowOrganizerController的远程代理,就能实现从应用侧访问系统侧的方法了,所以这里就是跨进程传输的具体位置了。通过这里去调用系统侧的applySyncTransaction方法。

而在系统侧,即WindowContainerController中,也有一个onTransactionReady方法,其实现如下,最终是通过callback.onTransactionReady()方法实现的此功能,而其中callback就是一个IWindowContainerTransactionCallback,一看就是一个Aidl接口,其实现在WindowContainerTransactionCallback类中,而SyncCallback就是继承自WindowContainerTransactionCallback类,所以系统侧最后调用的就是SyncCallback中的onTransactionReady方法。

public void onTransactionReady(int syncId, SurfaceControl.Transaction t) {
    ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Transaction ready, syncId=%d", syncId);
    final IWindowContainerTransactionCallback callback =
            mTransactionCallbacksByPendingSyncId.get(syncId);

    try {
        callback.onTransactionReady(syncId, t);
    } catch (RemoteException e) {
        // If there's an exception when trying to send the mergedTransaction to the client, we
        // should immediately apply it here so the transactions aren't lost.
        t.apply();
    }

    mTransactionCallbacksByPendingSyncId.remove(syncId);
}

6、 最后总结下应用侧需要修改WindowContainer的这个流程:

1)、根据需要修改的地方构建WindowContainerTransaction类对象wct;
2)、通过wct构建SyncCallback类对象cb,然后将cb放入SyncTransactionQueue类的对象mQueue中;
3)、调用cb的send()方法,通过WindowOrganizer的applySyncTransaction方法,将wct传送到系统侧;
4)、系统侧接收到wct后,在其维护的窗口结构中应用wct封装的修改,修改应用完成后,调用onTransactionReady回调到应用侧,并将修改的方式SurfaceControl.Transaction的对象t告知应用侧,应用侧再对其进行应用。直到这里,才完成了整个流程。
下面也画了一个图进行演示:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2051133.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JUC】06-可重入锁

可重入锁&#xff1a;又称递归锁。在外层使用锁后&#xff0c;内层仍然可以使用&#xff0c;并不发生死锁&#xff0c;这样的锁就叫可重入锁。synchronized默认是一个可重入锁。 public class Demo01 {public synchronized void m1() {System.out.println(Thread.currentThrea…

软件函数过期-软件开发故障处理-开发语言升级-全栈软件架构师-软件修仙界掌握几十门开发语言

一、软件界通用关键字 obsolete&#xff0c;deprecated&#xff0c;deprecation 二、多语言全栈&#xff0c;所有语言混合开发是什么&#xff1f;十几门开发语言 组合1、php/java/aspJSCandroid 平台物联网设备&#xff0c;智能音箱 组合2&#xff1a;C#PHPPYTHON 组合3&am…

云计算的三大服务模式:IaaS、PaaS、SaaS的深入解析

在数字化转型的浪潮中&#xff0c;云计算以其独特的灵活性、可扩展性和成本效益&#xff0c;正逐渐成为企业IT架构的核心。云计算提供了三种主要的服务模式&#xff0c;分别是基础设施即服务&#xff08;IaaS&#xff09;、平台即服务&#xff08;PaaS&#xff09;和软件即服务…

【算法/学习】双指针

✨ 少年要迎着朝阳&#xff0c;活得肆无忌惮 &#x1f30f; &#x1f4c3;个人主页&#xff1a;island1314 &#x1f525;个人专栏&#xff1a;算法学习 &#x1f680; 欢迎关注&#xff1a;&#x1f44d;点赞 &a…

挑战1G内存!如何在千万记录中找到最热TOP10查询串?

我是小米,一个喜欢分享技术的29岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货! 哈喽大家好!我是你们的技术小伙伴小米,今天又来和大家分享一个非常实用的算法题!假设我们现在有1000w个查询记录,这些记录中有很多重复的内容,但去…

内存碎片问题—容器启动状态卡在ContainerCreating

线上发现部分容器处于ContainerCreating状态: 查看kubelet日志&#xff1a; [rootdc07-prod-k8s-node /root] journalctl -u kubelet Jul 01 00:45:30 prod-k8s-node kubelet[12227]: I0701 00:45:30.491326 12227 kubelet.go:1908] SyncLoop (ADD, "api"): &quo…

RK3568笔记五十五:yolov10训练部署测试

若该文为原创文章,转载请注明原文出处。 yolov8还没熟悉,yolov10就出来了,本篇记录使用yolov10训练自己的数据,并部署到rk3568上。 参考大佬的博客yolov10 瑞芯微RKNN、地平线Horizon芯片部署、TensorRT部署,部署工程难度小、模型推理速度快_yolov10 rknn-CSDN博客 一、…

【网络编程】基于UDP的TFTP文件传输

1&#xff09;tftp协议概述 简单文件传输协议&#xff0c;适用于在网络上进行文件传输的一套标准协议&#xff0c;使用UDP传输 特点&#xff1a; 是应用层协议 基于UDP协议实现 数据传输模式 octet&#xff1a;二进制模式&#xff08;常用&#xff09; mail&#xff1a;已经不再…

深度学习入门:卷积神经网络 | CNN概述,图像基础知识,卷积层,池化层(还在等什么!!!超详解!!!)

目录 &#x1f354; 前言 &#x1f354; 图像基础知识 1. 像素和通道的理解 2. 小节 &#x1f354; 卷积层 1. 卷积计算 2. Padding 3. Stride 4. 多通道卷积计算 5. 多卷积核卷积计算 6. 特征图大小 7. PyTorch 卷积层 API 7. 小节 &#x1f354; 池化层 1. 池…

WEB之文件上传

一&#xff1a;思维导图 二&#xff1a;相关问题解答 1&#xff0c;什么是文件上传漏洞? 文件上传漏洞是一种常见的网络安全问题&#xff0c;它发生在网络应用程序允许用户上传文件到服务器的功能中。如果这一功能没有得到适当的安全控制和验证&#xff0c;攻击者就可以利用…

web开发,过滤器,前后端交互

目录 web开发概述 web开发环境搭建 Servlet概述 Servlet的作用&#xff1a; Servlet创建和使用 Servlet生命周期 http请求 过滤器 过滤器的使用场景&#xff1a; 通过Filter接口来实现&#xff1a; 前后端项目之间的交互&#xff1a; 1、同步请求 2、异步请求 优化…

利用telnet发送QQ邮箱的电子邮件时遇到的问题(2024最新)

问题1&#xff1a;即使在控制面板启用telnet客户端也无法使用telnet 解决&#xff1a;使用管理员权限打开cmd&#xff0c;执行命令&#xff1a;dism /online /Enable-Feature /FeatureName:TelnetClient&#xff0c;之后根据弹出信息键入Y重启即可 参考链接&#xff1a;https:…

开源新宠:RAG2SQL工具,超越Text2SQL的7K Star之作

查询数据库离不开SQL&#xff0c;那如何快速构建符合自己期望的SQL呢&#xff1f;AI发展带来了Text2SQL的能力&#xff0c;众多产品纷纷提供了很好的支持。 今天我们分享一个开源项目&#xff0c;它在Text2SQL的基础上还要继续提高&#xff0c;通过加入RAG的能力进一步增强&am…

虹软科技25届校招笔试算法 A卷

目录 1. 第一题2. 第二题3. 论述题 ⏰ 时间&#xff1a;2024/08/18 &#x1f504; 输入输出&#xff1a;ACM格式 ⏳ 时长&#xff1a;2h 本试卷分为不定项选择&#xff0c;编程题&#xff0c;必做论述题和选做论述题&#xff0c;这里只展示编程题和必做论述题&#xff0c;一共三…

代码随想录算法训练营_day17

题目信息 654. 最大二叉树 题目链接: https://leetcode.cn/problems/maximum-binary-tree/题目描述: 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值。递归地在最大值 左边 的 子数组前…

AVI-Talking——能通过语音生成很自然的 3D 说话面孔

概述 论文地址&#xff1a;https://arxiv.org/pdf/2402.16124v1.pdf 逼真的人脸三维动画在娱乐业中至关重要&#xff0c;包括数字人物动画、电影视觉配音和虚拟化身的创建。以往的研究曾试图建立动态头部姿势与音频节奏之间的关联模型&#xff0c;或使用情感标签或视频剪辑作…

【数据结构与算法】如何构建最小堆

最小堆的定义 最小堆&#xff0c;作为一种独特且重要的数据结构&#xff0c;它是一种特殊的二叉树。在这种二叉树中&#xff0c;有一个关键的规则&#xff1a;每一个父节点所存储的值&#xff0c;都必然小于或者等于其对应的子节点的值。这一规则确保了根节点总是承载着整个堆…

机器学习(3)-- 一元线性回归

文章目录 线性回归训练模型测试模型线性回归方程测试实用性 总结 线性回归 线性回归算法是一种用于预测一个或多个自变量&#xff08;解释变量&#xff09;与因变量&#xff08;响应变量&#xff09;之间关系的统计方法。这种方法基于线性假设&#xff0c;即因变量是自变量的线…

【学习笔记】Day 16-17

一、进度概述 1、ddnet_main 相关代码学习&#xff08;预计 3-4 天&#xff09; 二、详情 1、顶层结构 关于代码顶层结构的一些思考和总结&#xff0c;其中下图为师兄代码的文件结构 总结&#xff1a; 对于一个优秀的代码&#xff0c;其文件结构一定也是清晰的&#…

随笔五、开发板连接WIFI并通过SSH连接泰山派

摘要&#xff1a;通过wifi连接内网&#xff0c;电脑通过SSH连接泰山派 1. 泰山派接入WIFI 泰山派开发板SDK已经集成wifi_start.sh脚本&#xff0c;在脚本后面直接输入wifi名字和密码就能连接wifi rootRK356X:/# wifi_start.sh wifi名字 wifi密码 查看ip地址&#xff0c;wlan0就…