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

时间:2018-07-26 08:55:29

标签: android kotlin dagger-2 android-architecture-components android-mvvm

可能的this副本

我正在使用dagger2探索android注入api。因此,在示例应用程序中,我直接在活动中注入了var map; var myLatLng; $(document).ready(function() { geoLocationInit(); function geoLocationInit() { if (navigator.geolocation) { navigator.geoLocation.getCurrentPosition(success, fail); } else { alert("Browser not supported"); } } function success(position) { console.log(position); var Latval = position.coords.latitude; var Lngval = position.coords.longitude; myLatLng = new google.maps.LatLng(Latval, Lngval); createMap(myLatLng); } function fail() { alert("It Failed"); } //Create Map function createMap(myLatLng) { var myLatLng = new google.maps.LatLng(0.3285284, 32.5738946); map = new google.maps.Map(document.getElementById('map'), { center: myLatLng, zoom: 12 }); var marker = new google.maps.Marker({ position: myLatLng, map: map, }); } //Create marker function createMarker(LatLng, icn, name) { var marker = new google.maps.Marker({ position: LatLng, map: map, icon: icn, title: name }); } //Nearby search function nearbySearch(myLatLng, type) { var request = { location: myLatLng, radius: '1500', types: [type] }; service = new google.maps.places.PlacesService(map); service.nearbySearch(request, callback); function callback(results, status) { //console.log(results); if (status == google.maps.places.PlacesServiceStatus.OK) { for (var i = 0; i < results.length; i++) { var place = results[i]; LatLng = place.geometry.location; icn = place.icon; name = place.name; createMarker(LatLng, icn, name); } } } } }); ;看看下面的代码片段。

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注入MainViewModelProviderFactory中并执行

MainActivity返回与以前相同的实例。

我没有发现我的实现有什么问题。

任何指针都是有意义的。

1 个答案:

答案 0 :(得分:7)

因此,在检查了ViewModelProviderViewModelProvidersFragmentActivity和是dagger2 documentation的来源之后,我有了答案。

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

  

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

由于AndroidInjection.inject(this)这行,我正面临这个问题。

根据匕首作者

  

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

让我们从高层次看这里出了什么问题。

活动将使用onRetainNonConfigurationInstance保留其ViewModel的旋转状态,并将其恢复到onCreate()

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

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


当匕首尝试注入MainViewModel时,它将调用provideMainViewModel()的{​​{1}}方法,该方法将调用以下表达式(请注意,MainModule尚未被调用)

super.onCreate()

ViewModelProviders.of(activity, factory).get(MainViewModel.class) 将返回一个ViewModelProviders.of,其中包含相应活动的ViewModelProviderViewModelStore

的引用
ViewModelProviderFactory

public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) { . . return new ViewModelProvider(ViewModelStores.of(activity), factory); } 最终将调用活动ViewModelStore.of(activity),因为在这种情况下,活动为getViewModelStore(),它实现了AppCompatActivity

ViewModelStoreOwner如果为空,则创建新的AppCompatActivity并保留对其的引用。 ViewModelStoreViewModelStore的包装,并带有其他方法Map<String, ViewModel>

clear()

只要设备旋转,活动就会使用onRetainNonConfigurationInstance保留其非配置实例状态,并在@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; } } 中将其恢复。

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; } 方法,实现会要求super.onCreate()创建它并更新相应的factory

因此,我们最终有了两个不同的ViewModelStore对象。