3.1 Activity简单介绍
基本介绍
一开始创建完成一个项目之后,在Project
文件目录下打开:
这里重点关注三个红框里的文件:
Activity文件:
Activity
是一种可以包含用户界面的组件,主要用于和用户进行交互。一个应用程序中可以包含零个或多个Activity
,但不包含任何Activity的应用程序很少见。
简单来讲
Activity
对应的就是类似手机页面的后端部分。
主题语法部分重点关注红框中的部分,其余部分在项目创建的时候会默认创建好。
我们来看一下红框中的语法是什么意思:
setContentView(R.layout.activity_main)
这里的setContentView
意思是:设置内容视图,也就是设置这个Activity
的前端页面部分(这个在下面有讲到,就是layout下面的xml文件)。
参数部分暂时不用深究,前面R.layout
是固定的,后面的activity_main
就是这个Activity的内容视图文件的名称。
xml文件
这里的xml文件
是特指在layout
文件夹目录下的xml文件,也就是布局的意思,简单的来讲其实就是一个手机页面的前端部分,前端的样式就是在layout下面的xml文件中书写。
AndroidMainifest.xml
这个文件相当于配置文件
重点看两个红框中的内容:
最外层:<application ...> ... </appliction>
表示的就是应用。
前面一个...
是整体应用的配置,这里暂时不需要了解。
重点看<application>
内的内容,也就是<activity>
:
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
一句一句来看:
android:name=".MainActivity"
这个没什么好说的,就是表示这个Activity
的名字,对应的就是刚开始创建的MainActivity
。
android:exported="true"
总体来说它的主要作用是:是否支持其它应用调用当前组件。
在Activity中该属性用来标示:当前Activity是否可以被另一个Application的组件启动:
- true允许被启动
- false不允许被启动。
如果被设置为了false,那么这个Activity将只会被当前Application或者拥有同样user ID的Application的组件调用。
简单来讲,如果是
ture
那么其他手机应用也可以启动这个Activity
,例如我们在其他软件里面打开支付宝页面。 如果是false
,那么只有本应用才可以启动。
参照文档:
android:exported 属性详解_ ZhangGeng’s Blog -CSDN博客_android:exported
这个属性一般和下面这个联动使用:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
这一块的意思就是**配置主Activity**,一般就是打开APP后进入的**第一个界面**。<br />详细属性暂时不需要知道,只需要知道这个在哪个`<activity>`下面配置,哪个就是主Activity。<br />配置了主Activity的Activity,`android:exported`一般都设置为`ture`,其余一般为`false`。
3.2 Thoast和Menu
Thoast
定义
Toast是Android系统提供的一种非常好的提醒方式,在程序中可以使用它将一些短小的信息通知给用户,这些信息会在一段时间后自动消失,并且不会占用任何屏幕空间,尝试一下如何在Activity中使用Toast。
实例
首先在对应的布局文件xml中,添加一个button
组件。
首先在右上角选择Design
,把button组件
拖拽到屏幕当中。
这里先不需要知道这个组件的详细意义,只需要知道这是一个按钮即可
完成之后切换到Code
代码区,会发现多了一串代码,注意其中的id
。
接着在MainActivity
中的Oncreate()
方法内添加如下代码:
val button: Button = findViewById(R.id.button)
button.setOnClickListener {
Toast.makeText(this, "You clicked Button 1", Toast.LENGTH_SHORT).show()
}
这里讲解一下语法:<br />首先是申明变量部分,申明一个类型为`Button`类型的变量`button`,`findViewById`就是通过id寻找组件,括号里面的参数就是`R.id.`id的名称``,这里根据刚刚创建的button组件id为button,所以就是`R.id.button`,所以第一句的意思就是**找到id为button 的组件,并把它赋值给变量button**。<br />接着就是给这个变量设置了**监听事件**,`setOnClickListener`,也就是当这个按钮被点击的时候,会触发**大括号中的行为**。
Toast.makeText(this, "You clicked Button 1", Toast.LENGTH_SHORT).show()
Toast
对象调用makeText
方法,其中:
this
表示Thoast要求的上下文,一般都是this"You clicked Button 1"
就是需要弹出的文字内容Toast.LENGTH_SHORT
表示显示时间,这里是短时间也就是2000msshow()
就是展示信息
Toast.LENGTHLONG和Toast.LENGTH_SHORT分别对应多长时间天下布武-CSDN博客_toast.length_short
运行项目之后点击button之后会有如下界面:
Menu
创建文件
首先在res
资源文件目录下创建menu
文件夹,在menu
文件夹下面创建main.xml
文件
添加选项
菜单需要选项,所以在main.xml
文件中添加如下代码:
<item
android:id="@+id/add_item"
android:title="Add"/>
<item
android:id="@+id/remove_item"
android:title="Remove"/>
重写方法
设定了菜单之后,需要添加到Activity中去,因为Activity默认是没有菜单的,但是有对应的方法,所以需要在Activity中重写有关菜单的方法:
package com.example.myapplication
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.Menu
import android.widget.Button
import android.widget.Toast
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button: Button = findViewById(R.id.button)
button.setOnClickListener {
Toast.makeText(this, "You clicked Button 1", Toast.LENGTH_SHORT).show()
}
}
// 重写菜单方法
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
//return super.onCreateOptionsMenu(menu)
//上面的代码重写为下面的
menuInflater.inflate(R.menu.main,menu)
return true
}
}
重写方法的时候一般只要打出`onCreateOptionsMenu`大部分代码就会自动补全。<br />接下来讲解一下重写的代码的意思:
- 首先你会发现这里的
menuInflater
好像没有定义就直接使用,因为menuInflater
是父类的属性,这里是运用了kotlin
的语法糖,相当于getMenuInflater()方法
能够得到一个MenuInflater对象(理解为菜单填充对象即可) - 再调用它的
inflate()方法
,就可以给当前Activity创建菜单了。 inflate()方法
接收两个参数:- 第一个参数用于指定通过哪一个资源文件来创建菜单,这里传入
R.menu.main
,R
表示资源文件夹下,menu
就是刚刚创建的menu文件夹
,main
就是刚刚创建的main.xml
; - 第二个参数用于指定的菜单项将添加到哪一个Menu对象当中,这里直接使用onCreateOptionsMenu()方法中传入的
menu参数
。(这里一般都只需要写menu就可以了) - 最后给这个方法返回true,表示允许创建的菜单显示出来,如果返回了false,创建的菜单将无法显示。
设置监听事件
在给Activity中添加完菜单之后,需要给菜单中的选项添加事件,这里还是重写方法: ```kotlin package com.example.myapplication
- 第一个参数用于指定通过哪一个资源文件来创建菜单,这里传入
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.Menu import android.view.MenuItem import android.widget.Button import android.widget.Toast
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main)
val button: Button = findViewById(R.id.button)
button.setOnClickListener {
Toast.makeText(this, "You clicked Button 1", Toast.LENGTH_SHORT).show()
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.main,menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// 传入选择的选项的id 也就是item.itemId
when(item.itemId){
// 如果是添加选项,就弹出文案是You clicked Add的提示
R.id.add_item -> Toast.makeText(this, "You clicked Add", Toast.LENGTH_SHORT).show()
// 如果是去除选项,就弹出文案是You clicked Remove的提示
R.id.remove_item -> Toast.makeText(this, "You clicked Remove", Toast.LENGTH_SHORT).show()
}
// 作用同上
return true
}
}
看一下效果<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22022942/1647258903559-f3b4e7da-0de7-431a-b302-a0d3f6cf992c.png#averageHue=%23252524&clientId=ub3082c14-f2bf-4&from=paste&height=432&id=u879e0d5f&originHeight=864&originWidth=407&originalType=binary&ratio=1&rotation=0&showTitle=false&size=84969&status=done&style=none&taskId=u05b05440-6492-4c3f-ab90-f3b5704bac1&title=&width=203.5)![image.png](https://cdn.nlark.com/yuque/0/2022/png/22022942/1647258879444-9cf8932e-2d61-4d96-8cdd-2de6bbb0350a.png#averageHue=%237f5044&clientId=ub3082c14-f2bf-4&from=paste&height=432&id=u90901c62&originHeight=864&originWidth=407&originalType=binary&ratio=1&rotation=0&showTitle=false&size=87171&status=done&style=none&taskId=u2cc54782-1da4-45be-bb23-dd8ecf387c4&title=&width=203.5)![image.png](https://cdn.nlark.com/yuque/0/2022/png/22022942/1647258858203-ea596273-062b-4f51-b24a-043d23395006.png#averageHue=%23524831&clientId=ub3082c14-f2bf-4&from=paste&height=432&id=u268c3ae8&originHeight=864&originWidth=407&originalType=binary&ratio=1&rotation=0&showTitle=false&size=86651&status=done&style=none&taskId=udb1ad546-ef13-4ded-a533-3ad1a731a7e&title=&width=203.5)
<a name="EMUFj"></a>
### 销毁一个Activity
一般情况下按**返回**就可以退出当前界面,也就会销毁。<br />当然也可以通过代码进行销毁,只需在事件中添加`finish()`即可
```kotlin
button1.setOnClickListener {
finish()
}
比较简单就不演示了。
3.3 Intent
Intent是Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。Intent一般可用于启动Activity
、启动Service
以及发送广播等场景,由于Service、广播等概念暂时还未涉及,目光无疑就锁定在了启动Activity
上面。
Intent大致可以分为两种:显式Intent和隐式Intent。
显式Intent
我们只需要再添加一个button:
接下来添加一个新的空Activity:
跟着下面步骤即可,后面不要加改动:
接下来在MainActivity
文件的OnCreate
方法中添加:
val button2:Button = findViewById(R.id.button2)
button2.setOnClickListener{
val intent:Intent = Intent(this,MainActivity2::class.java)
startActivity(intent)
}
主要讲解一下`Intent`方法
- 第一个参数
Context
要求提供一个启动Activity的上下文;(一般都是this) - 第二个参数
Class
用于指定想要启动的目标Activity,通过这个构造函数就可以构建出Intent的“意图”
完整代码为:
package com.example.myapplication
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.widget.Button
import android.widget.Toast
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 注意id的选择
val button2:Button = findViewById(R.id.button2)
button2.setOnClickListener{
val intent:Intent = Intent(this,MainActivity2::class.java)
startActivity(intent)
}
}
}
为了便于观察,我们给新建的Activity设置一个label:<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22022942/1647314929798-0b44ce5b-4580-4805-9ae9-b9331b9d1394.png#averageHue=%235d7e56&clientId=u56e3b5fa-af3d-4&from=paste&height=511&id=u8af9f512&originHeight=1022&originWidth=1904&originalType=binary&ratio=1&rotation=0&showTitle=false&size=251262&status=done&style=none&taskId=u648faed5-9ce9-401d-b48b-80bcdc5f670&title=&width=952)<br />运行看一下效果:<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22022942/1647315143706-ea5d2da3-0f26-4b92-955d-795a12e86c0c.png#averageHue=%23574937&clientId=u56e3b5fa-af3d-4&from=paste&height=432&id=u8da13b15&originHeight=864&originWidth=407&originalType=binary&ratio=1&rotation=0&showTitle=false&size=85690&status=done&style=none&taskId=u931c3c36-b4cc-4005-bea5-d5733608f2c&title=&width=203.5)![image.png](https://cdn.nlark.com/yuque/0/2022/png/22022942/1647315164561-8c113b95-25ab-4418-a566-e1268f0de31e.png#averageHue=%23393732&clientId=u56e3b5fa-af3d-4&from=paste&height=432&id=ue16dc7d0&originHeight=864&originWidth=407&originalType=binary&ratio=1&rotation=0&showTitle=false&size=83296&status=done&style=none&taskId=u22c18993-4797-42be-8c50-eff9db4aeb7&title=&width=203.5)
隐式Activity
相比于显式Intent,隐式Intent则含蓄了许多,它并不明确指出想要启动哪一个Activity,而是指定了一系列更为抽象的action和category等信息,然后交由系统去分析这个Intent,找出合适Activity去启动。
隐式调用使用得比较少,使用情况为不知道应用的包名,但是又想使用相应的功能,比如打开浏览器、打开邮件、打开应用市场、打开相机应用、打开分享应用等。
一般情况下,想要打开应用内的组件使用上述显式Activity即可,如果想要打开其他应用的Activity
,就使用隐式Activity。
action和category
- Action属性:字符串类型,在该属性中可通过一个字符串来表示启动窗口时符合的“动作”。
- category属性:字符串类型,在该属性中可通过一个字符串来表示启动窗口时符合的类别。
实例
把上述代码稍作修改 ```kotlin package com.example.myapplication
import android.content.Intent import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.Menu import android.view.MenuItem import android.widget.Button import android.widget.Toast
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main)
// 注意id的选择
val button2:Button = findViewById(R.id.button2)
button2.setOnClickListener{
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://www.baidu.com")
startActivity(intent)
}
}
}
运行结果为:<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22022942/1647315143706-ea5d2da3-0f26-4b92-955d-795a12e86c0c.png#averageHue=%23574937&clientId=u56e3b5fa-af3d-4&from=paste&height=432&id=QOFpx&originHeight=864&originWidth=407&originalType=binary&ratio=1&rotation=0&showTitle=false&size=85690&status=done&style=none&taskId=u931c3c36-b4cc-4005-bea5-d5733608f2c&title=&width=203.5)![image.png](https://cdn.nlark.com/yuque/0/2022/png/22022942/1647479261437-ded5b27b-7a4b-4730-b9f8-0fa5ee00dd7b.png#averageHue=%23f69962&clientId=ua3e20f2a-140c-4&from=paste&height=432&id=ua629a89f&originHeight=864&originWidth=407&originalType=binary&ratio=1&rotation=0&showTitle=false&size=200995&status=done&style=none&taskId=u3d399432-ecad-40d7-b1a5-cf4d1385d91&title=&width=203.5)<br />详细语法参见:<br />[android Intent的Action、Category属性_年轻的zhangchang的博客-CSDN博客_android.intent.category.home](https://blog.csdn.net/wangliang198901/article/details/12320221)
<a name="jE8xZ"></a>
### 向下一个Activity传递数据
传递数据的方法很简单,只需要使用`putExtra()方法`即可:
```kotlin
button2.setOnClickListener {
// 定义想要传递的数据
val data = "Hello MainActivity2, this is from MainActivity"
// 定义intent
val intent = Intent(this, MainActivity2::class.java)
// 传递数据
intent.putExtra("extra_data", data)
// 启动目标Activity
startActivity(intent)
}
在`putExtra()`方法中,第一个参数是**键值**,第二个是需要传递的**数据**。<br />在下一个Activity调用数据就是通过**键值**调用。
package com.example.myapplication
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
val extraData = intent.getStringExtra("extra_data")
Toast.makeText(this,extraData,Toast.LENGTH_LONG).show()
}
}
来看一下效果:<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22022942/1647480219100-4f92d0d6-39a8-4e03-83f6-da2378b275d0.png#averageHue=%23c6c4b6&clientId=uad588812-2419-4&from=paste&height=432&id=u7cbd1bee&originHeight=864&originWidth=407&originalType=binary&ratio=1&rotation=0&showTitle=false&size=92239&status=done&style=none&taskId=ud679ca39-d05e-4262-9fef-da86c04df44&title=&width=203.5)
返回数据给上一个Activity
首先在MainActivity中修改intent代码:
button2.setOnClickListener {
val intent = Intent(this, MainActivity2::class.java)
startActivityForResult(intent, 1)
}
startActivityForResult()
方法接收两个参数:第一个参数还是Intent;第二个参数是请求码,用于在之后的回调中判断数据的来源。
在MainActivity2里添加一个button3。
MainActivity2中的代码修改如下:
package com.example.myapplication
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
val button3: Button = findViewById(R.id.button3)
button3.setOnClickListener() {
val intent = Intent()
intent.putExtra("data_return", "Hello MainActivity, this is MainActivity2")
setResult(RESULT_OK, intent)
finish()
}
}
}
setResult()
方法接收两个参数:第一个参数用于向上一个Activity返回处理结果,一般只使用RESULT_OK或RESULT_CANCELED这两个值;第二个参数则把带有数据的Intent传递回去。
然后调用了finish()方法来销毁当前Activity。
最后需要在MainActivity中获取数据,这里需要重新方法:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode){
1->if (resultCode == RESULT_OK){
val returnedData = data?.getStringExtra("data_return")
Toast.makeText(this,"We received: $returnedData",Toast.LENGTH_LONG).show()
}
}
}
首先来看重写方法的参数:
- requestCode:表示请求码,这里就是
1
。 - resultCode:表示处理结果,这里就是
RESULT_OK
。 - data:表示传递的数据
继承父类方法,这个一开始就会写出,不用关心。super.onActivityResult(requestCode, resultCode, data)
接下来就是判断请求码和处理结果:
上述操作是通过点击when (requestCode){
1->if (resultCode == RESULT_OK){
val returnedData = data?.getStringExtra("data_return")
Toast.makeText(this,"We received: $returnedData",Toast.LENGTH_LONG).show()
}
}
button3
返回上一个Activity,而如果点击BACK
键返回,则不会传递数据,如果想要点击返回键仍然可以返回数据,需要在MainActivity2
中重写方法: ```kotlin package com.example.myapplication
import android.content.Intent import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Button import android.widget.Toast
class MainActivity2 : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) val button3: Button = findViewById(R.id.button3) button3.setOnClickListener() { val intent = Intent() intent.putExtra(“data_return”, “Hello MainActivity, this is MainActivity2”) setResult(RESULT_OK, intent) finish() } }
override fun onBackPressed() {
//这里可以不用调用父类方法super.onBackPressed()
val intent = Intent()
intent.putExtra("data_return","Hello MainActivity, this is MainActivity2")
setResult(RESULT_OK,intent)
finish()
}
}
---
<a name="ffPkb"></a>
## 3.4 Activity生命周期
<a name="Cgn4M"></a>
### 返回栈
每启动一个新的Activity,就会**覆盖在原Activity之上**,然后点击Back键会销毁最上面的Activity,下面的一个Activity就会重新显示出来。<br />也就是所谓的**先进后出**,类似于数据结构中的`栈`。<br />系统总是会显示**处于栈顶**的Activity给用户。
![image.png](https://cdn.nlark.com/yuque/0/2022/png/22022942/1647482207102-2e66594a-f8a4-4b93-875e-b7a9051ca6eb.png#averageHue=%23fafafa&clientId=uad588812-2419-4&from=paste&height=202&id=u0b869f56&originHeight=403&originWidth=682&originalType=binary&ratio=1&rotation=0&showTitle=false&size=23225&status=done&style=none&taskId=u6d139cea-014a-4d45-8927-8cee48af64a&title=&width=341)
<a name="SnfTX"></a>
### Activity状态
1. **运行状态**
> 当一个Activity位于返回栈的栈顶时,Activity就处于运行状态。
2. **暂停状态**
> 当一个Activity不再处于栈顶位置,但仍然可见时,Activity就进入了暂停状态。
3. **停止状态**
> 当一个Activity不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。
4. **销毁状态**
> 一个Activity从返回栈中移除后就变成了销毁状态。
系统回收Activity的意愿也是由1-4是**从低到高**排列的。
<a name="oki0t"></a>
### Activity生存期
<a name="nwDJm"></a>
#### 7个回调方法
首先看一下什么是回调方法:<br />[回调方法是什么及其理解 - 随花四散 - 博客园](https://www.cnblogs.com/ouhaitao/p/8547907.html)<br /> 7个回调方法分别为:
- **onCreate()**
> 在每个Activity中都重写了这个方法,它会在Activity第一次被创建的时候调用。你应该在这个方法中完成Activity的**初始化操作**,比如加载布局、绑定事件等。
- **onStart()**
> 这个方法在Activity**由不可见变为可见**的时候调用。
- **onResume()**
> 这个方法在Activity准备好和用户**进行交互**的时候调用。此时的Activity一定位于返回栈的栈顶,并且处于运行状态。
- **onPause()**
> 这个方法在系统准备去启动或者恢复另一个Activity的时候调用。通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶Activity的使用。
- **onStop()**
> 这个方法在Activity**完全不可见**的时候调用。它和onPause()方法的主要区别在于,如果启动的新Activity是一个对话框式的Activity,那么onPause()方法会得到执行,而onStop()方法并不会执行。
- **onDestroy()**
> 这个方法在Activity被**销毁之前调用**,之后Activity的状态将变为销毁状态。
- **onRestart()。**
> 这个方法在Activity由停止状态变为运行状态之前调用,也就是Activity被重新启动了
<a name="Xz4Tg"></a>
#### 三种生存期
以上7个方法中除了`onRestart()`方法,其他都是两两相对的:
- **完整生存期**:`onCreate()`方法和`onDestroy()`方法之间
> 简单来讲就是**Activity的创建和销毁期间**
- **可见生存期**:`onStart()`方法和`onStop()`方法之间
> 简单来讲就是Activity启动了,但是是**在后台运行**的。
- **前台生存期**:`onResume()`方法和`onPause()`方法之间
> 简单来说就是Activity在前台使用期间。
![image.png](https://cdn.nlark.com/yuque/0/2022/png/22022942/1647499553098-46514c17-9730-4ab4-8ed0-a91f3b3078af.png#averageHue=%23f4f4f4&clientId=u6e8f7106-1ba8-4&from=paste&height=390&id=u2cc483d6&originHeight=752&originWidth=579&originalType=binary&ratio=1&rotation=0&showTitle=false&size=61491&status=done&style=none&taskId=u76bac152-ead8-4e1b-a803-d01567d9cfd&title=&width=300)
<a name="rh9EE"></a>
#### 体验生命周期代码
新建一个`ActivityLifeCycleTest`项目。再创建两个子Activity——`NormalActivity`和`DialogActivity`。<br />代码实现如下:<br />[MainActivity.docx](https://www.yuque.com/attachments/yuque/0/2022/docx/22022942/1647501605689-0901b76a-78b0-4666-81b8-0cd438d395bf.docx?_lake_card=%7B%22src%22%3A%22https%3A%2F%2Fwww.yuque.com%2Fattachments%2Fyuque%2F0%2F2022%2Fdocx%2F22022942%2F1647501605689-0901b76a-78b0-4666-81b8-0cd438d395bf.docx%22%2C%22name%22%3A%22MainActivity.docx%22%2C%22size%22%3A21986%2C%22ext%22%3A%22docx%22%2C%22source%22%3A%22%22%2C%22status%22%3A%22done%22%2C%22download%22%3Atrue%2C%22type%22%3A%22application%2Fvnd.openxmlformats-officedocument.wordprocessingml.document%22%2C%22mode%22%3A%22title%22%2C%22taskId%22%3A%22ua4c86e6c-5cb6-443f-ad3f-05c01287f5b%22%2C%22taskType%22%3A%22upload%22%2C%22id%22%3A%22ua6cd0d1a%22%2C%22card%22%3A%22file%22%7D)<br />日志文件如下:![image.png](https://cdn.nlark.com/yuque/0/2022/png/22022942/1647501661945-d6bf0f17-3dbe-44fd-b25b-e82df671d489.png#averageHue=%23afb895&clientId=u56eb4021-997c-4&from=paste&height=168&id=u1731ca34&originHeight=336&originWidth=1882&originalType=binary&ratio=1&rotation=0&showTitle=false&size=94627&status=done&style=none&taskId=ue353c9b7-c8a8-4377-867b-b2eda974e6b&title=&width=941)
> 生命周期了解即可,就不详细讲解了。
<a name="X5UBN"></a>
### Activity回收
当一个Activity进入了停止状态,是有可能被系统回收的。<br />当你从一个Activity回退到上一个Activity的时候,如果上一个Activity被系统回收了,那么回退的Activity其实是被**重新创建**的。
> 这里执行的不是`onRestart`方法,而是重新执行`onCreate`方法。
这个时候就可能遇到一个问题,上一个Activity存储的零时数据就不存在了。
> 例如在输入框输入的数据。
如果想要回收前保存数据,则需要重写`onSaveInstanceState()`方法。<br />`onSaveInstanceState()`方法会携带一个**Bundle类型**的参数,Bundle提供了一系列的方法用于保存数据,比如可以使用`putString()`方法保存**字符串**,使用`putInt()`方法保存**整型数据**,以此类推。<br />每个保存方法需要传入两个参数,第一个参数是**键**,用于后面从Bundle中**取值**,第二个参数是**真正要保存的内容**。<br />举个例子:
```kotlin
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
// 需要保存的临时数据
val tempData = "Something you just typed"
outState.putString("data_key", tempData)
}
接下来在Oncreate()
方法里面调用即可:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(tag, "onCreate")
setContentView(R.layout.activity_main)
savedInstanceState?.let {
// 根据键值获取临时存储的数据
val tempData = it.getString("data_key")
Log.d(tag,"data_key:$tempData")
}
}
3.5 Activity的启动模式
Activity的启动模式有四种:
- standard(默认)
- singleTop
- singleTask
- singleInstance
可以在AndroidManifest.xml
中通过给
<activity android:name=".SecondActivity"
android:launchMode="singleInstance">
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.example.activitytest.MY_CATEGORY" />
</intent-filter>
</activity>
standard
standard是Activity默认的启动模式,在不进行显式指定的情况下,所有Activity都会自动使用这种启动模式。
在standard模式下,每当启动一个新的Activity,它都会新创建一个页面,在返回栈中入栈,并处于栈顶的位置。
系统不会在乎这个Activity是否已经在返回栈中存在,每次启动都会创建一个该Activity的新实例。
![image.png](https://cdn.nlark.com/yuque/0/2022/png/22022942/1647514768750-b65ba5ad-d8be-4721-8017-b7006d7ff805.png#averageHue=%23f8f8f8&clientId=u6f18533a-6ee4-4&from=paste&height=189&id=u25eb9055&originHeight=378&originWidth=688&originalType=binary&ratio=1&rotation=0&showTitle=false&size=28360&status=done&style=none&taskId=u037dea39-42d7-4096-8792-e1a59b95f59&title=&width=344)
singleTop
区别于普通的standard
模式,singleTop
首先会进行一个判断,判断栈顶是否就是目标Activity,如果不是才会创建一个新的实例。
singleTask
相比较于singleTop
模式,singleTask
模式如果判断栈顶不是目标Activity,并且目标Activity已经存在,那么会直接让目标Activity上面的所有Activity出栈,从而启动目标Activity,如果目标Activity不存在,则创建一个新的实例。
singleInstance
不同于上面三个模式,设立singleInstance
的Activity会拥有一个单独的返回栈:
以下图为例子:
其中FirstActivity和ThirdActivity都是默认模式,SecondActivity就是singleInstance
模式。
首先从FirstActivity启动SecondActivity,SecondActivity会进入一个单独的返回栈。
在SecondActivity启动ThirdActivity的时候,ThirdActivity会在返回栈A入栈。
ThirdActivity按下回退键,会在返回栈A中出栈,所以下一个显示的Activity就是FirstActivity。
当FirstActivity在回退的时候,就是返回SecondActivity。
随时随地退出
首先写一个单例类:
package com.example.activitylifecycletest.colletcor
import android.app.Activity
object ActivityCollector {
// 定义所有Activities的集合
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()
}
}
接着写三个Activity进行模拟:<br />前两个:
// 第一个
package com.example.activitylifecycletest.testActivities
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import com.example.activitylifecycletest.R
import com.example.activitylifecycletest.colletcor.ActivityCollector
class FirstActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_first)
ActivityCollector.addActivity(this)
val button1:Button = findViewById(R.id.button)
button1.setOnClickListener{
val intent = Intent(this,SecondActivity::class.java)
startActivity(intent)
}
}
}
// 第二个
package com.example.activitylifecycletest.testActivities
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import com.example.activitylifecycletest.R
import com.example.activitylifecycletest.colletcor.ActivityCollector
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
ActivityCollector.addActivity(this)
val button2: Button = findViewById(R.id.button2)
button2.setOnClickListener{
val intent = Intent(this,ThirdActivity::class.java)
startActivity(intent)
}
}
}
最后一个Activity调用方法退出:
package com.example.activitylifecycletest.testActivities
import android.app.Activity
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import com.example.activitylifecycletest.R
import com.example.activitylifecycletest.colletcor.ActivityCollector
class ThirdActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_third)
ActivityCollector.addActivity(this)
val button3:Button = findViewById(R.id.button3)
// 调用方法一键退出
button3.setOnClickListener{
ActivityCollector.finishAll()
}
}
}
这里省略布局文件和配置。
可以参考下面
3.6 标准函数和静态方法
标准函数
with和run以及apply
直接上例子:
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
// 定义一个字符串构建对象
val builder = StringBuilder()
// 对象中不断增加元素
builder.append("Start eating fruits.\n")
for (fruit in list) {
builder.append(fruit).append("\n")
}
builder.append("Ate all fruits.")
val result = builder.toString()
println(result)
运行结果如下:<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22022942/1647518089488-aff6db56-1c48-4ed1-86d1-bc4591dde67c.png#averageHue=%23f6f6f6&clientId=u6f18533a-6ee4-4&from=paste&height=111&id=u796a272b&originHeight=173&originWidth=621&originalType=binary&ratio=1&rotation=0&showTitle=false&size=62297&status=done&style=none&taskId=ufef45a09-c02f-419e-a32d-a4b1c186e2c&title=&width=400)<br />上述代码可以化简为:
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
val result = with(StringBuilder()) {
append("Start eating fruits.\n")
for (fruit in list) {
append(fruit).append("\n")
}
append("Ate all fruits.")
toString()
}
println(result)
// 或者
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
val result = StringBuilder().run {
append("Start eating fruits.\n")
for (fruit in list) {
append(fruit).append("\n")
}
append("Ate all fruits.")
toString()
}
println(result)
// 或者
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
val result = StringBuilder().apply {
append("Start eating fruits.\n")
for (fruit in list) {
append(fruit).append("\n")
}
append("Ate all fruits.")
}
println(result.toString())
抽离出来方法如下:
val result = with(obj) {
// 这里是obj的上下文
"value" // with函数的返回值
}
val result = obj.run {
// 这里是obj的上下文
"value" // run函数的返回值
}
val result = obj.apply {
// 这里是obj的上下文
}
run和apply 的区别在于,**apply函数无法指定返回值,而是会自动返回调用对象本身**。
实际应用
val intent = Intent(context, SecondActivity::class.java)
intent.putExtra("param1", "data1")
intent.putExtra("param2", "data2")
context.startActivity(intent)
// 上述代码可以精简为
val intent = Intent(context, SecondActivity::class.java).apply {
putExtra("param1", "data1")
putExtra("param2", "data2")
}
context.startActivity(intent)
定义静态方法
我没学会,就不写了,大家自己康康PPT叭。