我想说你的直觉是正确地将图表实例移动到视图模型中,但是,正如你所指出的,当视图以外的对象需要上下文依赖关系时,它们可能会变得很麻烦。对我来说,这变成了依赖注入的问题,其中依赖是上下文,或者更广泛地说,是整个数据图表。我很想知道您如何获取视图模型,但我假设它依赖于 Android 视图模型提供程序(通过by viewModels() 或某种ViewModelProvider.Factory)。
此问题的直接解决方案是将视图模型转换为AndroidViewModel 的子类,它通过视图模型的构造函数提供对应用程序上下文的引用。虽然它仍然是一种反模式并且应该谨慎使用,但 Android 团队已经认识到某些用例是有效的。我个人不使用AndroidViewModel,因为我相信它是一个粗略的解决方案,否则可以通过改进依赖图来解决。但是,它是由官方文档批准的,这只是我个人的看法。根据经验,我必须说它的使用使测试视图模型成为事后的噩梦。如果您对依赖注入库感兴趣,我强烈推荐新的Hilt 实现,它最近在上个月发布了稳定的1.0.0 版本。
除此之外,我现在将为您的困境提供两种可能的解决方案:一种使用AndroidViewModel,另一种不使用。如果您的视图模型在上下文之外已经有其他依赖项,那么AndroidViewModel 解决方案不会为您节省太多开销,因为您可能已经在某个时候实例化了ViewModelProvider.Factory。这些解决方案将考虑 Android Fragment 的范围,但可以轻松地在 Activity 或 DialogFragment 中实现,以及对生命周期挂钩等进行一些调整。
与AndroidViewModel
import android.app.Application
import androidx.lifecycle.AndroidViewModel
class MyViewModel(application: Application) : AndroidViewModel(application) {
val dataChart: DataChart
init {
dataChart = DataChart(application.applicationContext)
}
}
片段可能在哪里
class MyFragment : Fragment() {
private val viewModel: MyViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View { ... }
}
没有AndroidViewModel
import androidx.lifecycle.ViewModel
class MyViewModel(args: Args) : ViewModel() {
data class Args(
val dataChart: DataChart
)
val dataChart: DataChart = args.dataChart
}
片段可能在哪里
class MyFragment : Fragment() {
private lateinit var viewModel: MyViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val applicationContext: Context = requireContext().applicationContext
val dataChart = DataChart(applicationContext)
val viewModel: MyViewModel by viewModels {
ArgsViewModelFactory(
args = MyViewModel.Args(
dataChart = dataChart,
),
argsClass = MyViewModel.Args::class.java,
)
}
this.viewModel = viewModel
...
}
}
ArgsViewModelFactory 是我自己的创作,如下所示
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
class ArgsViewModelFactory<T>(
private val args: T,
private val argsClass: Class<T>,
) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T = modelClass.getConstructor(
argsClass,
).newInstance(
args,
)
}
编辑(通过 Hilt 模块):
@Module
@InstallIn(...)
object DataChartModule {
@Provides
fun provideDataChart(
@ApplicationContext context: Context,
): DataChart = DataChart(context)
}