Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inject database in a ContentProvider with dagger

Tags:

android

dagger

I want to inject a singleton SqliteOpenHelper in a ContentProvider. However, it seems that the ContentProvider instance is being built before the Application instance is being created (getApplicationContext() returns null). When can I inject the database? I've tried in the constructor and in the onCreate() method of the ContentProvider.

like image 913
Catalin Morosan Avatar asked May 07 '14 14:05

Catalin Morosan


3 Answers

Simple Solution

There is a simple reason for this, onCreate() provider is called before the appropriate method in the Application. You can make the creation of a component in another method of the Application, for example attachBaseContext.


1 Move your logic from onCreate to attachBaseContext in your Application.

@Override
public void attachBaseContext(Context base){

    super.attachBaseContext(base);

    mApplicationComponent = DaggerApplicationComponent.builder()
            .applicationModule(new ApplicationModule(this))
            .build();
    mApplicationComponent.inject(this);
}

2 You can now inject in OnCreate in your ContentProvider:

public boolean onCreate() {

    YourMainApplication.get(getContext()).getComponent().inject(this);

    return true;
}

Disclaimer: Full Credits to @LeEnot from this russian blog: Dagger2 Inject in Content Provider. The answer is listed here for convenience as it is not available in English.

like image 153
Mick Avatar answered Nov 15 '22 19:11

Mick


I faced the same issue and had to defer injection until the database was needed. You might be able to use Dagger's lazy injection to achieve the same effect.

From the content provider's onCreate documentation:

You should defer nontrivial initialization (such as opening, upgrading, and scanning databases) until the content provider is used

Apparently this suggestion cannot be disregarded. Implementing the onCreate() method provides an example using an SQLiteOpenHelper with a content provider.

like image 35
Alex Baker Avatar answered Nov 15 '22 17:11

Alex Baker


As Alex Baker pointed out, the solution of the issue is to defer the injection when an operation (query, insert, update, delete) is called for the first time.

to make an example:

This won't work:

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);
}

}

but this will work correctly:

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);
}

}

like image 36
Noya Avatar answered Nov 15 '22 18:11

Noya