3.1 Activity简单介绍

基本介绍

一开始创建完成一个项目之后,在Project文件目录下打开:
image.png
这里重点关注三个红框里的文件:

Activity文件:

Activity是一种可以包含用户界面的组件,主要用于和用户进行交互。一个应用程序中可以包含零个或多个Activity,但不包含任何Activity的应用程序很少见。

简单来讲Activity对应的就是类似手机页面的后端部分

image.png
主题语法部分重点关注红框中的部分,其余部分在项目创建的时候会默认创建好。
我们来看一下红框中的语法是什么意思:

  1. setContentView(R.layout.activity_main)

这里的setContentView意思是:设置内容视图,也就是设置这个Activity的前端页面部分(这个在下面有讲到,就是layout下面的xml文件)。
参数部分暂时不用深究,前面R.layout是固定的,后面的activity_main就是这个Activity的内容视图文件的名称

xml文件

这里的xml文件是特指在layout文件夹目录下的xml文件,也就是布局的意思,简单的来讲其实就是一个手机页面的前端部分,前端的样式就是在layout下面的xml文件中书写。
image.png

AndroidMainifest.xml

这个文件相当于配置文件image.png
重点看两个红框中的内容:
最外层:<application ...> ... </appliction>表示的就是应用。
前面一个...是整体应用的配置,这里暂时不需要了解。
重点看<application>内的内容,也就是<activity>

  1. <activity
  2. android:name=".MainActivity"
  3. android:exported="true">
  4. <intent-filter>
  5. <action android:name="android.intent.action.MAIN" />
  6. <category android:name="android.intent.category.LAUNCHER" />
  7. </intent-filter>
  8. </activity>

一句一句来看:

  1. android:name=".MainActivity"

这个没什么好说的,就是表示这个Activity的名字,对应的就是刚开始创建的MainActivity

  1. 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
这个属性一般和下面这个联动使用:

  1. <intent-filter>
  2. <action android:name="android.intent.action.MAIN" />
  3. <category android:name="android.intent.category.LAUNCHER" />
  4. </intent-filter>
  1. 这一块的意思就是**配置主Activity**,一般就是打开APP后进入的**第一个界面**。<br />详细属性暂时不需要知道,只需要知道这个在哪个`<activity>`下面配置,哪个就是主Activity。<br />配置了主ActivityActivity`android:exported`一般都设置为`ture`,其余一般为`false`

3.2 Thoast和Menu

Thoast

定义

Toast是Android系统提供的一种非常好的提醒方式,在程序中可以使用它将一些短小的信息通知给用户,这些信息会在一段时间后自动消失,并且不会占用任何屏幕空间,尝试一下如何在Activity中使用Toast。

实例

首先在对应的布局文件xml中,添加一个button组件。
首先在右上角选择Design,把button组件拖拽到屏幕当中。

这里先不需要知道这个组件的详细意义,只需要知道这是一个按钮即可

image.png
完成之后切换到Code代码区,会发现多了一串代码,注意其中的id
image.png 接着在MainActivity中的Oncreate()方法内添加如下代码:

  1. val button: Button = findViewById(R.id.button)
  2. button.setOnClickListener {
  3. Toast.makeText(this, "You clicked Button 1", Toast.LENGTH_SHORT).show()
  4. }
  1. 这里讲解一下语法:<br />首先是申明变量部分,申明一个类型为`Button`类型的变量`button``findViewById`就是通过id寻找组件,括号里面的参数就是`R.id.`id的名称``,这里根据刚刚创建的button组件idbutton,所以就是`R.id.button`,所以第一句的意思就是**找到idbutton 的组件,并把它赋值给变量button**。<br />接着就是给这个变量设置了**监听事件**,`setOnClickListener`,也就是当这个按钮被点击的时候,会触发**大括号中的行为**。
  1. Toast.makeText(this, "You clicked Button 1", Toast.LENGTH_SHORT).show()

