在没有 ViewModel 的情况下通过配置更改保留 CoroutineScope 的简洁方法
Clean way to retain CoroutineScope through config changes without ViewModel
我知道建议将 ViewModel 与我们的 Activity 一起使用,这样我们就可以使用它的 viewModelScope
。由于 ViewModel 的寿命比 Activity 长,我们不必取消 activity.onDestroy()
中的作业。
但是,有时您会非常简单 Activity。例如,它可以使用已安装的过滤包填充列表视图。您可以非常简单地使用委托为 activity 创建范围,并取消 onDestroy()
:
中的作业
class MyActivity(): AppCompatActivity(), CoroutineScope by MainScope() {
private val listAdapter = MyAdapter()
override fun onCreate() {
super.onCreate()
setContentView(R.layout.my_activity)
recycler_view.apply {
layoutManager = LinearLayoutManager(this)
adapter = listAdapter
}
launch {
val packages = getOrgPackagesWithIcons()
adapter.apply {
data = packages
notifyDataSetChanged()
}
}
}
override fun onDestroy() {
super.onDestroy()
cancel() // CoroutineContext
}
private suspend fun getOrgPackagesWithIcons() = withContext(Dispatchers.Default) {
var toNextYield = 20
packageManager.getInstalledPackages(0)
.filter { it.packageName.startsWith("org")
.take(100)
.map {
if (--toNextYield == 0) { // Make it cancellable
toNextYield = 20
yield()
}
MyPackageData(
it.applicationInfo.loadLabel(packageManager).toString(),
it.packageName,
it.applicationInfo.loadIcon(packageManager)
)
}
}
}
对于这种情况,ViewModel 感觉有点矫枉过正。它只是抽象 PackageManager 的另一层,它本身实际上是一个视图模型。
以上代码可以很方便的assemble后台获取数据。问题是当屏幕旋转时,或者在其他配置更改期间,协程被取消并重新启动。是否有一个干净的方法可以通过像这样的非常简单的 Activity 配置更改来保持 CoroutineScope 存活?
onRetainNonConfigurationInstance()
已弃用。我想我们可以把它放在一个 Fragment 中并使用 retainInstance = true
,但是将 Fragment 层引入到如此简单的 Activity 也感觉有点矫枉过正。
也许有一种方法可以创建一个空的 ViewModel 实现,以便我们可以借用它的作用域?
For a case like this, ViewModel feels like overkill.
我会反驳,并且仍然建议这将是 AndroidViewModel
的一个很好的用例。
我认为 Activity
没有责任仅仅因为它可以访问 PackageManager
就获取包列表。 Activity
应该只负责显示列表。
使用 AndroidViewModel
可让您在 ViewModel
实例中访问 Context
和 viewModelScope
。
同意上面的回答,ViewModel 仍然是这里的最佳选择,因为您正在执行不属于 activity 的数据操作。也就是说,如果您查看 如何 ViewModel
保留数据 - 它是简单的静态数据。这里不引用完整的链条,只引用所有内容的部分
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
其中 NonConfigurationInstances 是:
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
将它放在这里是为了解决您的问题 "empty ViewModel implementation" - 那里就是这样做的,您可以做类似的事情 - 也许放在您自己的应用程序 class 中。但话又说回来,它就在那里,使用简单并提供其他好处......所以我总是使用 view model/androidviewmodel 并且在我看来根本没有开销,恰恰相反,代码组织得很好。
我知道建议将 ViewModel 与我们的 Activity 一起使用,这样我们就可以使用它的 viewModelScope
。由于 ViewModel 的寿命比 Activity 长,我们不必取消 activity.onDestroy()
中的作业。
但是,有时您会非常简单 Activity。例如,它可以使用已安装的过滤包填充列表视图。您可以非常简单地使用委托为 activity 创建范围,并取消 onDestroy()
:
class MyActivity(): AppCompatActivity(), CoroutineScope by MainScope() {
private val listAdapter = MyAdapter()
override fun onCreate() {
super.onCreate()
setContentView(R.layout.my_activity)
recycler_view.apply {
layoutManager = LinearLayoutManager(this)
adapter = listAdapter
}
launch {
val packages = getOrgPackagesWithIcons()
adapter.apply {
data = packages
notifyDataSetChanged()
}
}
}
override fun onDestroy() {
super.onDestroy()
cancel() // CoroutineContext
}
private suspend fun getOrgPackagesWithIcons() = withContext(Dispatchers.Default) {
var toNextYield = 20
packageManager.getInstalledPackages(0)
.filter { it.packageName.startsWith("org")
.take(100)
.map {
if (--toNextYield == 0) { // Make it cancellable
toNextYield = 20
yield()
}
MyPackageData(
it.applicationInfo.loadLabel(packageManager).toString(),
it.packageName,
it.applicationInfo.loadIcon(packageManager)
)
}
}
}
对于这种情况,ViewModel 感觉有点矫枉过正。它只是抽象 PackageManager 的另一层,它本身实际上是一个视图模型。
以上代码可以很方便的assemble后台获取数据。问题是当屏幕旋转时,或者在其他配置更改期间,协程被取消并重新启动。是否有一个干净的方法可以通过像这样的非常简单的 Activity 配置更改来保持 CoroutineScope 存活?
onRetainNonConfigurationInstance()
已弃用。我想我们可以把它放在一个 Fragment 中并使用 retainInstance = true
,但是将 Fragment 层引入到如此简单的 Activity 也感觉有点矫枉过正。
也许有一种方法可以创建一个空的 ViewModel 实现,以便我们可以借用它的作用域?
For a case like this, ViewModel feels like overkill.
我会反驳,并且仍然建议这将是 AndroidViewModel
的一个很好的用例。
我认为 Activity
没有责任仅仅因为它可以访问 PackageManager
就获取包列表。 Activity
应该只负责显示列表。
使用 AndroidViewModel
可让您在 ViewModel
实例中访问 Context
和 viewModelScope
。
同意上面的回答,ViewModel 仍然是这里的最佳选择,因为您正在执行不属于 activity 的数据操作。也就是说,如果您查看 如何 ViewModel
保留数据 - 它是简单的静态数据。这里不引用完整的链条,只引用所有内容的部分
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
其中 NonConfigurationInstances 是:
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
将它放在这里是为了解决您的问题 "empty ViewModel implementation" - 那里就是这样做的,您可以做类似的事情 - 也许放在您自己的应用程序 class 中。但话又说回来,它就在那里,使用简单并提供其他好处......所以我总是使用 view model/androidviewmodel 并且在我看来根本没有开销,恰恰相反,代码组织得很好。