Search code examples
androiddependency-injectionandroid-roomdagger

Dependency Injection in multiple Activities with Dagger and Room


I am fairly new to Android coming from a c# background and was came across dependency injection with dagger. I managed to create a simple example but was wondering how i can use my repository (i tried to use the repository pattern here) in another activity. So my structure is following: Project structure When i try to create a workout in my MainActivity class it works just fine. So i added following line to my AddWorkoutActivity class

@Inject
public WorkoutRepository workoutRepository;

But when i try to save the workout I always get a null reference error on the workoutRepository so i do miss something in my understanding how the DI works here. Maybe someone can give me a hint what i have to do so my repository is instantiated the correct way in my Activity.

// Edit: I think the problem is that i call the DaggerAppComponent.builder()... in the onCreate Method in my MainActivity class, so the AddWorkoutActivity won`t know anything of that. But where do i initialize the DaggerAppComponent so that both activities can acces it correctly?

Best regards

App Component:

package jh.projects.wodm8.di;

import android.app.Application;
import javax.inject.Singleton;
import dagger.Component;
import jh.projects.wodm8.view.MainActivity;
import jh.projects.wodm8.data.WorkoutDao;
import jh.projects.wodm8.repository.DemoDatabase;
import jh.projects.wodm8.repository.WorkoutRepository;

@Singleton
@Component(modules = {AppModule.class, RoomModule.class})
public interface AppComponent {

void inject(MainActivity mainActivity);

WorkoutDao workoutDao();

DemoDatabase demoDatabase();

WorkoutRepository productRepository();

Application application();
}

AppModule

package jh.projects.wodm8.di;

import android.app.Application;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;

@Module
public class AppModule {

    Application mApplication;

    public AppModule(Application application) {
        mApplication = application;
    }

    @Provides
    @Singleton
    Application providesApplication() {
        return mApplication;
    }
}

RoomModule

package jh.projects.wodm8.di;

import android.app.Application;

import androidx.room.Room;

import javax.inject.Singleton;

import dagger.Module;
import dagger.Provides;
import jh.projects.wodm8.data.WorkoutDao;
import jh.projects.wodm8.repository.DemoDatabase;
import jh.projects.wodm8.repository.WorkoutDataSource;
import jh.projects.wodm8.repository.WorkoutRepository;

@Module
public class RoomModule{

    private DemoDatabase demoDatabase;

    public RoomModule(Application mApplication) {
        demoDatabase = Room.databaseBuilder(mApplication, DemoDatabase.class, "demo-db").allowMainThreadQueries().build();
    }

    @Singleton
    @Provides
    DemoDatabase providesRoomDatabase() {
        return demoDatabase;
    }

    @Singleton
    @Provides
    WorkoutDao providesWorkoutDao(DemoDatabase demoDatabase) {
        return demoDatabase.getWorkoutDao();
    }

    @Singleton
    @Provides
    WorkoutRepository providesWorkoutRepository(WorkoutDao workoutDao) {
        return new WorkoutDataSource(workoutDao);
    }
}

WorkoutDataSource

package jh.projects.wodm8.repository;

import androidx.lifecycle.LiveData;

import java.util.List;

import javax.inject.Inject;

import jh.projects.wodm8.data.Workout;
import jh.projects.wodm8.data.WorkoutDao;

public class WorkoutDataSource implements WorkoutRepository {

    private WorkoutDao workoutDao;

    @Inject
    public WorkoutDataSource(WorkoutDao workoutDao) {
        this.workoutDao = workoutDao;
    }

    @Override
    public LiveData<Workout> findById(int id) {
        return workoutDao.findById(id);
    }

    @Override
    public LiveData<List<Workout>> findAll() {
        return workoutDao.findAll();
    }

    @Override
    public void insert(Workout workout) {
        workoutDao.insert(workout);
    }

    @Override
    public int delete(Workout workout) {
        return workoutDao.delete(workout);
    }
}

