[Android+JetPack] (Java实现) Retrofit2+RxJava3+Paging3+RecyclerView 实现加载网络数据例子 记录
文章目录
- 前言
- 参考链接
- 依赖库及版本
- Demo效果
- 接口及数据展示
- 各项模块
- `Retrofit2`
- Bean,对应上面的接口返回.
- Service API部分
- `Paging3`
- `PagingSource`以及 `RxPagingSource`
- `PagingDataAdapter` 适配器
- `ViewModel`
- `PublicInfoPage /Activity`
- 最后
前言
继续安卓学习之旅,本章的主要目标是:
1.完成一个无限上拉加载的列表(Paging3 + RecyclerView)
2.加载的是网络数据, 要采用主流的 Retrofit+okhttp方式
3.在了解了RxJava之后,也希望用上Rxjava
4.用到ViewModel来配合,以及一些jetpack的东西都用上
(为什么不用Paging2
? 这里主要是看说3比2还要方便些,所以就偷懒没去用Paging2
)
参考链接
这些是在学习和搜索中看到的比较好的文章,不过他们要么是kotlin 要么是RxJava2,都不是能直接套上去就用的,但是从文章里面总结归纳,也是有借鉴效果的.
- SmartRefreshLayout-github 这个后期再结合Paging3,完成一个有酷炫下拉及淘宝二楼效果的的demo
- Java实现)使用官方Paging3分页库实现RecyclerView加载更多(loadmore)的功能 这个较为简洁,没那么多原理的描述,方便更实战的理解借鉴
- Android paging3 使用和踩坑经验分享 这个虽然是kotlin,不过里面一些名词的解释不错, 适合快速扫盲
- Jetpack新成员,Paging3从吐槽到真香
依赖库及版本
为什么要说这个, 因为在实际百度各方面资料的时候,没仔细区分好版本,导致在练习过程中走了不少弯路,踩了坑.为避免这个情况,这里列出本Demo中的各个依赖库及版本
Retrofix2
// 引入 retrofix 网络框架(自带okhttp)// github :https://github.com/square/retrofit// 视频教学// https://www.bilibili.com/video/BV1vV411W75V?p=4&vd_source=3dc64571e08f84008d5c43796c009480implementation "com.squareup.retrofit2:retrofit:2.9.0"implementation 'com.squareup.retrofit2:converter-gson:2.9.0'implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
Rxjava3
// 支持RxJava/RxAndroidimplementation 'io.reactivex.rxjava3:rxandroid:3.0.2'implementation 'io.reactivex.rxjava3:rxjava:3.1.5'
Paging3
// 引入 paging 3// 注意, 由于上面我们用的是 retrofit2 + rxjava3// 所以,在使用 paging3的时候, 要选 支持rxjava3的 paging-rxjava3// 切记版本对应好def paging_version = "3.1.1"implementation "androidx.paging:paging-runtime:$paging_version"
// // optional - RxJava2 support
// implementation "androidx.paging:paging-rxjava2:$paging_version"// optional - RxJava3 supportimplementation "androidx.paging:paging-rxjava3:$paging_version"
这里稍微提一下, 如果用的是
RxJava3
, 就使用RxJava3 support的可选项, 不然不匹配,但同时也造成另一个问题, 这里插入说一下哈
就是
包括目前官网(点击进入)那边的, 关于对RxPagingSource
的示例里面, 也应该用的还是RxJava2
,如果你和我一样用RxJava3
,那大概率在做map
的时候,会报错说, 类型转换失败, 不能用 this::toLoadResult
这个稍后再说…
Demo效果
一个简单的demo
接口及数据展示
各项模块
Retrofit2
Bean,对应上面的接口返回.
Response_public_info_bean
package retrofit.bean;import java.util.List;/*** @author: tiannan* @time: 2023/4/12.* @email: tianNanYiHao@163.com* @descripetion: 此处添加描述*/
public class Response_public_info_bean {private String msg;private String code;private Datas data;public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public String getCode() {return code;}public void setCode(String code) {this.code = code;}public Datas getData() {return data;}public void setData(Datas data) {this.data = data;}public class Datas {private int pageSize;private List<Cell> list;public int getPageSize() {return pageSize;}public void setPageSize(int pageSize) {this.pageSize = pageSize;}public List<Cell> getList() {return list;}public void setList(List<Cell> list) {this.list = list;}@Overridepublic String toString() {return "Datas{" +"list=" + list +'}';}public class Cell {private String productName;private String productTypeName;private String riskRateName;private int id;private int pageNum; // 增加两个下标 page页下标private int indexNum;// 增加两个下标 newsInfo(cell)页下标@Overridepublic String toString() {return "News{" +"productName='" + productName + '\'' +", productTypeName='" + productTypeName + '\'' +", riskRateName='" + riskRateName + '\'' +", id=" + id +", pageNum=" + pageNum +", indexNum=" + indexNum +'}';}public String getProductName() {return productName;}public void setProductName(String productName) {this.productName = productName;}public String getProductTypeName() {return productTypeName;}public void setProductTypeName(String productTypeName) {this.productTypeName = productTypeName;}public String getRiskRateName() {return riskRateName;}public void setRiskRateName(String riskRateName) {this.riskRateName = riskRateName;}public int getId() {return id;}public void setId(int id) {this.id = id;}public int getPageNum() {return pageNum;}public void setPageNum(int pageNum) {this.pageNum = pageNum;}public int getIndexNum() {return indexNum;}public void setIndexNum(int indexNum) {this.indexNum = indexNum;}}}
}
Service API部分
这里不展开说太多
Retrofit2
部分的东西,这里只贴一下和本章有关的部分代码
/*** 请求公募数据列表* @param map* @return*/@GET(App_Url.admin_getPublicProductInfoPageList)Single<Response_public_info_bean> admin_getPublicProductInfoPageList(@QueryMap HashMap<String,String> map);
以上部分完成后 ,能够通过RxJava3
+ Retrofit2
配合完成一次网络请求, 基本就算完成Demo一半功能了
Paging3
PagingSource
以及 RxPagingSource
这里一开始我用错了
RxPagingSource
的导入版本, 用成了rxjava2的,踩了些坑
当时导入了paging.rxjava2
这个版本,
主要原因还是在配置依赖的时候, 把paging-rxjava2:3.1.1
版本也同步了
版本问题注意好
回到 RxPagingSource
按照网上的文章的示例, 先处理 loadSingle()函数的实现,这里有坑就是上面说的, RxJava3 + Paging3的情况下,
在 return
网络请求的时候,会报错
我这里没有在详细探究是否由于RxJava2的原因导致不能用 ::
这种双冒号的写法
这里仅仅贴一下RxJava2
下的map 和 RxJava3
下的map的源码区别
RxJava2版本:
RxJava3版本:
确实有一点区别, 这个先放一放, 等后期有空再看怎么处理…
先直接看怎么去写这个 this::toLoadResult
首先,既然通过Retrofit2
,我们已经定义了网络请求的返回值
那么我们在RxPagingSource
的 loadSingle()
中, 会去调用网络请求,得到一个 Single<Response_public_info_bean>
我们再看官网例子的这部分代码
注意看返回值其实是LoadResult<Integer, User>
,或者说,在本文章 我们要的返回值其实是 LoadResult<Integer, Response_public_info_bean.Datas.Cell>
所以.对于map操作符,在Rxjava3的下, 我们是可以自己去提供一个Function<T,R>
,这里面T
就是我们上面的Response_public_info_bean
R
就是Response_public_info_bean.Datas.Cell>
所以代码就是
(这里要注意下prevkey, 和 nextKey的入参 , 要做好逻辑判断, 一开始我参考别人的代码, 在prevKey填的是null, 在nextKey填入的是nextPageKey+1,结果导致加载页码瞬间冲到了几百页, 其实总page数量才不过十几页)
为了更加清晰明了的展示页码和条数下标, 我又添加了一个map操作符, 是给cell这个Bean数据再添加一下当前所属的页码 和 当前的下标
Function的入参依然是 Response_public_info_bean
返回也还是 Response_public_info_bean
, 相当于我们就对Response_public_info_bean
数据做了个数据加工
所以,基于RxJava3的map操作符这边就可以这样返回
PagingDataAdapter
适配器
public class PublicInfoAdapter extends PagingDataAdapter<Response_public_info_bean.Datas.Cell, PublicInfoAdapter.Holder> {public PublicInfoAdapter(@NotNull DiffUtil.ItemCallback<Response_public_info_bean.Datas.Cell> diffCallback) {super(diffCallback);}@NonNull@NotNull@Overridepublic Holder onCreateViewHolder(@NonNull @NotNull ViewGroup parent, int viewType) {PublicInfoCellBinding publicInfoCellBinding = PublicInfoCellBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);return new Holder(publicInfoCellBinding);}@Overridepublic void onBindViewHolder(@NonNull @NotNull Holder holder, int position) {Response_public_info_bean.Datas.Cell cell = getItem(position);Log.d("dfaddfsa", "onBindViewHolder: " + cell.getPageNum() +"."+ cell.getIndexNum() + "---" + cell.getProductName());holder.publicInfoCellBinding.textTitle.setText(cell.getProductName());holder.publicInfoCellBinding.underflag.setText("第"+cell.getPageNum() + "页.第" + cell.getIndexNum()+"条");}public class Holder extends RecyclerView.ViewHolder {/*** 给每个 Holder 实例 一个 viewBinding*/private PublicInfoCellBinding publicInfoCellBinding;public Holder(@NonNull @NotNull View itemView) {super(itemView);}public Holder(@NonNull PublicInfoCellBinding publicInfoCellBinding){super(publicInfoCellBinding.getRoot());this.publicInfoCellBinding = publicInfoCellBinding;}}
}
这里没太多可以说的,网上基本讲明白了, 我仅仅分享我遇到的一个问题
页面列表在加载完成后, 出现了一屏数据的重复渲染, 而且随着页面的滚动,该组数据的最后一条一直在渲染不同内容(但随着日志打印,数据都是正常输出的)
最后通过UI观察, 感觉最后一条数据随滚动而渲染,有点想是for循环没拦住的那种意思
就猜想,是不是 PagingDataAdapter
里面没处理好,
后来果然发现, PublicInfoCellBinding publicInfoCellBinding;
一不小心写成了全局的,而不是给每个Holder一个PublicInfoCellBinding publicInfoCellBinding;
, 最后修复下即可
这里还是由于对PagingDataAdapter
的不够熟悉, 刚写着玩意儿,才出现的低级错误
ViewModel
vm部分,网上也大同小异 ,写demo过程中未出现过多的坎儿
public class PublicInfoViewModel extends ViewModel {// paging3 page对象Pager<Integer, Response_public_info_bean.Datas.Cell> pager;// paging3 数据源对象PublicInfoSource publicInfoSource;// rxjava3 的 obserable 可观察对象Flowable<PagingData<Response_public_info_bean.Datas.Cell>> pagingDataFlowable;public PublicInfoViewModel(Context context) {CoroutineScope viewModelScope = ViewModelKt.getViewModelScope(this);publicInfoSource = new PublicInfoSource();// Maximum size must be at least pageSize + 2*prefetchDist, pageSize=20, prefetchDist=20, maxSize=20/*** pageSize 每页多少个条目* prefetchDistance 预加载下一页的距离,滑动到倒数第几个条目就加载下一页,无缝加载(可选)默认值是pageSize* enablePlaceholders 是否启用条目占位,当条目总数量确定的时候;列表一次性展示所有条目,* 但是没有数据;在adapter的onBindViewHolder里面绑定数据时候,是空数据,判断是空数据展示对应的占位item;可选,默认开启* initialLoadSize 第一页加载条目数量 ,可选,默认值是 3*pageSize (有时候需要第一页多点数据可用)* maxSize : 定义列表最大数量;可选,默认值是:Int.MAX_VALUE* jumpThreshold : 暂时还不知道用法,从文档注释上看,是滚动大距离导致加载失效的阈值;可选,默认值是:Int.MIN_VALUE (表示禁用此功能)**/PagingConfig pagingConfig = new PagingConfig(20,1,false,20*3);pager = new Pager<Integer, Response_public_info_bean.Datas.Cell>(pagingConfig, () -> publicInfoSource);pagingDataFlowable = PagingRx.getFlowable(pager);PagingRx.cachedIn(pagingDataFlowable, viewModelScope);}public Flowable<PagingData<Response_public_info_bean.Datas.Cell>> getPagingDataFlowable() {return pagingDataFlowable;}
}
PublicInfoPage /Activity
这里要注意的是, setLayoutManager
要设置 否则啥也不展示
public class PublicInfoPage extends AppCompatActivity {ActivityNewsPageBinding newsPageBinding;PublicInfoViewModel newsViewModel;PublicInfoAdapter newsAdapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);newsPageBinding = ActivityNewsPageBinding.inflate(getLayoutInflater());newsAdapter = new PublicInfoAdapter(new DiffUtil.ItemCallback<Response_public_info_bean.Datas.Cell>() {@Overridepublic boolean areItemsTheSame(@NonNull @NotNull Response_public_info_bean.Datas.Cell oldItem, @NonNull @NotNull Response_public_info_bean.Datas.Cell newItem) {return oldItem.getId() == newItem.getId();}@Overridepublic boolean areContentsTheSame(@NonNull @NotNull Response_public_info_bean.Datas.Cell oldItem, @NonNull @NotNull Response_public_info_bean.Datas.Cell newItem) {return oldItem.getProductName().equals(newItem.getProductName()) && oldItem.getProductTypeName().equals(newItem.getProductTypeName());}});newsPageBinding.recicleView.setAdapter(newsAdapter);newsPageBinding.recicleView.setLayoutManager(new LinearLayoutManager(this));setContentView(newsPageBinding.getRoot());}@Overrideprotected void onResume() {super.onResume();newsViewModel = new PublicInfoViewModel(this);newsViewModel.getPagingDataFlowable().subscribe(new Consumer<PagingData<Response_public_info_bean.Datas.Cell>>() {@Overridepublic void accept(PagingData<Response_public_info_bean.Datas.Cell> newsPagingData) throws Throwable {newsAdapter.submitData(getLifecycle(), newsPagingData);}});}
}
最后
以上就是这样了.
SmartRefreshLayout 也可以结合Paging3
这个有空也看一下,
安卓的玩法确实和iOS不一样, 也和RN不一样, 但互相又看得到对方的影子
相关文章:

