【发布时间】:2014-05-07 14:47:21
【问题描述】:
我想在ContentProvider 中注入一个单例SqliteOpenHelper。但是,似乎 ContentProvider 实例是在创建 Application 实例之前构建的(getApplicationContext() 返回 null)。什么时候可以注入数据库?我已经在构造函数和ContentProvider 的onCreate() 方法中尝试过。
【问题讨论】:
我想在ContentProvider 中注入一个单例SqliteOpenHelper。但是,似乎 ContentProvider 实例是在创建 Application 实例之前构建的(getApplicationContext() 返回 null)。什么时候可以注入数据库?我已经在构造函数和ContentProvider 的onCreate() 方法中尝试过。
【问题讨论】:
这有一个简单的原因,
onCreate()provider 被调用 在应用程序中的适当方法之前。你可以 用另一种方法创建组件 应用程序,例如attachBaseContext。
1 将您的逻辑从您的应用程序中的onCreate 移动到attachBaseContext。
@Override
public void attachBaseContext(Context base){
super.attachBaseContext(base);
mApplicationComponent = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
mApplicationComponent.inject(this);
}
2 您现在可以在 ContentProvider 中的 inject 中 OnCreate:
public boolean onCreate() {
YourMainApplication.get(getContext()).getComponent().inject(this);
return true;
}
免责声明: 来自此俄罗斯博客的@LeEnot 的全部功劳:Dagger2 Inject in Content Provider。为方便起见,此处列出了答案,因为它没有英文版本。
【讨论】:
基于@Mick 的回答和@LeEnot 文章,我在 Medium 上发布了一篇使用 Dagger 2 新功能的文章。
https://medium.com/@pedro.henrique.okawa/content-providers-dependency-injection-dagger-2-4ee3e49777b
但基本上我把 Dagger-Android 依赖:
// In my case I used 2.16 version
implementation 'com.google.dagger:dagger-android:[DaggerVersion]'
使用 HasContentProviderInjector 接口实现了我的自定义 Application 类,并更改为在 attachBaseContext 上注入我的依赖项:
class App : Application(), HasActivityInjector, HasContentProviderInjector {
@Inject
lateinit var androidInjector: DispatchingAndroidInjector<Activity>
@Inject
lateinit var contentProviderInjector: DispatchingAndroidInjector<ContentProvider>
override fun activityInjector() = androidInjector
override fun contentProviderInjector() = contentProviderInjector
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
setupDependencyInjection()
}
/**
* Injects the app component
*/
private fun setupDependencyInjection() {
DaggerAppComponent.builder().application(this).build().inject(this)
}
}
并在我的 ContentProvider 类上注入了我的 databaseHelper 实例:
class VoIPAppProvider: ContentProvider() {
@Inject
lateinit var databaseHelper: DatabaseHelper
override fun onCreate(): Boolean {
AndroidInjection.inject(this)
return true
}
override fun insert(uri: Uri?, contentValues: ContentValues?): Uri? {
...
return uri
}
override fun query(uri: Uri?, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor {
...
return cursor
}
override fun getType(uri: Uri?): String {
...
return type
}
override fun update(uri: Uri?, contentValues: ContentValues?, selection: String?, selectionArgs: Array<out String>?): Int {
...
return rowsUpdated
}
override fun delete(uri: Uri?, selection: String?, selectionArgs: Array<out String>?): Int {
...
return rowsDeleted
}
}
PS:我决定不使用 DaggerContentProvider 类,因为我们大多数人可能已经有一些 Android 组件的基类。
希望对您有所帮助,再次感谢@Mick 和@LeEnot
【讨论】:
正如 Alex Baker 所指出的,该问题的解决方案是在第一次调用操作(查询、插入、更新、删除)时推迟注入。
举个例子:
这行不通:
public class YourProviderNull extends ContentProvider {
@Inject
YourSQLHelper yourHelper;
@Override
public boolean onCreate() {
YourActivity.getComponent().inject(this); //NPE!!!
//other logic
return true;
}
@Override
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
SQLiteDatabase db = yourHelper.getReadableDatabase();
//other logic
}
@Override
public Uri insert(@NonNull Uri uri, ContentValues values) {
SQLiteDatabase db = yourHelper.getWritableDatabase();
//other logic
}
@Override
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = yourHelper.getWritableDatabase();
//other logic
}
@Override
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
SQLiteDatabase db = yourHelper.getWritableDatabase();
//other logicreturn db.update(resolveTableNameForUri(uri), values, selection, selectionArgs);
}
}
但这会正常工作:
public class YourProviderWorking extends ContentProvider {
@Inject
YourSQLHelper yourHelper;
@Override
public boolean onCreate() {
//other logic
return true;
}
@Override
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
if(yourHelper == null){
deferInit();
}
SQLiteDatabase db = yourHelper.getReadableDatabase();
//other logic
}
@Override
public Uri insert(@NonNull Uri uri, ContentValues values) {
if(yourHelper == null){
deferInit();
}
SQLiteDatabase db = yourHelper.getWritableDatabase();
//other logic
}
@Override
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
if(yourHelper == null){
deferInit();
}
SQLiteDatabase db = yourHelper.getWritableDatabase();
//other logic
}
@Override
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
if(yourHelper == null){
deferInit();
}
SQLiteDatabase db = yourHelper.getWritableDatabase();
//other logicreturn db.update(resolveTableNameForUri(uri), values, selection, selectionArgs);
}
private void deferInit(){
YourActivity.getComponent().inject(this);
}
}
【讨论】:
我遇到了同样的问题,不得不推迟注入,直到需要数据库。你或许可以使用 Dagger 的lazy injection 来达到同样的效果。
来自内容提供者的onCreate documentation:
您应该将重要的初始化(例如打开、升级和扫描数据库)推迟到使用内容提供程序之前
显然,这个建议不容忽视。 Implementing the onCreate() method 提供了一个使用 SQLiteOpenHelper 和内容提供程序的示例。
【讨论】:
我知道这个问题已经有了答案,但我发布了我的经验希望这对某人有所帮助
我添加了一个名为 ContentProviderBuilder 的模块:
@Module
abstract class ContentProviderBuilder {
@ActivityScope
@ContributesAndroidInjector
abstract fun myCustomSuggestionProvider(): MyCustomSuggestionProvider?
}
然后我将此模块添加到我的组件中,并从 DaggerContentProvider 扩展了 contentProvider
您必须将super.onCreate 添加到您的cotnentProvider 中,就是这样
这是我的组件:
@Singleton
@ApplicationScope
@Component(modules = [AppModule::class, AndroidSupportInjectionModule::class,
RemoteModule::class, ContentProviderBuilder::class, ActivityBuilder::class,
FragmentBuilder::class, ViewModelModule::class])
interface ApplicationComponent: AndroidInjector<MyApplication> {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: MyApplication): Builder
fun build(): ApplicationComponent
}
override fun inject(app: MyApplication)
}
【讨论】: