ViewModel 片段在屏幕旋转时重新创建
ViewModel Fragment Recreates On Screen Rotation
我正在使用最新的 android 架构组件构建应用程序。我使用 firebase firestore 作为带有 jetpack 导航(底部导航)的数据库。我成功地能够显示来自数据库的数据。但是每当我旋转 mt 屏幕时,商店片段都会重新创建并向数据库发出请求。
回购
override fun getAllStores() = callbackFlow<State<List<Store>>> {
// Emit loading state
send(State.loading())
val listener = remoteDB.collection(Constants.COLLECTION_STORES)
.addSnapshotListener { querySnapshot, exception ->
querySnapshot?.toObjects(Store::class.java)?.let { store ->
// Emit success state with data
offer(State.success(store))
}
exception?.let {
// emit exception with message
offer(State.failed(it.message!!))
cancel()
}
}
awaitClose {
listener.remove()
cancel()
}
}.catch {
// Thrown exception on State Failed
emit(State.failed(it.message.toString()))
}.flowOn(Dispatchers.IO)
ViewModel
@ExperimentalCoroutinesApi
@InternalCoroutinesApi
class StoreViewModel(private val repository: DBInterface = Repo()) : ViewModel() {
fun getAllStores() = repository.getAllStores()
}
存储片段
@ExperimentalCoroutinesApi
@InternalCoroutinesApi
class StoreFragment : Fragment(R.layout.fragment_store) {
private lateinit var storeAdapter: StoreAdapter
private val viewModel: StoreViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(activity as MainActivity).supportActionBar?.title = getString(R.string.store_title)
setUpRV()
// get all stores
lifecycleScope.launch {
getAllStores()
}
}
private suspend fun getAllStores() {
viewModel.getAllStores().collect { state ->
when (state) {
is State.Loading -> {
store_progress.show()
}
is State.Success -> {
storeAdapter.differ.submitList(state.data)
store_progress.animate().alpha(0f)
.withEndAction {
store_rv.animate().alpha(1f)
store_progress.hide()
}
}
is State.Failed -> {
store_progress.hide()
activity?.toast("Failed! ${state.message}")
}
}
}
}
private fun setUpRV() {
storeAdapter = StoreAdapter()
store_rv.apply {
adapter = storeAdapter
addItemDecoration(SpacesItemDecorator(16))
}
}
}
主要activity(导航图)
@InternalCoroutinesApi
@ExperimentalCoroutinesApi
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
// init bottom navigation
bottom_navigation.setupWithNavController(nav_host_fragment.findNavController())
}
}
每次它重新创建我的片段。我不想使用方法保存或保留任何视图。因为 ViewModel 用于保护屏幕旋转视图。请让我知道任何提示和技巧。提前致谢 ;)
您可以将 android:configChanges="orientation|screenSize|screenLayout"
添加到 activity 的清单中。这应该可以防止在方向改变时重新启动。
检查这个 site and also here 是一些信息。
Flow
本身不是有状态的——这是它与 LiveData
之间的主要区别。这意味着在您的 collect
完成后,下一个 collect
从头开始 callbackFlow
。
这正是 lifecycle-livedata-ktx
工件包含 asLiveData()
扩展的原因,它允许您在存储库层继续使用 Flow
,同时保持有状态(和生命周期)属性LiveData
的 UI:
@ExperimentalCoroutinesApi
@InternalCoroutinesApi
class StoreViewModel(private val repository: DBInterface = Repo()) : ViewModel() {
fun getAllStores() = repository.getAllStores().asLiveData()
}
您需要更改 UI 代码以继续使用 LiveData
和 observe()
。
Kotlin 正在研究 a shareIn
operation, 将 允许您的 ViewModel
保存 Flow
的状态。这将允许您在应用程序的所有层使用 Flow
,而无需在调用 collect
的 Fragment/Activity 被销毁并重新创建时从头开始重新查询信息。
我正在使用最新的 android 架构组件构建应用程序。我使用 firebase firestore 作为带有 jetpack 导航(底部导航)的数据库。我成功地能够显示来自数据库的数据。但是每当我旋转 mt 屏幕时,商店片段都会重新创建并向数据库发出请求。
回购
override fun getAllStores() = callbackFlow<State<List<Store>>> {
// Emit loading state
send(State.loading())
val listener = remoteDB.collection(Constants.COLLECTION_STORES)
.addSnapshotListener { querySnapshot, exception ->
querySnapshot?.toObjects(Store::class.java)?.let { store ->
// Emit success state with data
offer(State.success(store))
}
exception?.let {
// emit exception with message
offer(State.failed(it.message!!))
cancel()
}
}
awaitClose {
listener.remove()
cancel()
}
}.catch {
// Thrown exception on State Failed
emit(State.failed(it.message.toString()))
}.flowOn(Dispatchers.IO)
ViewModel
@ExperimentalCoroutinesApi
@InternalCoroutinesApi
class StoreViewModel(private val repository: DBInterface = Repo()) : ViewModel() {
fun getAllStores() = repository.getAllStores()
}
存储片段
@ExperimentalCoroutinesApi
@InternalCoroutinesApi
class StoreFragment : Fragment(R.layout.fragment_store) {
private lateinit var storeAdapter: StoreAdapter
private val viewModel: StoreViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(activity as MainActivity).supportActionBar?.title = getString(R.string.store_title)
setUpRV()
// get all stores
lifecycleScope.launch {
getAllStores()
}
}
private suspend fun getAllStores() {
viewModel.getAllStores().collect { state ->
when (state) {
is State.Loading -> {
store_progress.show()
}
is State.Success -> {
storeAdapter.differ.submitList(state.data)
store_progress.animate().alpha(0f)
.withEndAction {
store_rv.animate().alpha(1f)
store_progress.hide()
}
}
is State.Failed -> {
store_progress.hide()
activity?.toast("Failed! ${state.message}")
}
}
}
}
private fun setUpRV() {
storeAdapter = StoreAdapter()
store_rv.apply {
adapter = storeAdapter
addItemDecoration(SpacesItemDecorator(16))
}
}
}
主要activity(导航图)
@InternalCoroutinesApi
@ExperimentalCoroutinesApi
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
// init bottom navigation
bottom_navigation.setupWithNavController(nav_host_fragment.findNavController())
}
}
每次它重新创建我的片段。我不想使用方法保存或保留任何视图。因为 ViewModel 用于保护屏幕旋转视图。请让我知道任何提示和技巧。提前致谢 ;)
您可以将 android:configChanges="orientation|screenSize|screenLayout"
添加到 activity 的清单中。这应该可以防止在方向改变时重新启动。
检查这个 site and also here 是一些信息。
Flow
本身不是有状态的——这是它与 LiveData
之间的主要区别。这意味着在您的 collect
完成后,下一个 collect
从头开始 callbackFlow
。
这正是 lifecycle-livedata-ktx
工件包含 asLiveData()
扩展的原因,它允许您在存储库层继续使用 Flow
,同时保持有状态(和生命周期)属性LiveData
的 UI:
@ExperimentalCoroutinesApi
@InternalCoroutinesApi
class StoreViewModel(private val repository: DBInterface = Repo()) : ViewModel() {
fun getAllStores() = repository.getAllStores().asLiveData()
}
您需要更改 UI 代码以继续使用 LiveData
和 observe()
。
Kotlin 正在研究 a shareIn
operation, 将 允许您的 ViewModel
保存 Flow
的状态。这将允许您在应用程序的所有层使用 Flow
,而无需在调用 collect
的 Fragment/Activity 被销毁并重新创建时从头开始重新查询信息。