Я пытаюсь реализовать новую архитектуру (MVVM + RxJava2 + Dagger2 + Retrofit) в моем существующем проекте. Я настроил всю вышеуказанную архитектуру и протестировал ее на HomeActivity. Зависимости, внедренные в HomeViewModel. Итак, теперь я пытался внедрить зависимости, такие же, как HomeViewModel, в FollowViewModel из FollowFragment, который является контейнерным фрагментом HomeActivity, но внедренные зависимости всегда возвращают значение null (не инициализируется).
Я слежу за этим проектом Риггароо / андроид-арка-компоненты-дата-обратный отсчет для внедрения зависимостей, но в этом примере используются только действия без фрагмента. Поэтому я не знаю, что происходит и кому вводить deps в нескольких ViewModels.
Вот код для понимания некоторых важных классов:
AppApplication.class
public class AppApplication extends MultiDexApplication implements HasActivityInjector {
@Inject
DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
@Override
public void onCreate() {
...........................
AppInjector.init(this)
..........................
}
@Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjector;
}
}
AppInjector.class
public class AppInjector {
private AppInjector() {
}
public static void init(AppApplication appApplication) {
DaggerAppComponent.builder()
.application(appApplication)
.build()
.inject(appApplication);
appApplication
.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
handleActivity(activity);
}
});
}
private static void handleActivity(Activity activity) {
if (activity instanceof HasSupportFragmentInjector) {
AndroidInjection.inject(activity);
}
if (activity instanceof FragmentActivity) {
((FragmentActivity) activity).getSupportFragmentManager()
.registerFragmentLifecycleCallbacks(
new FragmentManager.FragmentLifecycleCallbacks() {
@Override
public void onFragmentCreated(FragmentManager fm, Fragment f,
Bundle savedInstanceState) {
if (f instanceof Injectable) {
AndroidSupportInjection.inject(f);
}
}
}, true);
}
}
}
AppComponent.class
@Singleton
@Component(modules = {AndroidSupportInjectionModule.class,ActivityBuilderModule.class, AppModule.class, NetworkModule.class})
public interface AppComponent {
@Component.Builder
interface Builder {
@BindsInstance
Builder application(AppApplication application);
AppComponent build();
}
void inject(AppApplication app);
AppModule.class
@Module(includes = { ViewModelModule.class})
public class AppModule {
@Provides
Context provideContext(AppApplication application) {
return application.getApplicationContext();
}
}
ActivityBuilderModule.class
@Module
public abstract class ActivityBuilderModule {
@ContributesAndroidInjector(modules = FragmentBuildersModule.class)
abstract HomeActivity bindHomeActivity();
}
FragmentBuildersModule.class
@Module
public abstract class FragmentBuildersModule {
@ContributesAndroidInjector
abstract FollowingListFragment contributeFollowingListFragment();
}
ViewModule.class
@Module
public abstract class ViewModelModule {
@Binds
@IntoMap
@ViewModelKey(FollowingViewModel.class)
abstract ViewModel bindFollowingViewModel(FollowingViewModel followingViewModel);
@Binds
@IntoMap
@ViewModelKey(HomeViewModel.class)
abstract ViewModel bindHomeViewModel(HomeViewModel homeViewModel);
@Binds
abstract ViewModelProvider.Factory bindViewModelFactory(MyViewModelFactory factory);
}
NetworkModule.class
@Module
public class NetworkModule {
public NetworkModule(){}
@Provides
@Singleton
CompositeDisposable getCompositeDisposable() {
return new CompositeDisposable();
}
@Provides
@Singleton
Retrofit provideCall() {
// OKHttps and Retrofit code...
}
@Provides
@Singleton
@SuppressWarnings("unused")
public ApiCallInterface providesNetworkService(
Retrofit retrofit) {
return retrofit.create(ApiCallInterface.class);
}
@Provides
@Singleton
@SuppressWarnings("unused")
public Repository providesService(
ApiCallInterface networkService) {
return new Repository(networkService);
}
}
MyViewModelFactory.class
@Singleton
public class MyViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;
@Inject
public MyViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
this.creators = creators;
}
@SuppressWarnings("unchecked")
@Override
public <T extends ViewModel> T create(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);
}
}
}
Этот класс используется для создания экземпляров всех моделей просмотра. Я отлаживал и обнаружил, что создается только экземпляр HomeViewModel.
Теперь я создал HomeViewModel, который вызывается из HomeActivity. Что работает нормально. Теперь такая же реализация выполняется в FollowFragment, но не работает. Позвольте мне показать вам, как я инициализировал FollowViewModel из FollowListFragment.class.
public class FollowingListFragment extends BaseFragment implementsInjectable {
@Inject
MyViewModelFactory myViewModelFactory;
private FollowingViewModel followingViewModel;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = getActivity();
followingViewModel = ViewModelProviders.of(this, plownsViewModelFactory)
.get(FollowingViewModel.class);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_following_list, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
followingViewModel.getFollowings(id,curser);
}
}
Вот FollowViewModel.class
public class FollowingViewModel extends ViewModel {
@Inject
Repository service;
@Inject
CompositeDisposable subscriptions;
@Inject
public FollowingViewModel() {
}
/*void setService(Repository service){
this.service = service;
}*/
void getFollowings(String id,String curser) {
if (service!=null) { **//HERE SERVICE RETURNS NULL**
Disposable subscription = service.getFollowings(id, curser, new IResponseCallback<FollowingResponse.FollowingResult>() {
@Override
public void onSuccess(FollowingResponse.FollowingResult followingResult) {
}
@Override
public void onError(Throwable throwable) {
}
});
subscriptions.add(subscription);
}else {
Log.d("FollowViewModel", "Service is null " );
}
}
Вот код классов HomeActivity и HomeViewModel, которые работают нормально. HomeActivity.class
public class HomeActivity extends BaseActivity implements HasSupportFragmentInjector, Injectable {
@Inject
DispatchingAndroidInjector<Fragment> supportFragmentInjector;
@Inject
MyViewModelFactory viewFactory;
HomeViewModel homeViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
homeViewModel = ViewModelProviders.of(this,viewFactory).get(HomeViewModel.class);
homeViewModel.getFollowings("xyx",null);
}
@Override
public AndroidInjector<Fragment> supportFragmentInjector() {
return supportFragmentInjector;
}
}
HomeViewModel.class
public class HomeViewModel extends ViewModel {
@Inject
Repository service;
@Inject
CompositeDisposable subscriptions;
@Inject
public HomeViewModel() {
}
void getFollowings(String id,String curser) {
if (service!=null) { **// Returing null service**
}
}else {
Log.d("FollowViewModel", "Service is null " );
}
}
}
@Pavneet_Singh Проверить сейчас *
@Pavneet_Singh ViewModelModule включен в мой AppModule, а AppModule уже указан в AppComonent. Пожалуйста, проверьте, что я также добавил код AppModule.
@Pavneet_Singh тот же результат со списком ViewModelModule.class в компоненте :(
@Pavneet_Singh Пожалуйста, проверьте MyViewModelFactory.class
Я добавил здесь аннотацию Binds вместо аннотации Provides: @Binds abstract ViewModelProvider.Factory bindViewModelFactory (MyViewModelFactory factory);
Вы смешиваете конструктор и внедрение поля. Пожалуйста, посмотрите почему мое поле пустое после инъекции?
@DavidMedenjak Итак, вы предлагаете мне использовать инъекцию поля или инъекцию конструктора? если да, то как этот код работает в HomeViewModel?
Вы смешиваете инъекцию поля с инъекцией конструктора, но ваша модель представления будет введена конструктором, поэтому поля будут нулевыми. Вы не включили HomeViewModel, поэтому я не знаю, как и почему он будет работать.
@DavidMedenjak Извините, я забыл добавить код HomeViewModel. Пожалуйста, проверьте сейчас.
Мне кажется, что поле не должно вводиться и в HomeViewModel по той же причине. У вас пустой конструктор, поэтому они должны быть неинициализированными. Также не должно быть необходимости в if (service!=null), поскольку Dagger никогда не будет внедрять объект null, если явно не указано иное.
@DavidMedenjak Итак, я пробовал как только инъекцию поля, так и только результаты инъекции конструктора: 1) инъекция конструктора (удаление инъекции в полях) возвращает ошибку java.lang.RuntimeException: Cannot create an instance of class .home.followings.FollowingViewModel at android.arch.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.java:153)
@DavidMedenjak Я запуталась. Не могли бы вы предоставить какой-либо образец кода для создания экземпляра ViewModel с внедрением конструктора.
Кажется, они используют инъекцию конструктора в Google GithubBrowserSample
@DavidMedenjak Большое спасибо. Я решил проблему, как вы предложили (с помощью конструктора @Injection).
@Pavneet_Singh Пожалуйста, убедитесь, что я добавил ViewModleModule, который является модулем поставщика для MyViewModelFactory.