修改WhatsApp StickerContentProvider从Firebase加载贴纸的方法
从Firebase Storage加载贴纸到WhatsApp的ContentProvider修改方案
核心修改思路
原StickerContentProvider依赖本地assets目录加载贴纸,我们需要替换为从Firebase获取贴纸元数据(包信息、贴纸URL),并将贴纸文件缓存到应用私有目录后提供给WhatsApp调用。
步骤1:添加Firebase依赖
在build.gradle中加入Firebase Storage和Firestore(用于存储贴纸包元数据)的依赖:
implementation 'com.google.firebase:firebase-storage:20.3.0' implementation 'com.google.firebase:firebase-firestore:24.10.0'
步骤2:修改ContentProvider核心逻辑
2.1 添加Firebase及缓存相关成员变量
在StickerContentProvider类中添加:
private FirebaseFirestore db; private FirebaseStorage storage; private File stickerCacheDir;
2.2 初始化Firebase与缓存目录
重写onCreate方法,初始化Firebase并创建缓存目录,同时调整UriMatcher规则的添加方式:
@Override public boolean onCreate() { final String authority = BuildConfig.CONTENT_PROVIDER_AUTHORITY; if (!authority.startsWith(Objects.requireNonNull(getContext()).getPackageName())) { throw new IllegalStateException("your authority (" + authority + ") for the content provider should start with your package name: " + getContext().getPackageName()); } // 初始化Firebase FirebaseApp.initializeApp(getContext()); db = FirebaseFirestore.getInstance(); storage = FirebaseStorage.getInstance(); // 创建应用私有缓存目录 stickerCacheDir = new File(getContext().getCacheDir(), "wa_stickers"); if (!stickerCacheDir.exists()) { stickerCacheDir.mkdirs(); } // 添加基础Uri匹配规则 MATCHER.addURI(authority, METADATA, METADATA_CODE); MATCHER.addURI(authority, METADATA + "/*", METADATA_CODE_FOR_SINGLE_PACK); MATCHER.addURI(authority, STICKERS + "/*", STICKERS_CODE); // 异步加载Firebase中的贴纸包数据,加载完成后动态添加文件Uri规则并通知刷新 loadStickerPacksFromFirebase(); return true; }
2.3 实现从Firebase加载贴纸包元数据
添加方法从Firestore读取贴纸包信息(需提前在Firestore中创建sticker_packs集合,文档结构与StickerPack类字段对应,额外添加trayImageUrl和贴纸的imageUrl字段):
private void loadStickerPacksFromFirebase() { db.collection("sticker_packs") .get() .addOnCompleteListener(task -> { if (task.isSuccessful() && task.getResult() != null) { stickerPackList = new ArrayList<>(); String authority = BuildConfig.CONTENT_PROVIDER_AUTHORITY; for (QueryDocumentSnapshot doc : task.getResult()) { StickerPack pack = doc.toObject(StickerPack.class); stickerPackList.add(pack); // 动态添加托盘图标和贴纸的Uri匹配规则 MATCHER.addURI(authority, STICKERS_ASSET + "/" + pack.identifier + "/" + pack.trayImageFile, STICKER_PACK_TRAY_ICON_CODE); for (Sticker sticker : pack.getStickers()) { MATCHER.addURI(authority, STICKERS_ASSET + "/" + pack.identifier + "/" + sticker.imageFileName, STICKERS_ASSET_CODE); } } // 通知ContentResolver数据更新,触发WhatsApp刷新贴纸列表 getContext().getContentResolver().notifyChange(AUTHORITY_URI, null); } else { Log.e("StickerProvider", "Failed to load sticker packs", task.getException()); } }); }
2.4 修改贴纸包列表获取逻辑
替换原getStickerPackList方法,避免空指针并支持异步加载:
private List<StickerPack> getStickerPackList() { if (stickerPackList == null) { stickerPackList = new ArrayList<>(); } return stickerPackList; }
2.5 替换贴纸文件获取逻辑
移除原openAssetFile、getImageAsset、fetchFile中与assets相关的代码,改为从Firebase下载并缓存文件:
@Nullable @Override public AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException { final int matchCode = MATCHER.match(uri); if (matchCode == STICKERS_ASSET_CODE || matchCode == STICKER_PACK_TRAY_ICON_CODE) { return getCachedStickerFile(uri); } return super.openAssetFile(uri, mode); } private AssetFileDescriptor getCachedStickerFile(Uri uri) throws FileNotFoundException { List<String> pathSegments = uri.getPathSegments(); if (pathSegments.size() != 3) { throw new FileNotFoundException("Invalid URI: " + uri); } String packId = pathSegments.get(1); String fileName = pathSegments.get(2); // 查找对应文件的Firebase Storage URL String fileUrl = null; for (StickerPack pack : stickerPackList) { if (pack.identifier.equals(packId)) { if (pack.trayImageFile.equals(fileName)) { fileUrl = pack.trayImageUrl; // 需在StickerPack类中添加该字段 break; } for (Sticker sticker : pack.getStickers()) { if (sticker.imageFileName.equals(fileName)) { fileUrl = sticker.imageUrl; // 需在Sticker类中添加该字段 break; } } if (fileUrl != null) break; } } if (fileUrl == null) { throw new FileNotFoundException("File not found: " + uri); } // 检查缓存文件是否存在,不存在则从Firebase同步下载 File cacheFile = new File(stickerCacheDir, packId + "_" + fileName); if (!cacheFile.exists()) { try { StorageReference fileRef = storage.getReferenceFromUrl(fileUrl); // 同步等待下载完成(ContentProvider调用为同步,必须阻塞等待) Tasks.await(fileRef.getFile(cacheFile)); } catch (ExecutionException | InterruptedException e) { throw new FileNotFoundException("Download failed: " + e.getMessage()); } } // 返回文件描述符给WhatsApp ParcelFileDescriptor pfd = ParcelFileDescriptor.open(cacheFile, ParcelFileDescriptor.MODE_READ_ONLY); return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH); }
2.6 清理冗余代码
删除原readContentFile、getImageAsset、fetchFile方法,以及与CONTENT_FILE_NAME相关的常量定义。
步骤3:调整数据模型
修改StickerPack和Sticker类,添加Firebase URL字段:
// StickerPack类新增字段 private String trayImageUrl; // Sticker类新增字段 private String imageUrl;
步骤4:Firebase配置
- Firestore规则:允许读取
sticker_packs集合rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /sticker_packs/{pack} { allow read: if true; } } } - Storage规则:允许读取贴纸文件路径
rules_version = '2'; service firebase.storage { match /b/{bucket}/o { match /stickers/{allPaths=**} { allow read: if true; } } }
注意事项
- 同步下载可能导致短时阻塞,建议优化缓存策略(如提前预加载热门贴纸包)
- 添加网络权限
<uses-permission android:name="android.permission.INTERNET"/>到AndroidManifest.xml - 贴纸文件需符合WhatsApp规范:格式为WebP(静态)或APNG/GIF(动态),尺寸不超过512x512px,文件大小不超过100KB
内容的提问来源于stack exchange,提问作者Furqan Zaidi
相关产品推荐
相关产品推荐

