无法获得Dagger来插入Viewmodel

问题描述 投票:0回答:1

我一直在遵循在Android应用程序中设置DI的指南,据我所知,我已经正确设置了所有设置。但是,出现以下错误:

java.lang.RuntimeException: Cannot create an instance of class com.topper.topper.ui.viewmodel.ProfileViewModel

以下是我的课程的简化版本(为简洁起见:

ActivityModule

@Module
public abstract class ActivityModule {
    @ContributesAndroidInjector(modules = FragmentModule.class)
    abstract MainActivity contributeMainActivity();
}

FragmentModule

@Module
public abstract class FragmentModule {
    @ContributesAndroidInjector
    abstract ProfileFragment contributeProfileFragment();
}

ViewModelModule

@Module
public abstract class ViewModelModule {
    @Binds
    @IntoMap
    @ViewModelKey(ProfileViewModel.class)
    abstract ViewModel bindProfileViewModel(ProfileViewModel profileViewModel);

    @Binds
    abstract ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory factory);
}

AppModule

@Module(includes = ViewModelModule.class)
public class AppModule {

    @Provides
    @Singleton
    TopperDB provideDatabase(Application application) {
        return Room.databaseBuilder(application,
                TopperDB.class, "TopperDB.db")
                .build();
    }

    @Provides
    @Singleton
    CachedImageDao provideCachedImageDao(TopperDB database) {
        return database.cachedImageDao();
    }

    @Provides
    @Singleton
    Executor provideExecutor() {
        return Executors.newSingleThreadExecutor();
    }

    @Provides
    @Singleton
    CachedImageRepository provideCachedImageRepository(CachedImageDao cachedImageDao, Executor executor) {
        return new CachedImageRepository(cachedImageDao, executor);
    }
}

AppComponent

@Singleton
@Component(modules = {AndroidSupportInjectionModule.class, AndroidInjectionModule.class, ActivityModule.class, FragmentModule.class, AppModule.class})
public interface AppComponent {

    void inject(TopperApp app);

    @Component.Builder
    interface Builder {
        @BindsInstance
        Builder application(Application application);

        AppComponent build();
    }
}

ViewModelKey

@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@MapKey
public @interface ViewModelKey {
    Class<? extends ViewModel> value();
}

ViewModelFactory

@Singleton
public class ViewModelFactory implements ViewModelProvider.Factory {

    private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;

    @Inject
    ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
        this.creators = creators;
    }

    @NotNull
    @SuppressWarnings("unchecked")
    @Override
    public <T extends ViewModel> T create(@NotNull Class<T> modelClass) {
        Provider<? extends ViewModel> creator = creators.get(modelClass);
        if (creator == null) {
            for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : creators.entrySet()) {
                if (modelClass.isAssignableFrom(entry.getKey())) {
                    creator = entry.getValue();
                    break;
                }
            }
        }
        if (creator == null) {
            throw new IllegalArgumentException("unknown model class " + modelClass);
        }
        try {
            return (T) creator.get();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

CachedImageRepository

@Singleton
public class CachedImageRepository extends BaseRepository {

    private final CachedImageDao cachedImageDao;
    private final Executor executor;

    @Inject
    public CachedImageRepository(CachedImageDao cachedImageDao, Executor executor) {
        this.cachedImageDao = cachedImageDao;
        this.executor = executor;
    }
}

ProfileViewModel

public class ProfileViewModel extends ViewModel {

    private CachedImageRepository cachedImageRepo;

    @Inject
    public ProfileViewModel(CachedImageRepository cachedImageRepo) {
        this.cachedImageRepo = cachedImageRepo;
    }
}

ProfileFragment

public class ProfileFragment extends BaseFragment {

    @Inject
    ViewModelProvider.Factory viewModelFactory;

    private ProfileViewModel mViewModel;

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        configureDagger();
    }

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {

        FragmentProfileBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_profile, container, false);

        mViewModel = ViewModelProviders.of(this.requireActivity(), viewModelFactory).get(ProfileViewModel.class);
        mViewModel.init();
        binding.setProfileViewModel(mViewModel);

        return binding.getRoot();
    }

    private void configureDagger() {
        AndroidSupportInjection.inject(this);
    }

}

MainActivity

public class MainActivity extends AppCompatActivity implements ProgressDisplay, HasSupportFragmentInjector {

    @Inject
    DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;

    @Inject
    ViewModelProvider.Factory viewModelFactory;

    private AppBarLayout appBarLayout;

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

        this.configureDagger();
    }

    private void configureDagger() {
        AndroidInjection.inject(this);
    }

}

TopperApp

public class TopperApp extends Application implements HasActivityInjector {

    public Context ctx;
    @Inject
    DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        this.initDagger();
        ctx = getApplicationContext();
    }

    public Context getAppContext() {
        return ctx;
    }

    @Override
    public DispatchingAndroidInjector<Activity> activityInjector() {
        return dispatchingAndroidInjector;
    }

我想我已经包括了以上所有相关细节,但是如果我错过了任何事情,请告诉我。

任何帮助将不胜感激,为此我已经将头撞墙了几天。

谢谢。

编辑:如果有帮助,我尝试向ProfileViewModel添加一个空的构造函数,这将导致以下错误:

java.lang.NullPointerException: Attempt to invoke virtual method 'void com.topper.topper.data.repo.CachedImageRepository.cacheImage(android.content.Context, java.lang.String, int)' on a null object reference

因此似乎匕首没有注入到ProfileViewModel的构造函数中。

android viewmodel dagger-2
1个回答
0
投票

Dagger无法自行创建ViewModel。 ViewModel实例化是通过ViewModelProvider.Factory的一个实例完成的。您需要告诉匕首它如何创建ProfileViewModel.

的实例

所以@Binds在这种情况下对您不起作用。您需要定义一个返回ProfileViewModel实例并用@ Provides注释的方法。

例如-

@Module
public class ViewModelModule {

@Provides
@IntoMap
@ViewModelKey(ProfileViewModel.class)
public ProfileViewModel bindProfileViewModel(ViewModelFactory factory) {
    return factory.create();
}

@Provides
public ViewModelProvider.Factory bindViewModelFactory(){
    return new ViewModelFactory();
}

请参阅此详细信息-

Why a viewmodel factory is needed in Android?

© www.soinside.com 2019 - 2024. All rights reserved.