Activity的三个实用技巧

快速定位当前Activity

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

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

首先创建一个 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 的共同父类,也就是前面说的 BaseActivityonCreate()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 的代码。

相关推荐
sweetying1 小时前
30了,人生按部就班
android·程序员
用户2018792831671 小时前
Binder驱动缓冲区的工作机制答疑
android
真夜1 小时前
关于rngh手势与Slider组件手势与事件冲突解决问题记录
android·javascript·app
用户2018792831671 小时前
浅析Binder通信的三种调用方式
android
用户092 小时前
深入了解 Android 16KB内存页面
android·kotlin
火车叼位3 小时前
Android Studio与命令行Gradle表现不一致问题分析
android
前行的小黑炭5 小时前
【Android】 Context使用不当,存在内存泄漏,语言不生效等等
android·kotlin·app
前行的小黑炭6 小时前
【Android】CoordinatorLayout详解;实现一个交互动画的效果(上滑隐藏,下滑出现);附例子
android·kotlin·app
用户20187928316718 小时前
Android黑夜白天模式切换原理分析
android
芦半山18 小时前
「幽灵调用」背后的真相:一个隐藏多年的Android原生Bug
android