Android开发中BaseActivityClass的广播接收器优化

使用 GPT-4o 回答,作者 GPT4oMini.app

问题:

Android开发,用Kotlin语言。所有Activity都继承以下BaseActivityClass: package com.example.activitytest

import android.os.Bundle import android.util.Log import androidx.appcompat.app.AppCompatActivity import android.app.AlertDialog import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter

open class BaseActivityClass : AppCompatActivity() {

lateinit var receiver: ForceLogoutReceiver

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    //打印当前实例类名
    Log.d("BaseActivity", javaClass.simpleName)
    ActivityCollector.addActivity(this)
}


override fun onResume() {
    super.onResume()
    val intentFilter = IntentFilter()
    intentFilter.addAction("com.example.activitytest.FORCE_LOGOUT")
    receiver = ForceLogoutReceiver()
    registerReceiver(receiver, intentFilter)
}

override fun onPause() {
    super.onPause()
    unregisterReceiver(receiver)
}

override fun onDestroy() {
    super.onDestroy()
    ActivityCollector.removeActivity(this)
}

inner class ForceLogoutReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        AlertDialog.Builder(context).apply {
            setTitle("Warning")
            setMessage("You are forced to be logout. Please try to login again.")
            setCancelable(false)  //不可取消
            setPositiveButton("确定") { _, _ ->
                ActivityCollector.finishAll() // 销毁所有Activity
                val i = Intent(context, LoginActivity::class.java)
                context.startActivity(i) // 重新启动LoginActivity
            }
            show()
        }
    }

}

}

编译时有告警如下: receiver is missing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED flag for unprotected broadcasts registered for com.example.activitytest.FORCE_LOGOUT; Property 'receiver' could be private

请分析原因,并解决,确保没有警告信息。

答案:

在Android 12(API 31)及更高版本中,广播接收器需要明确指定其导出状态,以确保应用的安全性。你需要在注册广播接收器时指定RECEIVER_EXPORTEDRECEIVER_NOT_EXPORTED标志,以表明广播接收器是否可以接收来自其他应用的广播。

此外,Kotlin编译器警告你receiver属性可以是私有的,因为它只在BaseActivityClass内部使用。

以下是修改后的代码,解决了上述警告:

package com.example.activitytest

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import android.app.AlertDialog
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter

open class BaseActivityClass : AppCompatActivity() {

    private lateinit var receiver: ForceLogoutReceiver

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //打印当前实例类名
        Log.d("BaseActivity", javaClass.simpleName)
        ActivityCollector.addActivity(this)
    }

    override fun onResume() {
        super.onResume()
        val intentFilter = IntentFilter()
        intentFilter.addAction("com.example.activitytest.FORCE_LOGOUT")
        receiver = ForceLogoutReceiver()
        registerReceiver(receiver, intentFilter, Context.RECEIVER_NOT_EXPORTED)
    }

    override fun onPause() {
        super.onPause()
        unregisterReceiver(receiver)
    }

    override fun onDestroy() {
        super.onDestroy()
        ActivityCollector.removeActivity(this)
    }

    inner class ForceLogoutReceiver : BroadcastReceiver() {

        override fun onReceive(context: Context, intent: Intent) {
            AlertDialog.Builder(context).apply {
                setTitle("Warning")
                setMessage("You are forced to be logout. Please try to login again.")
                setCancelable(false)  //不可取消
                setPositiveButton("确定") { _, _ ->
                    ActivityCollector.finishAll() // 销毁所有Activity
                    val i = Intent(context, LoginActivity::class.java)
                    context.startActivity(i) // 重新启动LoginActivity
                }
                show()
            }
        }

    }
}

主要修改点:

  1. receiver属性设为私有

    private lateinit var receiver: ForceLogoutReceiver
    
  2. 在注册广播接收器时指定RECEIVER_NOT_EXPORTED标志

    registerReceiver(receiver, intentFilter, Context.RECEIVER_NOT_EXPORTED)
    

这样修改后,编译时的警告信息应该会消失。RECEIVER_NOT_EXPORTED标志确保了广播接收器不会接收来自其他应用的广播,从而提高了应用的安全性。