Toast对象调用makeText方法,其中:

  • this表示Thoast要求的上下文,一般都是this
  • "You clicked Button 1"就是需要弹出的文字内容
  • Toast.LENGTH_SHORT表示显示时间,这里是短时间也就是2000ms
  • show()就是展示信息

Toast.LENGTHLONG和Toast.LENGTH_SHORT分别对应多长时间天下布武-CSDN博客_toast.length_short
运行项目之后点击button之后会有如下界面:
image.png

Menu

创建文件

首先在res资源文件目录下创建menu文件夹,在menu文件夹下面创建main.xml文件
image.png
image.png

添加选项

菜单需要选项,所以在main.xml文件中添加如下代码:

  1. <item
  2. android:id="@+id/add_item"
  3. android:title="Add"/>
  4. <item
  5. android:id="@+id/remove_item"
  6. android:title="Remove"/>

看图就知道两个属性的内容是什么了。image.png

重写方法

设定了菜单之后,需要添加到Activity中去,因为Activity默认是没有菜单的,但是有对应的方法,所以需要在Activity中重写有关菜单的方法:

  1. package com.example.myapplication
  2. import androidx.appcompat.app.AppCompatActivity
  3. import android.os.Bundle
  4. import android.view.Menu
  5. import android.widget.Button
  6. import android.widget.Toast
  7. class MainActivity : AppCompatActivity() {
  8. override fun onCreate(savedInstanceState: Bundle?) {
  9. super.onCreate(savedInstanceState)
  10. setContentView(R.layout.activity_main)
  11. val button: Button = findViewById(R.id.button)
  12. button.setOnClickListener {
  13. Toast.makeText(this, "You clicked Button 1", Toast.LENGTH_SHORT).show()
  14. }
  15. }
  16. // 重写菜单方法
  17. override fun onCreateOptionsMenu(menu: Menu?): Boolean {
  18. //return super.onCreateOptionsMenu(menu)
  19. //上面的代码重写为下面的
  20. menuInflater.inflate(R.menu.main,menu)
  21. return true
  22. }
  23. }
  1. 重写方法的时候一般只要打出`onCreateOptionsMenu`大部分代码就会自动补全。<br />接下来讲解一下重写的代码的意思:
  • 首先你会发现这里的menuInflater好像没有定义就直接使用,因为menuInflater是父类的属性,这里是运用了kotlin的语法糖,相当于getMenuInflater()方法能够得到一个MenuInflater对象(理解为菜单填充对象即可)
  • 再调用它的inflate()方法,就可以给当前Activity创建菜单了。
  • inflate()方法接收两个参数
    • 第一个参数用于指定通过哪一个资源文件来创建菜单,这里传入R.menu.mainR表示资源文件夹下,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)

  1. val button: Button = findViewById(R.id.button)
  2. button.setOnClickListener {
  3. Toast.makeText(this, "You clicked Button 1", Toast.LENGTH_SHORT).show()
  4. }
  5. }
  6. override fun onCreateOptionsMenu(menu: Menu?): Boolean {
  7. menuInflater.inflate(R.menu.main,menu)
  8. return true
  9. }
  10. override fun onOptionsItemSelected(item: MenuItem): Boolean {
  11. // 传入选择的选项的id 也就是item.itemId
  12. when(item.itemId){
  13. // 如果是添加选项,就弹出文案是You clicked Add的提示
  14. R.id.add_item -> Toast.makeText(this, "You clicked Add", Toast.LENGTH_SHORT).show()
  15. // 如果是去除选项,就弹出文案是You clicked Remove的提示
  16. R.id.remove_item -> Toast.makeText(this, "You clicked Remove", Toast.LENGTH_SHORT).show()
  17. }
  18. // 作用同上
  19. return true
  20. }

}

  1. 看一下效果<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)
  2. <a name="EMUFj"></a>
  3. ### 销毁一个Activity
  4. 一般情况下按**返回**就可以退出当前界面,也就会销毁。<br />当然也可以通过代码进行销毁,只需在事件中添加`finish()`即可
  5. ```kotlin
  6. button1.setOnClickListener {
  7. finish()
  8. }
  1. 比较简单就不演示了。

3.3 Intent

Intent是Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。Intent一般可用于启动Activity启动Service以及发送广播等场景,由于Service、广播等概念暂时还未涉及,目光无疑就锁定在了启动Activity上面。
Intent大致可以分为两种:显式Intent和隐式Intent

显式Intent

我们只需要再添加一个button:
image.png
接下来添加一个新的空Activity:
跟着下面步骤即可,后面不要加改动:
image.png
image.png
接下来在MainActivity文件的OnCreate方法中添加:

  1. val button2:Button = findViewById(R.id.button2)
  2. button2.setOnClickListener{
  3. val intent:Intent = Intent(this,MainActivity2::class.java)
  4. startActivity(intent)
  5. }
  1. 主要讲解一下`Intent`方法
  • 第一个参数Context要求提供一个启动Activity的上下文;(一般都是this)
  • 第二个参数Class用于指定想要启动的目标Activity,通过这个构造函数就可以构建出Intent的“意图”

完整代码为:

  1. package com.example.myapplication
  2. import android.content.Intent
  3. import androidx.appcompat.app.AppCompatActivity
  4. import android.os.Bundle
  5. import android.view.Menu
  6. import android.view.MenuItem
  7. import android.widget.Button
  8. import android.widget.Toast
  9. class MainActivity : AppCompatActivity() {
  10. override fun onCreate(savedInstanceState: Bundle?) {
  11. super.onCreate(savedInstanceState)
  12. setContentView(R.layout.activity_main)
  13. // 注意id的选择
  14. val button2:Button = findViewById(R.id.button2)
  15. button2.setOnClickListener{
  16. val intent:Intent = Intent(this,MainActivity2::class.java)
  17. startActivity(intent)
  18. }
  19. }
  20. }
  1. 为了便于观察,我们给新建的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)

  1. // 注意id的选择
  2. val button2:Button = findViewById(R.id.button2)
  3. button2.setOnClickListener{
  4. val intent = Intent(Intent.ACTION_VIEW)
  5. intent.data = Uri.parse("https://www.baidu.com")
  6. startActivity(intent)
  7. }
  8. }

}

  1. 运行结果为:<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)
  2. <a name="jE8xZ"></a>
  3. ### 向下一个Activity传递数据
  4. 传递数据的方法很简单,只需要使用`putExtra()方法`即可:
  5. ```kotlin
  6. button2.setOnClickListener {
  7. // 定义想要传递的数据
  8. val data = "Hello MainActivity2, this is from MainActivity"
  9. // 定义intent
  10. val intent = Intent(this, MainActivity2::class.java)
  11. // 传递数据
  12. intent.putExtra("extra_data", data)
  13. // 启动目标Activity
  14. startActivity(intent)
  15. }
  1. `putExtra()`方法中,第一个参数是**键值**,第二个是需要传递的**数据**。<br />在下一个Activity调用数据就是通过**键值**调用。
  1. package com.example.myapplication
  2. import androidx.appcompat.app.AppCompatActivity
  3. import android.os.Bundle
  4. import android.widget.Toast
  5. class MainActivity2 : AppCompatActivity() {
  6. override fun onCreate(savedInstanceState: Bundle?) {
  7. super.onCreate(savedInstanceState)
  8. setContentView(R.layout.activity_main2)
  9. val extraData = intent.getStringExtra("extra_data")
  10. Toast.makeText(this,extraData,Toast.LENGTH_LONG).show()
  11. }
  12. }
  1. 来看一下效果:<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代码:

  1. button2.setOnClickListener {
  2. val intent = Intent(this, MainActivity2::class.java)
  3. startActivityForResult(intent, 1)
  4. }