[Android+JetPack] (Java实现) Retrofit2+RxJava3+Paging3+RecyclerView 实现加载网络数据例子 记录
文章目录 前言参考链接依赖库及版本Demo效果接口及数据展示各项模块Retrofit2Bean,对应上面的接口返回.Service API部分 Paging3PagingSource以及 RxPagingSourcePagingDataAdapter 适配器ViewModelPublicInfoPage /Activity 最后 前言 继续安卓学习之旅,本章的主要目标是: 1.完…...
Java 解析配置文件注入到配置类属性中供全局使用【开发记录】
1、背景:假设目前有两个接口,一个是查询快递订单状态的JSF接口,一个是查询快运订单状态的JSF接口,现有一个需求,要将这两个接口统一为一个入口,发布到物流开放平台供外界调用。 注意:以下代码均…...

【Python开发手册】深入剖析Google Python开发规范:规范Python注释写作
💖 作者简介:大家好,我是Zeeland,全栈领域优质创作者。📝 CSDN主页:Zeeland🔥📣 我的博客:Zeeland📚 Github主页: Undertone0809 (Zeeland) (github.com)&…...

Python入门教程+项目实战-9.3节: 字符串的操作方法
目录 9.3.1 字符串常用操作方法 9.3.2 获取字符串长度 9.3.3 字符串的大小写操作 9.3.4 删除字符串中的空白字符 9.3.5 字符串的子串查找 9.3.6 字符串的子串统计 9.3.7 字符串的子串替换 9.3.8 字符串的拆分函数 9.3.9 字符串的前缀与后缀9.3.10 知识要点 9.3.11 系…...

