You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MVVM架构中点击视图需调用startActivityForResult,如何在ViewModel实现?

在MVVM中实现视图点击触发startActivityForResult的方案

嘿,这个问题我在日常开发里经常碰到,毕竟MVVM的核心就是让ViewModel和视图层解耦,ViewModel里确实不能直接调用startActivityForResult这类依赖Activity上下文的方法。下面给你分享几个成熟的解决方案,都是项目里验证过的:

方案1:LiveData + 视图层观察者(最经典的解耦方式)

这个方案的核心是用LiveData作为ViewModel和Activity/Fragment之间的事件传递桥梁,ViewModel只负责触发事件,具体的页面跳转逻辑交给视图层处理。

代码示例:

ViewModel 部分:

class MyViewModel : ViewModel() {
    // 定义一个用于传递跳转意图的MutableLiveData
    private val _startActivityEvent = MutableLiveData<Intent>()
    val startActivityEvent: LiveData<Intent> = _startActivityEvent

    // 视图点击时调用的方法(由View绑定触发)
    fun onTargetViewClicked() {
        // 构建目标页面的Intent
        val intent = Intent().apply {
            setClass(/* 视图层会处理上下文,这里无需传入 */, TargetActivity::class.java)
            putExtra("USER_ID", 123)
        }
        // 发送跳转事件
        _startActivityEvent.postValue(intent)
    }

    // 处理返回结果的方法(由视图层调用)
    fun handleActivityResult(data: Intent) {
        val result = data.getStringExtra("RESULT_DATA")
        // 处理返回的数据,更新UI状态等
    }
}

Activity 部分:

class MainActivity : AppCompatActivity() {
    private lateinit var viewModel: MyViewModel
    private val REQUEST_CODE = 1001

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        viewModel = ViewModelProvider(this)[MyViewModel::class.java]

        // 观察ViewModel的跳转事件
        viewModel.startActivityEvent.observe(this) { intent ->
            startActivityForResult(intent, REQUEST_CODE)
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
            data?.let { viewModel.handleActivityResult(it) }
        }
    }
}

方案2:使用接口回调(传统解耦方式)

如果习惯用接口回调的方式,也可以定义一个跳转回调接口,让Activity实现这个接口,然后把接口实例以弱引用的方式传给ViewModel(避免内存泄漏)。

代码示例:

定义回调接口:

interface ActivityLauncherCallback {
    fun launchActivityForResult(intent: Intent, requestCode: Int)
}

ViewModel 部分:

class MyViewModel(private val launcherRef: WeakReference<ActivityLauncherCallback>) : ViewModel() {
    fun onTargetViewClicked() {
        val intent = Intent(..., TargetActivity::class.java)
        // 调用回调触发跳转
        launcherRef.get()?.launchActivityForResult(intent, 1001)
    }
}

Activity 部分:

class MainActivity : AppCompatActivity(), ActivityLauncherCallback {
    private lateinit var viewModel: MyViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // 用弱引用传递回调实例
        val launcherRef = WeakReference<ActivityLauncherCallback>(this)
        viewModel = ViewModelProvider(this, object : ViewModelProvider.Factory {
            override fun <T : ViewModel> create(modelClass: Class<T>): T {
                return MyViewModel(launcherRef) as T
            }
        })[MyViewModel::class.java]
    }

    override fun launchActivityForResult(intent: Intent, requestCode: Int) {
        startActivityForResult(intent, requestCode)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == 1001 && resultCode == RESULT_OK) {
            data?.let { viewModel.handleActivityResult(it) }
        }
    }
}

方案3:使用Jetpack Activity Result API(官方推荐,替代startActivityForResult)

注意:startActivityForResult已经被官方标记为废弃,现在推荐使用Activity Result API,这个方案结合LiveData使用会更优雅。

代码示例:

Activity 部分:

class MainActivity : AppCompatActivity() {
    private lateinit var viewModel: MyViewModel

    // 注册Activity Result Contract,处理返回结果
    private val targetActivityLauncher = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { result ->
        if (result.resultCode == RESULT_OK) {
            result.data?.let { viewModel.handleActivityResult(it) }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        viewModel = ViewModelProvider(this)[MyViewModel::class.java]

        // 观察ViewModel的跳转请求
        viewModel.launchTargetEvent.observe(this) { intent ->
            targetActivityLauncher.launch(intent)
        }
    }
}

ViewModel 部分:

class MyViewModel : ViewModel() {
    private val _launchTargetEvent = MutableLiveData<Intent>()
    val launchTargetEvent: LiveData<Intent> = _launchTargetEvent

    fun onTargetViewClicked() {
        val intent = Intent(..., TargetActivity::class.java)
        intent.putExtra("USER_ID", 123)
        _launchTargetEvent.postValue(intent)
    }

    fun handleActivityResult(data: Intent) {
        val result = data.getStringExtra("RESULT_DATA")
        // 处理返回数据
    }
}

关键注意事项:

  • 绝对不要让ViewModel持有Activity/Fragment的强引用,否则会导致严重的内存泄漏;
  • 推荐优先使用方案3,因为它是官方推出的替代方案,API设计更合理,也能避免requestCode冲突的问题;
  • 如果需要传递更复杂的参数,可以自定义ActivityResultContract,而不是只用默认的StartActivityForResult。

内容的提问来源于stack exchange,提问作者MarStarck

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:33:16