Activity的三个实用技巧

快速定位当前Activity

在一个庞大且复杂的已有项目中,面对某个界面,你很难找到这个界面所对应的 Activity。而这个技巧,可以帮你解决这个问题。

假设当前项目有三个 Activity,分别是:FirstActivity、SecondActivity 和 ThirdActivity。前两个 Activity 的布局中都有一个按钮,分别可用于启动 SecondActivity 和 ThirdActivity。

首先创建一个 BaseActivity 类,然后继承自 AppCompatActivity。并重写 onCreate() 方法,在方法内部打印当前实例的类名:

注意:这里的 BaseActivity 类只是一个普通 Kotlin Class,不要创建 Activity。因为我们不需要它在 AndroidManifest.xml 清单文件中注册。

kotlin 复制代码
open class BaseActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d("BaseActivity", "Current Activity: ${javaClass.simpleName}")
    }
}

Kotlin 中的 javaClass 用于获取当前实例的Class对象,相当于在 Java 中调用 this.getClass() 方法,simpleName 表示获取不带包名的类名。而 Kotlin 中的 BaseActivity::class.java 表示获取 BaseActivity 类本身的 Class 对象,相当于在 Java 中调用 BaseActivity.class。

接下来让应用中所有的 Activity 类都继承自 BaseActivity。(BaseActivity 类的声明前已加上 open 关键字,表示这个类可被继承),当前应用中有三个Activity:FirstActivity, SecondActivity, ThirdActivity:

kotlin 复制代码
class FirstActivity : BaseActivity() {
    ...
}

class SecondActivity : BaseActivity() {
    ...
}

class ThirdActivity : BaseActivity() {
    ...
}

现在,每当一个新的 Activity 被启动(onCreate() 方法被执行)时,它的类名就会被打印出来。

重新运行程序,在 FirstActivity 界面中点击按钮进入 SecondActivity,然后在 SecondActivity 界面中点击按钮进入 ThirdActivity,Logcat 日志打印信息如下:

ini 复制代码
D/BaseActivity: Current Activity: FirstActivity
D/BaseActivity: Current Activity: SecondActivity
D/BaseActivity: Current Activity: ThirdActivity

随时随地退出程序

正常来说我们退出程序,应用中有多少个 Activity 实例,就需要按下多少次返回键。(Home键只是挂起程序,并没有退出)

假如现在有一个需求:添加一个"退出应用"的功能,该怎么实现?

很简单,我们可以创建一个集合把所有的 Activity 实例统一管理起来。创建一个单例类 ActivityCollector:

kotlin 复制代码
object ActivityCollector {
    private val activities = ArrayList<Activity>()

    // 新增 Activity 实例
    fun addActivity(activity: Activity) {
        activities.add(activity)
    }
    
    // 移除某个 Activity 实例
    fun removeActivity(activity: Activity) {
        activities.remove(activity)
    }
    
    // 一键销毁所有 Activity 实例
    fun finishAll() {
        for (activity in activities) {
            if (!activity.isFinishing) {
                activity.finish()
            }
        }
        activities.clear()
        Log.d("ActivityCollector", "All activities finished and cleared.")
    }
}

因为这个 Activity 集合需要全局唯一,所以在单例类中进行创建。

其中 finishAll() 方法,会将集合中所有的 Activity 销毁。注意:在销毁之前,需要通过 activity.isFinishing 判断 Activity 是否正在被销毁,因为 Activity 还可能通过按下返回键键等方式被销毁,正在销毁的话,就不通过它的 finish() 方法来销毁它。

有了这个集合,还需要进行管理:应用中,有新的 Activity 实例被创建时,需要将它添加到集合中;有 Activity 实例将要被销毁时,要从集合中移除。

为了自动完成这个操作,我们可以在所有 Activity 的共同父类,也就是前面说的 BaseActivity 的 onCreate() 和 onDestroy() 方法中,分别将当前正在创建的 Activity 添加到集合,从集合中移除将要被销毁的 Activity。

修改 BaseActivity 中的代码:

kotlin 复制代码
open class BaseActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d("BaseActivity", javaClass.simpleName)
        ActivityCollector.addActivity(this)
    }

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

然后,当你需要退出整个程序时,只需调用 ActivityCollector.finishAll() 方法就可以了。