WorkoutRepository

package jh.projects.wodm8.repository;

import androidx.lifecycle.LiveData;

import java.util.List;

import jh.projects.wodm8.data.Workout;

public interface WorkoutRepository {
    LiveData<Workout> findById(int id);

    LiveData<List<Workout>> findAll();

    void insert(Workout workout);

    int delete(Workout workout);

}

MainActivity package jh.projects.wodm8.view;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import com.google.android.material.floatingactionbutton.FloatingActionButton;

import java.util.List;

import javax.inject.Inject;

import jh.projects.wodm8.R;
import jh.projects.wodm8.data.Workout;
import jh.projects.wodm8.di.AppModule;
import jh.projects.wodm8.di.DaggerAppComponent;
import jh.projects.wodm8.di.RoomModule;
import jh.projects.wodm8.repository.WorkoutRepository;

public class MainActivity extends AppCompatActivity {

    @Inject
    public WorkoutRepository workoutRepository;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(view.getContext(), AddWorkoutActivity.class);
                startActivity(intent);
            }
        });

        DaggerAppComponent.builder()
                .appModule(new AppModule(getApplication()))
                .roomModule(new RoomModule(getApplication()))
                .build()
                .inject(this);

        workoutRepository.findAll().observe(this, new Observer<List<Workout>>() {
            @Override
            public void onChanged(@Nullable List<Workout> products) {
                Toast.makeText(MainActivity.this, String.format("Product size: %s", products.size()), Toast.LENGTH_SHORT).show();
            }
        });
    }
}

AddWorkoutActivity

package jh.projects.wodm8.view;

import androidx.appcompat.app.AppCompatActivity;

import jh.projects.wodm8.R;
import jh.projects.wodm8.data.Workout;
import jh.projects.wodm8.di.AppModule;
import jh.projects.wodm8.di.DaggerAppComponent;
import jh.projects.wodm8.di.RoomModule;
import jh.projects.wodm8.repository.DemoDatabase;
import jh.projects.wodm8.repository.WorkoutRepository;

import android.app.Application;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import javax.inject.Inject;

public class AddWorkoutActivity extends AppCompatActivity {

    @Inject
    public WorkoutRepository workoutRepository;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_workout);

        final Button button = findViewById(R.id.saveWorkout);
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Workout workout = new Workout();
                EditText txtTitle = (EditText) findViewById(R.id.workoutTitleText);
                workout.setTitle(txtTitle.getText().toString());

                try {
                    workoutRepository.insert(workout);
                } catch (Exception e) {
                    Log.v("AddWorkout", e.getMessage());
                }

                Toast.makeText(AddWorkoutActivity.this, "Inserted", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

// EDIT: Thanks to a_local_nobody for the links to start with Dagger: https://youtu.be/Qwk7ESmaCq0

I found another source to start with dagger that helped me too: https://www.raywenderlich.com/262-dependency-injection-in-android-with-dagger-2-and-kotlin#toc-anchor-005


Solution

  • to give you a bit of a more complete answer, taken from this online resource https://github.com/MindorksOpenSource/android-dagger2-example/blob/master/app/src/main/java/com/mindorks/example/android_dagger2_example/DemoApplication.java

    (i'm just going over it briefly to give you a better understanding)

    Create an application class which extends Application and implement this :

         @Override
         public void onCreate() {
            super.onCreate();
            applicationComponent = DaggerApplicationComponent
                                        .builder()
                                        .applicationModule(new ApplicationModule(this))
                                        .build();
            applicationComponent.inject(this);
    }
    

    you have to define your own applicationComponent though, just going through this briefly. after that, you should be able to use your repositories in several different places with the annotation of @Inject where you need them, just remember to actually call the injection where you need it, making use of

    getActivityComponent().inject(this); or AndroidInjection.inject(this) where you need to, hopefully this github example and the tutorials are enough to answer some of your questions