Android实战进阶 - 单点登录与系统维护的全局拦截

张开发
2026/4/16 8:55:40 15 分钟阅读

分享文章

Android实战进阶 - 单点登录与系统维护的全局拦截
在很多成熟项目中单点登录、系统维护均为核心的业务基础功能之一一般这种需求均为后台接口配合实现以下采用伪代码进行讲解更多的是梳理思路实现属于自己的逻辑安全保护篇Android实战进阶 - Android实战进阶 - 单点登录与系统维护的全局拦截Android实战进阶 - 用户闲置超时自动退出登录功能详解Q1如何相对实时的提示用户某些信息A1我想除了客户端的固有逻辑外更多的可能是通过实时访问接口来触发不同业务场景Q2什么时候会触发我们的目标功能A2当请求下一个接口时就会被触发争取在最早的时间进行拦截避免执行网络请求后的操作Q3单点登录、系统拦截如何判别A3通过双端定义固定场景返回码继续补场景拦截场景处理扩展函数辅助类IgnoreErrorAppException因为该篇需求的实现主要用到了后端接口的返回数据所以需要熟悉Retrofit网络框架那么我们就可以通过addInterceptor添加一个拦截器来实现以上场景的的需求 !场景拦截将后续写好的拦截器类似下方设置好后即可以前写的一篇 Retrofit封装部分基础配置可以借鉴valokHttpClient:OkHttpClientbylazy{valbuilderOkHttpClient.Builder()builder.connectTimeout(60,TimeUnit.SECONDS)builder.readTimeout(60,TimeUnit.SECONDS)builder.writeTimeout(60,TimeUnit.SECONDS)builder.hostnameVerifier{_,_-true}builder.addInterceptor(ErrorInterceptor())// 错误拦截器builder.build()}ErrorInterceptor 错误拦截器packagecn.com.xximportandroid.content.Intentimportandroid.text.TextUtilsimportokhttp3.Interceptorimportokhttp3.Responseimportokhttp3.ResponseBody.Companion.toResponseBodyimportorg.json.JSONArrayimportorg.json.JSONExceptionimportorg.json.JSONObjectimportretrofit2.InvocationinternalclassErrorInterceptor:Interceptor{overridefunintercept(chain:Interceptor.Chain):Response{//--- 非核心讲解区域 start---//if(!AppContext.isNetworkAvailable){// 无网判断AppException.error(-1,网络开小差请检查您的网络后再试)}valrequestchain.request()valignoreErrorrequest.tag(Invocation::class.java)?.method()?.getAnnotation(IgnoreError::class.java)valresponsechain.proceed(request)// 执行请求if(ignoreError.nonNull())returnresponse// 请求不做错误统一处理if(!response.isSuccessful){// 网络请求过程中出现错误throwAppException(-2,网络异常请重试)}if(response.body.isNull())returnresponse//--- 讲解区域 end---//valresultresponse.body?.string().string()valresponseBodyJSONObject(result).optString(content)//根据双方定义的字段进行数据读取、重组.toResponseBody(response.body?.contentType())response.newBuilder().body(responseBody).build()}// 错误统一处理根据不同错误场景执行对应逻辑checkException(result)}/** * 错误检测这部分是该篇核心讲解处 */privatefuncheckException(result:String){try{//获取后台返回的全json数据valjsonObjectJSONObject(result)//每家企业定义的字段有所不同各自理解即可valcontentObject:JSONObject?jsonObject.optJSONObject(content)//很多错误场景我们需要通过后台定义的响应码进行判别varrespCodejsonObject.optString(respCode,0000)//当respCode 00 或 0000 为正常接口请求状态//以下的业务逻辑仅可借鉴思路复制无用if(!TextUtils.equals(respCode,00)!TextUtils.equals(respCode,0000)){if(TextUtils.equals(respCode,20240102)){// 系统停业维护valintentIntent(ConstValue.SYS_MAINTAIN).putExtra(notice,getErrorMessage(jsonObject)).setPackage(AppContext.packageName)AppContext.sendBroadcast(intent)}elseif(TextUtils.equals(respCode,20250102)){//单点登录5分钟后再次使用先进入登录页面LoginInfo.setToken()valintentIntent(ConstValue.RE_LOGIN)intent.putExtra(reLogin,true)intent.setPackage(AppContext.packageName)AppContext.sendBroadcast(intent)}//这里先不用关注主要是上方错误码判别和逻辑处理AppException.error(contentObject?.toString(),respCode,getErrorMessage(jsonObject))// 向上层报错}}catch(e:JSONException){AppException.error(-3,网络异常请重试)}}privatefungetErrorMessage(json:JSONObject):String{varerrMsgjson.optString(respMsg)if(errMsg.isEmpty()){errMsgjson.optString(errMsg)}if(errMsg.isEmpty()){errMsgjson.optString(bizRetMsg)}if(errMsg.isEmpty()){errMsg无法识别错误}returnerrMsg}}场景处理关于下方广播在MainActivity的onCreate直接LogoutBroadcastReceiver(this)注册即可 (不再单独写代码记录了)packagecn.com.xximportandroid.content.BroadcastReceiverimportandroid.content.Contextimportandroid.content.Intentimportandroid.content.IntentFilterimportandroidx.appcompat.app.AppCompatActivityimportandroidx.lifecycle.Lifecycleimportandroidx.lifecycle.LifecycleObserverimportandroidx.lifecycle.OnLifecycleEventclassLogoutBroadcastReceiver(valactivity:AppCompatActivity):BroadcastReceiver(),LifecycleObserver{init{activity.lifecycle.addObserver(this)valintentFilterIntentFilter()intentFilter.addAction(ConstValue.RE_LOGIN_RECEIVIR)intentFilter.addAction(ConstValue.SYS_MAINTAIN)activity.registerReceiver(this,intentFilter)}//接收到对应的广播后自行跳转到相关业务场景即可overridefunonReceive(context:Context,intent:Intent){when(intent.action){ConstValue.SYS_MAINTAIN-{// 系统维护activity.startActivity(Intent(activity,MaintainDlgActivity::class.java).apply{putExtra(notice,intent.getStringExtra(notice))flagsIntent.FLAG_ACTIVITY_SINGLE_TOP})}ConstValue.RE_LOGIN-{// 弹窗提示重新登录activity.startActivity(Intent(activity,LogoutActivity::class.java).apply{putExtra(reLogin,intent.getBooleanExtra(reLogin,false))flagsIntent.FLAG_ACTIVITY_SINGLE_TOP})}}}OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)privatefunonDestroy(){activity.unregisterReceiver(this)}}扩展函数inlinevalconnectivityManager:ConnectivityManager?get()if(Build.VERSION.SDK_INTBuild.VERSION_CODES.M){AppContext.getSystemService(ConnectivityManager::class.java)}else{AppContext.getSystemService(Context.CONNECTIVITY_SERVICE)as?ConnectivityManager?}valContext.isNetworkAvailable:Booleanget(){returntry{connectivityManager?.activeNetworkInfo?.let{it.isConnectedit.stateNetworkInfo.State.CONNECTED}?:false}catch(_:Throwable){false}}辅助类IgnoreErrorMustBeDocumentedTarget(AnnotationTarget.FUNCTION)Retention(AnnotationRetention.RUNTIME)annotationclassIgnoreError()AppExceptionpublicclassAppExceptionextendsIOException{publicStringcode;publicStringmessage;publicStringbody;publicAppException(Stringcode,Stringmessage){this(,code,message);}publicAppException(Stringbody,Stringcode,Stringmessage){super(stringAdapter(message));this.messagestringAdapter(message);if(!TextUtils.isEmpty(body)){this.bodybody;}this.codestringAdapter(code);}privatestaticStringstringAdapter(Stringmessage){if(TextUtils.isEmpty(message)||TextUtils.equals(message,null)){return未取到远程错误描述;}returnmessage;}publicstaticvoiderror(Stringcode,Stringmessage)throwsIOException{thrownewAppException(code,message);}publicstaticvoiderror(Stringbody,Stringcode,Stringmessage)throwsIOException{thrownewAppException(body,code,message);}}

更多文章