[Android]四大组件简介
在 Android 开发中,“四大组件”(Four Major Components)是指构成 Android 应用程序的四种核心组件,它们通过各自的方式与系统交互,实现应用的多样功能。这些组件是:Activity、Service、Broadcast Receiver 和 Content Provider。每个组件都扮演着不同的角色,并且通过各自的生命周期、方法和目的与 Android 操作系统交互。
一、Activity
在 Android 应用中,Activity 是一个非常核心的组件,用于表示应用的一个单一屏幕,是用户与应用交互的主界面。每个 Activity 提供一个窗口,用于绘制界面和接收与用户的交互事件。理解 Activity 的创建、生命周期和其基本用法对于开发 Android 应用至关重要。
基本概念
一个 Android 应用通常由多个 Activity 组成,每个 Activity 都是一个独立的界面。当你打开一个应用,如邮箱应用,你看到的邮箱列表、邮件详情、写邮件等各个界面,通常都是不同的 Activity。
生命周期
Activity 的生命周期是其最重要的特征之一。Android 提供了一系列的回调方法来管理 Activity 的状态,包括用户开始使用 Activity、Activity 进入前台或后台,以及 Activity 被系统销毁的时刻。
下面是 Activity 生命周期的主要方法:
onCreate(Bundle savedInstanceState): 当Activity被创建时调用。这是初始化界面、成员变量等的地方。onStart(): 当Activity对用户可见时调用。onResume(): 当Activity准备好与用户交互时调用,此时Activity位于前台。onPause(): 当系统即将启动或恢复另一个Activity时调用。用于保存数据或释放资源。onStop(): 当Activity不再对用户可见时调用。onDestroy(): 当Activity即将被销毁时调用。

