Android中MediaStore无法获取新增照片的问题排查咨询
问题排查与修复方案
问题背景
我们的应用读取手机MediaStore中的照片,对含有人脸的照片做情绪分析。为避免每次启动全量扫描,只获取新增照片:首次启动扫描全部照片,后续启动仅查询上次扫描时间到当前时间的新增照片。但现在遇到问题:退出应用后新增照片,重新进入时无法获取这些新增照片,代码从未进入if (cursor.moveToFirst()) {}语句块。
核心问题定位
1. 时间戳来源不匹配,查询逻辑完全错误
你存储到SharedPreferences的lastPhotoDate是文件的本地修改时间(file.lastModified()),但查询MediaStore时用的是DATE_TAKEN字段——这两个时间戳根本不是一回事:
DATE_TAKEN是照片拍摄时的原始时间戳(来自照片EXIF,毫秒级)file.lastModified()是文件被复制/下载到本地后的最后修改时间
首次扫描后存储的是文件修改时间,后续用这个值和DATE_TAKEN做比较,相当于拿两个完全无关的时间做筛选,自然查不到新增照片。
2. 查询条件的边界限制过严
当前查询用DATE_TAKEN > ? and DATE_TAKEN < ?,如果新增照片的DATE_TAKEN刚好等于当前生成的timeStamp,会被直接排除,应该把<改成<=,覆盖所有在扫描时间段内的照片。
3. 首次扫描时的时间存储逻辑错误
首次扫描时,cursor是按DATE_TAKEN DESC排序的,你却取了第一条记录的文件修改时间存储,而不是这条记录的DATE_TAKEN值,直接导致后续查询的起始点错误。
修复代码
步骤1:统一时间戳来源,直接用MediaStore的DATE_TAKEN
不再使用文件修改时间,直接从cursor读取DATE_TAKEN字段的值来存储,同时修正查询条件和资源管理:
public static void listOfImages(Context context) { Uri uri; String ablosutePathOfImage; emotionDatabase = EmotionDatabase.getEmotionDatabase(context); Long currentTime = System.currentTimeMillis(); String timeStamp = currentTime.toString(); SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); String lastPhotoDate = sharedPreferences.getString("last_photo_date", null); uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; // 确保投影包含DATE_TAKEN字段 String [] projection = {MediaStore.MediaColumns.DATA, MediaStore.Images.Media.DATE_TAKEN}; String orderBy = MediaStore.Images.Media.DATE_TAKEN + " DESC"; Cursor cursor = null; if(lastPhotoDate == null){ cursor = context.getContentResolver().query(uri, projection, null, null, orderBy); } else { // 修正查询条件的边界,把<改成<= cursor = context.getContentResolver().query(uri, projection, MediaStore.Images.Media.DATE_TAKEN + ">? and " + MediaStore.Images.Media.DATE_TAKEN + "<=?", new String[] {lastPhotoDate , timeStamp}, orderBy); } if(cursor == null) { return; } int column_index_data = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA); int column_index_date_taken = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_TAKEN); // 先判断cursor是否有数据,再处理 if(cursor.moveToFirst()){ // 直接读取第一条记录的DATE_TAKEN作为上次扫描的最后时间 Long photoDate = cursor.getLong(column_index_date_taken); lastPhotoDate = photoDate.toString(); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString("last_photo_date", lastPhotoDate); editor.apply(); // 初始化FaceDetector放到循环外,提升性能 FaceDetector faceDetector = new FaceDetector.Builder(context.getApplicationContext()) .setTrackingEnabled(false) .setLandmarkType(FaceDetector.ALL_LANDMARKS) .setMode(FaceDetector.FAST_MODE) .build(); do { ablosutePathOfImage = cursor.getString(column_index_data); final Bitmap myBitmap = BitmapFactory.decodeFile(ablosutePathOfImage); Frame frame = new Frame.Builder().setBitmap(myBitmap).build(); SparseArray<Face> sparseArray = faceDetector.detect(frame); for (int i = 0; i < sparseArray.size(); i++) { Face face = sparseArray.valueAt(i); float x1 = face.getPosition().x; float y1 = face.getPosition().y; Bitmap faceBitmap = Bitmap.createBitmap( myBitmap, (int) x1, (int) y1, (int) face.getWidth(), (int) face.getHeight()); classifyEmotions(faceBitmap, context, ablosutePathOfImage); } } while (cursor.moveToNext()); } // 记得关闭cursor,避免内存泄漏 cursor.close(); }
步骤2:额外优化点
- 把
FaceDetector的初始化放到循环外面,避免重复创建实例浪费资源 - 新增cursor空值判断,避免空指针异常
- 最后关闭cursor,防止内存泄漏
验证方法
- 卸载重装应用,首次启动确认全量扫描正常
- 退出应用,新增几张照片(可以拍照或下载)
- 重新启动应用,检查是否能获取到这些新增照片并进行情绪分析
内容的提问来源于stack exchange,提问作者Barış Balcıkoca
相关产品推荐
相关产品推荐