比如我在 ThirdActivity 的界面中点击按钮,需要退出程序,我可以这样做:

kotlin 复制代码
binding.button3.setOnClickListener {
    // 销毁所有 Activity
    ActivityCollector.finishAll()
}

应用的进程会由 Android 系统自动回收。

启动Activity的最佳实践

我相信你已经知道如何启动 Activity 了,只需通过 Intent 构建出"意图",然后通过 startActivity() 或 ActivityResultLauncher.launch() 方法启动 Activity 即可。但如何让这个过程更规范、更不容易出错呢?

比如一个 Activity 启动时需要特定参数时,调用方如果直接构造 Intent 对象,并通过 putExtra() 方法传递数据,很容易出现参数遗漏、类型错误、键名拼写错误等问题。

这时,只需在目标 Activity 中提供一个静态方法,封装Intent的创建和参数传递逻辑即可。像这样:

kotlin 复制代码
class SecondActivity : BaseActivity() {

    private lateinit var binding: SecondLayoutBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 使用视图绑定
        binding = SecondLayoutBinding.inflate(layoutInflater)
        val linearLayout = binding.root
        setContentView(linearLayout)

        // 获取传递过来的数据
        val param1 = intent.getStringExtra(EXTRA_PARAM1)
        val param2 = intent.getStringExtra(EXTRA_PARAM2)
        Log.d("SecondActivity", "Received: param1=$param1, param2=$param2")
    }
    
    
    companion object {
        // 定义键常量,使用包名作为前缀避免冲突
        private const val EXTRA_PARAM1 = "com.example.PARAM1"
        private const val EXTRA_PARAM2 = "com.example.PARAM2"

        /**
         * 创建启动 SecondActivity 的 Intent 对象
         * 这种方式更灵活,调用者可以决定如何使用这个Intent(因为有多种启动 Intent 对象的方式)
         */
        fun newIntent(context: Context, data1: String, data2: String): Intent {
            return Intent(context, SecondActivity::class.java).apply {
                putExtra(EXTRA_PARAM1, data1)
                putExtra(EXTRA_PARAM2, data2)
            }
        }

        /**
         * 直接启动 SecondActivity 的方法
         */
        fun actionStart(context: Context, data1: String, data2: String) {
            val intent = newIntent(context, data1, data2)
            context.startActivity(intent)
        }
    }
}

companion object 是一个语法结构,在其中定义的方法和属性类似 Java 中的静态成员。

其中 newIntent() 方法会构建一个携带启动当前 Activity 所需参数的 Intent 对象并返回。在 actionStart() 方法中,我们调用了 newIntent() 方法来创建 Intent 对象,然后通过 startActivity() 方法启动了当前 Activity,也就是 SecondActivity。

从其他 Activity 中启动 SecondActivity:

kotlin 复制代码
// 方式一
SecondActivity.actionStart(this, "Hello", "World")

// 方式二
val intent: Intent = SecondActivity.newIntent(
    context = this,
    data1 = "This is ",
    data2 = "FirstActivity"
)
activityResultLauncher.launch(intent)

这样写,你可以通过方法签名很清楚地告知调用者启动 SecondActivity,需要传递哪些数据,以及分别是什么类型。另外,简化了启动 Activity 的代码。

相关推荐
右手吉他32 分钟前
高通平台IA APN问题分析之二
android·物联网
夜雪一千1 小时前
MySQL 自增 ID 的底层原理与坑点
android·mysql·adb
不浪费一个字节1 小时前
一个纯 Python 实现的 APK 解析与设备操控工具箱
android
itmigrate2 小时前
PHP 导出大 CSV 时内存溢出的一次排查
android·开发语言·php
hai_android2 小时前
LiveEventBusCore 跨 Activity 通讯源码解析
android·kotlin·android jetpack
维克兜率天2 小时前
【维克】统计套利建模:从因子到信号的完整流程
android·开发语言·python·算法·kotlin
hunterandroid2 小时前
Room 数据库迁移与多表关联查询实战
android
吐了啊取名字太难2 小时前
Android 相机 USB 连接别写在 Activity 里:锁屏、切页和后台传输怎么设计?
android·数码相机
hunterandroid3 小时前
Android ViewModel + LiveData 工程实践:配置变更、一次性事件与状态恢复
android