在我的应用程序中,各种组件将连接尝试从Android MediaStore获取照片和音乐信息。
我像这样遇到DatabaseObjectNotClosedExcepton:
? E/CursorLeakDetecter: PossibleCursorLeak:content://media/external/images/media,QueryCounter:6
? E/CursorLeakDetecter: android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
? E/CursorLeakDetecter: at android.content.ContentResolver.query(ContentResolver.java:491)
? E/CursorLeakDetecter: at android.content.ContentResolver.query(ContentResolver.java:405)
? E/CursorLeakDetecter: at com.company.app.media.Photo.getAllPhotosInAlbumCursor(Photo.java:229)
? E/CursorLeakDetecter: at com.company.app.media.Photo.getAllPhotosInAlbumCount(Photo.java:263)
? E/CursorLeakDetecter: at com.company.app.view.PhotoAlbumSizeRetriever$1.run(PhotoAlbumSizeRetriever.java:89)
? E/CursorLeakDetecter: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
? E/CursorLeakDetecter: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
? E/CursorLeakDetecter: at java.lang.Thread.run(Thread.java:848)
以下是getAllPhotosAlbumCount
函数的样子:
public int getAllPhotoAlbumsCount(int flags) {
try (Cursor f = getAllPhotoAlbumsCursor(flags)) {
return f != null ? f.getCount() : 0;
}
}
getAllPhotoAlbumsCursor
:
public Cursor getAllPhotoAlbumsCursor(int flags) {
final CursorFields l = getAllPhotoAlbumsFields(flags);
return context.getContentResolver().query(l.getUri(),
l.getProjection(), l.getSelection(), l.getSelectionArgs(), l.getSortOrder());
}
根据我的理解,Cursor
实现了Closeable
,它应该很好地处理Cursor。
我尝试了其他版本(try
,catch
,finally
),这也没有帮助。
这些函数是从各种线程执行的,包括执行程序和后台线程,偶尔也会在UI线程上执行。
也会从View
和RecyclerView
适配器中的ViewPager
调用它们。
这种行为的潜在原因是什么?
答案 0 :(得分:2)
像往常一样,阅读Android源代码会有很大帮助。
CursorLeakDetecter
用于课程ActivityThread
,这是重要的一点:
// ...
if(uriMap.get(uri) == null) {
uriMap.put(uri,1);
}else {
uriMap.put(uri,uriMap.get(uri)+1);
}
// when uri>5, print log information
if(uriMap.get(uri) >= 5) {
Log.e("CursorLeakDetecter", "PossibleCursorLeak:"+uri+",QueryCounter:"+uriMap.get(uri), stackTrace);
}
// ...
因此,假设查询在被请求5次时是潜在的泄漏。回顾上面的方法,让我们检查一下如何检索相册列表:
return new CursorFields(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, PHOTO_PROJECTION,
MediaStore.Images.Media.BUCKET_ID + " = ? " + AND_PHOTO_FILTER, new String[]{Long.toString(albumId)},
MediaStore.Images.Media.DEFAULT_SORT_ORDER + " ASC");
正如我所提到的,该函数可能会被各种线程多次调用,因此对于不同的存储桶,可以多次调用相同的 URI。< / p>
解决方法是附加查询参数:
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
.buildUpon()
.appendQueryParameter("bucket", Long.toString(albumId))
.build()
这样,每个专辑的查询都不同,并且不会生成CursorLeak错误消息。