专栏地址
文章目录
- Android开发笔记——快速入门(优雅的开发Activity)
- 往期文章
- 软件环境:
- 配套代码获取地址:
- 使用Android手机在AS直接进行开发
- Activity的启动模式
- standard
- sigleTop
- sigleTask
- singleInstance
- 充分管理和了解你的ACT
- 如何知晓当前ACT的名字?
- 随时退出程序
- 启动ACT的最佳写法
软件环境:
- Jetbrains Toolbox
- Android Sudio 2021.1.1 Bumblebee
- JDK 17.0.2
请先参考前一篇文章复习一下Kotlin的一些语法。
大部分内容参考了郭霖先生的《第一行代码》,在书的基础上针对目前的实际情况进行实践记录。
配套代码获取地址:Gitee直接下载,全部开源
使用Android手机在AS直接进行开发参考过的文章链接:
如何在Android12开启开发者模式
荣耀 打开USB调试 Android开发
首先安装google的驱动:打开SDK管理器:
找到:Google USB Driver下载:
然后开启设备管理器:
为设备安装驱动:
在本地找到刚下载的驱动,路径如下,需要参考你的具体路径:
等待安装完成
打开开发者模式:
连续点击内部版本号:直到提示打开开发者模式:
打开如下选项就可以看到开发者选项:
打开开发者选项:
往下拉找到USB配置选项:
如果不是RNDIS选择改成RNDIS,不知道为什么即使开启仅充电模式下开启ADB调试选项,也不无法在仅充电模式下调试,所以手动开启RNDIS稳妥一点。
如果选择此模式还是没有反应,就在其它模式和本模式切换一下。
可以看到AS中出现了设备:
Activity的启动模式Android的Activity的启动模式一共有四种。
standard、sigleTop、sigleTask、singleInstace。
standardstandard模式是Activity的默认启动模式,在不显式指定的情况下,所有Activity都会使用这种模式。
举个例子:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//此处注意
Log.d("FirstActivity","Task id is $taskId")
val binding = ActivityFirstBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.button1.setOnClickListener()
{
val intent = Intent(this,FirstActivity::class.java)
startActivity(intent)
}
}
我们给一个Act设定一个操作当点击按钮的时候再次创建一个一摸一样的Act出来。
实际上两个ACT的实例句柄并不相同,也就是说ACT中有两个相同的ACT实例。
具体实现模式如下图所示:
sigleTop对于标准启动模式你会觉得奇怪,明明我的ACT已经在栈顶了为什么还要启动一个重复的呢?
在sigleTop模式下就能解决你这个疑问,在本模式下,当ACT的启动模式指定为sigleTop,再启动之前就会查询栈顶元素,如果发现返回栈顶已经是该ACT,则认为可以直接使用它,不会在创建新的实例。
我们这里使用一个ACT来测试,在第一个ACT中在来创建一个相同ACT,看是否会创建。
果然点击多次并没有新的ACT产生。
模式图如下:
sigleTask在SigleTop模式下你会有一个疑问,如果有两个ACT相互调用怎么办呢?如果我们使用sigleTop模式,在第二个ACT2中调用第一个ACT(此时栈顶是ACT2)仍会导致创建一个和第一个ACT相同的ACT导致资源的浪费,sigleTask就是能完美解决重复创建栈顶ACT的问题。
每次启动ACT的时候就会检查ACT是否存在该ACT的实例,如果发现已经存在则直接使用这个ACT,并将本ACT之上的所有ACT统统出栈,如果发现没有则创建一个新的ACT实例在栈顶。
我们这里创建两种测试,首先创建两个ACT,一个ACT中调用第二个ACT,在第二个ACT中调用第一个ACT,此时应该会把第一个ACT重新唤醒。
可以看到ACT1被重新唤醒了。
模式图如下:
singleInstance不同于以上三种模式,singleInstance模式会启用一个新的返回栈来管理新的ACT,那么这样做有什么意义呢?
假设我们的程序中有一个ACT是允许其他程序调用的,如果想实现其他程序和我们的程序可以共享找个ACT的实例,应该如何实现呢?
前三种方式肯定是不正确的,因为他们都是在自己的返回栈的基础上进行操作,每一个应用程序都有自己的返回栈,同一个ACT在不同的返回栈中入栈的时候都必然创建新的实例。而在本模式下,不管是哪个应用程序来访问这个ACT,都会共用一个返回栈,就解决了ACT实例的问题。
我们这里使用三个ACT来实现操作:
将第一个第三个设置为stander启动模式,再将第二个设置成singleInstance启动模式。
我们让第一个调用第二个ACT,第二个调用第三个ACT.
可以通过输出的信息看到:
我们先启动了第一个ACT他的返回栈ID是106,在启动了第二个ACT他的返回栈ID是107,最后启动了第三个ACT他的返回栈是106和第一个返回栈id相同,因为他们三个都是standard模式。
我们在第三个ACT按下返回键,最后我们看到在提示信息里面有ACT1的restart方法的调用,我们在第三个ACT返回却直接返回的是ACT1,这里很好理解因为ACT1和ACT3在同一个返回栈里面。
singleInstance的返回模式图如下:
充分管理和了解你的ACT在阅读之一部分前请先阅读:关于JAVA的反射与Class。
如何知晓当前ACT的名字?创建一个BaseActivity类,注意是Kotlin的类,并不是创建一个ACT,让他继承AppCompatActivity类,并重写onCreate方法:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("BaseActivity",javaClass.simpleName)
}
使用Log来输出当前的类名,值得注意的是,这里使用的是类似于java的反射机制,你可以注意到当前的类就是BaseActivity为什么还要通过javaClass.simpleName来输出类名呢?
实际上并不是这样的,我们先看一个结果:
运行一个FirstActivity以后:
在Koltin中javaclass表示获取当前实例的Class对象,相当于在Java中调用getClass方法。
具体什么是Class类和对象请参考上文链接,简单的来说就是JVM中用来管理每个类的一个专门的管理类,通过这个类可以获取具体的信息包括对应类的存储位置,类的方法,类名等。
我们在重写父类的onCreate方法同时调用了父类的super.onCreate(savedInstanceState)方法,这样就相当于在本类中使用了Log.d("BaseActivity",javaClass.simpleName)方法,通过class来获取当前的实例名。
只要我们让BaseActivity成为所有ACT的父类就能输出类的信息。
当然你也可以这样写:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("BaseActivity",this.toString())
}
这样输出内容在创建相同实例的时候更准确:
在这里插入图片描述
随时退出程序如果你目前栈内已经有了三个ACT,而现在你想检测退出程序对应ACT阶段回调函数的运行效果怎么样,总不能连续按下三次返回按键吧,那如果有很多的ACT,就连续按下多次?当然不现实了。
解决思路很简单,这里创建一个专门的类针对所有类进行管理。
先看个例子,创建了一个ActitvityCollector类:
object ActitvityCollector
{
private val activities = ArrayList()
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()
}
}
这里ActitvityCollector是一个单例类,因为全局实际上只需要一个ActitvityCollector来管理所有ACT就可以了。
我们创建了一个ArrayList来存放当前所有Activity,又声明了三个方法:
- addActivity 用于向集合添加现有的ACT。
- removeActivity 用于移除某个ACT
- finishALL 用于将所有ACT销毁,销毁的时候需要注意当前ACT是否正在被销毁。
接下来我们使用这个三个方法来管理自己的ACT
首先我们要配合BaseActivity来管理,首先是addActivity我们不需要自己手动每次调用的时候都添加这到集合中,我们配合BaseActivity的onCreate就可以在创建的时候自动调用了,修改后的onCreate如下:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("BaseActivity",javaClass.simpleName)
ActitvityCollector.addActivity(this)
}
接下来修改BaseActivity的onDestroy方法,让他在退出或者销毁的的时候直接把自己从队列里面移除。
override fun onDestroy() {
super.onDestroy()
ActitvityCollector.removeActivity(this)
}
实现一键退出也很简单,我们在任何需要退出的地方调用:
ActitvityCollector.finishALL()启动ACT的最佳写法
在一个ACT中启动到另一个ACT的方法很简单,首先通过intent来构造意图,指明需要启动的ACT:
val intent = Intent(this,SecondActivity::class.java)
其中this为上下文对象,SecondActivity::class.java也是获取对应类的Class对象,为启动传输class参数。
然后通过startActivity(intent)或者startActivityForResult()将ACT启动起来,如果有数据可以使用Intent来传输,这里就不再赘述。
但是如果在另一个ACT需要启动这个ACT我们该如何传输参数呢?这时候就得跑去阅读代码了或者去问写这个ACT的人,但实际上我们应该把每一个ACT的启动都规整化把它封装一下,这样和别人一起协作的时候你写完的ACT很容易就能让别人利用起来。
在这里我们利用了companion object来实现因为这样方便在调用的时候不需要自己创建一个对象再去调用它的方法,我们本着高内聚低耦合的想法,就把启动方法声明在伴生对象里面。
companion object {
fun actionStart(context: Context, data1: String, data2: String) {
val intent = Intent(context, SecondActivity::class.java).apply {
putExtra("param1", data1)
putExtra("param2", data2)
}
context.startActivity(intent)
}
}
我们通过这个方法把上下文传递进来然后传入要传递的参数最后构造intent实现数据传递和ACT启动。



