我想在SqliteOpenHelper
中注入一个单身ContentProvider
。但是,似乎在创建Application实例之前构建了ContentProvider
实例(getApplicationContext()
返回null)。我什么时候可以注入数据库?我已尝试使用构造函数和onCreate()
的{{1}}方法。
答案 0 :(得分:7)
这有一个简单的原因,
onCreate()
提供程序被调用 在应用程序中的适当方法之前。您可以 在另一个方法中创建一个组件 应用程序,例如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;
}
:
def appendCount(l: List[String]): List[String] = {
// Since we're doing zero-based counting, we need to use `getOrElse(e, -1) + 1`
// to indicate a first-time element count as 0.
val counts =
l.foldLeft(Map[String, Int]())((acc, e) =>
acc + (e -> (acc.getOrElse(e, -1) + 1))
)
val (appendedList, _) =
l.foldRight(List[String](), counts){ case (e, (li, m)) =>
// Prepend the element with its count to the accumulated list.
// Decrement that element's count within the map of element counts
(s"$e${m(e)}" :: li, m + (e -> (m(e) - 1)))
}
appendedList
}
免责声明: 来自俄罗斯博客@LeEnot的全部积分:Dagger2 Inject in Content Provider。为方便起见,此处列出了答案,因为它没有英文版本。
答案 1 :(得分:6)
我遇到了同样的问题,不得不推迟注射直到需要数据库。您或许可以使用Dagger的lazy injection来达到同样的效果。
来自内容提供商的onCreate documentation:
您应该推迟非常重要的初始化(例如打开,升级和扫描数据库),直到使用内容提供程序
显然这个建议不能被忽视。 Implementing the onCreate() method提供了一个使用SQLiteOpenHelper和内容提供程序的示例。
答案 2 :(得分:6)
正如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);
}
}
答案 3 :(得分:0)
基于@Mick答案和@LeEnot文章,我在Diddle上发布了一篇使用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)
}
}
并将我的databaseHelper实例注入到我的ContentProvider类中:
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
答案 4 :(得分:0)
我知道问题已经有了答案,但是我发表经验希望这对某人有帮助
我添加了一个名为ContentProviderBuilder
的模块:
@Module
abstract class ContentProviderBuilder {
@ActivityScope
@ContributesAndroidInjector
abstract fun myCustomSuggestionProvider(): MyCustomSuggestionProvider?
}
然后我将此模块添加到组件中,并从DaggerContentProvider
您必须将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)
}