旋转后重新创建ViewModel; 如果直接注射dagger2

Rup*_*esh 2 android kotlin dagger-2 android-mvvm android-architecture-components

可能重复

我正在用dagger2探索android注射api.所以,在我的示例应用程序中,我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)
    }
}
Run Code Online (Sandbox Code Playgroud)
@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)
}
Run Code Online (Sandbox Code Playgroud)
@Module
public abstract class ActivityBindingModule {

    @ContributesAndroidInjector(modules = MainModule.class)
    public abstract MainActivity contributeMainActivityInjector();
}
Run Code Online (Sandbox Code Playgroud)
class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var mainViewModel: mainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        AndroidInjection.inject(this)

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_dashboard)
    }
}
Run Code Online (Sandbox Code Playgroud)
@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);
    }
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,我已MainViewModel直接注入活动.现在,如果我旋转活动,则注入的实例是不同的.

但是,如果我注入MainViewModelProviderFactoryMainActivity并执行

ViewModelProviders.of(activity, factory).get(MainViewModel.class) 它返回与以前相同的实例.

我没有弄到我的实施有什么问题.

任何指针都会很明显.

Rup*_*esh 7

因此,通过对源去后ViewModelProvider,ViewModelProviders,FragmentActivity和是的dagger2 documentation我有一个答案..

如果我错了,请随意纠正我.

我们不能直接注入ViewModel,我们应该注入工厂.

由于这条线,我正面临着这个问题AndroidInjection.inject(this).

根据匕首作者

在Activity中的super.onCreate()之前调用AndroidInjection.inject()至关重要

让我们看看这里出现了什么问题.

活动将ViewModel使用它保持轮换onRetainNonConfigurationInstance,并将其恢复onCreate()

正如我们在调用之前注入的那样super.onCreate(),我们不会获得保留的MainViewModel对象而是新的对象.

如果您需要详细信息,请继续阅读..


当dagger尝试注入MainViewModel时调用provideMainViewModel()方法MainModule,调用以下表达式(请记住super.onCreate()尚未调用)

ViewModelProviders.of(activity, factory).get(MainViewModel.class)
Run Code Online (Sandbox Code Playgroud)

ViewModelProviders.of会返回一个ViewModelProvider保存用于该引用ViewModelStore各自活动和ViewModelProviderFactory

public static ViewModelProvider of(@NonNull FragmentActivity activity,
        @Nullable Factory factory) {
    .
    .

    return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
Run Code Online (Sandbox Code Playgroud)

ViewModelStore.of(activity)最终会给活动打电话,getViewModelStore()因为在这种情况下,活动是AppCompatActivity实施的ViewModelStoreOwner

AppCompatActivityViewModelStore如果它为null,则创建new 并保存对它的引用.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;
    }
}
Run Code Online (Sandbox Code Playgroud)

每当设备被旋转时,活动将保留其非配置实例状态,onRetainNonConfigurationInstance并使用并恢复它onCreate.

ViewModelProvider.get将尝试ViewModel从活动中获取ViewModelStore

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;
}
Run Code Online (Sandbox Code Playgroud)

在这个特定的例子中; 因为我们还没有调用super.onCreate()方法,实现将要求factory创建它并将更新相应的ViewModelStore.

因此我们最终得到了两个不同的MainViewModel对象.