当前位置: 首页 > news >正文

Android进阶之路 - app后台切回前台触发超时保护退出登录

我们经常会在银行、金融或者其他行业的app中看到用户长时间将app放置于后台,当再次唤醒app时就会提示用户已退出登录,需要重新登录,那么该篇主要就是用于处理这种场景的

针对于放置后台的超时保护属于进程级别,所以我们需要监听进程的生命周期,主要用到了 Lifecycle 组件,有兴趣的可以去 组件化之路 - Lifecycle一知半解 了解一下如何监听进程的生命周期?

以前写过一篇 前后台切换监听 ,也是用于监听组件生命周期的,可以参考参考

    • 创建观察者 - 监听生命周期
    • 关于 Handler 、Thread 扩展函数
    • 观察者 绑定 进程生命周期
    • 初始化配置
      • Application 初始化监听
      • 绑定 Application

实现思路:通过监听进程的生命周期从而判断app处于前后台的状态,在不同状态下进行计时操作,当状态切换后判断是否超过所设时间,从而执行相关逻辑

这里并不涉及什么原理,最多就是有兴趣看看 Lifecycle 对于 Android 常用组件(Activity、Service、Process)在生命周期方面如何绑定、监听等

废话不多说,直接向目标进发!

创建观察者 - 监听生命周期

主要用于监听app处于前后台的一个状态,以及前后台切换后的时差是否超过保护时间,如果超过则可以将用户踢出去,让其重新登录

