package com.xw.common.base import android.app.Application import android.os.Bundle import com.xw.common.utils.LogUtils import android.view.View import androidx.lifecycle.* import com.blankj.utilcode.util.AppUtils import com.xw.common.utils.MMKVManager import com.xw.common.net.interceptor.ApiException import com.xw.common.utils.ToastUtilCommon import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent import java.io.IOException import java.net.ConnectException import java.net.SocketException import java.net.SocketTimeoutException import java.net.UnknownHostException import java.util.* open class BaseViewModel(application: Application) : AndroidViewModel(application), IBaseViewModel, KoinComponent { var TAG = "zmc-${this.javaClass.simpleName}" /** * 协程全局异常处理器,防止未捕获异常导致崩溃 */ private val exceptionHandler = CoroutineExceptionHandler { _, throwable -> LogUtils.e(TAG, "Uncaught coroutine exception: ${throwable.message}") runOnMainThread { ToastUtilCommon.showError(getNetworkErrorMessage(throwable)) } } var startActivityEvent: SingleLiveEvent> = SingleLiveEvent() var onBackEvent: SingleLiveEvent = SingleLiveEvent() var closeEvent: SingleLiveEvent = SingleLiveEvent() var dialogEvent: SingleLiveEvent = SingleLiveEvent() fun finish() { onBackEvent.postValue("finish") } fun onBackPressed() { onBackEvent.postValue("back") } // 当前正在显示的dialog数量 var currentDialogNum = 0 fun showDialog(content: String = "请稍后...") { currentDialogNum++ dialogEvent.postValue(content) } fun dismissDialog() { if (currentDialogNum > 0) currentDialogNum-- if (currentDialogNum == 0) { dialogEvent.postValue("dismiss") } } fun forceDismissDialog() { LogUtils.e(TAG, "forceDismissDialog: $currentDialogNum") currentDialogNum = 0 dialogEvent.postValue("dismiss") } fun launchRequest( tips: String = "加载中...", showLoading: Boolean = true, error: suspend (code: Int, msg: String) -> Unit = { code, msg -> ToastUtilCommon.showError(msg) LogUtils.e(TAG, "launchRequest: code = $code msg = $msg", ) }, request: suspend () -> Unit ) { if (showLoading) showDialog(tips) viewModelScope.launch(Dispatchers.IO + exceptionHandler) { try { request() } catch (e: Throwable) { try { errorHandling(e, error) } catch (handlingError: Throwable) { LogUtils.e(TAG, "Error handling failed: ${handlingError.message}") } } finally { if (showLoading) dismissDialog() } } } fun launchRequestFlow( tips: String = "加载中...", showLoading: Boolean = true, error: suspend (code: Int, msg: String) -> Unit = { code, msg -> ToastUtilCommon.showError(msg) LogUtils.e(TAG, "launchRequestFlow: code = $code msg = $msg") }, flowBlock: () -> Flow, onCollect: (T) -> Unit ) { if (showLoading) showDialog(tips) viewModelScope.launch(Dispatchers.IO + exceptionHandler) { try { flowBlock().collect { data -> onCollect(data) } } catch (e: Throwable) { try { errorHandling(e, error) } catch (handlingError: Throwable) { LogUtils.e(TAG, "Error handling failed: ${handlingError.message}") } } finally { if (showLoading) dismissDialog() } } } private suspend fun errorHandling(e: Throwable, error: suspend (code: Int, msg: String) -> Unit) { if (e is ApiException) { when { e.isTokenExpired() -> { MMKVManager.getInstance().clear() ToastUtilCommon.showWarn("登录已过期,请重新登录") viewModelScope.launch { delay(1000) AppUtils.relaunchApp(true) } } else -> { error.invoke(e.code, e.message ?: "") } } } else { val errorMsg = getNetworkErrorMessage(e) error.invoke(-1, errorMsg) } } /** * 获取网络异常的友好提示信息 */ private fun getNetworkErrorMessage(e: Throwable): String { return when (e) { is UnknownHostException -> "网络连接失败,请检查网络设置" is ConnectException -> "无法连接到服务器,请检查网络" is SocketTimeoutException -> "连接超时,请稍后重试" is SocketException -> "网络异常,请检查网络连接" is IOException -> "网络请求失败,请检查网络" else -> e.message ?: "未知错误" } } fun runOnMainThread(block: () -> Unit) { viewModelScope.launch(Dispatchers.Main) { block() } } fun onBack(view: View) { onBackPressed() } /** * 需要登录的跳转页面 * * @param clz 所跳转的目的Activity类 */ open fun startActivityNeedLoad(clz: Class<*>?) { startActivity(clz, null) } /** * 跳转页面 * * @param clz 所跳转的目的Activity类 */ open fun startActivity(clz: Class<*>?) { startActivity(clz, null) } /** * 跳转页面 * * @param clz 所跳转的目的Activity类 * @param bundle 跳转所携带的信息 */ open fun startActivity(clz: Class<*>?, bundle: Bundle?) { val params: MutableMap = HashMap() params["CLASS"] = clz if (bundle != null) { params["BUNDLE"] = bundle } startActivityEvent.postValue(params) } fun startActivityForResult(clz: Class<*>?, code: Int, bundle: Bundle? = null) { val params: MutableMap = HashMap() params["CLASS"] = clz params["requestCode"] = code if (bundle != null) { params["BUNDLE"] = bundle } startActivityEvent.postValue(params) } fun closeKeyboard() { closeEvent.postValue("close") } fun hideKeyboard() { closeEvent.postValue("hide") } override fun onCreate() {} override fun onDestroy() {} override fun onStart() {} override fun onStop() {} override fun onResume() {} override fun onPause() {} }