我一直在遵循在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的构造函数中。
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(); }
请参阅此详细信息-