无法访问Application类的Android模块如何正确获取Context避免内存泄漏及循环依赖?
可行解决方案
方案1:依赖反转+手动初始化(推荐,可控性最高)
通过在当前模块定义接口,上层app模块实现接口并注入实例的方式规避循环依赖,同时确保获取的是安全的全局ApplicationContext:
- 在当前需要Context的模块中定义上下文提供者接口,不需要依赖任何app层代码:
public interface ContextProvider { Context getApplicationContext(); }
- 在模块内增加全局初始化入口,存储接口实现类实例,静态持有ApplicationContext不会产生内存泄漏:
public class YourModuleEntry { private static ContextProvider sProvider; // 仅需在app模块的Application#onCreate中调用一次 public static void init(ContextProvider provider) { sProvider = provider; } // 模块内部获取全局Context的统一方法 public static Context getAppContext() { if (sProvider == null) { throw new IllegalStateException("模块未初始化,请先在Application中调用YourModuleEntry.init()"); } return sProvider.getApplicationContext(); } }
- app层不需要修改任何原有业务调用链,仅需在Application初始化时传入实现:
// app模块的Application类中 @Override public void onCreate() { super.onCreate(); YourModuleEntry.init(() -> getApplicationContext()); }
- 改造原方法:
private static synchronized String createDataPath(String path) { File fileDir = YourModuleEntry.getAppContext().getExternalFilesDir(path); return fileDir != null ? fileDir.getAbsolutePath() : null; }
优点:无额外性能损耗,完全可控,无依赖冲突
缺点:需要上层加一行初始化代码
方案2:ContentProvider自动初始化(无侵入首选)
利用Android系统中ContentProvider的初始化机制,无需上层做任何配置即可自动获取ApplicationContext:
- 在当前模块中实现一个空的ContentProvider:
public class ModuleContextProvider extends ContentProvider { private static Context sAppContext; public static Context getAppContext() { return sAppContext; } @Override public boolean onCreate() { // 此处getContext返回的就是Application上下文,直接缓存即可 sAppContext = getContext().getApplicationContext(); return true; } // 其余增删改查方法无需实现,直接返回默认值即可 @Nullable @Override public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { return null; } @Nullable @Override public String getType(@NonNull Uri uri) { return null; } @Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { return null; } @Override public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { return 0; } @Override public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { return 0; } }
- 在当前模块的
AndroidManifest.xml中注册该ContentProvider:
<provider android:name=".ModuleContextProvider" android:authorities="${applicationId}.yourmodule.contextprovider" android:exported="false" />
优点:完全无侵入,上层不需要做任何修改即可使用
缺点:会增加一个空的ContentProvider组件,对应用启动速度的影响几乎可以忽略,Jetpack AppStartup组件的底层就是基于该原理实现
方案3:依赖注入框架适配(适合已接入DI的项目)
如果项目已经接入Hilt、Dagger等依赖注入框架,直接通过DI提供ApplicationContext即可:
- 模块仅需依赖DI的注解包,不需要依赖app模块
- 在需要Context的位置直接注入全局上下文即可,以Hilt为例:
@Inject @ApplicationContext Context appContext;
注意事项
你之前遇到的静态Context内存泄漏问题,都是因为持有了Activity、Service等生命周期短于进程的组件实例,而静态持有ApplicationContext是完全安全的,它的生命周期和应用进程完全一致,不会产生任何内存泄漏,是Android官方推荐的全局Context获取方式。
内容的提问来源于stack exchange,提问作者Ludiras
相关产品推荐
相关产品推荐

