[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…...

服务端JavaScript(Node.js)与去IO编程:Node.js的事件驱动和非阻塞IO模型,它是如何使JavaScript走向后端的
在Node.js中,JavaScript代码运行在V8引擎上。由于JavaScript是单线程语言,一次只能处理一个事件。为了解决这个问题,Node.js引入了事件驱动模型。每个进行IO操作的函数都是异步的,当这个函数被调用的时候,它不会立即执…...

一键局域网共享工具
一键局域网共享工具:实现文件快速共享的新选择 在数字化时代,文件共享已成为我们日常工作和生活中的重要需求。无论是在家庭还是在办公环境中,我们经常需要在不同的设备之间传输文件。为了满足这一需求,一键局域网共享工具应运而…...

python实现把doc文件批量转化为docx
python实现把doc文件批量转化为docx import os from win32com import client as wcdef doSaveAas(doc_path,docx_path):#该函数参考https://blog.csdn.net/m0_38074612/article/details/128985384word wc.Dispatch(Word.Application)doc word.Documents.Open(doc_path) # 目…...

WEB基础---反射
什么是反射 相对官方解释 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力; 在运行时期,动态地去获取类中的信息(类的信息,方法信息,构造器信息,字段等信息); 在运行的时候获取到的类信息 封装一个字节码对象…...

impdp恢复表后发现比原表多了100多行
因客户删除数据,恢复表时发现恢复表后发现比原表多了100多行,啥原因暂不清楚,继续学习 [oraclehydb ~]$ more expdp_orcl_20240406_2100.log |grep "USR_HY"."T_COPIES". . exported "USR_HY"."T_COPIES…...

Jupyter配置远程访问的密码
安装 下载Anaconda的.sh文件后,上传到服务器,然后进行安装: chmod x anaconda.sh ./anaconda.sh创建虚拟环境 可以指定Python版本创建虚拟环境: conda create --name langchain python3.11.7 conda activate langchain conda …...

Windows下通过MySQL Installer安装MySQL服务
在Windows下,使用MySQL Installer来安装MySQL服务是一个相对简单的过程。以下是一步一步的详细指南: 下载MySQL Installer: 访问MySQL官方网站(https://www.mysql.com/downloads/),在下载页面选择合适的MyS…...

C语言 [力扣]详解环形链表和环形链表II
各位友友们,好久不见呀!又到了我们相遇的时候,每次相遇都是一种缘分。但我更加希望我的文章可以帮助到大家。下面就来具体看看今天所要讲的题目。 文章目录 1.环形链表2.环形链表II 1.环形链表 题目描述:https://leetcode.cn/problems/link…...

Threejs 学习笔记 | 灯光与阴影
文章目录 Threejs 学习笔记 | 灯光与阴影如何让灯光照射在物体上有阴影LightShadow - 阴影类的基类平行光的shadow计算投影属性 - DirectionalLightShadow类平行光的投射相机 聚光灯的shadow计算投影属性- SpotLightShadow类聚光灯的投射相机 平行光 DirectionalLight聚光灯 Sp…...

SSH:安全远程访问的基石
SSH:安全远程访问的基石 一、引言 在当今这个数字化、网络化的时代,远程访问和管理计算机资源已成为日常工作的重要组成部分。然而,如何在不安全的网络环境中确保数据传输的机密性、完整性和可靠性,成为了一个亟待解决的问题。S…...