使用ExoPlayer搭配SimpleCache时出现资源泄漏问题求助
ExoPlayer SimpleCache 触发 LeakedCloseableViolation 异常的原因及修复
问题场景
使用ExoPlayer搭配SimpleCache实现流媒体缓存时,已调用simpleCache.release()释放缓存,但每次启动新文件播放时,仍抛出LeakedCloseableViolation异常。
初始化代码
... this.simpleCache = new SimpleCache(new File(context.getCacheDir(), "streamCaching"), new LeastRecentlyUsedCacheEvictor(25 * 1024 * 1024), new StandaloneDatabaseProvider(context)); factory = new CacheDataSource.Factory().setCache(this.simpleCache).setUpstreamDataSourceFactory(httpDataSourceFactory); ...
释放代码
this.player.release(); if (this.simpleCache != null) { this.simpleCache.release(); this.simpleCache = null; }
报错日志
StrictMode policy violation: android.os.strictmode.LeakedClosableViolation: A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks. at android.os.StrictMode$AndroidCloseGuardReporter.report(StrictMode.java:1987) at dalvik.system.CloseGuard.warnIfOpen(CloseGuard.java:345) at android.database.sqlite.SQLiteDatabase.dispose(SQLiteDatabase.java:427) at android.database.sqlite.SQLiteDatabase.finalize(SQLiteDatabase.java:408) at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:291) at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:278) at java.lang.Daemons$Daemon.run(Daemons.java:139) at java.lang.Thread.run(Thread.java:920) Caused by: java.lang.Throwable: Explicit termination method 'close' not called at dalvik.system.CloseGuard.openWithCallSite(CloseGuard.java:295) at dalvik.system.CloseGuard.open(CloseGuard.java:263) at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:1415) at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:1355) at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:996) at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:470) at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:437) at com.google.android.exoplayer2.upstream.cache.CachedContentIndex$DatabaseStorage.exists(CachedContentIndex.java:799) at com.google.android.exoplayer2.upstream.cache.CachedContentIndex.initialize(CachedContentIndex.java:201) at com.google.android.exoplayer2.upstream.cache.SimpleCache.initialize(SimpleCache.java:582) at com.google.android.exoplayer2.upstream.cache.SimpleCache.access$000(SimpleCache.java:49) at com.google.android.exoplayer2.upstream.cache.SimpleCache$1.run(SimpleCache.java:268)
异常原因
从报错栈可定位核心问题:StandaloneDatabaseProvider关联的SQLiteDatabase未被正确关闭
- SimpleCache的
release()方法仅释放自身缓存资源,不会主动关闭StandaloneDatabaseProvider持有的SQLite数据库连接; - 若每次播放都重新创建SimpleCache实例,旧实例对应的
StandaloneDatabaseProvider会被丢弃,但它的SQLite连接未关闭,被Android StrictMode检测为资源泄漏。
修复方案
方案1:全局复用SimpleCache(推荐)
SimpleCache设计为可全局复用的组件,无需每次播放都销毁重建。将其封装为单例,在App启动时初始化,退出时统一释放:
public class CacheManager { private static SimpleCache simpleCache; public static synchronized SimpleCache getInstance(Context context) { if (simpleCache == null) { File cacheDir = new File(context.getCacheDir(), "streamCaching"); LeastRecentlyUsedCacheEvictor evictor = new LeastRecentlyUsedCacheEvictor(25 * 1024 * 1024); StandaloneDatabaseProvider provider = new StandaloneDatabaseProvider(context); simpleCache = new SimpleCache(cacheDir, evictor, provider); } return simpleCache; } public static synchronized void release() { if (simpleCache != null) { simpleCache.release(); simpleCache = null; } } }
方案2:手动关闭StandaloneDatabaseProvider(若必须销毁重建)
初始化时保留StandaloneDatabaseProvider的引用,释放阶段手动调用其close()方法关闭数据库连接:
// 初始化代码 private StandaloneDatabaseProvider databaseProvider; private SimpleCache simpleCache; ... this.databaseProvider = new StandaloneDatabaseProvider(context); this.simpleCache = new SimpleCache(new File(context.getCacheDir(), "streamCaching"), new LeastRecentlyUsedCacheEvictor(25 * 1024 * 1024), databaseProvider); factory = new CacheDataSource.Factory().setCache(this.simpleCache).setUpstreamDataSourceFactory(httpDataSourceFactory);
// 释放代码 this.player.release(); if (this.simpleCache != null) { this.simpleCache.release(); this.simpleCache = null; } // 手动关闭数据库连接 if (this.databaseProvider != null) { this.databaseProvider.close(); this.databaseProvider = null; }
内容的提问来源于stack exchange,提问作者user1026605
相关产品推荐
相关产品推荐

