安卓开发--Activity组件与广播消息
人生各自精彩
Andriod组件
Andriod系统中存在四大组件,四大组件是Android程序调用的基本模块。
Activity
- Android程序的呈现层,显示可视化的用户界面,接受与用户交互所产生的界面事件。
- Android应用程序可以包含一个或多个Activity,一般需要指定一个程序启动时显示的Activity。
Service
- Service一般用于没有用户界面但需要长时间在后台运行的应用。
- 可公开Service的程序接口供其他进程调用。
BroadcaseReceiver
- 用来接收广播消息的组件,不包含任何用户界面。
- 可以启动Activity或者Notification通知用户收到的重要信息。
Notification能够通过多种方法提示用户,包括闪动背景、震动设备、发出声音或在状态栏上放置一个图标
ContentProvider
- 是Android系统提供的一种标准的共享数据的机制,其他程序通过ContentProvider访问程序的私有数据。
- Andriod系统内部提供一些内置的ContentProvider,能够为应用程序提供重要数据信息,如:联系人,通话记录。
Activity
Activity是Andriod组件中最基本也是最常用的组件。Activity用于提供可视化用户界面的组件,它为用户提供了一个完成操作指令的窗口,可以与用户交互来完成某项内容。
在Android应用中,一个Activity通常解释一个单独的屏幕,它上面可以显示一些控件同时监听并处理用户事件做出响应。
新建Activity
图形化创建
包名处点击右键,选择New -> Activity -> [合适的Activity]

Android现在有很多不同内置Activity,帮助你的程序快速成型:

手动创建
创建Java/Kotlin类文件

在一个新的项目中,通常有一个初始化的HelloWorld页面,接下来我们在此基础上创建一个二级页面。

在新建的类中继承 AppCompatActivity:
1 | |
创建Activity的布局XML文件

即可快捷创建一个空的xml布局文件:

在AndroidManifest.xml文件中注册Activity
所有的Activity都必须在 AndroidMainfest.xml文件中注册,否则系统无法启动它。
我们打开 app/manifests/AndroidManifest.xml,在 <application>标签内,添加一个新的 <activity>标签来注册您的Activity。
1 | |
android:name中需要设置为您的Activity的完整包名和类名,如果你的类文件在主包名下,可以使用 .简写,例如 .SecondaryActivity。
Activity的生命周期
Android开发指南中对于生命周期的讲解:activity 生命周期 | App architecture | Android Developers
工作流程
在Android系统中会维持一个Activity栈,当一个新的Activity创建时,它就会放到栈顶,这个Activity就处于运行状态。当再有一个新的Activity被创建后,会重新压入栈顶,而之前的Activity则会在这个新的Activity底下,同时进入后台。

