4

可能与此重复

我正在使用 dagger2 探索 android injections 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)
    }
}

@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直接注入到活动中。现在,如果我旋转活动,注入的实例会有所不同。

但是,如果我注入MainViewModelProviderFactoryMainActivity执行

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

我不明白我的实施有什么问题。

任何指针都将是可观的。

4

1 回答 1

11

因此,在通过 , 的来源之后ViewModelProvider,是的,ViewModelProviders我有一个答案..FragmentActivitydagger2 documentation

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

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

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

根据匕首作者

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

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

活动将保持它的ViewModel旋转使用onRetainNonConfigurationInstance并将其恢复到onCreate()

由于我们在调用之前进行注入super.onCreate(),所以我们不会得到保留的MainViewModel对象,而是新的。

如果您想了解详细信息,请继续阅读。


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

ViewModelProviders.of(activity, factory).get(MainViewModel.class)

ViewModelProviders.of返回一个ViewModelProvider包含ViewModelStore相应活动的引用和ViewModelProviderFactory

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

    return new ViewModelProvider(ViewModelStores.of(activity), factory);
}

ViewModelStore.of(activity)最终将调用活动getViewModelStore(),因为在这种情况下活动是AppCompatActivity实现ViewModelStoreOwner

AppCompatActivityViewModelStore如果它为 null 并持有对它的引用,则创建新的。是带有附加方法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;
    }
}

每当设备旋转时,活动都会保留其非配置实例状态,onRetainNonConfigurationInstance并使用onCreate. (例如 mViewModelStore)

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;
}

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

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

于 2018-07-26T13:18:55.530 回答