首页 > 解决方案 > 使用 viewModel 处理屏幕旋转而不丢失数据 - Android

问题描述

我有一个未指定方向的活动,并且该活动附加了一个片段,该片段具有纵向和横向模式的不同布局,并且在该片段上,有条件地进行了多个API调用,我的问题是当屏幕旋转时,所有数据都丢失了并且该片段上有很多数据,我不想将每个数据保存在 saveInstance 方法上。我试过android:configChanges="keyboardHidden|orientation|screenSize"了,但这并没有解决我的问题。我想使用 viewModel 处理这个问题。请帮助,在此先感谢。这是我的代码

存储库

class GetDataRepository {
val TAG = GetDataRepository::class.java.canonicalName
var job: CompletableJob = Job()

fun getData(
    token: String?,
    sslContext: SSLContext,
    matchId: Int
): LiveData<ResponseModel> {
    job = Job()
    return object : LiveData<ResponseModel>() {
        override fun onActive() {
            super.onActive()
            job.let { thejob ->
                CoroutineScope(thejob).launch {
                    try {
                        val apiResponse = ApiService(sslContext).getData(
                            token
                         
                        )
                        LogUtil.debugLog(TAG, "apiResponse ${apiResponse}")
                        withContext(Dispatchers.Main) {
                            value = apiResponse
                        }
                    } catch (e: Throwable) {
                        LogUtil.errorLog(TAG, "error: ${e.message}")
                        withContext(Dispatchers.Main) {
                            when (e) {
                                is HttpException -> {
                                    value =
                                        Gson().fromJson<ResponseModel>(
                                            (e as HttpException).response()?.errorBody()
                                                ?.string(),
                                            ResponseModel::class.java
                                        )
                                }
                                else -> value = ResponseModel(error = e)
                            }
                        }

                    } finally {
                        thejob.complete()
                    }
                }

            }
        }
    }
}

fun cancelJob() {
    job.cancel()
}

}

视图模式:

class DataViewModel : ViewModel() {
val TAG = DataViewModel::class.java.canonicalName
var mListener: DataListener? = null

private val mGetDataRepository: GetDataRepository = GetDataRepository()


fun getData() {
    LogUtil.debugLog(TAG, "getData")
    if (mListener?.isInternetAvailable()!!) {
        mListener?.onStartAPI()

        val context = mListener?.getContext()

        val token: String? = String.format(
            context?.resources!!.getString(R.string.user_token),
            PreferenceUtil.getUserData(context).token
        )

        val sslContext = mListener?.getSSlContext()

        if (sslContext != null) {
            val getData =
                mGetDataRepository.getData(
                    token
                )
            LogUtil.debugLog(TAG, "getData ${getData}")
            mListener?.onApiCall(getData)
        } else {
            LogUtil.debugLog(TAG, "getData Invalid certificate")
            mListener?.onError("Invalid certificate")
        }
    } else {
        LogUtil.debugLog(TAG, "getData No internet")
        mListener?.onError("Please check your internet connectivity!!!")
    }
    LogUtil.debugLog(TAG, "Exit getData()")
}

}

活动:

class DataActivity : AppCompatActivity() {
val TAG = DataActivity::class.java.canonicalName
lateinit var fragment: DataFragment

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    LogUtil.debugLog(TAG, "onCreate: Enter")

    var binding: ActivityDataBinding =
        DataBindingUtil.setContentView(this, R.layout.activity_data)
    if (savedInstanceState == null) {
         fragment = DataFragment.newInstance()
        supportFragmentManager.beginTransaction().add(R.id.container, fragment, DataFragment.TAG)
    } else {
        fragment = supportFragmentManager.findFragmentByTag(DataFragment.TAG) as DataFragment
    }

    LogUtil.debugLog(TAG, "onCreate: Exit")
}

}

分段:

class DataFragment : Fragment(), DataListener {
private var mBinding: FragmentDataBinding? = null
private lateinit var mViewModel: DataViewModel

companion object {
    val TAG = DataFragment::class.java.canonicalName
    fun newInstance(): DataFragment {
        return DataFragment()
    }
}

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    mBinding =
        DataBindingUtil.inflate(inflater, R.layout.fragment_data, container, false)
    mViewModel = ViewModelProvider(this).get(DataViewModel::class.java)
    mViewModel.mListener = this
    getData()
    return mBinding?.root
}

private fun getData() {
    LogUtil.debugLog(TAG, "Enter getMatchScore()")
    mViewModel.getData()
    LogUtil.debugLog(TAG, "Exit getMatchScore()")
}

override fun <T> onApiCall(response: LiveData<T>) {
    response.observe(this, Observer {
        it as DataResponseModel
        //
    })

}

}

标签: androidkotlinmvvmandroid-livedatakotlin-coroutines

解决方案


默认情况下 viewModel 的生命周期比您的活动长(在您的情况下,屏幕旋转)。

ViewModel 不会因为配置更改而销毁,您可以查看此链接

您似乎在活动/片段的其他地方犯了错误,请将您的活动/片段代码放在这里。

在您的片段中,您在 onCreateView 中调用 mViewModel.getData(),每次旋转活动时,此方法调用和所有存储数据都会重置并再次获取!您只需检查片段中 ViewModel 的数据,如果它是空调用getData(),看起来你的 ViewModel 引用你的 view(Fragment) (你将一个监听器从你的 Fragment 传递给你的 ViewModel),它也是一种反模式(推荐这篇文章


推荐阅读