无法让 Dagger 注入 Viewmodel
Unable to get Dagger to Inject Viewmodel
我一直在按照指南在 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();
}
片段模块
@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
看来 dagger 没有注入 ProfileViewModel 的构造函数。
Dagger 无法自行创建 ViewModel。 ViewModel 实例化是通过 ViewModelProvider.Factory 的实例完成的。
您需要告诉 dagger 如何创建 ProfileViewModel.
的实例
所以 @Binds
在这种情况下对您不起作用。您需要定义一个 returns 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();
}
有关详细信息,请参阅此处 -
原来问题出在我的片段 class。
更改自;
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);
}
}
至
public class ProfileFragment extends BaseFragment {
@Inject
ViewModelProvider.Factory viewModelFactory;
private ProfileViewModel mViewModel;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
configureDagger();
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);
}
}
我一直在按照指南在 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();
}
片段模块
@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
看来 dagger 没有注入 ProfileViewModel 的构造函数。
Dagger 无法自行创建 ViewModel。 ViewModel 实例化是通过 ViewModelProvider.Factory 的实例完成的。
您需要告诉 dagger 如何创建 ProfileViewModel.
所以 @Binds
在这种情况下对您不起作用。您需要定义一个 returns 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();
}
有关详细信息,请参阅此处 -
原来问题出在我的片段 class。
更改自;
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);
}
}
至
public class ProfileFragment extends BaseFragment {
@Inject
ViewModelProvider.Factory viewModelFactory;
private ProfileViewModel mViewModel;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
configureDagger();
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);
}
}