startActivityForResult()方法接收两个参数:第一个参数还是Intent;第二个参数是请求码,用于在之后的回调中判断数据的来源。
在MainActivity2里添加一个button3。
MainActivity2中的代码修改如下:

  1. package com.example.myapplication
  2. import android.content.Intent
  3. import androidx.appcompat.app.AppCompatActivity
  4. import android.os.Bundle
  5. import android.widget.Button
  6. import android.widget.Toast
  7. class MainActivity2 : AppCompatActivity() {
  8. override fun onCreate(savedInstanceState: Bundle?) {
  9. super.onCreate(savedInstanceState)
  10. setContentView(R.layout.activity_main2)
  11. val button3: Button = findViewById(R.id.button3)
  12. button3.setOnClickListener() {
  13. val intent = Intent()
  14. intent.putExtra("data_return", "Hello MainActivity, this is MainActivity2")
  15. setResult(RESULT_OK, intent)
  16. finish()
  17. }
  18. }
  19. }

setResult()方法接收两个参数:第一个参数用于向上一个Activity返回处理结果,一般只使用RESULT_OKRESULT_CANCELED这两个值;第二个参数则把带有数据的Intent传递回去。
然后调用了finish()方法来销毁当前Activity。
最后需要在MainActivity中获取数据,这里需要重新方法:

  1. override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
  2. super.onActivityResult(requestCode, resultCode, data)
  3. when (requestCode){
  4. 1->if (resultCode == RESULT_OK){
  5. val returnedData = data?.getStringExtra("data_return")
  6. Toast.makeText(this,"We received: $returnedData",Toast.LENGTH_LONG).show()
  7. }
  8. }
  9. }
  1. 首先来看重写方法的参数:
  • requestCode:表示请求码,这里就是1
  • resultCode:表示处理结果,这里就是RESULT_OK
  • data:表示传递的数据
    1. super.onActivityResult(requestCode, resultCode, data)
    继承父类方法,这个一开始就会写出,不用关心。
    接下来就是判断请求码和处理结果
    1. when (requestCode){
    2. 1->if (resultCode == RESULT_OK){
    3. val returnedData = data?.getStringExtra("data_return")
    4. Toast.makeText(this,"We received: $returnedData",Toast.LENGTH_LONG).show()
    5. }
    6. }
    上述操作是通过点击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() } }

  1. override fun onBackPressed() {
  2. //这里可以不用调用父类方法super.onBackPressed()
  3. val intent = Intent()
  4. intent.putExtra("data_return","Hello MainActivity, this is MainActivity2")
  5. setResult(RESULT_OK,intent)
  6. finish()
  7. }

}

  1. ---
  2. <a name="ffPkb"></a>
  3. ## 3.4 Activity生命周期
  4. <a name="Cgn4M"></a>
  5. ### 返回栈
  6. 每启动一个新的Activity,就会**覆盖在原Activity之上**,然后点击Back键会销毁最上面的Activity,下面的一个Activity就会重新显示出来。<br />也就是所谓的**先进后出**,类似于数据结构中的`栈`。<br />系统总是会显示**处于栈顶**的Activity给用户。
  7. ![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)
  8. <a name="SnfTX"></a>
  9. ### Activity状态
  10. 1. **运行状态**
  11. > 当一个Activity位于返回栈的栈顶时,Activity就处于运行状态。
  12. 2. **暂停状态**
  13. > 当一个Activity不再处于栈顶位置,但仍然可见时,Activity就进入了暂停状态。
  14. 3. **停止状态**
  15. > 当一个Activity不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。
  16. 4. **销毁状态**
  17. > 一个Activity从返回栈中移除后就变成了销毁状态。
  18. 系统回收Activity的意愿也是由1-4是**从低到高**排列的。
  19. <a name="oki0t"></a>
  20. ### Activity生存期
  21. <a name="nwDJm"></a>
  22. #### 7个回调方法
  23. 首先看一下什么是回调方法:<br />[回调方法是什么及其理解 - 随花四散 - 博客园](https://www.cnblogs.com/ouhaitao/p/8547907.html)<br /> 7个回调方法分别为:
  24. - **onCreate()**
  25. > 在每个Activity中都重写了这个方法,它会在Activity第一次被创建的时候调用。你应该在这个方法中完成Activity的**初始化操作**,比如加载布局、绑定事件等。
  26. - **onStart()**
  27. > 这个方法在Activity**由不可见变为可见**的时候调用。
  28. - **onResume()**
  29. > 这个方法在Activity准备好和用户**进行交互**的时候调用。此时的Activity一定位于返回栈的栈顶,并且处于运行状态。
  30. - **onPause()**
  31. > 这个方法在系统准备去启动或者恢复另一个Activity的时候调用。通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶Activity的使用。
  32. - **onStop()**
  33. > 这个方法在Activity**完全不可见**的时候调用。它和onPause()方法的主要区别在于,如果启动的新Activity是一个对话框式的Activity,那么onPause()方法会得到执行,而onStop()方法并不会执行。
  34. - **onDestroy()**
  35. > 这个方法在Activity被**销毁之前调用**,之后Activity的状态将变为销毁状态。
  36. - **onRestart()。**
  37. > 这个方法在Activity由停止状态变为运行状态之前调用,也就是Activity被重新启动了
  38. <a name="Xz4Tg"></a>
  39. #### 三种生存期
  40. 以上7个方法中除了`onRestart()`方法,其他都是两两相对的:
  41. - **完整生存期**:`onCreate()`方法和`onDestroy()`方法之间
  42. > 简单来讲就是**Activity的创建和销毁期间**
  43. - **可见生存期**:`onStart()`方法和`onStop()`方法之间
  44. > 简单来讲就是Activity启动了,但是是**在后台运行**的。
  45. - **前台生存期**:`onResume()`方法和`onPause()`方法之间
  46. > 简单来说就是Activity在前台使用期间。
  47. ![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)
  48. <a name="rh9EE"></a>
  49. #### 体验生命周期代码
  50. 新建一个`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)
  51. > 生命周期了解即可,就不详细讲解了。
  52. <a name="X5UBN"></a>
  53. ### Activity回收
  54. 当一个Activity进入了停止状态,是有可能被系统回收的。<br />当你从一个Activity回退到上一个Activity的时候,如果上一个Activity被系统回收了,那么回退的Activity其实是被**重新创建**的。
  55. > 这里执行的不是`onRestart`方法,而是重新执行`onCreate`方法。
  56. 这个时候就可能遇到一个问题,上一个Activity存储的零时数据就不存在了。
  57. > 例如在输入框输入的数据。
  58. 如果想要回收前保存数据,则需要重写`onSaveInstanceState()`方法。<br />`onSaveInstanceState()`方法会携带一个**Bundle类型**的参数,Bundle提供了一系列的方法用于保存数据,比如可以使用`putString()`方法保存**字符串**,使用`putInt()`方法保存**整型数据**,以此类推。<br />每个保存方法需要传入两个参数,第一个参数是**键**,用于后面从Bundle中**取值**,第二个参数是**真正要保存的内容**。<br />举个例子:
  59. ```kotlin
  60. override fun onSaveInstanceState(outState: Bundle) {
  61. super.onSaveInstanceState(outState)
  62. // 需要保存的临时数据
  63. val tempData = "Something you just typed"
  64. outState.putString("data_key", tempData)
  65. }

接下来在Oncreate()方法里面调用即可:

  1. override fun onCreate(savedInstanceState: Bundle?) {
  2. super.onCreate(savedInstanceState)
  3. Log.d(tag, "onCreate")
  4. setContentView(R.layout.activity_main)
  5. savedInstanceState?.let {
  6. // 根据键值获取临时存储的数据
  7. val tempData = it.getString("data_key")
  8. Log.d(tag,"data_key:$tempData")
  9. }
  10. }

3.5 Activity的启动模式

Activity的启动模式有四种:

  • standard(默认)
  • singleTop
  • singleTask
  • singleInstance

可以在AndroidManifest.xml中通过给标签指定android:launchMode属性来选择启动模式。

  1. <activity android:name=".SecondActivity"
  2. android:launchMode="singleInstance">
  3. <intent-filter>
  4. <action android:name="com.example.activitytest.ACTION_START" />
  5. <category android:name="android.intent.category.DEFAULT" />
  6. <category android:name="com.example.activitytest.MY_CATEGORY" />
  7. </intent-filter>
  8. </activity>

standard

standard是Activity默认的启动模式,在不进行显式指定的情况下,所有Activity都会自动使用这种启动模式。
在standard模式下,每当启动一个新的Activity,它都会新创建一个页面,在返回栈中入栈,并处于栈顶的位置

系统不会在乎这个Activity是否已经在返回栈中存在,每次启动都会创建一个该Activity的新实例。

  1. ![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如果不是才会创建一个新的实例
image.png

singleTask

相比较于singleTop模式,singleTask模式如果判断栈顶不是目标Activity,并且目标Activity已经存在那么会直接让目标Activity上面的所有Activity出栈,从而启动目标Activity,如果目标Activity不存在,则创建一个新的实例
image.png

singleInstance

不同于上面三个模式,设立singleInstance的Activity会拥有一个单独的返回栈:
以下图为例子:
image.png
其中FirstActivity和ThirdActivity都是默认模式,SecondActivity就是singleInstance模式。
首先从FirstActivity启动SecondActivity,SecondActivity会进入一个单独的返回栈
在SecondActivity启动ThirdActivity的时候,ThirdActivity会在返回栈A入栈。
ThirdActivity按下回退键,会在返回栈A中出栈,所以下一个显示的Activity就是FirstActivity
当FirstActivity在回退的时候,就是返回SecondActivity。

随时随地退出

首先写一个单例类:

  1. package com.example.activitylifecycletest.colletcor
  2. import android.app.Activity
  3. object ActivityCollector {
  4. // 定义所有Activities的集合
  5. private val activities = ArrayList<Activity>()
  6. // 把传入的Activity到添加集合中
  7. fun addActivity(activity: Activity) {
  8. activities.add(activity)
  9. }
  10. // 把传入的Activity在集合中去除
  11. fun removeActivity(activity: Activity) {
  12. activities.remove(activity)
  13. }
  14. // 遍历集合中的所有Activity,并逐个销毁
  15. fun finishAll() {
  16. for (activity in activities) {
  17. if (!activity.isFinishing) {
  18. activity.finish()
  19. }
  20. }
  21. activities.clear()
  22. }
  23. }
  1. 接着写三个Activity进行模拟:<br />前两个:
  1. // 第一个
  2. package com.example.activitylifecycletest.testActivities
  3. import android.content.Intent
  4. import androidx.appcompat.app.AppCompatActivity
  5. import android.os.Bundle
  6. import android.widget.Button
  7. import com.example.activitylifecycletest.R
  8. import com.example.activitylifecycletest.colletcor.ActivityCollector
  9. class FirstActivity : AppCompatActivity() {
  10. override fun onCreate(savedInstanceState: Bundle?) {
  11. super.onCreate(savedInstanceState)
  12. setContentView(R.layout.activity_first)
  13. ActivityCollector.addActivity(this)
  14. val button1:Button = findViewById(R.id.button)
  15. button1.setOnClickListener{
  16. val intent = Intent(this,SecondActivity::class.java)
  17. startActivity(intent)
  18. }
  19. }
  20. }
  21. // 第二个
  22. package com.example.activitylifecycletest.testActivities
  23. import android.content.Intent
  24. import androidx.appcompat.app.AppCompatActivity
  25. import android.os.Bundle
  26. import android.widget.Button
  27. import com.example.activitylifecycletest.R
  28. import com.example.activitylifecycletest.colletcor.ActivityCollector
  29. class SecondActivity : AppCompatActivity() {
  30. override fun onCreate(savedInstanceState: Bundle?) {
  31. super.onCreate(savedInstanceState)
  32. setContentView(R.layout.activity_second)
  33. ActivityCollector.addActivity(this)
  34. val button2: Button = findViewById(R.id.button2)
  35. button2.setOnClickListener{
  36. val intent = Intent(this,ThirdActivity::class.java)
  37. startActivity(intent)
  38. }
  39. }
  40. }
  1. 最后一个Activity调用方法退出:
  1. package com.example.activitylifecycletest.testActivities
  2. import android.app.Activity
  3. import androidx.appcompat.app.AppCompatActivity
  4. import android.os.Bundle
  5. import android.widget.Button
  6. import com.example.activitylifecycletest.R
  7. import com.example.activitylifecycletest.colletcor.ActivityCollector
  8. class ThirdActivity : AppCompatActivity() {
  9. override fun onCreate(savedInstanceState: Bundle?) {
  10. super.onCreate(savedInstanceState)
  11. setContentView(R.layout.activity_third)
  12. ActivityCollector.addActivity(this)
  13. val button3:Button = findViewById(R.id.button3)
  14. // 调用方法一键退出
  15. button3.setOnClickListener{
  16. ActivityCollector.finishAll()
  17. }
  18. }
  19. }

这里省略布局文件和配置。

可以参考下面
image.png


3.6 标准函数和静态方法

标准函数

with和run以及apply

直接上例子:

  1. val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
  2. // 定义一个字符串构建对象
  3. val builder = StringBuilder()
  4. // 对象中不断增加元素
  5. builder.append("Start eating fruits.\n")
  6. for (fruit in list) {
  7. builder.append(fruit).append("\n")
  8. }
  9. builder.append("Ate all fruits.")
  10. val result = builder.toString()
  11. println(result)
  1. 运行结果如下:<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 />上述代码可以化简为:
  1. val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
  2. val result = with(StringBuilder()) {
  3. append("Start eating fruits.\n")
  4. for (fruit in list) {
  5. append(fruit).append("\n")
  6. }
  7. append("Ate all fruits.")
  8. toString()
  9. }
  10. println(result)
  11. // 或者
  12. val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
  13. val result = StringBuilder().run {
  14. append("Start eating fruits.\n")
  15. for (fruit in list) {
  16. append(fruit).append("\n")
  17. }
  18. append("Ate all fruits.")
  19. toString()
  20. }
  21. println(result)
  22. // 或者
  23. val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
  24. val result = StringBuilder().apply {
  25. append("Start eating fruits.\n")
  26. for (fruit in list) {
  27. append(fruit).append("\n")
  28. }
  29. append("Ate all fruits.")
  30. }
  31. println(result.toString())
  1. 抽离出来方法如下:
  1. val result = with(obj) {
  2. // 这里是obj的上下文
  3. "value" // with函数的返回值
  4. }
  5. val result = obj.run {
  6. // 这里是obj的上下文
  7. "value" // run函数的返回值
  8. }
  9. val result = obj.apply {
  10. // 这里是obj的上下文
  11. }
  1. runapply 的区别在于,**apply函数无法指定返回值,而是会自动返回调用对象本身**。

实际应用

  1. val intent = Intent(context, SecondActivity::class.java)
  2. intent.putExtra("param1", "data1")
  3. intent.putExtra("param2", "data2")
  4. context.startActivity(intent)
  5. // 上述代码可以精简为
  6. val intent = Intent(context, SecondActivity::class.java).apply {
  7. putExtra("param1", "data1")
  8. putExtra("param2", "data2")
  9. }
  10. context.startActivity(intent)

定义静态方法

我没学会,就不写了,大家自己康康PPT叭。
image.png


参考PPT

第3章 先从看得到的入手,探究Activity.pptx