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

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

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

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

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

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

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

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

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

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

kotlin 复制代码
package com.example.lifestartupdemo

import 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 = 0
            mainHandler.postDelayed(300) {
                //常见于清空用户信息,请用户重新登录
                Log.e("tag", "应用由后台到了前台,进入了超时逻辑")
            }
        }
    }

    /**
     * 应用程序退出到后台时调用
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun onPause() {
        Log.e("tag", "应用 onPause() 已被切换至后台")
        timestamp = System.currentTimeMillis()
    }

}

关于 Handler 、Thread 扩展函数

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

kotlin 复制代码
package com.example.lifestartupdemo

import android.os.Build
import android.os.Handler
import android.os.Looper


fun 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文件)

kotlin 复制代码
@file:Suppress("UNUSED")

package com.example.lifestartupdemo

import android.os.Looper

/** This main looper cache avoids synchronization overhead when accessed repeatedly. */
@JvmField
val mainLooper: Looper = Looper.getMainLooper()!!

@JvmField
val mainThread: Thread = mainLooper.thread

val isMainThread: Boolean inline get() = mainThread === Thread.currentThread()

@PublishedApi
internal val currentThread: Any?
    inline get() = Thread.currentThread()

观察者 绑定 进程生命周期

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

kotlin 复制代码
package com.example.lifestartupdemo

import android.app.Application


interface ApplicationStartup {
    fun onCreate(application: Application)
}

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

kotlin 复制代码
package com.example.lifestartupdemo

import android.app.Application
import androidx.lifecycle.ProcessLifecycleOwner

internal class MineApplicationStartup : ApplicationStartup {

    override fun onCreate(application: Application) {
        ProcessLifecycleOwner.get().lifecycle.addObserver(LoginStateObserver())
    }
}

初始化配置

Application 初始化监听

kotlin 复制代码
package com.example.lifestartupdemo

import android.app.Application

class OurApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        //应用启动则初始化该配置
        val mineApplicationStartup = MineApplicationStartup()
        mineApplicationStartup.onCreate(this)
    }
}

绑定 Application

kotlin 复制代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android: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">
        <activity
            android: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>
相关推荐
小羊子说9 小时前
Android系统中 socketpair 的源码解读与应用分析小结
android·java
FL4m3Y4n10 小时前
MySQL索引原理与SQL优化
android·sql·mysql
我命由我1234511 小时前
Android Gradle - Gradle 自定义插件(Build Script 自定义插件、buildSrc 自定义插件、独立项目自定义插件)
android·java·java-ee·kotlin·android studio·android-studio·android runtime
冬奇Lab11 小时前
AudioFlinger混音机制深度解析
android·音视频开发·源码阅读
滑雪的企鹅.13 小时前
Kotlin云头条技术点剖析(项目复习02)——用户协议页面
android·开发语言·kotlin
JMchen12313 小时前
Android NDK开发从入门到实战:解锁应用性能的终极武器
android·开发语言·c++·python·c#·android studio·ndk开发
脚大江山稳14 小时前
单独为mysql数据库的某个库创建用户
android·数据库·mysql
吉哥机顶盒刷机14 小时前
XDBL安卓玩机刷机工具V2.8_解压缩版
android·智能手机·电脑
XiaoLeisj17 小时前
Android 广播机制实战:从系统广播监听、自定义登录通知到有序广播分发
android