FileObserver仅捕获删除事件,无法监听拍照/文件移动等操作问题排查
嘿,我来帮你排查下这个FileObserver只捕获删除事件的问题,大概率是存储权限限制、相机文件生成逻辑,还有代码里的小细节导致的,咱们一步步来解决:
1. 先搞定Android 10+的存储权限问题
从Android 10(API 29)开始,系统启用了分区存储,你用的Environment.getExternalStoragePublicDirectory()已经被官方废弃了。直接通过这个路径创建的FileObserver,很可能因为权限不足,没法完整监控DCIM/Camera目录下的文件变化——毕竟现在系统是通过MediaStore来管理媒体文件,不是直接暴露传统的文件路径。
解决思路:
- 如果你的APP目标SDK是29+,要么申请
MANAGE_EXTERNAL_STORAGE权限(需要用户去设置里手动开启),要么改用MediaStore监听方案(更推荐,符合现代Android存储规范)。 - 如果是API 28及以下,务必确保已经动态申请了
READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限,且用户已经授权。
2. 相机文件的生成逻辑坑了你的事件捕获
很多相机APP拍照片时,并不是直接在DCIM/Camera下创建文件:通常是先在临时目录生成文件,最后**移动(MOVED_TO)**到目标目录;或者先创建空文件,逐步写入内容(触发多次MODIFY事件),最后重命名(触发MOVED_FROM+MOVED_TO)。
而你的代码里有个多余操作:event &= FileObserver.ALL_EVENTS,这步完全没必要,甚至可能干扰事件判断,建议直接删掉。另外,相机拍完照后,最可能触发的是MOVED_TO事件,你可以重点关注这个事件。
3. 要不要监控子文件夹?
你说当前只监控DCIM/Camera根目录,但部分相机APP会在这个目录下创建按日期分类的子文件夹(比如DCIM/Camera/20240520/),如果照片存在子文件夹里,你的FileObserver就完全监控不到。
如果需要监控所有子文件夹,得做递归式FileObserver:在父文件夹的CREATE事件里,判断如果是新创建的子目录,就为它单独创建FileObserver并启动监控。
4. 修正后的FileObserver代码
我帮你调整了代码,修复了事件处理问题,还加了子文件夹监控的逻辑(按需使用):
private static int mask = FileObserver.ALL_EVENTS; private String rootDir; private UploadPhotosService myService; private List<FileObserver> subObservers = new ArrayList<>(); // 存储子文件夹的Observer public PicturesFileObserver(String path, UploadPhotosService myService) { super(path, mask); this.rootDir = path; this.myService = myService; Log.e(TAG,"Observer is initialized on: "+rootDir); // 初始化时先监控已存在的子文件夹 File rootFile = new File(rootDir); if (rootFile.exists() && rootFile.isDirectory()) { File[] subFiles = rootFile.listFiles(); if (subFiles != null) { for (File subFile : subFiles) { if (subFile.isDirectory()) { addSubFolderObserver(subFile.getAbsolutePath()); } } } } } private void addSubFolderObserver(String path) { FileObserver subObserver = new FileObserver(path, mask) { @Override public void onEvent(int event, @Nullable String subPath) { handleEvent(event, path + "/" + subPath); } }; subObserver.startWatching(); subObservers.add(subObserver); } @Override public void onEvent(int event, @Nullable String path) { // 删掉多余的 event &= FileObserver.ALL_EVENTS; Log.e(TAG,"Received event code: "+event); if (path != null) { String fullPath = rootDir + "/" + path; File file = new File(fullPath); // 监控新创建的子文件夹 if ((event & FileObserver.CREATE) != 0 && file.isDirectory()) { addSubFolderObserver(fullPath); return; } if (path.endsWith(".jpg") || path.endsWith(".png")) { if ((event & FileObserver.CREATE) != 0) { Log.e(TAG, "File was created " + fullPath); } else if ((event & FileObserver.MODIFY) != 0) { Log.e(TAG, "File was modified " + fullPath); } else if ((event & FileObserver.DELETE) != 0) { Log.e(TAG, "File was deleted " + fullPath); } else if ((event & FileObserver.MOVED_FROM) != 0) { Log.e(TAG, "File was moved away: " + fullPath); } else if ((event & FileObserver.MOVED_TO) != 0) { Log.e(TAG, "File was moved into directory: " + fullPath); // 相机拍照完成后,通常会触发这个事件,这里可以处理上传逻辑 } } else { Log.e(TAG, "Path not being a photo: " + fullPath); } } } // 记得在Service销毁时停止所有Observer,避免内存泄漏 public void stopAllWatching() { this.stopWatching(); for (FileObserver observer : subObservers) { observer.stopWatching(); } subObservers.clear(); }
5. 额外提醒
- 把Service设为前台Service:如果Service被系统后台回收,FileObserver也会停止工作,所以最好给Service加个前台通知,防止被回收。
- 多测试不同相机APP:不同厂商的相机生成文件的逻辑可能不一样,用系统自带相机和第三方相机都测一遍,确保事件能被捕获。
内容的提问来源于stack exchange,提问作者IAmJustAUser

