refactor(net): 优化API请求错误处理和基础组件功能

- 添加JsonSyntaxException异常处理,提供更友好的错误提示
- 修复API响应判断逻辑,移除不必要的空值检查
- 优化HttpException转换为ApiException时的数据解析
- 移除BaseActivity中废弃的launch方法和相关变量
- 重构BaseActivity中的权限请求和页面跳转回调机制
- 更新Loading对话框的显示方法,支持自定义消息和内容更新
- 修复CollectFragment中的语法错误和日志记录功能
- 优化DishSamplingActivity中的页面跳转实现方式
- 完善SettingActivity中的相机权限检查逻辑
This commit is contained in:
2026-03-30 15:28:52 +08:00
parent 28c7628321
commit d6a9c3562d
6 changed files with 147 additions and 83 deletions
@@ -35,18 +35,8 @@ import kotlin.getValue
open class BaseActivity : AppCompatActivity() { open class BaseActivity : AppCompatActivity() {
private lateinit var binding: ActivityBaseBinding private lateinit var binding: ActivityBaseBinding
private var launcher: ActivityResultLauncher<Intent>? = null
private var launchCallback: ((Intent?) -> Unit)? = null
public fun launch(cls: Class<*>, launchCallback: (Intent?) -> Unit) {
this.launchCallback = launchCallback
launcher?.launch(Intent(this, cls))
}
public fun launch(intent: Intent, launchCallback: (Intent?) -> Unit) {
this.launchCallback = launchCallback
launcher?.launch(intent)
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -57,12 +47,6 @@ open class BaseActivity : AppCompatActivity() {
} }
setContentView(binding.root) setContentView(binding.root)
statusBarDarkFont(enable = false) statusBarDarkFont(enable = false)
launcher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
launchCallback?.invoke(result.data)
}
}
// window.setDecorFitsSystemWindows(false) // 启用 Edge-to-Edge // window.setDecorFitsSystemWindows(false) // 启用 Edge-to-Edge
// window.insetsController?.apply { // window.insetsController?.apply {
@@ -95,8 +79,7 @@ open class BaseActivity : AppCompatActivity() {
fun statusBarDarkFont(enable: Boolean) { fun statusBarDarkFont(enable: Boolean) {
//window.decorView.systemUiVisibility = if (enable) View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR else 0 //window.decorView.systemUiVisibility = if (enable) View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR else 0
val color = ContextCompat.getColor( val color = ContextCompat.getColor(
this, this, if (enable) R.color.black else R.color.white
if (enable) R.color.black else R.color.white
) )
binding.tvLeftTime.setTextColor(color) binding.tvLeftTime.setTextColor(color)
binding.tvRightTime.setTextColor(color) binding.tvRightTime.setTextColor(color)
@@ -161,8 +144,11 @@ open class BaseActivity : AppCompatActivity() {
handler.removeCallbacks(updateTask) handler.removeCallbacks(updateTask)
} }
fun showLoading() { fun showLoading(msg: String = "加载中……") {
Loading.show(this) Loading.show(
context = this,
message = msg
)
} }
fun dismissLoading() { fun dismissLoading() {
@@ -193,4 +179,52 @@ open class BaseActivity : AppCompatActivity() {
netViewModel.querySeasoningList(param, onSuccess, onFailure) netViewModel.querySeasoningList(param, onSuccess, onFailure)
} }
private var permissionCallback: ((isGranted: Boolean) -> Unit)? = null
private var activityCallback: ((intent: Intent?) -> Unit)? = null
fun requestMultiplePermissions(
permissions: Array<String>,
callback: (isGranted: Boolean) -> Unit
) {
this.permissionCallback = callback
requestMultiplePermissionsLauncher.launch(permissions)
}
fun requestPermission(permission: String, callback: (isGranted: Boolean) -> Unit) {
this.permissionCallback = callback
requestPermissionLauncher.launch(permission)
}
val requestMultiplePermissionsLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
var isGranted = true
permissions.entries.forEach {
if (!it.value) {
isGranted = false
}
}
permissionCallback?.invoke(isGranted)
}
// 权限请求回调
val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
permissionCallback?.invoke(isGranted)
}
fun startActivity(intent: Intent, callback: (Intent?) -> Unit) {
this.activityCallback = callback
startActivityLauncher.launch(intent)
}
// activity页面返回的回调
private val startActivityLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
activityCallback?.invoke(it.data)
}
} }
@@ -13,14 +13,22 @@ object Loading {
private var dialog: LoadingDialog? = null private var dialog: LoadingDialog? = null
fun show(context: Context) { fun isShowing(): Boolean {
return dialog != null && dialog!!.isShowing
}
fun updateContent(content: String) {
if (isShowing().not()) return
dialog!!.updateContent(content)
}
fun show(context: Context, message: String = "加载中……", onDismiss: () -> Unit = {}) {
try { try {
if (dialog?.isShowing == true) { if (dialog?.isShowing == true) {
dialog?.dismiss() dialog?.dismiss()
} }
if (dialog == null) { if (dialog == null) {
dialog = LoadingDialog(context) dialog = LoadingDialog(context = context, message = message, onDismiss = onDismiss)
} }
dialog?.show() dialog?.show()
} catch (e: Exception) { } catch (e: Exception) {
@@ -45,11 +53,14 @@ object Loading {
class LoadingDialog( class LoadingDialog(
context: Context, context: Context,
message: String = "加载中……", message: String,
private var onDismiss: () -> Unit = {} private var onDismiss: () -> Unit
) : Dialog(context, R.style.LoadingDialog) { ) : Dialog(context, R.style.LoadingDialog) {
private val binding: DialogLoadingBinding by lazy {
DialogLoadingBinding.inflate(LayoutInflater.from(context))
}
init { init {
val binding = DialogLoadingBinding.inflate(LayoutInflater.from(context))
binding.tvMessage.text = message binding.tvMessage.text = message
setContentView(binding.root) setContentView(binding.root)
setCancelable(true) setCancelable(true)
@@ -64,6 +75,10 @@ class LoadingDialog(
onDismiss() onDismiss()
} }
} }
fun updateContent(content: String) {
binding.tvMessage.text = content
}
} }
//class LoadingDialog3( //class LoadingDialog3(
@@ -1,5 +1,6 @@
package com.shuwei.dish.match.net package com.shuwei.dish.match.net
import com.google.gson.JsonSyntaxException
import com.shuwei.dish.match.utils.ext.toJsonString import com.shuwei.dish.match.utils.ext.toJsonString
import com.shuwei.dish.match.utils.ext.toObject import com.shuwei.dish.match.utils.ext.toObject
import com.shuwei.dish.match.utils.ext.toType import com.shuwei.dish.match.utils.ext.toType
@@ -60,10 +61,10 @@ suspend fun <T> request(
onFailure("-1", apiException.errorMsg) onFailure("-1", apiException.errorMsg)
return return
} }
if (response?.isSuccess() == true) { if (response.isSuccess()) {
onSuccess(response.data as T) onSuccess(response.data as T)
} else { } else {
onFailure(response?.code ?: "-1", response?.msg ?: "") onFailure(response.code, response.msg ?: "")
} }
} }
@@ -77,6 +78,11 @@ fun getApiException(e: Exception): ApiException {
return when (e) { return when (e) {
is HttpException -> httpException2ApiException(e) is HttpException -> httpException2ApiException(e)
is JsonSyntaxException -> ApiException(
ErrorType.NETWORK_ERROR,
errorMsg = "服务器响应异常,请稍后重试"
)
is UnknownHostException -> ApiException( is UnknownHostException -> ApiException(
ErrorType.NETWORK_ERROR, ErrorType.NETWORK_ERROR,
errorMsg = "网络未连接,请检查网络" errorMsg = "网络未连接,请检查网络"
@@ -108,10 +114,15 @@ fun getApiException(e: Exception): ApiException {
fun httpException2ApiException(e: HttpException): ApiException { fun httpException2ApiException(e: HttpException): ApiException {
val body = e.response()?.errorBody()?.string() val body = e.response()?.errorBody()?.string()
val codeMsg: CodeMsg? = body?.toObject<CodeMsg>() val codeMsg: CodeMsg? = try {
body?.toObject<CodeMsg>()
} catch (ex: Exception) {
null
}
return ApiException( return ApiException(
ErrorType.NETWORK_ERROR, ErrorType.NETWORK_ERROR,
errorMsg = codeMsg?.msg ?: "" errorMsg = if (codeMsg == null) "数据解析异常(${e.code()})"
else codeMsg.msg ?: "服务器错误(${e.code()})"
) )
} }
@@ -238,7 +238,7 @@ class DishSamplingActivity : BaseActivity() {
} }
val food = FoodRecord() val food = FoodRecord()
binding.etInputDish.let { binding.etInputDish.let {
if (it.tag!=null && it.tag.toString() == it.text.toString()) { if (it.tag != null && it.tag.toString() == it.text.toString()) {
food.foodId = foodId food.foodId = foodId
} }
} }
@@ -259,19 +259,18 @@ class DishSamplingActivity : BaseActivity() {
} }
private fun jumpSearch(v: View) { private fun jumpSearch(v: View) {
launch(Intent(this, FoodSearchActivity::class.java).apply { val intent = Intent(this, FoodSearchActivity::class.java).apply {
putExtra(FoodSearchActivity.FOOD_NAME, binding.etInputDish.text.toString().trim()) putExtra(FoodSearchActivity.FOOD_NAME, binding.etInputDish.text.toString().trim())
}) { intent -> }
intent?.let { startActivity(intent) {
foodId = it.getStringExtra(FoodSearchActivity.FOOD_ID) foodId = it?.getStringExtra(FoodSearchActivity.FOOD_ID)
foodName = it.getStringExtra(FoodSearchActivity.FOOD_NAME) foodName = it?.getStringExtra(FoodSearchActivity.FOOD_NAME)
binding.etInputDish.run { binding.etInputDish.run {
setText(foodName) setText(foodName)
tag = foodName tag = foodName
setSelection(text.length) setSelection(text.length)
} }
} }
}
KeyboardUtil.hideKeyboard(v.context, v) KeyboardUtil.hideKeyboard(v.context, v)
} }
@@ -1,12 +1,12 @@
package com.shuwei.dish.match.ui package com.shuwei.dish.match.ui
import android.Manifest import android.Manifest
import android.annotation.SuppressLint
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.ViewGroup import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.graphics.toColorInt import androidx.core.graphics.toColorInt
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@@ -23,7 +23,6 @@ import com.shuwei.dish.match.utils.ext.dp
import com.shuwei.dish.match.utils.ext.gone import com.shuwei.dish.match.utils.ext.gone
import com.shuwei.dish.match.utils.ext.toast import com.shuwei.dish.match.utils.ext.toast
import com.shuwei.dish.match.utils.ext.visible import com.shuwei.dish.match.utils.ext.visible
import kotlin.text.replace
/** /**
* 设置Activity * 设置Activity
@@ -41,18 +40,6 @@ class SettingActivity : BaseActivity() {
} }
private lateinit var previewView: PreviewView private lateinit var previewView: PreviewView
// 权限申请
private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
if (isGranted) {
// 权限已授予,显示 CollectFragment
showFragment(fragmentList[1])
} else {
// 权限被拒绝
toast("暂无相机权限,无法使用菜品采集功能")
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivitySettingBinding.inflate(layoutInflater) binding = ActivitySettingBinding.inflate(layoutInflater)
@@ -138,8 +125,13 @@ class SettingActivity : BaseActivity() {
/** /**
* 检查相机权限并显示 CollectFragment * 检查相机权限并显示 CollectFragment
*/ */
@SuppressLint("ObsoleteSdkInt")
private fun checkCameraPermissionAndShowFragment() { private fun checkCameraPermissionAndShowFragment() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// Android 6.0 以下,直接显示 Fragment
showFragment(fragmentList[1])
return
}
if (ContextCompat.checkSelfPermission( if (ContextCompat.checkSelfPermission(
this, this,
Manifest.permission.CAMERA Manifest.permission.CAMERA
@@ -149,11 +141,15 @@ class SettingActivity : BaseActivity() {
showFragment(fragmentList[1]) showFragment(fragmentList[1])
} else { } else {
// 权限未授予,申请权限 // 权限未授予,申请权限
requestPermissionLauncher.launch(Manifest.permission.CAMERA) requestPermission(Manifest.permission.CAMERA) { isGranted ->
} if (isGranted) {
} else { // 权限已授予,显示 CollectFragment
// Android 6.0 以下,直接显示 Fragment
showFragment(fragmentList[1]) showFragment(fragmentList[1])
} else {
// 权限被拒绝
toast("暂无相机权限,无法使用菜品采集功能")
}
}
} }
} }
@@ -2,9 +2,9 @@ package com.shuwei.dish.match.ui.fragment
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import android.net.Uri import android.net.Uri
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -15,19 +15,16 @@ import com.shuwei.dish.match.R
import com.shuwei.dish.match.adapter.FoodCollectionAdapter import com.shuwei.dish.match.adapter.FoodCollectionAdapter
import com.shuwei.dish.match.base.BaseFragment import com.shuwei.dish.match.base.BaseFragment
import com.shuwei.dish.match.databinding.FragmentCollectBinding import com.shuwei.dish.match.databinding.FragmentCollectBinding
import com.shuwei.dish.match.dialog.Loading
import com.shuwei.dish.match.objbox.FoodCollectionBean import com.shuwei.dish.match.objbox.FoodCollectionBean
import com.shuwei.dish.match.objbox.FoodModule import com.shuwei.dish.match.objbox.FoodModule
import com.shuwei.dish.match.objbox.ObjectBox
import com.shuwei.dish.match.ui.SettingActivity import com.shuwei.dish.match.ui.SettingActivity
import com.shuwei.dish.match.utils.BitmapSaver import com.shuwei.dish.match.utils.BitmapSaver
import com.shuwei.dish.match.utils.Debouncer import com.shuwei.dish.match.utils.Debouncer
import com.shuwei.dish.match.utils.ImageUtil import com.shuwei.dish.match.utils.ImageUtil
import com.shuwei.dish.match.utils.LogSaveUtil
import com.shuwei.dish.match.utils.ext.clickWithDebounce import com.shuwei.dish.match.utils.ext.clickWithDebounce
import com.shuwei.dish.match.utils.ext.toast import com.shuwei.dish.match.utils.ext.toast
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
class CollectFragment : BaseFragment<FragmentCollectBinding>() { class CollectFragment : BaseFragment<FragmentCollectBinding>() {
@@ -46,7 +43,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
} }
} }
// private val searchFoodList = mutableListOf<FoodInfo>() // private val searchFoodList = mutableListOf<FoodInfo>()
// private var settingActivity: SettingActivity? = null // private var settingActivity: SettingActivity? = null
// private var checkedItem: FoodInfo? = null // private var checkedItem: FoodInfo? = null
// private val searchFoodAdapter by lazy { // private val searchFoodAdapter by lazy {
@@ -96,7 +93,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
if (index == -1) { if (index == -1) {
toast("每次只允许保存${MAX_COUNT}条数据") toast("每次只允许保存${MAX_COUNT}条数据")
hideWaitingDialog() hideWaitingDialog()
rerurn@cameraCallback rerurn@ cameraCallback
} }
activity?.runOnUiThread { activity?.runOnUiThread {
foodCollectionList[index].let { foodCollectionList[index].let {
@@ -268,7 +265,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
// //} // //}
// runBlocking { // runBlocking {
// activity?.runOnUiThread { // activity?.runOnUiThread {
// showWaitingDialog2("图片上传中$count/$totalFileCount") // showWaitingDialog("图片上传中$count/$totalFileCount")
// } // }
// val foodList = batch.mapIndexed { index, it -> // val foodList = batch.mapIndexed { index, it ->
// Food( // Food(
@@ -351,9 +348,21 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
binding.llCameraFlag.visibility = View.VISIBLE binding.llCameraFlag.visibility = View.VISIBLE
} }
private fun log(msg: String) {} private fun log(msg: String) {
private fun searchByFoodName(name: String, block:(String)-> Unit) {} Log.d(TAG, "log: $msg")
private fun hideWaitingDialog() {} LogSaveUtil.saveLogFile(msg)
private fun showWaitingDialog(msg: String) {} }
private fun showWaitingDialog2(msg: String) {}
private fun searchByFoodName(name: String, block: (String) -> Unit) {}
private fun hideWaitingDialog() {
settingActivity.dismissLoading()
}
private fun showWaitingDialog(msg: String) {
if (Loading.isShowing()) {
Loading.updateContent(msg)
return
}
settingActivity.showLoading(msg)
}
} }