在 activity android 中传递对 ViewModel 的引用的好方法
Good way to pass a reference to a ViewModel in an activity android
我有一个从基础 class 继承的 ViewModel
,我希望相应的 Activity
也从基础 class 继承。 activity 每次都会调用派生的 ViewModel
的相同方法。所以它会是这样的:
BaseViewModel:
abstract class BaseViewModel(application: Application) : AndroidViewModel(application) {
protected val context = getApplication<Application>().applicationContext
protected var speechManager: SpeechRecognizerManager? = null
var _actionToTake : MutableLiveData<AnalyseVoiceResults.Actions> = MutableLiveData()
var actionToTake : LiveData<AnalyseVoiceResults.Actions> = _actionToTake
open fun stopListening() {
if (speechManager != null) {
speechManager?.destroy()
speechManager = null
}
open fun startListening() {
val isListening = speechManager?.ismIsListening() ?: false
if (speechManager == null) {
SetSpeechListener()
} else if (!isListening) {
speechManager?.destroy()
SetSpeechListener()
}
}
}
基础Activity
class BaseActivity : AppCompatActivity() {
private lateinit var baseViewModel: BaseViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
fun goback() {
super.onBackPressed()
baseViewModel.stopListening()
finish()
}
fun startListening() {
baseViewModel.startListening()
}
override fun onDestroy() {
super.onDestroy()
baseViewModel.stopListening()
}
}
派生Activity:
class DerivedActivity : BaseActivity() {
private val nextActivityViewModel: NextActivityViewModel by inject()
///^^inherits from BaseViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
/*** pass reference ***/
baseViewModel = nexActivityViewModel
nextActivityViewModel.actionToTake.observe(this, object : Observer<AnalyseVoiceResults.Actions?> {
override fun onChanged(t: AnalyseVoiceResults.Actions?) {
if (t?.equals(AnalyseVoiceResults.Actions.GO_BACK) ?: false) {
goback()
}
}
})
startListening()
}
}
这是否会导致内存泄漏,为此 activity 有两个视图模型实例?有一个更好的方法吗?我不想为我的所有活动重复相同的代码。 (如果我用一个碱基片段做这件事,我也会有同样的问题)。
使这个 var baseViewModel: BaseViewModel 成为一个抽象变量,所有子 class 都必须覆盖它。因此,当您调用 startListening 和 stopListening 时,这些方法将从子实现中调用。
编辑:
将 BaseActivity 设为抽象 class,将 baseViewModel 设为抽象变量
abstract class BaseActivity : AppCompatActivity() {
private abstract var baseViewModel: BaseViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
fun goback() {
super.onBackPressed()
baseViewModel.stopListening()
finish()
}
fun startListening() {
baseViewModel.startListening()
}
override fun onDestroy() {
super.onDestroy()
baseViewModel.stopListening()
}
}
因此,您的 DerivedActivity 必须覆盖 baseViewModel,每次调用父亲的 class 都会触发子
class DerivedActivity : BaseActivity() {
override val baseViewModel: NextActivityViewModel by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Will this cause memory leaks to have two instances of a view model for
this activity?
不,这种方法没有内存泄漏。您也没有相同 activity 的 2 个 ViewModel 实例。它是 ViewModel 的单个实例,在 BaseActivity 和 BaseViewModel 中被不同的变量引用。
Is there a better way to do this?
我看到的第一个问题是您在 ViewModels 中有 Android 特定代码,这不是一个好的做法。您应该将语音管理器代码移动到基础 activity 本身,并且 ViewModel 应该只保存您希望在方向更改时保留的 "state" 数据。这将确保所有语音管理方法(创建、恢复、销毁)都在基础 activity 中。具体 activity 只有在状态改变时才会有观察者。
如果您遵循任何架构模式(如 MVP),一旦您将 Speech Manager 代码移出到 activity,将其进一步移出到 Presenter 将变得显而易见。
编辑:我没有在生产中使用 MVVM 模式,但这是您可能想要的轻型变体:
基本思想是将语音管理代码移动到生命周期感知组件中。 view/activity 中的所有 UI 代码和视图模型中的业务逻辑/非 android 状态。根据您目前分享的要求,我认为使用基础 activity 或视图模型没有意义。
/**
* All the speech related code is encapsulated here, so any new activity/fragment can use it by registering it's lifecycle
*/
class SpeechManager(private val context: Context): LifecycleObserver {
val TAG = "SpeechManager"
private var speechRecognizer: SpeechRecognizer? = null
fun registerWithLifecycle(lifecycle: Lifecycle) {
Log.e(TAG, "registerWithLifecycle")
lifecycle.addObserver(this)
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun start() {
Log.e(TAG, "start")
speechRecognizer = (speechRecognizer ?: SpeechRecognizer.createSpeechRecognizer(context)).apply {
// setRecognitionListener(object : RecognitionListener {
// //implement methods
// })
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stop() {
Log.e(TAG, "stop")
speechRecognizer?.run {
stopListening()
destroy()
}
}
}
视图模型:
class SpeechViewModel: ViewModel() {
val TAG = "SpeechViewModel"
//List all your "data/state" that needs to be restores across activity restarts
private val actions: MutableLiveData<Actions> = MutableLiveData<Actions>().apply { value = Actions.ActionA }
//Public API for getting observables and all use-cases
fun getActions() = actions
fun doActionA(){
//validations, biz logic
Log.e(TAG, "doActionA")
actions.value = Actions.ActionA
}
fun doActionB(){
Log.e(TAG, "doActionB")
actions.value = Actions.ActionB
}
}
sealed class Actions{
object ActionA: Actions()
object ActionB: Actions()
}
Activity/View:
class SpeechActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_speech)
setSupportActionBar(toolbar)
initSpeechManager()
}
private lateinit var speechManager: SpeechManager
private lateinit var speechViewModel: SpeechViewModel
/**
* Register lifecycle aware components and start observing state changes from ViewModel.
* All UI related code should ideally be here (or your view equivalent in MVVM)
*/
private fun initSpeechManager() {
speechManager = SpeechManager(this).apply {registerWithLifecycle(lifecycle)}
speechViewModel = ViewModelProviders.of(this).get(SpeechViewModel::class.java).apply {
getActions().observe(this@SpeechActivity, Observer<Actions>{
when(it){
is Actions.ActionA -> {
Log.e(TAG, "Perform ActionA")
speechManager.start()
}
is Actions.ActionB -> {
Log.e(TAG, "Perform ActionB")
speechManager.stop()
super.onBackPressed()
}
}
})
}
}
}
我有一个从基础 class 继承的 ViewModel
,我希望相应的 Activity
也从基础 class 继承。 activity 每次都会调用派生的 ViewModel
的相同方法。所以它会是这样的:
BaseViewModel:
abstract class BaseViewModel(application: Application) : AndroidViewModel(application) {
protected val context = getApplication<Application>().applicationContext
protected var speechManager: SpeechRecognizerManager? = null
var _actionToTake : MutableLiveData<AnalyseVoiceResults.Actions> = MutableLiveData()
var actionToTake : LiveData<AnalyseVoiceResults.Actions> = _actionToTake
open fun stopListening() {
if (speechManager != null) {
speechManager?.destroy()
speechManager = null
}
open fun startListening() {
val isListening = speechManager?.ismIsListening() ?: false
if (speechManager == null) {
SetSpeechListener()
} else if (!isListening) {
speechManager?.destroy()
SetSpeechListener()
}
}
}
基础Activity
class BaseActivity : AppCompatActivity() {
private lateinit var baseViewModel: BaseViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
fun goback() {
super.onBackPressed()
baseViewModel.stopListening()
finish()
}
fun startListening() {
baseViewModel.startListening()
}
override fun onDestroy() {
super.onDestroy()
baseViewModel.stopListening()
}
}
派生Activity:
class DerivedActivity : BaseActivity() {
private val nextActivityViewModel: NextActivityViewModel by inject()
///^^inherits from BaseViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
/*** pass reference ***/
baseViewModel = nexActivityViewModel
nextActivityViewModel.actionToTake.observe(this, object : Observer<AnalyseVoiceResults.Actions?> {
override fun onChanged(t: AnalyseVoiceResults.Actions?) {
if (t?.equals(AnalyseVoiceResults.Actions.GO_BACK) ?: false) {
goback()
}
}
})
startListening()
}
}
这是否会导致内存泄漏,为此 activity 有两个视图模型实例?有一个更好的方法吗?我不想为我的所有活动重复相同的代码。 (如果我用一个碱基片段做这件事,我也会有同样的问题)。
使这个 var baseViewModel: BaseViewModel 成为一个抽象变量,所有子 class 都必须覆盖它。因此,当您调用 startListening 和 stopListening 时,这些方法将从子实现中调用。
编辑:
将 BaseActivity 设为抽象 class,将 baseViewModel 设为抽象变量
abstract class BaseActivity : AppCompatActivity() {
private abstract var baseViewModel: BaseViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
fun goback() {
super.onBackPressed()
baseViewModel.stopListening()
finish()
}
fun startListening() {
baseViewModel.startListening()
}
override fun onDestroy() {
super.onDestroy()
baseViewModel.stopListening()
}
}
因此,您的 DerivedActivity 必须覆盖 baseViewModel,每次调用父亲的 class 都会触发子
class DerivedActivity : BaseActivity() {
override val baseViewModel: NextActivityViewModel by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Will this cause memory leaks to have two instances of a view model for this activity?
不,这种方法没有内存泄漏。您也没有相同 activity 的 2 个 ViewModel 实例。它是 ViewModel 的单个实例,在 BaseActivity 和 BaseViewModel 中被不同的变量引用。
Is there a better way to do this?
我看到的第一个问题是您在 ViewModels 中有 Android 特定代码,这不是一个好的做法。您应该将语音管理器代码移动到基础 activity 本身,并且 ViewModel 应该只保存您希望在方向更改时保留的 "state" 数据。这将确保所有语音管理方法(创建、恢复、销毁)都在基础 activity 中。具体 activity 只有在状态改变时才会有观察者。
如果您遵循任何架构模式(如 MVP),一旦您将 Speech Manager 代码移出到 activity,将其进一步移出到 Presenter 将变得显而易见。
编辑:我没有在生产中使用 MVVM 模式,但这是您可能想要的轻型变体:
基本思想是将语音管理代码移动到生命周期感知组件中。 view/activity 中的所有 UI 代码和视图模型中的业务逻辑/非 android 状态。根据您目前分享的要求,我认为使用基础 activity 或视图模型没有意义。
/**
* All the speech related code is encapsulated here, so any new activity/fragment can use it by registering it's lifecycle
*/
class SpeechManager(private val context: Context): LifecycleObserver {
val TAG = "SpeechManager"
private var speechRecognizer: SpeechRecognizer? = null
fun registerWithLifecycle(lifecycle: Lifecycle) {
Log.e(TAG, "registerWithLifecycle")
lifecycle.addObserver(this)
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun start() {
Log.e(TAG, "start")
speechRecognizer = (speechRecognizer ?: SpeechRecognizer.createSpeechRecognizer(context)).apply {
// setRecognitionListener(object : RecognitionListener {
// //implement methods
// })
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stop() {
Log.e(TAG, "stop")
speechRecognizer?.run {
stopListening()
destroy()
}
}
}
视图模型:
class SpeechViewModel: ViewModel() {
val TAG = "SpeechViewModel"
//List all your "data/state" that needs to be restores across activity restarts
private val actions: MutableLiveData<Actions> = MutableLiveData<Actions>().apply { value = Actions.ActionA }
//Public API for getting observables and all use-cases
fun getActions() = actions
fun doActionA(){
//validations, biz logic
Log.e(TAG, "doActionA")
actions.value = Actions.ActionA
}
fun doActionB(){
Log.e(TAG, "doActionB")
actions.value = Actions.ActionB
}
}
sealed class Actions{
object ActionA: Actions()
object ActionB: Actions()
}
Activity/View:
class SpeechActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_speech)
setSupportActionBar(toolbar)
initSpeechManager()
}
private lateinit var speechManager: SpeechManager
private lateinit var speechViewModel: SpeechViewModel
/**
* Register lifecycle aware components and start observing state changes from ViewModel.
* All UI related code should ideally be here (or your view equivalent in MVVM)
*/
private fun initSpeechManager() {
speechManager = SpeechManager(this).apply {registerWithLifecycle(lifecycle)}
speechViewModel = ViewModelProviders.of(this).get(SpeechViewModel::class.java).apply {
getActions().observe(this@SpeechActivity, Observer<Actions>{
when(it){
is Actions.ActionA -> {
Log.e(TAG, "Perform ActionA")
speechManager.start()
}
is Actions.ActionB -> {
Log.e(TAG, "Perform ActionB")
speechManager.stop()
super.onBackPressed()
}
}
})
}
}
}