Search code examples
javaandroidandroid-roomdagger-2dagger-hilt

error: [Dagger/DependencyCycle] Found a dependency cycle error after using ActivityRetainedScoped


I am working on app that uses MVVM, Room and dagger hilt, I have implemented first part that handle server response, remote DataSource, viewmodel and all this stuff, and it's worked fine, but after I implemented the database module, I got this error when I tried to build

public abstract static class SingletonC implements BaseApplication_GeneratedInjector,
                         ^
      com.test.dummyappv3.data.database.ItemDAO is injected at
          com.test.dummyappv3.di.DatabaseModule.provideDao(itemDAO)
      com.test.dummyappv3.data.database.ItemDAO is injected at
          com.test.dummyappv3.data.LocalDataSource(itemDAO)
      com.test.dummyappv3.data.LocalDataSource is injected at
          com.test.dummyappv3.data.Repository(�, localDataSource)
      com.test.dummyappv3.data.Repository is injected at
          com.test.dummyappv3.viewmodels.PostViewModel(repository)
      com.test.dummyappv3.viewmodels.PostViewModel is injected at
          com.test.dummyappv3.viewmodels.PostViewModel_HiltModules.BindsModule.binds(vm)
      @dagger.hilt.android.internal.lifecycle.HiltViewModelMap java.util.Map<java.lang.String,javax.inject.Provider<androidx.lifecycle.ViewModel>> is requested at
          dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.ViewModelFactoriesEntryPoint.getHiltViewModelMap() [com.test.dummyappv3.BaseApplication_HiltComponents.SingletonC ? com.test.dummyappv3.BaseApplication_HiltComponents.ActivityRetainedC ? com.test.dummyappv3.BaseApplication_HiltComponents.ViewModelC]

and I think it's related with ActivityRetainedScoped in Repository Class

@ActivityRetainedScoped
public class Repository {

    public RemoteDataSource remoteDataSource;
    public LocalDataSource localDataSource;

    @Inject
    public Repository(RemoteDataSource remoteDataSource, LocalDataSource localDataSource) {
        this.remoteDataSource = remoteDataSource;
        this.localDataSource = localDataSource;
    }
}

DatabaseModule

@InstallIn(SingletonComponent.class)
@Module
public class DatabaseModule {

    @Singleton
    @Provides
    public static ItemsDatabase provideDatabase(@ApplicationContext Context context){
        return Room.databaseBuilder(context,ItemsDatabase.class,"items_database")
                .fallbackToDestructiveMigration()
                .build();

    }

    @Singleton
    @Provides
    public static ItemDAO provideDao(ItemDAO itemDAO){
        return itemDAO;
    }
}

RemoteDataSource

    private final PostAPIService postAPIService;


    @Inject
    public RemoteDataSource(PostAPIService postAPIService) {
        this.postAPIService = postAPIService;
    }

    public Observable<PostList> getPostList(String URL) {
        return postAPIService.getPostList(URL);
    }

    public Observable<PostList> getPostListByLabel(String URL) {
        return postAPIService.getPostListByLabel(URL);
    }

}

LocalDataSource

public class LocalDataSource {

    private final ItemDAO itemDAO;

    @Inject
    public LocalDataSource(ItemDAO itemDAO) {
        this.itemDAO = itemDAO;
    }

    public Completable insertItem(Item item){
        return itemDAO.insert(item);
    }

    public Flowable<List<Item>> getAlItems(){
        return itemDAO.getAlItems();
    }

}

and finally the viewmodel

@HiltViewModel
public class PostViewModel extends ViewModel {

    public static final String TAG = "PostViewModel";


    private Repository repository = null;
    public MutableLiveData<PostList> postListMutableLiveData = new MutableLiveData<>();
    public MutableLiveData<String> finalURL = new MutableLiveData<>();
    public MutableLiveData<String> token = new MutableLiveData<>();
    public MutableLiveData<String> label = new MutableLiveData<>();

    public MutableLiveData<Boolean> ifAnythingWrongHappened = new MutableLiveData<>();

    @Inject
    public PostViewModel(Repository repository) {
        this.repository = repository;
    }

    public final LiveData<List<Item>> getAllPostsFromDB
            = LiveDataReactiveStreams.fromPublisher(repository.localDataSource.getAlItems());

    @SuppressLint("CheckResult")
    public void getPosts() {
        ifAnythingWrongHappened.setValue(false);
        Log.e(TAG, finalURL.getValue());

        repository.remoteDataSource.getPostList(finalURL.getValue())
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<PostList>() {
                    @Override
                    public void onSubscribe(@io.reactivex.rxjava3.annotations.NonNull Disposable d) {

                    }

                    @Override
                    public void onNext(@io.reactivex.rxjava3.annotations.NonNull PostList postList) {
                        Log.e(TAG, postList.getNextPageToken());
                        token.setValue(postList.getNextPageToken());
                        postListMutableLiveData.setValue(postList);
                        for (int i = 0; i < postList.getItems().size(); i++) {
                            repository.localDataSource.insertItem(postList.getItems().get(i))
                                    .subscribeOn(Schedulers.computation())
                                    .subscribe(new CompletableObserver() {
                                        @Override
                                        public void onSubscribe(@io.reactivex.rxjava3.annotations.NonNull Disposable d) {

                                        }

                                        @Override
                                        public void onComplete() {

                                        }

                                        @Override
                                        public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) {
                                            Log.e(TAG, "onError: "+e.getMessage() );
                                            ifAnythingWrongHappened.setValue(true);
                                        }
                                    });
                        }
                        finalURL.setValue(finalURL.getValue() + "&pageToken=" + token.getValue());

                    }

                    @Override
                    public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) {
                        Log.e(TAG, e.getMessage() + e.getCause());
                        ifAnythingWrongHappened.setValue(true);
                    }

                    @Override
                    public void onComplete() {

                    }
                });
}

I tried to delete this @ActivityRetainedScoped and @ApplicationContext but the same error also I tried the @ViewModelScoped in the viewmodel


Solution

  •     @Singleton
        @Provides
        public static ItemDAO provideDao(ItemDAO itemDAO){
            return itemDAO;
        }
    

    This is the circular dependency. The important part of that error trace to look at is everything from the first entry (ItemDAO) up to and excluding the second appearance of that same class. The rest of the error message is showing one use of ItemDAO in the dependency graph, since Dagger only validates reachable dependencies.

    I assume you intended to do something like this instead:

        @Singleton
        @Provides
        public static ItemDAO provideDao(ItemsDatabase database){
            return database.itemDAO();
        }