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

Android MVVM架构下屏幕旋转重复请求问题排查

问题分析与修复方案

首先,你的问题核心有两个:ViewModel没有被正确复用,以及每次调用数据获取方法都会重复发起请求,导致屏幕旋转时重复触发整个流程。下面一步步拆解问题并给出修复:

1. ViewModel实例化错误(最关键的问题)

你当前的ViewModel初始化方式:

val viewModel: Fragment1ViewModel = ViewModelProviders.of(this).get(Fragment1ViewModel(Application())::class.java)

这种写法会每次都创建新的ViewModel实例,包括屏幕旋转后。因为ViewModelProviders.get()如果传入带参数的构造器,它无法复用已有的ViewModel,而是会每次调用构造函数生成新对象。正确的做法是让ViewModel依托自身的AndroidViewModel特性持有Application,不需要手动传参初始化。

修复后的Fragment初始化代码:

val viewModel: Fragment1ViewModel = ViewModelProvider(this).get(Fragment1ViewModel::class.java)

注:如果用的是旧版依赖,可能还是ViewModelProviders,但新版已推荐使用ViewModelProvider。修改后,屏幕旋转时ViewModel会被系统自动复用,不会重新创建。

2. Repository与ServerFetch的重复请求问题

即使ViewModel被复用了,你的getData()方法每次调用都会触发新请求:

  • ViewModel的getData()每次调用都会调用repository.getListItem()
  • Repository的getListItem()每次都会重新初始化serverFetch
  • ServerFetch的getData()每次都会调用connect()发起新的Volley请求

修复方案:缓存LiveData结果,避免重复请求

我们需要让Repository只在第一次请求时发起网络调用,后续复用已有的LiveData结果。

修改Repository类:

class Repository(private val context: Context) {
    private val serverFetch: ServerFetch = ServerFetch(context)
    // 缓存第一次请求的LiveData结果
    private var cachedLiveData: LiveData<String>? = null

    fun getListItem(): LiveData<String> {
        if (cachedLiveData == null) {
            cachedLiveData = serverFetch.getData()
        }
        return cachedLiveData!!
    }
}

这里移除了静态的serverFetch(避免上下文泄漏和重复覆盖),改为实例变量,并且缓存第一次请求的LiveData,后续调用直接返回缓存实例,避免重复触发请求。

修改ServerFetch类:

确保connect()只被调用一次,而不是每次getData()都执行:

class ServerFetch(private val context: Context) {
    private val api: String = "https://jsonplaceholder.typicode.com/posts"
    private val result = MutableLiveData<String>()
    // 标记是否已经发起过请求
    private var isRequested = false

    private fun connect() {
        if (isRequested) return
        isRequested = true
        val request = StringRequest(Request.Method.GET, api, Response.Listener {
            Log.i("Log", "ServerFetch Successfully")
            result.value = it
        }, Response.ErrorListener {
            Log.i("Log", "ServerFetch field: ${it.toString()}")
        })
        val queue = Volley.newRequestQueue(context)
        queue.add(request)
    }

    fun getData(): LiveData<String> {
        connect()
        return result
    }
}

添加isRequested标记,确保connect()只会执行一次,避免重复发起Volley请求。

3. 额外优化:ViewModel中缓存LiveData

为了进一步确保ViewModel复用后不会重复调用Repository方法,也可以在ViewModel中缓存LiveData:

class Fragment1ViewModel(application: Application) : AndroidViewModel(application) {
    private val repository = Repository(application)
    private var dataLiveData: LiveData<String>? = null

    fun getData(): LiveData<String> {
        if (dataLiveData == null) {
            Log.i("Log","get data in view model")
            dataLiveData = repository.getListItem()
        }
        return dataLiveData!!
    }
}

这样就算多次调用getData(),也只会在第一次时初始化LiveData,后续直接返回缓存实例。

验证修复后的流程

  • 首次运行:ViewModel初始化 → 调用getData() → 发起网络请求 → 返回结果
  • 屏幕旋转:ViewModel被复用 → 调用getData()直接返回缓存的LiveData → 不会发起新的网络请求,Observer会收到之前缓存的结果

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 15:18:12