Activity的相关实践
1 当前是哪一个Activity
首先需要新建一个BaseActivity
类。New —> Kotlin File/Class
,在弹出的窗口中输入BaseActivity
,创建类型选择Class
。注意,这里的BaseActivity
和普通Activity
的创建方式并不一样,因为不需要让BaseActivity
在AndroidManifest.xml
中注册,所以选择创建一个普通的Kotlin
类就可以 了。然后让BaseActivity
继承自AppCompatActivity
,并重写onCreate()
方法,如下所示:
open class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.e("CAH", javaClass.simpleName)
}
}
在onCreate()
方法中加了一行日志,用于打印当前实例的类名。**Kotlin
中的javaClass
表示获取当前实例的Class
对象,相当于在Java
中调用getClass()
方法;而Kotlin
中的BaseActivity::class.java
表示获取BaseActivity
类的Class
对象,相当于在Java
中调用BaseActivity.class
。**在上述代码中,先是获取了当前实例的Class
对象,然后再调用simpleName
获取当前实例的类名。
接下来需要让BaseActivity
成为项目中所有Activity
的父类,为了使BaseActivity
可以被继承,已经提前在类名的前面加上了open
关键字。 然后修改FirstActivity
、SecondActivity
和ThirdActivity
的继承结构,让它们不再继承自AppCompatActivity
,而是继承自BaseActivity
。而由于BaseActivity
又是继承自AppCompatActivity
的,所以项目中所有Activity
的现有功能并不受影响,它们仍然继承了Activity
中的所有特性。
现在重新运行程序,然后通过点击按钮分别进入FirstActivity
、SecondActivity
和ThirdActivity
的界面,这时观察Logcat
中的打印信息,如图所示:
// CAH: BaseActivity: MainActivity
// CAH: BaseActivity: NormalActivity
现在每当进入一个Activity
的界面,该Activity
的类名就会被打印出来,这样就可以时刻知晓当前界面对应的是哪一个Activity
了。
2 随时退出程序
如果目前界面还停留在ThirdActivity
,会发现当前想退出程序是非常不方便的,需要连按3
次Back
键才行。按Home
键只是把程序挂起,并没有退出程序。如果程序需要注销或者退出的功能该怎么办呢?
这时需要用一个专门的集合对所有的Activity
进行管理就可以了。新建一个单例类ActivityCollector
作为Activity
的集合,代码如下所示:
object ActivityController {
private val activities = ArrayList<Activity>()
fun addActivity(activity: Activity) {
activities.add(activity)
}
fun removeActivity(activity: Activity) {
activities.remove(activity)
}
fun finishAll() {
for (activity in activities) {
if (!activity.isFinishing) {
activity.finish()
}
}
activities.clear()
}
}
这里使用了单例类,是因为全局只需要一个Activity
集合。在集合中,通过一个ArrayList
来暂存Activity
,然后提供了一个addActivity()
方法,用于向ArrayList
中添加Activity
;提供了一个removeActivity()
方法,用于从ArrayList
中移除Activity
;最后提供了一个finishAll()
方法,用于将ArrayList
中存储的Activity
全部销毁。注意在销毁Activity
之前,需要先调用activity.isFinishing
来判断Activity
是否正在销毁中,因为Activity
还可能通过按下Back
键等方式被销毁,如果该Activity
没有正在销毁中,再去调用它的finish()
方法来销毁它。
接下来修改BaseActivity
中的代码,如下所示:
open class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.e("CAH", "BaseActivity: ${javaClass.simpleName}")
ActivityController.addActivity(this)
}
override fun onDestroy() {
super.onDestroy()
ActivityController.removeActivity(this)
}
}
在BaseActivity.onCreate()
方法中调用了ActivityCollector.addActivity()
方 法,表明将当前正在创建的Activity
添加到集合里。然后在BaseActivity
中重写onDestroy()
方法,并调用了ActivityCollector.removeActivity()
方法,表明从集合里移除一个马上要销毁的Activity
。
从此以后,不管你想在什么地方退出程序,只需要调用ActivityCollector.finishAll()
方法就可以了。例如在ThirdActivity
界面想通过点击按钮直接退出程序,只需将代码改成如下形式:
class ThirdActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_third)
button.setOnClickListener {
ActivityController.finishAll()
}
}
}
当然还可以在销毁所有Activity
的代码后面再加上杀掉当前进程的代码,以保证程序完全退出,杀掉进程的代码如下所示:
android.os.Process.killProcess(android.os.Process.myPid())
killProcess()
方法用于杀掉一个进程,它接收一个进程id
参数,可以通过myPid()
方法来获得当前程序的进程id
。需要注意的是,killProcess()
方法只能用于杀掉当前程序的进程,不能用于杀掉其他程序。
3 启动Activity
的最佳写法
启动Activity
的方法是首先通过Intent
构建出当前的“意图”,然后调用startActivity()
或startActivityForResult()
方法将Activity
启动起来,如果有数据需要在Activity
之间传递,也可以借助Intent
来完成。
假设SecondActivity
中需要用到两个非常重要的字符串参数,在启动SecondActivity
的时候必须传递过来,那么很容易会写出如下代码:
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("param1", "data1")
intent.putExtra("param2", "data2")
startActivity(intent)
虽然这样写是完全正确的,但是在真正的项目开发中经常会出现对接的问题。比如SecondActivity
并不是由你开发的,但现在你负责开发的部分需要启动SecondActivity
,而你却不清楚启动SecondActivity
需要传递哪些数据。这时无非就有两个办法:一个是你自己去阅读SecondActivity
中的代码,另一个是询问负责编写SecondActivity
的同事。这样会比较麻烦,其实只需要换一种写法,就可以轻松解决上面的窘境。
修改SecondActivity
中的代码,如下所示:
class SecondActivity : BaseActivity() {
companion object {
fun actionStart(context: Context, data1: String, data2: String) {
val intent = Intent(context, NormalActivity::class.java)
intent.putExtra("param1", data1)
intent.putExtra("param2", data2)
context.startActivity(intent)
}
}
....
}
在这里使用了一个新的语法结构companion object
,并在companion object
中定义 了一个actionStart()
方法。之所以要这样写,是因为Kotlin
规定,所有定义在companion object
中的方法都可以使用类似于Java
静态方法的形式调用。
在actionStart()
方法,在这个方法中完成了Intent
的构建,另外所有SecondActivity
中需要的数据都是通过actionStart()
方法的参数传递过来的,然后把它们存储到Intent
中,最后调用startActivity()
方法启动SecondActivity
。
这样写的好处是SecondActivity
所需要的数据在方法参数中全部体现出来了,这样即使不用阅读SecondActivity
中的代码,不去询问负责编写SecondActivity
的同事,也可以非常清晰地知道启动SecondActivity
需要传递哪些数据。另外,这样写还简化了启动Activity
的代码,现在只需要一行代码就可以启动SecondActivity
, 如下所示:
button.setOnClickListener {
NormalActivity.actionStart(this, "data1", "data2")
}