You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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未被正确关闭

  1. SimpleCache的release()方法仅释放自身缓存资源,不会主动关闭StandaloneDatabaseProvider持有的SQLite数据库连接;
  2. 若每次播放都重新创建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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 05:53:20