package com.example.lifestartupdemoimport android.util.Log
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent// 超时重新登录提示
internal class LoginStateObserver : LifecycleObserver {companion object {private const val interval = 10 * 1000L //保护时间10s,可自行设置}private var timestamp = 0L  //onPause 时间点/*** 应用程序出现到前台时调用*/@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)fun onResume() {Log.e("tag", "应用 onResume() - 前台")val currentTime = System.currentTimeMillis()if (timestamp != 0L && currentTime - timestamp > interval) {     // 后台超过保护时间,需要执行的逻辑timestamp = 0mainHandler.postDelayed(300) {//常见于清空用户信息,请用户重新登录Log.e("tag", "应用由后台到了前台,进入了超时逻辑")}}}/*** 应用程序退出到后台时调用*/@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)fun onPause() {Log.e("tag", "应用 onPause() 已被切换至后台")timestamp = System.currentTimeMillis()}}

关于 Handler 、Thread 扩展函数

这里主要涉及到了一些Handler原理,例如 LooperTherad

package com.example.lifestartupdemoimport android.os.Build
import android.os.Handler
import android.os.Looperfun Handler.postDelayed(delayMillis: Long,runnable: Runnable
) = this.postDelayed(runnable, delayMillis)@JvmField
val mainHandler: Handler = if (Build.VERSION.SDK_INT >= 28) Handler.createAsync(mainLooper) else try {Handler::class.java.getDeclaredConstructor(Looper::class.java,Handler.Callback::class.java,Boolean::class.javaPrimitiveType // async).newInstance(mainLooper, null, true)
} catch (ignored: NoSuchMethodException) {Handler(mainLooper) // Hidden constructor absent. Fall back to non-async constructor.
}

MainThread(kt文件)

@file:Suppress("UNUSED")package com.example.lifestartupdemoimport android.os.Looper/** This main looper cache avoids synchronization overhead when accessed repeatedly. */
@JvmField
val mainLooper: Looper = Looper.getMainLooper()!!@JvmField
val mainThread: Thread = mainLooper.threadval isMainThread: Boolean inline get() = mainThread === Thread.currentThread()@PublishedApi
internal val currentThread: Any?inline get() = Thread.currentThread()

观察者 绑定 进程生命周期

从架构而言有很多东西需要初始化,可以写一个接口便于解耦

package com.example.lifestartupdemoimport android.app.Applicationinterface ApplicationStartup {fun onCreate(application: Application)
}

具体绑定组件生命周期的实现类

package com.example.lifestartupdemoimport android.app.Application
import androidx.lifecycle.ProcessLifecycleOwnerinternal class MineApplicationStartup : ApplicationStartup {override fun onCreate(application: Application) {ProcessLifecycleOwner.get().lifecycle.addObserver(LoginStateObserver())}
}

初始化配置

Application 初始化监听

package com.example.lifestartupdemoimport android.app.Applicationclass OurApplication : Application() {override fun onCreate() {super.onCreate()//应用启动则初始化该配置val mineApplicationStartup = MineApplicationStartup()mineApplicationStartup.onCreate(this)}
}

绑定 Application

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><applicationandroid:allowBackup="true"android:dataExtractionRules="@xml/data_extraction_rules"android:fullBackupContent="@xml/backup_rules"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:supportsRtl="true"android:name=".OurApplication"android:theme="@style/Theme.LifeStartupDemo"tools:targetApi="31"><activityandroid:name=".MainActivity"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Java Web —— 第四天(HTTP协议,Tomcat)
  • 关于RCE
  • 白骑士的Matlab教学附加篇 5.2 代码规范与最佳实践
  • vue.config.js 配置多入口文件
  • LVS负载均衡集群部署之—NAT模式的介绍及搭建步骤
  • DBAPI如何用SQL查询出类似嵌套JSON的树状结构数据(例如省市区父子结构数据)
  • 开源力量,智领云KDP为大数据处理领域注入云原生活力
  • [C++内存管理]new,delete,operator new,opreator delete
  • 生成随机字符串(字母+数字)-批发行业进销存- PHP源码CyberWinApp-SAAS 本地化及未来之窗行业应用跨平台架构
  • Redis7.0.15 主从复制、哨兵模式搭建
  • Topsis法模型(评价类问题)
  • Sql Server索引的创建及优化
  • 多模态:Seed-story故事生成
  • 七、ESP32-S3上使用MicroPython点亮WS2812智能LED灯珠并通过web控制和JS颜色选择器改变灯珠颜色
  • 记一次 .NET某智慧出行系统 CPU爆高分析
  • 【5+】跨webview多页面 触发事件(二)
  • canvas 高仿 Apple Watch 表盘
  • CSS 专业技巧
  • css系列之关于字体的事
  • IIS 10 PHP CGI 设置 PHP_INI_SCAN_DIR
  • laravel with 查询列表限制条数
  • Perseus-BERT——业内性能极致优化的BERT训练方案
  • PHP 使用 Swoole - TaskWorker 实现异步操作 Mysql
  • PHP变量
  • PV统计优化设计
  • Python实现BT种子转化为磁力链接【实战】
  • 仿天猫超市收藏抛物线动画工具库
  • 技术:超级实用的电脑小技巧
  • 解决iview多表头动态更改列元素发生的错误
  • 理清楚Vue的结构
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 漂亮刷新控件-iOS
  • 小试R空间处理新库sf
  • 学习JavaScript数据结构与算法 — 树
  • CMake 入门1/5:基于阿里云 ECS搭建体验环境
  • UI设计初学者应该如何入门?
  • 第二十章:异步和文件I/O.(二十三)
  • ​Kaggle X光肺炎检测比赛第二名方案解析 | CVPR 2020 Workshop
  • ​MySQL主从复制一致性检测
  • ​经​纬​恒​润​二​面​​三​七​互​娱​一​面​​元​象​二​面​
  • #162 (Div. 2)
  • #define、const、typedef的差别
  • #include<初见C语言之指针(5)>
  • ${ }的特别功能
  • $NOIp2018$劝退记
  • (06)金属布线——为半导体注入生命的连接
  • (1/2)敏捷实践指南 Agile Practice Guide ([美] Project Management institute 著)
  • (33)STM32——485实验笔记
  • (Ruby)Ubuntu12.04安装Rails环境
  • (黑客游戏)HackTheGame1.21 过关攻略
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)
  • (七)Flink Watermark
  • (十七)Flask之大型项目目录结构示例【二扣蓝图】
  • (实测可用)(3)Git的使用——RT Thread Stdio添加的软件包,github与gitee冲突造成无法上传文件到gitee
  • (完整代码)R语言中利用SVM-RFE机器学习算法筛选关键因子