You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ForegroundService中使用BiometricPrompt的参数适配问题

解决App Lock应用中Service无法弹出BiometricPrompt的问题

核心思路

由于BiometricPrompt必须依附于Fragment或FragmentActivity,无法在Service中直接实例化,我们可以通过启动全屏透明的Activity来承载生物识别弹窗——用户只会看到验证界面,不会感知到额外的Activity存在。

步骤1:创建透明验证Activity

这个Activity负责初始化BiometricPrompt、处理验证回调,并根据结果控制用户是否能进入锁定应用。

1.1 配置透明主题

在res/values/styles.xml中添加透明主题:

<style name="Theme.App.Transparent" parent="Theme.MaterialComponents.DayNight.NoActionBar">
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowFullscreen">true</item>
    <item name="android:windowContentOverlay">@null</item>
</style>

1.2 实现验证Activity代码

class BiometricAuthActivity : AppCompatActivity() {
    private lateinit var biometricPrompt: BiometricPrompt
    private lateinit var promptInfo: BiometricPrompt.PromptInfo

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setupBiometricPrompt()
        biometricPrompt.authenticate(promptInfo)
    }

    private fun setupBiometricPrompt() {
        val executor = ContextCompat.getMainExecutor(this)
        biometricPrompt = BiometricPrompt(this, executor, object : BiometricPrompt.AuthenticationCallback() {
            override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
                super.onAuthenticationSucceeded(result)
                // 验证成功,允许进入应用,关闭当前Activity
                finish()
            }

            override fun onAuthenticationFailed() {
                super.onAuthenticationFailed()
                Toast.makeText(this@BiometricAuthActivity, "验证失败,请重试", Toast.LENGTH_SHORT).show()
                moveAppToBackground()
                finish()
            }

            override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
                super.onAuthenticationError(errorCode, errString)
                Toast.makeText(this@BiometricAuthActivity, errString, Toast.LENGTH_SHORT).show()
                moveAppToBackground()
                finish()
            }
        })

        promptInfo = BiometricPrompt.PromptInfo.Builder()
            .setTitle("应用锁定验证")
            .setSubtitle("请验证身份以访问应用")
            .setNegativeButtonText("取消")
            .build()
    }

    private fun moveAppToBackground() {
        // 将锁定应用移至后台
        val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        activityManager.moveTaskToBack(true)
    }

    override fun onBackPressed() {
        // 禁止返回,强制切后台
        moveAppToBackground()
        super.onBackPressed()
    }
}

1.3 在Manifest中声明Activity

<activity
    android:name=".BiometricAuthActivity"
    android:theme="@style/Theme.App.Transparent"
    android:excludeFromRecents="true"
    android:launchMode="singleInstance"
    android:taskAffinity="" />

步骤2:修改监测服务触发验证

当检测到锁定应用启动时,启动上述透明验证Activity。

示例:在AccessibilityService中触发

class AppLaunchAccessibilityService : AccessibilityService() {
    private lateinit var sharedPreferences: SharedPreferences

    override fun onCreate() {
        super.onCreate()
        sharedPreferences = getSharedPreferences("AppLockPrefs", MODE_PRIVATE)
    }

    override fun onAccessibilityEvent(event: AccessibilityEvent) {
        if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
            val packageName = event.packageName?.toString() ?: return
            val lockedPackages = sharedPreferences.getStringSet("locked_packages", emptySet()) ?: emptySet()
            if (lockedPackages.contains(packageName)) {
                val intent = Intent(this, BiometricAuthActivity::class.java)
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                startActivity(intent)
            }
        }
    }

    override fun onInterrupt() {}
}

示例:在ForegroundService中触发(基于UsageStatsManager)

class ForegroundAppLockService : Service() {
    private lateinit var sharedPreferences: SharedPreferences
    private var usageStatsManager: UsageStatsManager? = null
    private var currentPackage: String? = null

    private val checkAppRunnable = object : Runnable {
        override fun run() {
            val latestPackage = getLatestForegroundApp()
            if (latestPackage != currentPackage && latestPackage != null) {
                currentPackage = latestPackage
                val lockedPackages = sharedPreferences.getStringSet("locked_packages", emptySet()) ?: emptySet()
                if (lockedPackages.contains(latestPackage)) {
                    val intent = Intent(this@ForegroundAppLockService, BiometricAuthActivity::class.java)
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                    startActivity(intent)
                }
            }
            Handler(Looper.getMainLooper()).postDelayed(this, 1000)
        }
    }

    private fun getLatestForegroundApp(): String? {
        usageStatsManager ?: return null
        val endTime = System.currentTimeMillis()
        val startTime = endTime - 60000 // 查最近1分钟的使用记录
        val stats = usageStatsManager?.queryUsageStats(UsageStatsManager.INTERVAL_BEST, startTime, endTime)
        stats?.sortByDescending { it.lastTimeUsed }
        return stats?.firstOrNull()?.packageName
    }

    override fun onCreate() {
        super.onCreate()
        sharedPreferences = getSharedPreferences("AppLockPrefs", MODE_PRIVATE)
        usageStatsManager = getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
        Handler(Looper.getMainLooper()).post(checkAppRunnable)
        // 启动前台服务的逻辑(省略)
    }

    override fun onBind(intent: Intent): IBinder? = null
}

额外注意事项

  • 权限申请:确保已申请USE_BIOMETRIC权限,AccessibilityService需引导用户在系统设置中开启权限。
  • 后台启动限制:Android 10+对后台启动Activity有严格限制,使用AccessibilityService是更可靠的监测方案。
  • 结果同步:若需在Service中处理验证结果,可通过LocalBroadcastManager或LiveData传递状态。

内容的提问来源于stack exchange,提问作者Pranay Das

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 08:16:08