示例代码
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivityclass MainActivity : AppCompatActivity() {// 当 Activity 被创建时调用override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 设置 Activity 的布局文件setContentView(R.layout.activity_main)// 进行初始化操作,比如从 Bundle 恢复数据if (savedInstanceState != null) {val savedValue = savedInstanceState.getString("key")// 使用恢复的数据}}// 当 Activity 开始对用户可见时调用override fun onStart() {super.onStart()}// 当 Activity 准备好与用户交互时调用override fun onResume() {super.onResume()}// 当 Activity 即将停止与用户交互时调用override fun onPause() {super.onPause()// 保存数据或释放资源}// 当 Activity 不再完全可见时调用override fun onStop() {super.onStop()}// 当 Activity 即将被销毁时调用override fun onDestroy() {super.onDestroy()}// 保存 Activity 状态override fun onSaveInstanceState(outState: Bundle) {super.onSaveInstanceState(outState)outState.putString("key", "value")}
}
注意事项
- 管理资源:由于 Android 设备的资源有限,合理管理
Activity的资源非常重要。例如,在onPause()或onStop()中释放那些不需要的资源。 - 状态保存与恢复:Android 系统可能因为资源不足等原因随时终止
Activity。因此,在onSaveInstanceState(Bundle outState)中保存重要状态,并在onCreate(Bundle savedInstanceState)中恢复状态是很重要的。 - 用户界面响应:保证
Activity响应用户的操作,避免在主线程(UI线程)进行耗时操作,这样可以防止应用界面冻结。
二、Service
在 Android 开发中,Service 是一种用于在后台执行长时间运行的任务而不提供用户界面的应用组件。Service 可以在应用的前台或者后台执行任务,即使用户离开了应用。服务是用来处理不需要与用户交互而需要长期运行的操作,例如在后台播放音乐、执行文件下载等。
基本类型
Service 主要有两种形式:
- 前台服务(Foreground Service):前台服务显示一个持续的通知,这意味着用户清楚地知道正在运行的服务。这种服务用于用户积极参与的任务(如播放音乐)或对用户很重要的任务(如文件下载)。
- 后台服务(Background Service):在应用不在屏幕上显示时执行的服务。从 Android Oreo(8.0)开始,后台服务的运行受到了严格限制以优化应用对设备电池生命的影响。
生命周期方法
Service 有自己的生命周期方法,用于管理其创建、启动、绑定和销毁过程:
onCreate(): 服务创建时调用。onStartCommand(Intent intent, int flags, int startId): 每次通过startService()方法启动服务时调用。onBind(Intent intent): 当其他组件想要与服务绑定时调用,需要返回一个IBinder对象,通过该对象组件可以与服务进行通信。onUnbind(Intent intent): 当所有组件都与服务解除绑定时调用。onDestroy(): 服务销毁之前调用。
示例代码
下面是一个简单的服务示例,该服务在后台记录日志。
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Logclass MyService : Service() {// 当服务被创建时调用override fun onCreate() {super.onCreate()Log.d("MyService", "服务已创建")}// 每次服务启动时调用override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {Log.d("MyService", "服务正在运行")// 如果我们希望服务在终止后重启,则返回 START_STICKYreturn START_STICKY}// 当服务被销毁时调用override fun onDestroy() {super.onDestroy()Log.d("MyService", "服务已销毁")}// 当其他组件想要绑定服务时调用override fun onBind(intent: Intent): IBinder? {// 本示例不提供绑定功能,因此返回 nullreturn null}
}
在 AndroidManifest.xml 中注册服务
要使服务能够运行,必须在应用的 AndroidManifest.xml 文件中进行声明:
<application...><service android:name=".MyService" />
</application>
启动和停止服务
你可以从 Activity 或其他组件中启动和停止服务。
val intent = Intent(this, MyService::class.java)
startService(intent) // 启动服务
stopService(intent) // 停止服务
注意事项
- 资源管理:服务可以无限运行,但这可能消耗大量的电池和计算资源。确保服务不会无谓地消耗资源。
- 服务和线程:服务运行在应用的主线程中,因此如果在服务中执行耗时操作,需要手动创建新线程来处理这些操作,以避免阻塞主线程。
- 服务的适用场景:在考虑使用服务之前,评估是否真的需要服务。对于简单的、短暂的后台操作,可以考虑使用
WorkManager或AlarmManager。
三、Broadcast Receiver
在 Android 中,Broadcast Receiver(简称广播接收器)是一个用来处理来自系统或应用发出的广播通知的组件。它可以对诸如设备启动完成、电池电量变化、短信接收等系统事件做出响应,也可以接收应用自定义的广播消息。
基本概念
广播接收器主要用于监听和响应广播消息。广播可以是系统广播(比如网络状态改变、屏幕关闭等),也可以是应用程序发送的广播。广播接收器本身没有用户界面,但它可以启动一个活动或服务来响应接收到的信息。
生命周期和类型
广播接收器不像 Activity 或 Service 那样拥有完整的生命周期。它只有一个回调方法 onReceive(Context context, Intent intent),当接收到广播时被调用。广播接收器的类型分为两种:
- 静态注册:在
AndroidManifest.xml中注册。即使应用没有运行,只要事件发生,系统就会创建广播接收器的实例并调用它。 - 动态注册:在代码中注册,通常在
Activity或Service中注册。它只在其宿主组件(如Activity)存在时活跃。
示例代码
下面是一个静态注册的广播接收器,用于接收设备启动完成后的广播:
AndroidManifest.xml:
<application ... ><receiver android:name=".BootCompletedReceiver"><intent-filter><action android:name="android.intent.action.BOOT_COMPLETED" /></intent-filter></receiver>
</application>
BootCompletedReceiver.kt:
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.widget.Toastclass BootCompletedReceiver : BroadcastReceiver() {override fun onReceive(context: Context, intent: Intent) {// 确认接收到的是设备启动完成的广播if (intent.action == Intent.ACTION_BOOT_COMPLETED) {Toast.makeText(context, "设备启动完成!", Toast.LENGTH_LONG).show()// 可以在这里启动一个服务或进行其他操作}}
}
下面是动态注册的广播接收器示例,用于在 Activity 中监听网络变化:
MainActivity.kt:
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivityclass MainActivity : AppCompatActivity() {private lateinit var networkChangeReceiver: BroadcastReceiveroverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// 初始化广播接收器networkChangeReceiver = object : BroadcastReceiver() {override fun onReceive(context: Context, intent: Intent) {// 检查网络状态变化Toast.makeText(context, "网络状态变化", Toast.LENGTH_SHORT).show()}}// 注册接收器监听网络变化IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION).also {registerReceiver(networkChangeReceiver, it)}}override fun onDestroy() {super.onDestroy()// 动态注册的接收器必须要取消注册unregisterReceiver(networkChangeReceiver)}
}
注意事项
- 性能和资源管理:广播接收器的
onReceive()方法应该尽快完成,不要进行任何耗时操作,以免阻塞主线程。若需要执行较长时间的任务,应该启动一个Service。 - 权限问题:接收某些系统广播可能需要声明相应的权限,比如接收开机广播需要声明
RECEIVE_BOOT_COMPLETED权限。 - 条件广播和有序广播:Android 提供了条件广播和有序广播两种方式。有序广播允许多个接收器按顺序接收到同一个广播,每个接收器可以终止广播,防止它传递给其他接收器。
四、Content Provider
在 Android 中,Content Provider 是四大组件之一,用于在不同应用程序之间共享数据。它提供了一种封装数据的方式,并通过一套标准的 API 在应用之间进行数据访问。通过使用 Content Provider,一个应用可以允许其他应用访问其数据,而不需要直接访问底层数据库或文件系统。
基本概念
Content Provider 管理对一个或多个数据源(如 SQLite 数据库)的访问,通过 URI(统一资源标识符)来暴露数据。每个 URI 可以指代 Content Provider 中的数据表或表内的特定数据行。通过这种方式,Content Provider 为数据访问提供了封装,并确保了数据访问的安全性。
核心组件
- URI: 每个
Content Provider都通过一个唯一的authority来标识,该authority与URI结合使用,用来找到对应的Content Provider。 - ContentResolver: 提供了一组 API,允许应用查询或修改由
Content Provider管理的数据。应用通过调用ContentResolver的方法,如query(),insert(),delete(), 和update()来执行操作。
创建一个 Content Provider
创建一个 Content Provider 通常包括以下几个步骤:
-
扩展
ContentProvider类:
实现必要的方法:onCreate(),query(),insert(),delete(), 和update()。 -
在
AndroidManifest.xml中声明:
注册Content Provider并定义一个唯一的authority。 -
使用 URI 访问数据:
定义和解析URI来访问Content Provider管理的数据。
示例代码
下面是一个简单的 Content Provider 实现示例:
MyContentProvider.kt:
import android.content.ContentProvider
import android.content.ContentValues
import android.database.Cursor
import android.net.Uriclass MyContentProvider : ContentProvider() {// 初始化内容提供者override fun onCreate(): Boolean {// 初始化数据源等return true}// 查询数据override fun query(uri: Uri,projection: Array<String>?,selection: String?,selectionArgs: Array<String>?,sortOrder: String?): Cursor? {// 根据 uri 查询数据return null // 示例中返回 null,实际使用时返回查询结果的 Cursor}// 插入数据override fun insert(uri: Uri, values: ContentValues?): Uri? {// 插入数据到数据源return null // 示例中返回 null,实际使用时返回新插入数据的 Uri}// 删除数据override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {// 根据 uri 和条件删除数据return 0 // 示例中返回 0,实际使用时返回被删除的行数}// 更新数据override fun update(uri: Uri,values: ContentValues?,selection: String?,selectionArgs: Array<String>?): Int {// 根据 uri 更新数据return 0 // 示例中返回 0,实际使用时返回被更新的行数}// 返回 MIME 类型override fun getType(uri: Uri): String? {// 根据 uri 返回 MIME 类型return null}
}
AndroidManifest.xml:
<application ... ><providerandroid:name=".MyContentProvider"android:authorities="com.example.myapp.provider"android:exported="true"/>
</application>
使用 Content Provider
其他应用可以通过 ContentResolver 访问 Content Provider:
val contentResolver = getContentResolver()
val uri = Uri.parse("content://com.example.myapp.provider/table_name")
val cursor = contentResolver.query(uri, null, null, null, null)
注意事项
- 权限管理:确保在
AndroidManifest.xml中配置合适的权限,以保护数据不被未授权访问。可以通过android:exported和android:permission控制对Content Provider的访问。 - 线程安全:
Content Provider的方法可能会被多个线程同时调用,因此实现时需要考虑线程安全。 - 性能优化:由于
Content Provider可能会频繁地进行数据库操作,合理设计和优化数据库访问逻辑非常重要,以避免性能瓶颈。
相关文章:
[Android]四大组件简介
在 Android 开发中,“四大组件”(Four Major Components)是指构成 Android 应用程序的四种核心组件,它们通过各自的方式与系统交互,实现应用的多样功能。这些组件是:Activity、Service、Broadcast Receiver…...
一次完整的GC流程
Java堆中内存区分 Java的堆由新生代(Young Generation)和老年代(Old Generation)组成。新生代存放新分配的对象,老年代存放长期存在的对象。 新生代(Young)由年轻区(Eden&a…...
GAME101-Lecture06学习
前言 上节课主要讲的是三角形的光栅化。重要的思想是要利用像素的中心对三角形可见性的函数进行采样。 这节课主要就是反走样。 课程链接:Lecture 06 Rasterization 2 (Antialiasing and Z-Buffering)_哔哩哔哩_bilibili 反走样引入 通过采样,得到…...
202203青少年软件编程(Python)等级考试试卷(二级)
第 1 题 【单选题】 关于Python中的列表,下列描述错误的是?( ) A :列表是Python中内置可变序列,是若干元素的有序集合; B :列表中的每一个数据称为“元素”; C :在Python中,一个列表中的数据类型可以各不相同; D :可以使用s[1]来获取列表s的第一个元素。 正确答案…...
带有-i选项的sed命令在Linux上执行成功,但在MacOS上失败了
问题: 我已经成功地使用以下 sed 命令在Linux中搜索/替换文本: sed -i s/old_string/new_string/g /path/to/file然而,当我在Mac OS X上尝试时,我得到: command i expects \ followed by text我以为我的Mac运行的是…...
[Linux_IMX6ULL驱动开发]-GPIO子系统和Pinctrl子系统
目录 Pinctrl子系统的概念 GPIO子系统的概念 定义自己的GPIO节点 GPIO子系统的函数 引脚号的确定 基于GPIO子系统的驱动程序 驱动程序 设备树修改 之前我们进行驱动开发的时候,对于硬件的操作是依赖于ioremap对寄存器的物理地址进行映射,以此来达…...
Elasticsearch:理解人工智能相似性搜索
理解相似性搜索(也称为语义搜索)的指南,这是人工智能最新阶段的关键发现之一。 最新阶段人工智能的关键发现之一是根据相似性搜索和查找文档的能力。相似性搜索是一种比较信息的方法,其基于含义而非关键字。 相似性搜索也被称为语…...
Mac YOLO V9推理测试(基于ultralytics)
环境: Mac M1 (MacOS Sonoma 14.3.1) Python 3.11PyTorch 2.1.2 一、准备工作 使用YOLO一般都会接触ultralytics这个框架,今天来试试用该框架进行YOLO V9模型的推理。 YOLOv9目前提供了四种模型下载:yolov9-c.pt、yolov9-e.pt、gelan-c.p…...
OuterClass.this cannot be referenced from a static context
目标,定义了一个内部类,然后把这个内部类设置为单例 一 使用非静态内部类 public class OuterClass {public class InnerClass {} } 直接定义单例: .OuterClass.this cannot be referenced from a static context public class OuterClass …...
CAP与BASE分布式理论
一、分布式理论 1.CAP理论 CAP理论是说对于分布式数据存储,最多只能同时满足一致性(C,Consistency)、可用性(A, Availability)、分区容忍性(P,Partition Tolerance&…...
JavaScript性能优化策略
JavaScript性能优化策略可以分为以下几个方面: 减少内存使用:避免创建不必要的对象和数组,使用对象池或数组缓存来重复利用已有的对象和数组。此外,及时释放不再需要的对象和数组,避免内存泄漏。 减少重绘和回流&…...
curl访问流式非流式大模型openai api接口
参考:https://platform.openai.com/docs/api-reference/making-requests 命令行访问: 直接是vllm的openai api接口 curl http://192.168.***:10860/v1/chat/completions -H "Content-Type: application/json" -H "Authorization: EMPTY" -d {"mod…...
Go 使用 MongoDB
MongoDB 安装(Docker)安装 MongoDB Go 驱动使用 Go Driver 连接到 MongoDB在 Go 里面使用 BSON 对象CRUD 操作 插入文档更新文档查询文档删除文档 下一步 MongoDB 安装(Docker) 先装个 mongo,为了省事就用 docker 了。 docker 的 daemon.json 加一个国内的源地址…...
什么是g++-arm-linux-gnueabihf
2024年5月3日,周五晚上 g-arm-linux-gnueabihf 是针对 ARM 架构(ARMv7 和 ARMv8)的 Linux 系统开发的 GNU C 编译器套件,可以在 x86 或 x86_64 架构的主机上使用,用于交叉编译 ARM Linux 应用程序和库。 与 gcc-arm-l…...
Unity延时触发的几种常规方法
目录 1、使用协程Coroutine2、使用Invoke、InvokeRepeating函数3、使用Time.time4、使用Time.deltaTime5、使用DOTween。6、使用Vision Timer。 1、使用协程Coroutine public class Test : MonoBehaviour {// Start is called before the first frame updatevoid Start(){ …...
CSS文字描边,文字间隔,div自定义形状切割
clip-path: polygon( 0 0, 68% 0, 100% 32%, 100% 100%, 0 100% );//这里切割出来是少一角的正方形 letter-spacing: 1vw; //文字间隔 -webkit-text-stroke: 1px #fff; //文字描边1px uniapp微信小程序顶部导航栏设置透明,下拉改变透明度 onP…...
XWiki 服务没有正确部署在tomcat中,如何尝试手动重新部署?
1. 停止 Tomcat 服务 首先,您需要停止正在运行的 Tomcat 服务器,以确保在操作文件时不会发生冲突或数据损坏: sudo systemctl stop tomcat2. 清空 webapps 下的 xwiki 目录和 work 目录中相关的缓存 删除 webapps 下的 xwiki 目录和 work …...
【退役之重学Java】关于 Redis
一、Redis 都有哪些数据类型 String 最基本的类型,普通的set和get,做简单的kv缓存hash 这是一个类似map 的一种结构,这个一般可以将结构化的数据,比如一个对象(前提是这个对象没有嵌套其他的对象)给缓存在…...
DateKit
目录 1、 DateKit 1.1、 DaysBetween 1.2、 compareDate 1.3、 dateFormat 1.4、 birthdayFormat 1.5、 getYesterday...
百度智能云数据仓库 Palo 实战课程
通过本课程,您将学习如何使用 Palo 构建高性能、低延迟的分布式数仓服务,掌握数据建模、数据导入、查询优化和系统调优等技能,掌握如何管理和运维 Palo 集群,提高数据处理和分析的效率。同时,我们将进一步向您介绍 Pal…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
