我一直在遵循在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的构造函数中。
答案 0 :(得分:1)
发现问题出在我的Fragment类中。
更改自;
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);
}
}
答案 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();
}
有关更多详细信息,请参考-