布景
最近项目上有接入腾讯libpag 用作动效完结,至此项目上现已包括了三种格式的动效,svga,lottile,pag。而事务需求是客户端要可以支持多种格式的动效处理。 因而事务代码完结经常是下面的逻辑
when(图片类型){
"svga"->{
处理svga的逻辑
}
"lottile"->{
处理了lottile的逻辑
}
"pag"->{
处理pag动效的逻辑
}
else->{
处理为普通图片
}
}
这样的事务逻辑代码散布在项目的各个地方,运用的运用不只需求重复的处理对应的事务逻辑,也不利于维护。而且腾讯 libpag 的 PagView 作为ui的出现, 支持的是从本地文件途径或者直接设置一个 PAGComposition 方针到 PagView 进行显现,并不对文件的下载进程进行处理。 对应文件的下载功用天然落在了开发者身上,假如为了接入libpag 而自己去完结一套 下载缓存的功用功用巨大且复杂,且整个进程需求研发、测试的完全介入,本钱相对较高。运用项目上已有的下载计划天然是一个不错的挑选。 Glide 作为一个老练安稳的图片加载框架,具有图片加载和文件下载的能力。并在内部完结了多级缓存的机制,与列表错位的处理。
将 libpag 与 Glide 进行结合不只效果安稳而且图片加载的方法不会进行改动,运用者也不需求额外的学习本钱。
方针与设计完结
本次扩展主要有两个方针
- 可以运用Glide 加载显现到 PagView
- 做到图片加载的时分不需求进行事务区别,让 Glide 像加载普通图片一样加载pag
计划完结:
方针一:
Glide 数据加载大致有 数据加载-> 解码 -> 转码 三个流程 现在Glide 会将网络数据加载成InputStream 流 咱们需求扩展的是当检测到stram 是 PAG 时将流解码成文件,再对文件进行转码,转码结果是 PagView 需求的 PAGComposition 。
方针二
Glide 默认加载出的方针是Drawable,然后直接将Drawable方针设置给ImageView 运用,而 PagView 需求的是一个 PAGComposition 。为此 可以自定义一个Drawable 方针 PAGFileDrawable 内部包括PAGComposition 对下。 可是 PagView 并不是直接承继自ImageView 并不能直接运用Drawable方针。为了到达运用时不区别详细的资源类型的目的,咱们封装一个ViewGroup AutoAnimationView,AutoAnimationView 内部包括ImageView和PagView 当加载结果是 PAGFileDrawable 时显现 PagView 隐藏 ImageView 反之显现 ImageView 隐藏 PagView
代码完结
说了这么多,主角总算来了。测验让chatGpt 帮我完结上面的逻辑
- 先进行下衬托 看看gpt能不能扩展,显然gpt知道怎样扩展,并给出一段wep解码的示例
- 紧接着咱们让gpt依照 添加一个解码器
public class PagDecoder implements ResourceDecoder<InputStream, File> {
private final Context context;
public PagDecoder(Context context) {
this.context = context;
}
@Override
public Resource<File> decode(InputStream source, int width, int height, Options options) throws IOException {
// 将输入流保存为本地文件
File file = saveInputStreamToFile(source);
// 返回本地文件的Resource方针
return new FileResource(file);
}
@Override
public boolean handles(InputStream source, Options options) throws IOException {
// 判别输入流是否为PAG类型
return isPag(source);
}
private boolean isPag(InputStream source) {
// 判别输入流是否为PAG类型的逻辑
// 这儿仅仅一个示例,需求依据实际情况自行完结
return true;
}
private File saveInputStreamToFile(InputStream input) throws IOException {
// 创立一个临时文件来保存输入流
File outputDir = context.getCacheDir();
File outputFile = File.createTempFile("temp", ".tmp", outputDir);
// 运用流的方法将输入流写入文件
OutputStream output = new FileOutputStream(outputFile);
byte[] buffer = new byte[4 * 1024]; // 4KB
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
output.flush();
output.close();
return outputFile;
}
}
- 显着看到这儿的isPag 直接返回了true 这个不是咱们想要的,于是咱们又问gpt 怎样判别,图片的类型
4. 重写方才的类
由于gpt 完结是java 我又让它把代码转换成kotlin
class PagDecoder(private val context: Context) : ResourceDecoder<InputStream, File> {
override fun decode(source: InputStream, width: Int, height: Int, options: Options): Resource<File>? {
// 将输入流保存为本地文件
val file = saveInputStreamToFile(source)
// 返回本地文件的Resource方针
return FileResource(file)
}
override fun handles(source: InputStream, options: Options): Boolean {
// 判别输入流是否为PAG类型
return isPag(source)
}
private fun isPag(source: InputStream): Boolean {
// 判别输入流是否为PAG类型的逻辑
// 这儿仅仅一个示例,需求依据实际情况自行完结
val header = ByteArray(4)
try {
source.read(header)
} catch (e: IOException) {
e.printStackTrace()
}
return header.contentEquals(byteArrayOf(0x50, 0x41, 0x47, 0x00))
}
@Throws(IOException::class)
private fun saveInputStreamToFile(input: InputStream): File {
// 创立一个临时文件来保存输入流
val outputDir: File = context.cacheDir
val outputFile = File.createTempFile("temp", ".tmp", outputDir)
// 运用流的方法将输入流写入文件
val output: OutputStream = FileOutputStream(outputFile)
val buffer = ByteArray(4 * 1024) // 4KB
var bytesRead: Int
while (input.read(buffer).also { bytesRead = it } != -1) {
output.write(buffer, 0, bytesRead)
}
output.flush()
output.close()
return outputFile
}
}
5.转码器完结
这一次gpt并没有完结对应的转码器 而是完结了一个解码器,因而我再次强调强调了一遍是转码器。而gpt 也认识到了他的过错。就不经验它了
class PagFileTranscoder : ResourceTranscoder<File, PAGFile> {
override fun transcode(toTranscode: Resource<File>, options: Options): Resource<PAGFile>? {
// 获取文件途径
val filePath = toTranscode.get().path
// 运用 PAGFile.Load 方法将文件转换成 PAGFile 方针
val pagFile = PAGFile.Load(filePath)
// 返回 PAGFile 方针的 Resource 方针
return PagFileResource(pagFile)
}
override fun getTranscodeOptions(options: Options): Options {
return options
}
class PagFileResource(val pagFile: PAGFile) : Resource<PAGFile> {
override fun getResourceClass(): Class<PAGFile> {
return PAGFile::class.java
}
override fun get(): PAGFile {
return pagFile
}
override fun getSize(): Int {
return pagFile.getData().size
}
override fun recycle() {
// 不需求进行资源收回操作
}
}
}
显着可以看到的是转码的时分 是都一切文件执行的。而咱们想转码的文件是PAG 动效资源。于是
一起让gpt 删去一些不必要的代码之后最终的代码是
class PagFileTranscoder(private val context: Context) : ResourceTranscoder<File, PAGFile> {
override fun transcode(
toTranscode: Resource<File>,
options: Options
): Resource<PAGFile>? {
// 获取文件途径
val filePath = toTranscode.get().path
// 读取文件头部信息,用于判别文件类型
val header = ByteArray(8)
FileInputStream(filePath).use { inputStream ->
inputStream.read(header)
}
// 判别文件是否是 PAG 类型的文件
if (isPAGFile(header)) {
// 运用 PAGFile.Load 方法将文件转换成 PAGFile 方针
val pagFile = PAGFile.Load(filePath)
// 返回 PAGFile 方针的 Resource 方针
return SimpleResource(pagFile)
}
return null
}
private fun isPAGFile(header: ByteArray): Boolean {
// 判别文件头部信息是否与 PAG 文件的头部信息相同
return header[0] == 'P'.toByte() &&
header[1] == 'A'.toByte() &&
header[2] == 'G'.toByte() &&
header[3] == 'F'.toByte()
}
}
然后我自己完结简单完结了一些封装
class PAGViewTarget(
private val view: PAGView
) : CustomViewTarget<PAGView, PAGFile>(view) {
override fun onResourceLoading(placeholder: Drawable?) {
view.stop()
}
override fun onResourceCleared(placeholder: Drawable?) {
view.stop()
}
override fun onResourceReady(resource: PAGFile, transition: Transition<in PAGFile>?) {
view.composition = resource
view.setRepeatCount(50)
view.play()
}
override fun onLoadFailed(errorDrawable: Drawable?) {
}
}
fun RequestBuilder<PAGFile>.into(pagView:PAGView){
into(PAGViewTarget(pagView))
}
fun RequestManager.asPAGFile():RequestBuilder<PAGFile>{
return `as`(PAGFile::class.java)
}
在Application中加解码器和转码器注册
Glide.get(this).registry.append(InputStream::class.java, File::class.java, PagDecoder(this))
.register(
File::class.java,
PAGFile::class.java,
PagFileTranscoder()
)
在页面当中进行运用
Glide.with(this).asPAGFile().load("https://github.com/Tencent/libpag/blob/main/assets/data_video.pag?raw=true").into(pagView)
然后加载失败了,经过排查 原来是gpt 判别文件类型出错了。
至此 扩展Glide 加载 pag 图片的功用现已完结啦
至于方针二的完结 就不粘贴与gpt的聊天内容了。
最终结果
这儿直接献上 最终整理的代码 和 效果图 刚兴趣的小伙伴可以直接下载代码运行测验
代码地址:
github.com/xiaolutang/…
留意demo上的pag 地址是腾讯官方 github 地址 访问要挂vpn
运用chatgpt的感触
当前chatgpt 关于一些深入的事务场景还没方法具有足够的理解能力。并不能直接输出可用代码。乃至经常输出过错的代码。可是这并不妨碍gpt 未来对程序员作业的影响。整体而言 关于代码的落地 需求咱们不断的引导gpt进行批改。当与gpt 交流的次数足够多的是他就可以依照你的要求产生你想要的东西(可是这个交流进程可能极其苦楚,某些时分 你明明现已纠正了可是gpt 仍是会依照过错的内容进行输出)。 关于个人而言,在作业中有用的运用gpt 可以进步个人的开发效率,每个人都应该具有自己独立的人工智能帮手,经过训练这个帮手让人工智能更加了解你的作业习气,然后进步个人的开发效率。