BiliRoamingX-integrations项目中评论区图片查看功能的实现与优化
你是否曾经在B站评论区看到有趣的图片,想要保存下来却苦于没有便捷的方法?BiliRoamingX-integrations项目通过巧妙的Hook技术,为B站客户端添加了评论区图片长按保存功能,让图片保存变得触手可及。
功能概述
BiliRoamingX-integrations项目中的评论区图片查看功能主要实现了以下核心特性:
- 长按保存图片:在评论区图片查看界面长按即可保存图片到本地相册
- 智能URL处理:自动处理B站特有的图片URL格式,去除冗余参数
- 权限管理:完善的存储权限申请和处理机制
- 触觉反馈:保存操作时提供震动反馈,提升用户体验
技术实现原理
核心架构
整个功能基于ReVanced Patcher框架,通过Hook B站客户端的图片查看器组件来实现功能增强。以下是核心架构图:
关键代码分析
1. CommentImagePatch - 核心Hook类
public class CommentImagePatch {
private static final int imageViewId = Utils.getResId("image_view", "id");
@Keep
public static void bindClickListener(ImageFragment fragment) {
if (!Settings.SaveCommentImage.get()) return;
// 解析图片URL
var arguments = fragment.getArguments();
var imageItem = arguments.getParcelable("image_item");
var field = Reflex.findFirstFieldByExactTypeOrNull(
imageItem.getClass().getSuperclass(), String.class);
var imageUrl = (String) Reflex.getFieldValue(imageItem, field);
// 处理B站特有URL格式
if (TextUtils.isEmpty(imageUrl) || !imageUrl.startsWith("http")) return;
var atIndex = imageUrl.indexOf('@');
if (atIndex != -1)
imageUrl = imageUrl.substring(0, atIndex);
// 绑定长按监听器
var view = fragment.getView();
var imageView = view.findViewById(imageViewId);
final var finalImageUrl = imageUrl;
imageView.setOnLongClickListener(v -> {
v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
KtUtils.saveImage(finalImageUrl);
return true;
});
}
}
2. 图片保存实现 - KtUtils.saveImage
fun saveImage(url: String) {
fun save(url: String) = runCatching {
URL(url).openStream().use { input ->
val picDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES)
val filename = url.substringAfterLast('/')
val saveDir = File(picDir, "bili").also { it.mkdirs() }
val imageFile = File(saveDir, filename)
// 文件保存操作
imageFile.outputStream().use { input.copyTo(it) }
// 更新媒体库
MediaScannerConnection.scanFile(
Utils.getContext(),
arrayOf(imageFile.absolutePath),
null,
null
)
Toasts.showLongWithId("biliroaming_toast_image_save_success", imageFile.path)
}
}.onFailure {
Logger.error(it) { "image save failed, url: $url" }
Toasts.showShortWithId("biliroaming_toast_image_save_failed")
}
// 权限处理
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
Utils.async { save(url) }
} else {
val activity = ApplicationDelegate.requireTopActivity()
activity.requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) {
granted, shouldExplain ->
if (granted) {
Utils.async { save(url) }
} else if (shouldExplain) {
Toasts.showShortWithId("biliroaming_write_storage_failed")
}
}
}
}
3. 配置管理 - Settings类
object Settings {
@JvmField val SaveCommentImage = BooleanSetting(key = "save_comment_image")
// ... 其他配置项
}
技术难点与解决方案
难点1:B站图片URL特殊格式处理
B站的图片URL通常包含特殊参数,如 https://example.com/image.jpg@100w_100h,需要去除@后面的参数才能获取原始图片。
解决方案:
var atIndex = imageUrl.indexOf('@');
if (atIndex != -1)
imageUrl = imageUrl.substring(0, atIndex);
难点2:反射获取私有字段
B站的图片数据对象包含私有字段,需要通过反射机制获取真实的图片URL。
解决方案:
var field = Reflex.findFirstFieldByExactTypeOrNull(
imageItem.getClass().getSuperclass(), String.class);
var imageUrl = (String) Reflex.getFieldValue(imageItem, field);
难点3:Android权限适配
不同Android版本对存储权限的处理方式不同,需要做版本适配。
解决方案:
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
// Android 10+ 使用Scoped Storage
Utils.async { save(url) }
} else {
// 旧版本需要申请存储权限
activity.requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) {
granted, shouldExplain ->
if (granted) Utils.async { save(url) }
}
}
性能优化策略
1. 异步文件操作
所有文件保存操作都在后台线程执行,避免阻塞UI线程:
Utils.async { save(url) }
2. 资源ID缓存
通过资源名获取资源ID并进行缓存,避免重复查找:
private static final int imageViewId = Utils.getResId("image_view", "id");
3. 错误处理机制
完善的异常捕获和处理机制,确保功能稳定性:
.runCatching {
// 可能抛出异常的操作
}.onFailure {
Logger.error(it) { "image save failed" }
Toasts.showShortWithId("biliroaming_toast_image_save_failed")
}
功能扩展与自定义
配置选项
用户可以通过模块设置开启或关闭此功能:
| 配置项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| save_comment_image | Boolean | true | 是否启用评论区图片保存功能 |
扩展可能性
基于当前架构,可以轻松扩展更多功能:
- 图片分享功能:添加分享到其他应用的支持
- 图片编辑:集成简单的图片编辑功能
- 批量保存:支持多张图片批量保存
- 自定义保存路径:允许用户指定图片保存目录
最佳实践与使用建议
开发建议
- 遵循Android开发规范:正确处理权限和生命周期
- 异常处理:确保所有可能抛出异常的操作都有适当的异常处理
- 性能考虑:文件操作在后台线程执行,避免ANR
- 用户体验:提供适当的反馈(震动、Toast提示)
使用建议
- 确保存储权限:在Android 10以下版本需要授予存储权限
- 检查功能开关:在模块设置中确认"保存评论区图片"功能已开启
- 文件管理:保存的图片位于
Pictures/bili/目录下
总结
BiliRoamingX-integrations项目中的评论区图片查看功能通过巧妙的Hook技术和完善的实现细节,为用户提供了便捷的图片保存体验。该功能不仅技术实现优雅,而且在性能、稳定性和用户体验方面都做了充分的考虑。
关键技术亮点包括:
- 反射机制获取私有数据
- B站特有URL格式处理
- 多版本Android权限适配
- 异步文件操作避免阻塞
- 完善的错误处理和用户反馈
这个功能的实现展示了如何在现有应用中通过Patch方式优雅地添加新功能,为Android应用的功能扩展提供了很好的参考范例。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



