Вставить сохраненное состояние во ViewModelFactory с помощью kodein - PullRequest
0 голосов
/ 19 марта 2020

Я разрабатываю приложение с шаблоном MVVM. Я хочу сохранить пользовательский интерфейс, когда пользователь поворачивает экран.

MyViewModel.kt

class MyViewModel(val repository: SomeRepository,
                       state : SavedStateHandle) : ViewModel() {

    private val savedStateHandle = state
    companion object {
        const val KEY = "KEY"
    }

    fun saveCityId(cityId: String) {
        savedStateHandle.set(CITY_KEY, cityId)
    }

    fun getCityId(): String? {
        return savedStateHandle.get(CITY_KEY)
    }

}

ViewModelFactory.kt

@Suppress("UNCHECKED_CAST")
class ViewModelFactory(
    private val repository: SomeRepository,
    private val state: SavedStateHandle
) : ViewModelProvider.NewInstanceFactory() {

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return MyViewModel(repository,state) as T
    }

}

Я называю это в MainActivity MainActivity.kt

class MainActivity: AppCompatActivity(), KodeinAware {
    private val factory: ViewModelFactoryby instance()
    override val kodein by kodein()
    private lateinit var viewModel: MyViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    cityId = intent.getStringExtra("cityId") ?: viewModel.getCityId()
        if (cityId != null) {
            viewModel.saveCityId(cityId!!)
            viewModel.getCurrentWeather(cityId!!)
        }
}

Здесь я вставляю зависимости

Application.kt

class ForecastApplication: Application(), KodeinAware {
    override val kodein = Kodein.lazy {
        import(androidXModule(this@ForecastApplication))

        bind<SomeApi>() with singleton {
            Retrofit.create()
        }

        bind<WeatherRepository>() with singleton {
            WeatherRepository(instance())
        }
        bind() from provider {
            WeatherViewModelFactory(
                instance(), instance()
            )
        }
}
}

И у меня есть эта ошибка

 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.simpleforecast/com.example.simpleapp.UI.Cities.Activity}:org.kodein.di.Kodein$NotFoundException: No binding found for bind<SavedStateHandle>() 
with ?<Activity>().? { ? }

Как я могу построить ViewModelFactory и внедрить модуль Saved State для ViewModel?

1 Ответ

0 голосов
/ 30 марта 2020

SavedStateHandle - это параметр, который не может быть привязан к графу DI, поскольку он извлекается из Fragment (или Activity), поэтому для его работы необходимо выполнить несколько шагов:

1) Определение DI viewmodel - поскольку у вас есть пользовательский параметр, вам нужно использовать from factory:

bind() from factory { handle: SavedStateHandle ->
    WeatherViewModel(
        state = handle,
        repository = instance()
    )
}

2) ViewModel Factory - вам нужно наследовать от AbstractSavedStateViewModelFactory

val vmFactory = object : AbstractSavedStateViewModelFactory(this, arguments) {
    override fun <T : ViewModel> create(key: String, modelClass: Class<T>, handle: SavedStateHandle): T {
        val vmFactory: ((SavedStateHandle) -> WeatherViewModel) = kodein.direct.factory()
        return vmFactory(handle) as T
    }
}

Внутри метода create вы извлекаете фабрику из графика DI (из шага 1).

3) Вы получаете ViewModel с указанной фабрикой:

lateinit var vm : WeatherViewModel

fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    vm = ViewModelProvider(this, vmFactory)[WeatherViewModel::class.java]
}

или android KTX путь:

val vm : WeatherViewModel by viewModels { vmFactory }
...