旋转后重新创建 ViewModel;如果直接用 dagger2 注入
ViewModel is recreated after rotation; if injected directly with dagger2
可能与 this
重复
我正在探索 android 使用 dagger2 进行注入 api。因此,在我的示例应用程序中,我直接在 activity 中注入了 ViewModel
;看看下面的代码片段。
class SampleApp : Application(), HasActivityInjector {
@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
override fun activityInjector(): AndroidInjector<Activity> =
dispatchingAndroidInjector
override fun onCreate() {
super.onCreate()
DaggerApplicationComponent.builder()
.application(this)
.build()
.inject(this)
}
}
@Component(modules = [
AndroidInjectionModule::class,
ActivityBindingModule::class,
AppModule::class
/** Other modules **/
])
@Singleton
interface ApplicationComponent {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: Application): Builder
fun build(): ApplicationComponent
}
fun inject(sampleApp: SampleApp)
}
@Module
public abstract class ActivityBindingModule {
@ContributesAndroidInjector(modules = MainModule.class)
public abstract MainActivity contributeMainActivityInjector();
}
class MainActivity : AppCompatActivity() {
@Inject
lateinit var mainViewModel: mainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_dashboard)
}
}
@Module
public class MainModule {
@Provides
public static MainViewModelProviderFactory provideMainViewModelProviderFactory(/** some dependencies **/) {
return new MainViewModelProviderFactory(/** some dependencies **/);
}
@Provides
public static MainViewModel provideMainViewModel(MainActivity activity, MainViewModelProviderFactory factory) {
return ViewModelProviders.of(activity, factory).get(MainViewModel.class);
}
}
如您所见,我已将 MainViewModel
直接注入 activity。现在,如果我旋转 activity 注入的实例是不同的。
但是,如果我在 MainActivity
中注入 MainViewModelProviderFactory
并执行
ViewModelProviders.of(activity, factory).get(MainViewModel.class)
它 returns 与之前相同的实例。
我没有发现我的实现有什么问题。
如有指点,将不胜感激。
所以在查看了 ViewModelProvider
、ViewModelProviders
、FragmentActivity
的来源之后,是的 dagger2 documentation
我得到了答案..
如果我错了,请随时纠正我..
We must not inject ViewModel directly, we should inject factories instead.
由于这一行 AndroidInjection.inject(this)
.
,我遇到了这个问题
根据匕首作者
It is crucial to call AndroidInjection.inject() before super.onCreate() in an Activity
让我们在非常高的级别上看看这里出了什么问题..
Activity 将在使用 onRetainNonConfigurationInstance
旋转时保留它的 ViewModel
并将在 onCreate()
中恢复它
因为我们在调用 super.onCreate()
之前注入,我们不会得到保留的 MainViewModel
对象,而是新对象。
如果您需要详细信息,请继续阅读。
当 dagger 尝试注入 MainViewModel
时,它会调用 MainModule
的 provideMainViewModel()
方法,该方法会调用以下表达式(请记住 super.onCreate()
尚未调用)
ViewModelProviders.of(activity, factory).get(MainViewModel.class)
ViewModelProviders.of
将 return 一个 ViewModelProvider
,其中包含 activity 和 ViewModelProviderFactory
[=54= 的 ViewModelStore
的引用]
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
.
.
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
ViewModelStore.of(activity)
最终会调用 activity 的 getViewModelStore()
,因为 activity 在这种情况下是 AppCompatActivity
,它实现了 ViewModelStoreOwner
AppCompatActivity
如果它为 null 并持有对它的引用,则创建新的 ViewModelStore
。 ViewModelStore
是 Map<String, ViewModel>
的包装器,带有附加方法 clear()
@NonNull
public ViewModelStore getViewModelStore() {
if (this.getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the Application instance. You can't request ViewModel before onCreate call.");
} else {
if (this.mViewModelStore == null) {
this.mViewModelStore = new ViewModelStore();
}
return this.mViewModelStore;
}
}
每当设备旋转时 activity 使用 onRetainNonConfigurationInstance
保留其非配置实例状态并在 onCreate
中恢复它。 (例如 mViewModelStore)
ViewModelProvider.get
将尝试从 activity 的 ViewModelStore
中获取 ViewModel
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
在这个特定的例子中;因为我们还没有调用 super.onCreate()
方法,实现将要求 factory
创建它并更新相应的 ViewModelStore
.
因此我们最终得到了两个不同的 MainViewModel
对象。
可能与 this
重复我正在探索 android 使用 dagger2 进行注入 api。因此,在我的示例应用程序中,我直接在 activity 中注入了 ViewModel
;看看下面的代码片段。
class SampleApp : Application(), HasActivityInjector {
@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
override fun activityInjector(): AndroidInjector<Activity> =
dispatchingAndroidInjector
override fun onCreate() {
super.onCreate()
DaggerApplicationComponent.builder()
.application(this)
.build()
.inject(this)
}
}
@Component(modules = [
AndroidInjectionModule::class,
ActivityBindingModule::class,
AppModule::class
/** Other modules **/
])
@Singleton
interface ApplicationComponent {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: Application): Builder
fun build(): ApplicationComponent
}
fun inject(sampleApp: SampleApp)
}
@Module
public abstract class ActivityBindingModule {
@ContributesAndroidInjector(modules = MainModule.class)
public abstract MainActivity contributeMainActivityInjector();
}
class MainActivity : AppCompatActivity() {
@Inject
lateinit var mainViewModel: mainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_dashboard)
}
}
@Module
public class MainModule {
@Provides
public static MainViewModelProviderFactory provideMainViewModelProviderFactory(/** some dependencies **/) {
return new MainViewModelProviderFactory(/** some dependencies **/);
}
@Provides
public static MainViewModel provideMainViewModel(MainActivity activity, MainViewModelProviderFactory factory) {
return ViewModelProviders.of(activity, factory).get(MainViewModel.class);
}
}
如您所见,我已将 MainViewModel
直接注入 activity。现在,如果我旋转 activity 注入的实例是不同的。
但是,如果我在 MainActivity
中注入 MainViewModelProviderFactory
并执行
ViewModelProviders.of(activity, factory).get(MainViewModel.class)
它 returns 与之前相同的实例。
我没有发现我的实现有什么问题。
如有指点,将不胜感激。
所以在查看了 ViewModelProvider
、ViewModelProviders
、FragmentActivity
的来源之后,是的 dagger2 documentation
我得到了答案..
如果我错了,请随时纠正我..
We must not inject ViewModel directly, we should inject factories instead.
由于这一行 AndroidInjection.inject(this)
.
根据匕首作者
It is crucial to call AndroidInjection.inject() before super.onCreate() in an Activity
让我们在非常高的级别上看看这里出了什么问题..
Activity 将在使用 onRetainNonConfigurationInstance
旋转时保留它的 ViewModel
并将在 onCreate()
因为我们在调用 super.onCreate()
之前注入,我们不会得到保留的 MainViewModel
对象,而是新对象。
如果您需要详细信息,请继续阅读。
当 dagger 尝试注入 MainViewModel
时,它会调用 MainModule
的 provideMainViewModel()
方法,该方法会调用以下表达式(请记住 super.onCreate()
尚未调用)
ViewModelProviders.of(activity, factory).get(MainViewModel.class)
ViewModelProviders.of
将 return 一个 ViewModelProvider
,其中包含 activity 和 ViewModelProviderFactory
[=54= 的 ViewModelStore
的引用]
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
.
.
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
ViewModelStore.of(activity)
最终会调用 activity 的 getViewModelStore()
,因为 activity 在这种情况下是 AppCompatActivity
,它实现了 ViewModelStoreOwner
AppCompatActivity
如果它为 null 并持有对它的引用,则创建新的 ViewModelStore
。 ViewModelStore
是 Map<String, ViewModel>
的包装器,带有附加方法 clear()
@NonNull
public ViewModelStore getViewModelStore() {
if (this.getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the Application instance. You can't request ViewModel before onCreate call.");
} else {
if (this.mViewModelStore == null) {
this.mViewModelStore = new ViewModelStore();
}
return this.mViewModelStore;
}
}
每当设备旋转时 activity 使用 onRetainNonConfigurationInstance
保留其非配置实例状态并在 onCreate
中恢复它。 (例如 mViewModelStore)
ViewModelProvider.get
将尝试从 activity 的 ViewModelStore
ViewModel
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
在这个特定的例子中;因为我们还没有调用 super.onCreate()
方法,实现将要求 factory
创建它并更新相应的 ViewModelStore
.
因此我们最终得到了两个不同的 MainViewModel
对象。