ENVI 5.6软件安装教程
软件下载 [软件名称]:ENVI 5.6 [软件大小]:3.25G [安装环境]:Win7~Win11或更高 软件介绍 ENVI 5.6是一款实现遥感图像处理的工具,已经广泛应用于科研、环境保护、气象、石油矿产勘探、农业、林业、医学、地球科学、公用设施管…...

在Windbg中设置断点追踪打开C++程序远程调试开关的模块
目录 1、Windbg动态调试 2、在Windbg中设置断点 2.1、在函数入口处设置断点 2.2、在函数内部某一行上设置断点 3、设置断点跟踪对打开远程调试开关接口的调用 3.1、编写演示代码 3.2、在Windbg中设置调用SetRemoteDebugOn接口的断点进行跟踪 4、最后 VC常用功能开发汇总…...

CRM客户管理软件开发功能有哪些?
互联网技术的不断提高使得企业管理方式也发生了变化,企业CRM系统应用市场逐渐扩大,相关软件开发也引起越来越多商家企业的关注。因为企业CRM系统软件开发能够根据企业需求制作,帮助企业更好的追踪管理客户信息,实时更新并进行相关…...
C++函数式魔法之旅(Journey of Functional Magic)
C函数式魔法之旅(Journey of Functional Magic) 一、引言(Introduction)C Functional模板库简介(Overview of C Functional Template Library)Functional模板库的重要性和作用(The Importance a…...

Vue基础入门(上)
<script src"https://unpkg.com/vuenext"></script> 从面向dom编程到面向数据编程 输入显示列表 const appVue.createApp({data(){return{inputValue:,list:[]}},methods:{handleAddItem(){this.list.push(this.inputValue);this.inputValue;}},templ…...
字符串匹配—KMP算法
字符串匹配的应用非常广泛,例如在搜索引擎中,我们通过键入一些关键字就可以得到相关的搜索结果,搜索引擎在这个过程中就使用字符串匹配算法,它通过在资源中匹配关键字,最后给出符合条件的搜索结果。并且我们在使用计算…...

【微信小程序】 权限接口梳理以及代码实现
1、权限接口说明 官方权限说明 部分接口需要经过用户授权统一才能调用。我们把这些接口按使用范围分成多个scope,用户选择对scope进行授权,当授权给一个scope之后,其对应的所有接口都可以直接使用。 此类接口调用时: 如…...
【每日一词】leit-motif
1、释义 leit-motif: n. 主乐调;主题;主旨。 复数:leit-motifs 2、例句 Hence the ‘ancient’ rhyme that appears as the leit-motif of The Lord of the Rings, Three Rings for the Elven-Kings under the sky, Seven for the Dwarf-lor…...
windows 环境修改 Docker 存储目录
windows 环境修改存储目录 docker 安装时不提供指定安装路径和数据存储路径的选项,且默认是安装在C盘的。C盘比较小的,等docker运行久了,一大堆的东西放在上面容易导致磁盘爆掉。所以安装前可以做些准备,让安装的实际路径不在C盘&…...
上海市青少年算法月赛丙组—目录汇总
上海市青少年算法2023年3月月赛(丙组) T1 神奇的字母序列 T2 约数的分类 T3 循环播放 T4 数对的个数 T5 选取子段 上海市青少年算法2023年2月月赛(丙组) T1 格式改写 T2 倍数统计 T3 区间的并 T4 平分数字(一…...
手动实现promise.all
手动实现promise.all function promiseAll(promises) {return new Promise((resolve, reject) > {const results [];let count 0;promises.forEach((promise, index) > {Promise.resolve(promise).then(result > {results[index] result;count;if (count promise…...

如何搭建关键字驱动自动化测试框架?这绝对是全网天花板的教程
目录 1. 关键字驱动自动化测试介绍 2. 搭建关键字驱动自动化测试框架 步骤1:选择测试工具 步骤2:定义测试用例 步骤3:编写测试驱动引擎 步骤4:实现测试关键字库 步骤5:执行测试 3. 实现关键字驱动自动化测试的关…...
字符串反转操作
1:将字符串反转 给定一句英语,要求你编写程序,将句中所有单词的顺序颠倒输出。 输入格式: 测试输入包含一个测试用例,在一行内给出总长度不超过 80 的字符串。字符串由若干单词和若干空格组成,其中单词是由英文字母…...
TensorFlow 智能移动项目:1~5
原文:Intelligent mobile projects with TensorFlow 协议:CC BY-NC-SA 4.0 译者:飞龙 本文来自【ApacheCN 深度学习 译文集】,采用译后编辑(MTPE)流程来尽可能提升效率。 不要担心自己的形象,只…...

[MAUI 项目实战] 手势控制音乐播放器(四):圆形进度条
文章目录 关于图形绘制创建自定义控件使用控件创建专辑封面项目地址 我们将绘制一个圆形的音乐播放控件,它包含一个圆形的进度条、专辑页面和播放按钮。 关于图形绘制 使用MAUI的绘制功能,需要Microsoft.Maui.Graphics库。 Microsoft.Maui.Graphics 是…...

web路径专题+会话技术
目录 自定义快捷键 1. 工程路径问题及解决方案1.1 相对路径1.2 相对路径缺点1.3 base标签1.4 作业11.5 作业21.6注意细节1.7 重定向作业1.8 web工程路径优化 2. Cookie技术2.1 Cookie简单示意图2.2 Cookie常用方法2.2 Cookie创建2.3 Cookie读取2.3.1 JSESSIONID2.3.2 读取指定C…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 问题可视化2.2 核心挑战 3. 解法一:HashSet 标记访问法3.1 算法思路3.2 Java代码实现3.3 详细执行过程演示3.4 执行结果示例3.5 复杂度分析3.6 优缺点分析 4. 解法二:Floyd 快慢指针法(…...

EasyRTC音视频实时通话功能在WebRTC与智能硬件整合中的应用与优势
一、WebRTC与智能硬件整合趋势 随着物联网和实时通信需求的爆发式增长,WebRTC作为开源实时通信技术,为浏览器与移动应用提供免插件的音视频通信能力,在智能硬件领域的融合应用已成必然趋势。智能硬件不再局限于单一功能,对实时…...
ffmpeg(三):处理原始数据命令
FFmpeg 可以直接处理原始音频和视频数据(Raw PCM、YUV 等),常见场景包括: 将原始 YUV 图像编码为 H.264 视频将 PCM 音频编码为 AAC 或 MP3对原始音视频数据进行封装(如封装为 MP4、TS) 处理原始 YUV 视频…...