DatabaseObjectNotClosedException甚至使用try-with-resources

时间:2015-11-20 08:18:29

标签: android

在我的应用程序中,各种组件将连接尝试从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。

我尝试了其他版本(trycatchfinally),这也没有帮助。

这些函数是从各种线程执行的,包括执行程序和后台线程,偶尔也会在UI线程上执行。

也会从ViewRecyclerView适配器中的ViewPager调用它们。

这种行为的潜在原因是什么?

1 个答案:

答案 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错误消息。