四个状态
Active/Running运行状态:
- 一个新的Activity启动入栈后,它显示在屏幕最前端,处于站的最顶端(Activity栈顶),此时它处于可见并和用户交互的激活状态,叫做火种状态或运行状态。
paused暂停状态:
- 当Activity失去焦点,被一个新的非全屏的Activity或者一个透明的Activity被放置在栈顶,此时的状态叫做暂停状态(paused)。此时它依然与窗口管理器保持连接,Activity依然保持活力(保持所有的状态,成员信息,和窗口管理器保持连接),除非在系统内存极端低下时会被强行终止。所以它仍然可见,但已经失去焦点故不可与用户进行交互。
stopped停止状态:
- 如果一个Activity被另外的Activity完全覆盖掉,叫做停止状态(stopped)。它依然保持所有的状态和成员信息,但是它不再可见,所以它的窗口被隐藏,当系统内存需要被用在其他地方的时候,stopped的Activity被强行终止。
killed销毁状态:
如果一个Activity时paused或者stopped状态,系统可以将该Activity从内存中删除,Andriod系统采用的两种方式进行删除
- 要求该Activity结束(会调用
onDestroy()方法)) - 直接终止它的进程(结束时不会执行
onDestroy())
当该Activity再次显示给用户时,它必须重新开始和重置前面的状态。
- 要求该Activity结束(会调用
对于进入pause和stop状态的Activity,开发者需要自己在回调函数
onPause()和onStop()中自行决定释放哪些资源,暂停哪些工作。Android系统只保证:
- Activity 失去焦点和可见性
- 调用生命周期回调
系统不会自动暂停你代码中启动的资源和线程。
三个生命周期
Activity的生命周期可以分为全生命周期、可视生命周期和活动生命周期。每种生命周期中包含不同的事件回调函数。

全生命周期:
- 从
onCreate()开始到onDestroy()结束。Activity在onCreate()设置所有的”全局”状态,在onDestroy()释放所有的资源。
可视生命周期:
- 从
onStart()开始到onStop()结束。这段时间,可以看到Activity在屏幕上,尽管有可能不是焦点,不能和用户交互。在这两个接口之间,需要保持显示给用户的UI数据和资源等。
活动生命周期:
- 从
onResume()开始到onPause()结束。在这段时间,该Activity处于所有Activity的最前面,和用户进行交互。Activity可以经常在Resume和Pause状态之间切换。所以在这些接口方法中的代码应该属于非常轻量级的。
回调方法详解
onCreate:
- 当Activity第一次创建时被调用。是生命周期开始的第一个方法。
- 工作:
- 调用setContentView方法加载页面
- 绑定布局控件
- 初始化数据
- Activity状态:被创建、不可见、不可交互
- 下一方法:onStart()
onStart:
- 当Activity正在变为可见时调用。
- 工作:
- 界面相关的初始化
- 启动界面可见时的运行逻辑
- Activity状态:可见,但还没有进入前台、不可交互
- 下一方法:onResume()
onResume:
- 当Activity可以跟用户交互时调用。
- 工作:
- 启动需要前台运行的功能
- 每次返回前台都会调用
- Activity状态:处于栈顶顶部,可交互,用户的焦点
- 下一方法:失去焦点时->onPause()
onPause:
- 当Activity暂停时调用这个方法。
- 工作:
- 暂停不必要的操作:动画、相机、传感器
- 释放轻量资源
- Activity状态:失去焦点,仍部分可见
- 下一方法:
- 重新获得焦点->onResume()
- 完全不可见->onStop()
onStop:
- 当Activity进入后台,且不会被用户看到时调用。
- 工作:
- 释放调整不再需要的资源(停止动画,降低定位精度)
- 保存持久化数据
- Activity状态:完全不可见
- 下一方法:
- 用户返回->onResart()->onStart()
- 如果结束->onDestroy()
onDestroy:
- 这个方法是Activity生命周期中调用的最后一个方法。会在Activity被销毁之前调用。
- 工作:
- 释放所有未释放的资源
- Activity:被彻底销毁
- 下一方法:生命周期结束。
onRestart:
- 这个方法是Activity处于停止状态后,又回到可视状态时调用,之后会调用
onResume()。 - 工作:
- 恢复在onStop中释放的资源
- 重新初始化在后台被关闭的逻辑(注册监听器,刷新数据)
- Activity状态:不可见
- 下一方法:onRestart()
生命周期演示程序
为了方便我们最直观的感受到回调函数的触发时机以及触发顺序。这里设计了一个拥有三级页面的小型Demo。我们将主要观测二级页面即中间层页面的生命周期回调函数的触发情况。当回调函数被触发时,程序将发送系统通知,系统通知的内容为被调用的回调函数的名字。我们可以通过通知内容与顺序,掌握生命周期的触发时机以及顺序。
程序主要代码如下:
1 | |
运行结果展示
从创建到可交互
从一级页面进入二级页面:

系统通知栏结果如下:

Android通知显示顺序为:新的消息在上,旧的消息在下。所以三条通知产生的顺序为:onCreate->onStrart->onResume。
完全关闭页面
从二级页面返回一级页面:

二级窗口被完全关闭,调用三个生命周期,顺序分别为:onPause->onStop->onDestory

页面进入后台
从二级页面进入三级页面:

二级页面调用两个生命周期:onPause->onStop

页面返回前台
从三级页面返回二级页面

调用三个生命周期:onRestart->onStart->onResume
不完全遮蔽页面
对三级页面进行修改,修改为一个弹窗,而非一个完整的页面,我们再次进行测试:

打开弹窗,只执行了onPause生命周期:

关闭弹窗,只执行了onResume生命周期:
四种启动模式
Standard
standard模式是Activity 的默认启动方式,每启动一个Activity就会在栈顶创建一个新的实例。

SingleTop模式
singleTop模式会判断要启动的Activity实例是否位于栈顶,如果位于栈顶则直接复用,否则创建新的实例。

SingleTask模式
singleTask模式下每次启动该Activity时,系统首先会检查栈中是否存在当前Activity实例,如果存在则直接使用,并把当前Activity之上的所有实例全部出栈。

SingleInstance模式
singleInstance模式会启动一个新的任务栈来管理Activity实例,无论从哪个任务栈中启动该Activity,该实例在整个系统中只有一个。
整个安卓系统中只存在一个该Activity实例。
使用场景:系统级的页面 :比如拨号界面、相机、播放器等,通常希望全局只有一个实例。

组件通信与广播消息
Intent
Intent是一个动作的完整描述,包含了动作的产生组件、接受组件和传递的数据信息。
- Intent也可以称为一个再不同组件之间传递的消息,这个消息在到达接受组件后,接受组件会执行相关的动作。
- Intent为Activity、Service和BroadcastReceiver等组件提供交互能力。
用途:
- 启动Activity和Service
- 点击一个按钮查看详细信息,就会用Intent启动一个新的详细信息Activity,并将要显示的数据(如商品ID)通过Intent传递过去。
- 监听Android系统发布的广播消息(Broadcast)
- 在应用中注册的
BroadcastReceiver会接收它感兴趣的广播Intent,并执行相应的操作。
- 在应用中注册的
启动Activity
在Android系统中,应用程序一般都有多个Activity,Intent可以实现不同Activity之间的切换和数据传递。
启动Activity的方式:
- 显示启动,必须在Intent中指明启动的Activity所在的类。
- 隐式启动,Android系统根据Intent的动作和数据来决定启动哪一个Activity,也就是说在隐式启动时,Intent只包含需要执行的动作和所包含的数据,而无需指明具体启动哪一个Activity,选择权由Android系统和最终用户来决定。
隐式启动的例子:

显示启动
实现方法:
- 创建一个Intent。
- 指定当前应用程序上下文以及要启动的Activity。
- 把创建好的Intent最为参数传递给startActivity()方法。
示例:
首先我们先创建两个Activity以让我们后期调用:

我们在activity_main.xml中创建一个按钮:

在MainActivity的 onCreate方法中,我们对Button创建一个事件监听器:
1 | |
MainActivity.this:代表在当前应用的上下文中进行寻找。MainActivity2.this:指明要启动的Activity的类对象。
显示启动通常只能启动应用程序内的Activity。优点是写法简单直接。
隐式启动
隐式启动的好处在于不需要指明需要启动哪一个Activity,而由Android系统来决定。
当隐式启动Activity,Android系统会在程序运行时解析Intent,并根据一定的规则对Intent和Activity进行匹配,使得Intent上的动作、数据与Activity完全吻合。
匹配范围:
- 程序本身Activity
- Android系统内的Activity
- 第三方应用程序提供的Activity
示例:(启动浏览器并打开指定页面)
在activity_main2.xml中定义一个Button:

并在MainActivity2中给Button设置监听器:
1 | |
点击按钮后,就会自动跳转到 https://www.baidu.com(使用手机默认浏览器)
Intent.ACTION_VIEW:是一个预定义的字符串常量,告诉Android系统:”我想查看(VIEW)一些数据。”
Uri.parase("https://..."):这是一个统一资源标识符(URI)对象,它将一个URL字符串,解析为Intent可以处理的数据格式(具体来说是一个Uri对象)。
Android系统会遍历所有已安装的App的Intent过滤器,寻找一个组件,声明可以处理:
- 动作:
android.intent.action.VIEW - 接收数据:
Uri对象
找到后,系统会启动它,并将数据传递给它,浏览器随后就会打开这个网址。
Android常用动作说明:

获取Activity返回值
之前的所有示例中,我们通过 startActivity(Intent)方法启动Activity。通过该方法启动后的两个Activity之间相互独立,没有任何关联。
有时,后启动的Activity是为了让用户对特定的信息进行选择,在后启动的Activity关闭时,这些信息是需要返回给先前启动的Avtivity的。
此时我们可以通过Sub-Activity的方式来启动Activity。
关系:
- 子Activity:被调用Activity
- 父Activity:调用者
步骤:
- 以Sub-Activity的方式启动子Activity。
- 设置子Activity的返回值。
- 在父Activity中获取返回值。
详细步骤
以Sub-Activity方式启动子Activity
调用startActivityForResult(Intent, requestCode)函数。
Intent:决定启动的ActivityrequestCode:请求码,所有子Activity返回时,父Activity都调用相同的处理函数,通过requestCode来区分不同子Activity返回的数据。
代码示例:
1 | |
悲,Android Studio已经将
startActivityForResult标记为弃用。老师的ppt何时才能更新。。。喜报,虽然标记为弃用,但是仍然可以用。。。
获取请求的Intent
在被调用的Activity中,如果我们需要获取调用该Activity的Intent消息,可以使用Actitivy对象的getIntent()方法:
1 | |
设置子Activity的返回值
在子Activity调用 finish()函数关闭之前,调用 setResult()函数设定需要返回给父Activity的数据。
1 | |
resultCode:结果码,用于表明子Activity的返回状态,通常为Activiy.RESULT_OK:正常返回。Activity.RESULT_CANCELED:取消返回数据。- 也可以自定义结果码。
data:返回值封装在Intent中,即子Activity通过Intent将需要返回的数据传递给父Activity。
代码示例:
1 | |
获取Activity返回值
在子Activity关闭后,父Activity会调用 onActivityResult()函数,用于获取子Activity的返回值。我们想要处理子Activity的返回值,就需要重写父Activity中的 onActivityResult()函数。
1 | |
requestCode:请求码,区分不同子Activity的返回值。resultCode:返回状态码。data:返回的数据。
示例:
1 | |
Toast.makeText:用于创建一个短时间的提示框(Toast),内容是上面获取的message。
Intent过滤器
当使用隐式启动Activity时,并没有在Intent中指明Activity所在的类,因此Android系统中一定存在某种匹配机制,使得Android系统能够根据Intent中的数据信息,找到需要启动的Activity。
这种匹配机制就是Intent过滤器(Intent Filter)。
定义:
- Intent过滤器是一种根据Intent中的动作(Action)、类别(Categorie)和数据(Data)等内容,对适合接收该Intent的组件进行匹配和筛选的机制。
Intent过滤器可以匹配数据类型、路径和协议,还可以确定多个匹配项顺序的优先级(Priority)。
应用程序的Activity、Service和BroadcastReceiver组件都可以注册Intent过滤器。这样,这些组件在特定的数据格式上则可以产生相应的动作。
注册Intent Filter
- 在AndroidManifest.xml文件中定义
<intent-filter>(常用) - 在程序代码中动态的组件设置
<intent-filter>
<intent-filter>的的子标签:
<action>:动作<category>:类别<data>:数据

<category>标签用来指定Intent过滤器的服务方式,每个Intent过滤器可以定义多个 <category>标签。
<category>标签支持自定义类别也可以使用Android系统提供的类别。
Intent解析规则
收集过滤器
- Android 系统会将所有应用程序包中的 Intent 过滤器 (
<intent-filter></intent-filter>) 汇总,形成一个完整的过滤器列表。
- Android 系统会将所有应用程序包中的 Intent 过滤器 (
动作(Action)与类别(Category)匹配
- 当一个
Intent发出时,系统会遍历过滤器列表,检查: Intent的 action 是否与过滤器中的<action></action>匹配。Intent的 category 是否与过滤器中的<category></category>匹配。- 只要匹配成功,就会被加入候选列表。
- 当一个
数据(Data)匹配
- 系统会进一步检查
Intent中的数据(URI、MIME 类型等)与过滤器<data></data>标签的属性是否匹配。 - 注意:
- 如果
Intent没有携带数据,也可以匹配上仅依赖 category 的过滤器。 - 如果过滤器声明了
<data></data>,但Intent的数据不满足要求,则该过滤器会被剔除。
- 系统会进一步检查
MIME 类型与最终筛选
- 如果过滤器的
<data></data>标签中声明了 MIME 类型 ,系统会严格校验。 - 只有完全匹配的过滤器才会进入最终的匹配结果列表。
- 如果过滤器的
多个匹配结果的处理
- 如果最终有多个过滤器同时匹配:
- 系统会根据
<intent-filter></intent-filter>中的 priority(优先级) 属性来决定。 - 优先级数值越高,匹配结果越优先。
1 | |
广播消息
Intent的另一种用途是发送广播消息,应用程序和Android系统都可以使用Intent发送广播消息。
发送广播消息
方法:
- 创建一个Intent。(必须有全局唯一的字符标识其要执行的动作)。
- 使用putExtra()添加数据。
- 调用sendBroadcast()发送广播Intent。
1 | |
从 Android 8.0 开始,为了节省电量和提升系统性能,Android 对在清单文件中静态注册的广播接收器施加了严格的限制。对于绝大多数隐式广播(比如您自定义的 com.example.intentdemo.MY_ACTION),系统将不会将其传递给静态注册的接收器。
我们需要显示的告诉系统,我们的广播是发给程序自己的。
接收广播消息
BroadcastReceiver用于监听广播消息,可以在AndroidManifest.xml文件或代码中注册一个BroadcastReceiver,并使用Intent过滤器指定要处理的广播消息。
静态注册
创建一个BroadcastReceiver类
1
2
3
4
5
6
7
8public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String message = intent.getStringExtra("massage");
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
}注册MyReceiver
1
2
3
4
5
6
7
8<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.intentdemo.MY_ACTION" />
</intent-filter>
</receiver>
动态注册
我们也可以在Activity的生命周期中动态的注册和注销广播接收器。
首先在Activity类中定义一个广播类:
1 | |
在生命周期中:
1 | |
在安卓13(API 33)后,registerReceiver中必须显示指出接收器的范围:
| 常量 | 含义 |
|---|---|
Context.RECEIVER_EXPORTED |
明确允许此接收器接收来自应用外部的广播。如果您需要与其他应用通信,则必须使用此标志。 |
Context.RECEIVER_NOT_EXPORTED |
不允许此接收器接收来自应用外部的广播。(您代码中使用的,推荐用于私有广播)。 |
动态注销
使用 unregisterReceiver()方法来注销。
1 | |
