Android15 ContentProvider 深度源码分析(下)
目录
- ContactsProvider深度分析
- CalendarProvider深度分析
- 四大Provider综合对比
- 高级数据共享示例
- [ContentProvider 创建流程分析](#ContentProvider 创建流程分析)
- 组件启动顺序对比分析
11. ContactsProvider深度分析
11.1 概述
ContactsProvider是Android系统中管理联系人数据的核心组件,提供了联系人、通话记录、语音邮件等数据的存储和访问功能。它采用了复杂的三层数据模型设计,支持多账户联系人聚合。
源码位置 : packages/providers/ContactsProvider/src/com/android/providers/contacts/
11.2 AndroidManifest.xml配置
xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.providers.contacts"
android:sharedUserId="android.uid.shared"
android:sharedUserLabel="@string/sharedUserLabel">
<!-- 权限声明 -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<application android:process="android.process.acore">
<!-- 联系人Provider -->
<provider android:name="ContactsProvider2"
android:authorities="contacts;com.android.contacts"
android:multiprocess="false"
android:exported="true"
android:grantUriPermissions="true"
android:readPermission="android.permission.READ_CONTACTS"
android:writePermission="android.permission.WRITE_CONTACTS">
<!-- 搜索建议的路径权限 -->
<path-permission
android:pathPrefix="/search_suggest_query"
android:readPermission="android.permission.GLOBAL_SEARCH" />
</provider>
<!-- 通话记录Provider -->
<provider android:name="CallLogProvider"
android:authorities="call_log"
android:exported="true"
android:readPermission="android.permission.READ_CALL_LOG"
android:writePermission="android.permission.WRITE_CALL_LOG" />
<!-- 语音邮件Provider -->
<provider android:name="VoicemailContentProvider"
android:authorities="com.android.voicemail"
android:exported="true" />
</application>
</manifest>
11.3 三层数据模型架构
ContactsProvider采用了独特的三层数据模型:
aggregates
contains
belongs to
has type
1 1 * * * * 1 1 Contacts
+_id: long
+lookup_key: String
+display_name: String
+photo_id: long
+photo_uri: String
+starred: int
+pinned: int
+last_time_contacted: long
+times_contacted: int
+contact_last_updated_timestamp: long
RawContacts
+_id: long
+contact_id: long
+account_name: String
+account_type: String
+source_id: String
+version: int
+dirty: int
+deleted: int
+sync1~sync4: String
+starred: int
+pinned: int
Data
+_id: long
+raw_contact_id: long
+mimetype_id: long
+is_primary: int
+is_super_primary: int
+data1~data15: String
+data_sync1~data_sync4: String
MimeTypes
+_id: long
+mimetype: String
Accounts
+_id: long
+account_name: String
+account_type: String
+data_set: String
11.4 数据库表结构
11.4.1 核心表定义
java
public interface Tables {
String CONTACTS = "contacts"; // 聚合后的联系人
String RAW_CONTACTS = "raw_contacts"; // 原始联系人(每个账户一条)
String DATA = "data"; // 联系人详细数据
String MIMETYPES = "mimetypes"; // MIME类型定义
String ACCOUNTS = "accounts"; // 账户信息
String GROUPS = "groups"; // 联系人分组
String PHONE_LOOKUP = "phone_lookup"; // 电话号码查找索引
String NAME_LOOKUP = "name_lookup"; // 姓名查找索引
String AGGREGATION_EXCEPTIONS = "agg_exceptions"; // 聚合例外
String SETTINGS = "settings"; // 联系人设置
String PRESENCE = "presence"; // 在线状态
String STATUS_UPDATES = "status_updates"; // 状态更新
String PHOTO_FILES = "photo_files"; // 照片文件
String DIRECTORIES = "directories"; // 目录(企业联系人等)
String SEARCH_INDEX = "search_index"; // 全文搜索索引
String DELETED_CONTACTS = "deleted_contacts"; // 已删除联系人
}
11.4.2 Data表的MIME类型
java
// 结构化姓名
public static final String CONTENT_ITEM_TYPE_STRUCTURED_NAME =
"vnd.android.cursor.item/name";
// 电话号码
public static final String CONTENT_ITEM_TYPE_PHONE =
"vnd.android.cursor.item/phone_v2";
// 电子邮件
public static final String CONTENT_ITEM_TYPE_EMAIL =
"vnd.android.cursor.item/email_v2";
// 邮政地址
public static final String CONTENT_ITEM_TYPE_POSTAL =
"vnd.android.cursor.item/postal-address_v2";
// 组织信息
public static final String CONTENT_ITEM_TYPE_ORGANIZATION =
"vnd.android.cursor.item/organization";
// 照片
public static final String CONTENT_ITEM_TYPE_PHOTO =
"vnd.android.cursor.item/photo";
// 群组成员
public static final String CONTENT_ITEM_TYPE_GROUP_MEMBERSHIP =
"vnd.android.cursor.item/group_membership";
11.5 URI设计和路由机制
java
public class ContactsProvider2 extends AbstractContactsProvider {
// URI匹配码定义
public static final int CONTACTS = 1000;
public static final int CONTACTS_ID = 1001;
public static final int CONTACTS_LOOKUP = 1002;
public static final int CONTACTS_LOOKUP_ID = 1003;
public static final int CONTACTS_ID_DATA = 1004;
public static final int CONTACTS_FILTER = 1005;
public static final int CONTACTS_ID_PHOTO = 1009;
public static final int RAW_CONTACTS = 2002;
public static final int RAW_CONTACTS_ID = 2003;
public static final int RAW_CONTACTS_ID_DATA = 2004;
public static final int DATA = 3000;
public static final int DATA_ID = 3001;
public static final int PHONES = 3002;
public static final int PHONES_FILTER = 3004;
public static final int EMAILS = 3005;
public static final int EMAILS_FILTER = 3008;
public static final int PHONE_LOOKUP = 4000;
public static final int GROUPS = 10000;
public static final int GROUPS_ID = 10001;
// URI匹配器初始化
static {
// 联系人URI
matcher.addURI(AUTHORITY, "contacts", CONTACTS);
matcher.addURI(AUTHORITY, "contacts/#", CONTACTS_ID);
matcher.addURI(AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
matcher.addURI(AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
matcher.addURI(AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
matcher.addURI(AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
matcher.addURI(AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
// 原始联系人URI
matcher.addURI(AUTHORITY, "raw_contacts", RAW_CONTACTS);
matcher.addURI(AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
matcher.addURI(AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_ID_DATA);
// 数据URI
matcher.addURI(AUTHORITY, "data", DATA);
matcher.addURI(AUTHORITY, "data/#", DATA_ID);
matcher.addURI(AUTHORITY, "data/phones", PHONES);
matcher.addURI(AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
matcher.addURI(AUTHORITY, "data/emails", EMAILS);
matcher.addURI(AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
// 电话查找
matcher.addURI(AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
}
}
11.6 URI结构示例
# 联系人相关
content://contacts/contacts # 所有聚合联系人
content://contacts/contacts/123 # 指定ID的联系人
content://contacts/contacts/lookup/0r1-xxx # 通过lookup_key查找
content://contacts/contacts/123/data # 联系人的所有数据
content://contacts/contacts/123/photo # 联系人照片
content://contacts/contacts/filter/john # 按名字筛选
# 原始联系人相关
content://contacts/raw_contacts # 所有原始联系人
content://contacts/raw_contacts/456 # 指定原始联系人
content://contacts/raw_contacts/456/data # 原始联系人的数据
# 数据相关
content://contacts/data # 所有数据
content://contacts/data/phones # 所有电话号码
content://contacts/data/phones/filter/1380000 # 按电话筛选
content://contacts/data/emails # 所有邮箱
content://contacts/data/emails/filter/test@ # 按邮箱筛选
# 电话查找(反向查找联系人)
content://contacts/phone_lookup/13800000000 # 通过电话查联系人
11.7 联系人聚合机制
匹配规则
找到匹配
未找到
新建RawContact
触发聚合
查找匹配的Contact
合并到现有Contact
创建新Contact
更新Contact显示名
更新聚合数据
更新搜索索引
姓名完全匹配
电话号码匹配
邮箱匹配
账户相同
java
// ContactAggregator2.java 聚合算法核心
public class ContactAggregator2 extends AbstractContactAggregator {
// 匹配分数阈值
private static final int SCORE_THRESHOLD_PRIMARY = 70;
private static final int SCORE_THRESHOLD_SECONDARY = 50;
// 聚合过程
public void aggregateContact(SQLiteDatabase db, long rawContactId) {
// 1. 获取原始联系人的匹配数据
MatchCandidateList candidates = new MatchCandidateList();
updateMatchCandidatesBasedOnName(db, rawContactId, candidates);
updateMatchCandidatesBasedOnPhoneNumber(db, rawContactId, candidates);
updateMatchCandidatesBasedOnEmail(db, rawContactId, candidates);
// 2. 计算匹配分数
for (MatchCandidate candidate : candidates) {
candidate.mScore = computeScore(db, rawContactId, candidate);
}
// 3. 选择最佳匹配
long bestMatchContactId = pickBestMatchBasedOnScore(candidates);
// 4. 执行聚合
if (bestMatchContactId != -1) {
setContactId(rawContactId, bestMatchContactId);
} else {
createContact(rawContactId);
}
}
}
11.8 ContactsProvider类图
ContactsProvider2
-ContactsDatabaseHelper mDbHelper
-ContactAggregator mContactAggregator
-SearchIndexManager mSearchIndexManager
-ContactDirectoryManager mDirectoryManager
+onCreate() : boolean
+query() : Cursor
+insert() : Uri
+update() : int
+delete() : int
+openAssetFile() : AssetFileDescriptor
<<abstract>>
AbstractContactsProvider
#Context mContext
#ProfileAwareUriMatcher mMatcher
+getType() : String
+applyBatch() : ContentProviderResult[]
ContactsDatabaseHelper
-SQLiteDatabase mDatabase
+getReadableDatabase() : SQLiteDatabase
+getWritableDatabase() : SQLiteDatabase
+onCreate() : void
+onUpgrade() : void
ContactAggregator2
-ContactsDatabaseHelper mDbHelper
+aggregateContact() : void
+computeScore() : int
+pickBestMatch() : long
SearchIndexManager
-ContactsDatabaseHelper mDbHelper
+updateIndex() : void
+query() : Cursor
ContactDirectoryManager
+scanAllDirectories() : void
+getDirectoryProviders() : List
CallLogProvider
-CallLogDatabaseHelper mDbHelper
+query() : Cursor
+insert() : Uri
ContentProvider
11.9 数据存储位置
/data/data/com.android.providers.contacts/databases/
contacts2.db # 联系人数据库
profile.db # 用户个人资料数据库
calllog.db # 通话记录数据库
/data/data/com.android.providers.contacts/files/
photos/ # 联系人照片目录
<photo_file_id> # 照片文件
12. CalendarProvider深度分析
12.1 概述
CalendarProvider是Android系统中管理日历和事件数据的组件,支持多账户日历同步、重复事件、提醒等功能。
源码位置 : packages/providers/CalendarProvider/src/com/android/providers/calendar/
12.2 AndroidManifest.xml配置
xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.providers.calendar"
android:sharedUserId="android.uid.calendar">
<!-- 权限声明 -->
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<application android:label="@string/calendar_storage">
<provider android:name="CalendarProvider2"
android:authorities="com.android.calendar"
android:multiprocess="false"
android:exported="true"
android:readPermission="android.permission.READ_CALENDAR"
android:writePermission="android.permission.WRITE_CALENDAR" />
<!-- 闹钟接收器 -->
<receiver android:name="CalendarReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- 提醒广播接收器 -->
<receiver android:name="CalendarProviderBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.EVENT_REMINDER" />
</intent-filter>
</receiver>
</application>
</manifest>
12.3 数据库表结构
java
public interface Tables {
String CALENDARS = "Calendars"; // 日历表
String EVENTS = "Events"; // 事件表
String EVENTS_RAW_TIMES = "EventsRawTimes"; // 事件原始时间
String INSTANCES = "Instances"; // 事件实例(展开重复事件)
String ATTENDEES = "Attendees"; // 参与者
String REMINDERS = "Reminders"; // 提醒
String CALENDAR_ALERTS = "CalendarAlerts"; // 日历提醒
String EXTENDED_PROPERTIES = "ExtendedProperties"; // 扩展属性
String CALENDAR_META_DATA = "CalendarMetaData"; // 元数据
String CALENDAR_CACHE = "CalendarCache"; // 缓存
String COLORS = "Colors"; // 颜色定义
String SYNC_STATE = "_sync_state"; // 同步状态
}
public interface Views {
String EVENTS = "view_events"; // 事件视图
}
12.4 数据库ER图
contains
has
has
generates
has
triggers
uses
uses
Calendars
long
_id
PK
string
account_name
string
account_type
string
name
string
calendar_displayName
int
calendar_color
string
calendar_color_key
int
calendar_access_level
int
visible
int
sync_events
string
calendar_location
string
calendar_timezone
string
ownerAccount
int
isPrimary
Events
long
_id
PK
long
calendar_id
FK
string
title
string
description
string
eventLocation
int
eventColor
string
eventColor_key
int
eventStatus
int
selfAttendeeStatus
long
dtstart
long
dtend
string
eventTimezone
string
duration
int
allDay
int
accessLevel
int
availability
int
hasAlarm
string
rrule
string
rdate
string
exrule
string
exdate
long
original_id
string
original_sync_id
long
originalInstanceTime
int
originalAllDay
long
lastDate
int
hasAttendeeData
int
guestsCanModify
int
guestsCanInviteOthers
int
guestsCanSeeGuests
string
organizer
int
isOrganizer
string
uid2445
int
dirty
int
deleted
string
_sync_id
Attendees
long
_id
PK
long
event_id
FK
string
attendeeName
string
attendeeEmail
int
attendeeStatus
int
attendeeRelationship
int
attendeeType
string
attendeeIdentity
string
attendeeIdNamespace
Reminders
long
_id
PK
long
event_id
FK
int
minutes
int
method
Instances
long
_id
PK
long
event_id
FK
long
begin
long
end
int
startDay
int
endDay
int
startMinute
int
endMinute
ExtendedProperties
CalendarAlerts
long
_id
PK
long
event_id
FK
long
begin
long
end
long
alarmTime
long
creationTime
long
receivedTime
long
notifyTime
int
state
int
minutes
Colors
12.5 URI设计
java
public class CalendarProvider2 extends SQLiteContentProvider {
// URI匹配码
private static final int CALENDARS = 1;
private static final int CALENDARS_ID = 2;
private static final int EVENTS = 3;
private static final int EVENTS_ID = 4;
private static final int INSTANCES = 5;
private static final int INSTANCES_BY_DAY = 6;
private static final int ATTENDEES = 7;
private static final int ATTENDEES_ID = 8;
private static final int REMINDERS = 9;
private static final int REMINDERS_ID = 10;
private static final int CALENDAR_ALERTS = 11;
private static final int CALENDAR_ALERTS_ID = 12;
private static final int CALENDAR_ALERTS_BY_INSTANCE = 13;
private static final int EXTENDED_PROPERTIES = 14;
private static final int EXTENDED_PROPERTIES_ID = 15;
private static final int COLORS = 19;
// URI匹配器初始化
static {
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(AUTHORITY, "calendars", CALENDARS);
sUriMatcher.addURI(AUTHORITY, "calendars/#", CALENDARS_ID);
sUriMatcher.addURI(AUTHORITY, "events", EVENTS);
sUriMatcher.addURI(AUTHORITY, "events/#", EVENTS_ID);
sUriMatcher.addURI(AUTHORITY, "instances/when/*/*", INSTANCES);
sUriMatcher.addURI(AUTHORITY, "instances/groupbyday/*/*", INSTANCES_BY_DAY);
sUriMatcher.addURI(AUTHORITY, "attendees", ATTENDEES);
sUriMatcher.addURI(AUTHORITY, "attendees/#", ATTENDEES_ID);
sUriMatcher.addURI(AUTHORITY, "reminders", REMINDERS);
sUriMatcher.addURI(AUTHORITY, "reminders/#", REMINDERS_ID);
sUriMatcher.addURI(AUTHORITY, "calendar_alerts", CALENDAR_ALERTS);
sUriMatcher.addURI(AUTHORITY, "calendar_alerts/#", CALENDAR_ALERTS_ID);
sUriMatcher.addURI(AUTHORITY, "extendedproperties", EXTENDED_PROPERTIES);
sUriMatcher.addURI(AUTHORITY, "extendedproperties/#", EXTENDED_PROPERTIES_ID);
sUriMatcher.addURI(AUTHORITY, "colors", COLORS);
}
}
12.6 URI结构示例
# 日历相关
content://com.android.calendar/calendars # 所有日历
content://com.android.calendar/calendars/1 # 指定日历
# 事件相关
content://com.android.calendar/events # 所有事件
content://com.android.calendar/events/123 # 指定事件
# 实例(展开重复事件)
content://com.android.calendar/instances/when/<begin>/<end>
content://com.android.calendar/instances/groupbyday/<begin>/<end>
# 参与者
content://com.android.calendar/attendees # 所有参与者
content://com.android.calendar/attendees/456 # 指定参与者
# 提醒
content://com.android.calendar/reminders # 所有提醒
content://com.android.calendar/reminders/789 # 指定提醒
# 日历提醒
content://com.android.calendar/calendar_alerts # 所有提醒
content://com.android.calendar/calendar_alerts/101 # 指定提醒
# 颜色
content://com.android.calendar/colors # 颜色定义
12.7 重复事件处理流程
是
否
是
否
RRULE示例
FREQ=WEEKLY;BYDAY=MO,WE,FR
FREQ=MONTHLY;BYMONTHDAY=15
FREQ=YEARLY;BYMONTH=12;BYMONTHDAY=25
创建重复事件
保存RRULE到Events表
触发实例展开
CalendarInstancesHelper
解析RRULE规则
计算时间范围内的实例
遍历每个实例
检查EXDATE排除
被排除?
插入Instances表
还有更多实例?
展开完成
更新提醒
调度闹钟
12.8 CalendarProvider类图
CalendarProvider2
-CalendarDatabaseHelper mDbHelper
-CalendarInstancesHelper mInstancesHelper
-CalendarAlarmManager mCalendarAlarm
-MetaData mMetaData
+onCreate() : boolean
+query() : Cursor
+insert() : Uri
+update() : int
+delete() : int
<<abstract>>
SQLiteContentProvider
#SQLiteOpenHelper mOpenHelper
+applyBatch() : ContentProviderResult[]
#beforeTransactionCommit() : void
CalendarDatabaseHelper
-SyncStateContentProviderHelper mSyncState
+calendarsInsert() : long
+eventsInsert() : long
+instancesInsert() : long
+attendeesInsert() : long
+remindersInsert() : long
CalendarInstancesHelper
-CalendarDatabaseHelper mDbHelper
+performInstanceExpansion() : void
+updateInstancesLocked() : void
+acquireInstanceRange() : void
CalendarAlarmManager
-AlarmManager mAlarmManager
+scheduleNextAlarm() : void
+checkNextAlarm() : void
+removeAlarms() : void
MetaData
+localTimezone: String
+minInstance: long
+maxInstance: long
RecurrenceProcessor
+expand() : long[]
+getLastOccurrence() : long
12.9 同步机制
Cloud Server CalendarDatabaseHelper CalendarProvider2 SyncAdapter Cloud Server CalendarDatabaseHelper CalendarProvider2 SyncAdapter 请求日历数据 返回日历和事件 insert/update Calendars 写入Calendars表 insert/update Events 写入Events表 触发实例展开 更新提醒调度 查询dirty=1的事件 返回本地修改 上传本地修改 确认 清除dirty标记 更新dirty=0
12.10 数据存储位置
/data/data/com.android.providers.calendar/databases/
calendar.db # 日历数据库
13. 四大Provider综合对比
13.1 基本信息对比
| 特性 | SettingsProvider | MediaProvider | ContactsProvider | CalendarProvider |
|---|---|---|---|---|
| Authority | settings | media | contacts;com.android.contacts | com.android.calendar |
| 包名 | com.android.providers.settings | com.android.providers.media.module | com.android.providers.contacts | com.android.providers.calendar |
| 进程 | system_server | 独立进程 | android.process.acore | 独立进程 |
| SharedUserId | android.uid.system | - | android.uid.shared | android.uid.calendar |
| 数据库 | XML文件 | SQLite (internal.db, external.db) | SQLite (contacts2.db, calllog.db) | SQLite (calendar.db) |
13.2 权限模型对比
| 特性 | SettingsProvider | MediaProvider | ContactsProvider | CalendarProvider |
|---|---|---|---|---|
| 读权限 | 无(公开)/WRITE_SETTINGS | READ_EXTERNAL_STORAGE/READ_MEDIA_* | READ_CONTACTS | READ_CALENDAR |
| 写权限 | WRITE_SETTINGS/WRITE_SECURE_SETTINGS | WRITE_EXTERNAL_STORAGE | WRITE_CONTACTS | WRITE_CALENDAR |
| 签名权限 | 是(secure/global) | 部分 | 否 | 否 |
| URI权限 | 否 | 是 | 是 | 否 |
| 路径权限 | 否 | 否 | 是(搜索建议) | 否 |
| 运行时权限 | 否 | 是(Android 6.0+) | 是(Android 6.0+) | 是(Android 6.0+) |
13.3 数据模型对比
| 特性 | SettingsProvider | MediaProvider | ContactsProvider | CalendarProvider |
|---|---|---|---|---|
| 模型复杂度 | 简单(键值对) | 中等(单表+视图) | 复杂(三层模型) | 中等(多表关联) |
| 主要表数 | 无(XML) | 1主表+多视图 | 10+表 | 8+表 |
| 数据关系 | 无 | 文件-元数据 | Contact-RawContact-Data | Calendar-Event-Instance |
| 聚合机制 | 无 | 无 | 有(联系人聚合) | 有(实例展开) |
| 搜索索引 | 无 | 无 | 有(FTS) | 无 |
13.4 性能优化对比
| 特性 | SettingsProvider | MediaProvider | ContactsProvider | CalendarProvider |
|---|---|---|---|---|
| call()优化 | 是(主要方式) | 部分 | 否 | 否 |
| 批量操作 | 否 | 是 | 是 | 是 |
| 缓存机制 | 内存缓存 | 缩略图缓存 | 无 | 实例缓存 |
| 延迟加载 | 否 | 是(媒体扫描) | 是 | 是(实例展开) |
| WAL模式 | 否 | 是 | 是 | 是 |
13.5 同步特性对比
| 特性 | SettingsProvider | MediaProvider | ContactsProvider | CalendarProvider |
|---|---|---|---|---|
| 支持同步 | 否 | 否 | 是 | 是 |
| SyncAdapter | 否 | 否 | 是 | 是 |
| 多账户 | 否 | 否 | 是 | 是 |
| 脏数据标记 | 否 | 否 | 是(dirty列) | 是(dirty列) |
| 删除标记 | 否 | 是(is_trashed) | 是(deleted列) | 是(deleted列) |
14. 高级数据共享示例
14.1 共享内存示例 (MemoryFile/SharedMemory)
使用共享内存可以高效地在进程间传递大数据,避免Binder传输限制。
14.1.1 Provider端实现
java
public class SharedMemoryProvider extends ContentProvider {
private static final String AUTHORITY = "com.example.sharedmemory";
private static final int SHARED_DATA = 1;
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sUriMatcher.addURI(AUTHORITY, "shared_data/*", SHARED_DATA);
}
// 缓存大数据
private final Map<String, byte[]> mDataCache = new ConcurrentHashMap<>();
@Override
public boolean onCreate() {
return true;
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
if (sUriMatcher.match(uri) != SHARED_DATA) {
throw new FileNotFoundException("Unknown URI: " + uri);
}
String dataId = uri.getLastPathSegment();
byte[] data = mDataCache.get(dataId);
if (data == null) {
throw new FileNotFoundException("Data not found: " + dataId);
}
try {
// 方式1: 使用MemoryFile (Android 8.0之前推荐)
return createMemoryFileDescriptor(data);
// 方式2: 使用SharedMemory (Android 8.0+推荐)
// return createSharedMemoryDescriptor(data);
} catch (IOException e) {
throw new FileNotFoundException("Failed to create shared memory: " + e.getMessage());
}
}
/**
* 使用MemoryFile创建共享内存
*/
private ParcelFileDescriptor createMemoryFileDescriptor(byte[] data) throws IOException {
// 创建MemoryFile
MemoryFile memoryFile = new MemoryFile("shared_data", data.length);
// 写入数据
memoryFile.writeBytes(data, 0, 0, data.length);
// 获取FileDescriptor (需要反射,因为getFileDescriptor是隐藏方法)
try {
Method method = MemoryFile.class.getDeclaredMethod("getFileDescriptor");
method.setAccessible(true);
FileDescriptor fd = (FileDescriptor) method.invoke(memoryFile);
// 转换为ParcelFileDescriptor
return ParcelFileDescriptor.dup(fd);
} catch (Exception e) {
memoryFile.close();
throw new IOException("Failed to get FileDescriptor", e);
}
}
/**
* 使用SharedMemory创建共享内存 (Android 8.0+)
*/
@RequiresApi(api = Build.VERSION_CODES.O_MR1)
private ParcelFileDescriptor createSharedMemoryDescriptor(byte[] data) throws IOException {
try {
// 创建SharedMemory
SharedMemory sharedMemory = SharedMemory.create("shared_data", data.length);
// 映射内存并写入数据
ByteBuffer buffer = sharedMemory.mapReadWrite();
buffer.put(data);
SharedMemory.unmap(buffer);
// 设置为只读
sharedMemory.setProtect(OsConstants.PROT_READ);
// 获取FileDescriptor
return sharedMemory.getFdDup();
} catch (ErrnoException e) {
throw new IOException("Failed to create SharedMemory", e);
}
}
/**
* 存储数据并返回访问URI
*/
public Uri storeData(String id, byte[] data) {
mDataCache.put(id, data);
Uri uri = Uri.parse("content://" + AUTHORITY + "/shared_data/" + id);
getContext().getContentResolver().notifyChange(uri, null);
return uri;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// 返回可用数据的元信息
if (sUriMatcher.match(uri) == SHARED_DATA) {
String dataId = uri.getLastPathSegment();
byte[] data = mDataCache.get(dataId);
if (data != null) {
MatrixCursor cursor = new MatrixCursor(new String[]{"_id", "size"});
cursor.addRow(new Object[]{dataId, data.length});
return cursor;
}
}
return null;
}
// 其他方法...
@Override public String getType(Uri uri) { return "application/octet-stream"; }
@Override public Uri insert(Uri uri, ContentValues values) { return null; }
@Override public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; }
@Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; }
}
14.1.2 客户端读取共享内存
java
public class SharedMemoryClient {
/**
* 从共享内存读取大数据
*/
public byte[] readSharedData(Context context, Uri dataUri) throws IOException {
ContentResolver resolver = context.getContentResolver();
// 先查询数据大小
Cursor cursor = resolver.query(dataUri, null, null, null, null);
if (cursor == null || !cursor.moveToFirst()) {
throw new IOException("Data not found");
}
int size = cursor.getInt(cursor.getColumnIndex("size"));
cursor.close();
// 打开共享内存
try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(dataUri, "r");
FileInputStream fis = new FileInputStream(pfd.getFileDescriptor())) {
byte[] data = new byte[size];
int bytesRead = 0;
while (bytesRead < size) {
int read = fis.read(data, bytesRead, size - bytesRead);
if (read == -1) break;
bytesRead += read;
}
return data;
}
}
/**
* 使用SharedMemory直接映射读取 (Android 8.0+)
*/
@RequiresApi(api = Build.VERSION_CODES.O_MR1)
public ByteBuffer readSharedDataDirect(Context context, Uri dataUri) throws IOException {
ContentResolver resolver = context.getContentResolver();
try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(dataUri, "r")) {
// 从ParcelFileDescriptor创建SharedMemory
SharedMemory sharedMemory = SharedMemory.fromFileDescriptor(pfd);
// 映射为只读ByteBuffer
return sharedMemory.mapReadOnly();
} catch (ErrnoException e) {
throw new IOException("Failed to map SharedMemory", e);
}
}
}
14.2 文件共享示例
14.2.1 完整的FileProvider实现
java
public class SecureFileProvider extends ContentProvider {
private static final String AUTHORITY = "com.example.files";
private static final int FILE_ID = 1;
private static final int ASSET_ID = 2;
private static final int TYPED_ASSET_ID = 3;
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sUriMatcher.addURI(AUTHORITY, "files/*", FILE_ID);
sUriMatcher.addURI(AUTHORITY, "assets/*", ASSET_ID);
sUriMatcher.addURI(AUTHORITY, "typed/*", TYPED_ASSET_ID);
}
private File mFilesDir;
@Override
public boolean onCreate() {
mFilesDir = new File(getContext().getFilesDir(), "shared");
if (!mFilesDir.exists()) {
mFilesDir.mkdirs();
}
return true;
}
/**
* 基本文件访问 - openFile()
* 用于读写完整文件
*/
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
if (sUriMatcher.match(uri) != FILE_ID) {
throw new FileNotFoundException("Unknown URI: " + uri);
}
// 权限检查
enforcePermission(uri, mode);
String fileName = uri.getLastPathSegment();
File file = new File(mFilesDir, fileName);
// 安全检查: 防止路径遍历攻击
if (!isPathSafe(file)) {
throw new SecurityException("Path traversal attempt detected");
}
int fileMode = parseMode(mode);
// 如果是写模式且文件不存在,创建文件
if ((fileMode & ParcelFileDescriptor.MODE_WRITE_ONLY) != 0 && !file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
throw new FileNotFoundException("Cannot create file: " + e.getMessage());
}
}
return ParcelFileDescriptor.open(file, fileMode);
}
/**
* 资源文件访问 - openAssetFile()
* 支持返回文件的一部分(offset和length)
*/
@Override
public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
if (sUriMatcher.match(uri) == ASSET_ID) {
String assetName = uri.getLastPathSegment();
// 从assets目录读取
try {
return getContext().getAssets().openFd(assetName);
} catch (IOException e) {
// 如果assets中没有,尝试从APK的res/raw读取
int resId = getContext().getResources().getIdentifier(
assetName.replace(".", "_"),
"raw",
getContext().getPackageName());
if (resId != 0) {
return getContext().getResources().openRawResourceFd(resId);
}
throw new FileNotFoundException("Asset not found: " + assetName);
}
}
// 对于普通文件,调用openFile并包装
ParcelFileDescriptor pfd = openFile(uri, mode);
return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH);
}
/**
* 类型化资源访问 - openTypedAssetFile()
* 根据请求的MIME类型返回不同格式的数据
*/
@Override
public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts)
throws FileNotFoundException {
if (sUriMatcher.match(uri) != TYPED_ASSET_ID) {
return super.openTypedAssetFile(uri, mimeTypeFilter, opts);
}
String fileName = uri.getLastPathSegment();
String actualMimeType = getType(uri);
// 检查请求的MIME类型是否匹配
if (!ClipDescription.compareMimeTypes(actualMimeType, mimeTypeFilter)) {
// 尝试转换格式
AssetFileDescriptor converted = tryConvertFormat(uri, mimeTypeFilter, opts);
if (converted != null) {
return converted;
}
throw new FileNotFoundException("Cannot convert to: " + mimeTypeFilter);
}
return openAssetFile(uri, "r");
}
/**
* 尝试转换文件格式
*/
private AssetFileDescriptor tryConvertFormat(Uri uri, String targetMimeType, Bundle opts)
throws FileNotFoundException {
String fileName = uri.getLastPathSegment();
File sourceFile = new File(mFilesDir, fileName);
// 图片格式转换示例
if (targetMimeType.startsWith("image/")) {
try {
Bitmap bitmap = BitmapFactory.decodeFile(sourceFile.getAbsolutePath());
if (bitmap == null) {
return null;
}
// 创建管道用于流式传输
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
// 在后台线程压缩并写入
new Thread(() -> {
try (OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(pipe[1])) {
Bitmap.CompressFormat format;
if (targetMimeType.equals("image/png")) {
format = Bitmap.CompressFormat.PNG;
} else if (targetMimeType.equals("image/webp")) {
format = Bitmap.CompressFormat.WEBP;
} else {
format = Bitmap.CompressFormat.JPEG;
}
bitmap.compress(format, 90, os);
} catch (IOException e) {
Log.e("FileProvider", "Failed to convert image", e);
} finally {
bitmap.recycle();
}
}).start();
return new AssetFileDescriptor(pipe[0], 0, AssetFileDescriptor.UNKNOWN_LENGTH);
} catch (IOException e) {
throw new FileNotFoundException("Conversion failed: " + e.getMessage());
}
}
return null;
}
/**
* 返回支持的MIME类型
*/
@Override
public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
String actualType = getType(uri);
if (actualType == null) {
return null;
}
// 返回支持的类型列表
List<String> types = new ArrayList<>();
types.add(actualType);
// 如果是图片,支持多种格式
if (actualType.startsWith("image/")) {
types.add("image/jpeg");
types.add("image/png");
types.add("image/webp");
}
// 过滤匹配请求的类型
List<String> matching = new ArrayList<>();
for (String type : types) {
if (ClipDescription.compareMimeTypes(type, mimeTypeFilter)) {
matching.add(type);
}
}
return matching.isEmpty() ? null : matching.toArray(new String[0]);
}
@Override
public String getType(Uri uri) {
String fileName = uri.getLastPathSegment();
String extension = MimeTypeMap.getFileExtensionFromUrl(fileName);
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
/**
* 安全检查: 防止路径遍历
*/
private boolean isPathSafe(File file) {
try {
String canonicalBase = mFilesDir.getCanonicalPath();
String canonicalFile = file.getCanonicalPath();
return canonicalFile.startsWith(canonicalBase);
} catch (IOException e) {
return false;
}
}
/**
* 权限检查
*/
private void enforcePermission(Uri uri, String mode) {
int permissionMode = mode.contains("w")
? Intent.FLAG_GRANT_WRITE_URI_PERMISSION
: Intent.FLAG_GRANT_READ_URI_PERMISSION;
if (getContext().checkCallingUriPermission(uri, permissionMode)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Permission denied for URI: " + uri);
}
}
private int parseMode(String mode) {
int modeBits;
if ("r".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
} else if ("w".equals(mode) || "wt".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
| ParcelFileDescriptor.MODE_CREATE
| ParcelFileDescriptor.MODE_TRUNCATE;
} else if ("wa".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
| ParcelFileDescriptor.MODE_CREATE
| ParcelFileDescriptor.MODE_APPEND;
} else if ("rw".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_READ_WRITE
| ParcelFileDescriptor.MODE_CREATE;
} else if ("rwt".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_READ_WRITE
| ParcelFileDescriptor.MODE_CREATE
| ParcelFileDescriptor.MODE_TRUNCATE;
} else {
throw new IllegalArgumentException("Invalid mode: " + mode);
}
return modeBits;
}
// 基本CRUD操作(可选实现)
@Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return null; }
@Override public Uri insert(Uri uri, ContentValues values) { return null; }
@Override public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; }
@Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; }
}
14.2.2 AndroidX FileProvider配置
xml
<!-- AndroidManifest.xml -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
xml
<!-- res/xml/file_paths.xml -->
<?xml version="1.0" encoding="utf-8"?>
<paths>
<!-- 内部存储的files目录 -->
<files-path name="internal_files" path="shared/" />
<!-- 内部存储的cache目录 -->
<cache-path name="cache" path="images/" />
<!-- 外部存储的files目录 -->
<external-files-path name="external_files" path="documents/" />
<!-- 外部存储的cache目录 -->
<external-cache-path name="external_cache" path="temp/" />
<!-- 外部存储根目录 -->
<external-path name="external" path="Download/" />
<!-- 外部媒体目录 -->
<external-media-path name="media" path="pictures/" />
</paths>
14.2.3 使用FileProvider分享文件
java
public class FileShareHelper {
/**
* 分享文件给其他应用
*/
public void shareFile(Context context, File file, String mimeType) {
Uri contentUri = FileProvider.getUriForFile(
context,
context.getPackageName() + ".fileprovider",
file);
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType(mimeType);
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
context.startActivity(Intent.createChooser(shareIntent, "Share via"));
}
/**
* 授权指定应用访问URI
*/
public void grantUriPermission(Context context, File file, String targetPackage) {
Uri contentUri = FileProvider.getUriForFile(
context,
context.getPackageName() + ".fileprovider",
file);
context.grantUriPermission(
targetPackage,
contentUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
/**
* 撤销URI权限
*/
public void revokeUriPermission(Context context, File file) {
Uri contentUri = FileProvider.getUriForFile(
context,
context.getPackageName() + ".fileprovider",
file);
context.revokeUriPermission(
contentUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
}
14.3 网络数据缓存ContentProvider示例
14.3.1 完整实现
java
/**
* 网络数据缓存ContentProvider
* 作为网络请求的本地缓存层,支持:
* 1. 自动缓存网络数据
* 2. 缓存过期策略
* 3. 离线访问
* 4. 数据同步
*/
public class NetworkCacheProvider extends ContentProvider {
private static final String AUTHORITY = "com.example.networkcache";
private static final String TAG = "NetworkCacheProvider";
// URI匹配码
private static final int CACHE_ITEMS = 1;
private static final int CACHE_ITEM_ID = 2;
private static final int CACHE_ITEM_KEY = 3;
private static final int NETWORK_DATA = 4;
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sUriMatcher.addURI(AUTHORITY, "cache", CACHE_ITEMS);
sUriMatcher.addURI(AUTHORITY, "cache/#", CACHE_ITEM_ID);
sUriMatcher.addURI(AUTHORITY, "cache/key/*", CACHE_ITEM_KEY);
sUriMatcher.addURI(AUTHORITY, "network/*", NETWORK_DATA);
}
private CacheDatabaseHelper mDbHelper;
private OkHttpClient mHttpClient;
private ExecutorService mExecutor;
// 默认缓存有效期: 1小时
private static final long DEFAULT_CACHE_DURATION_MS = 60 * 60 * 1000;
@Override
public boolean onCreate() {
mDbHelper = new CacheDatabaseHelper(getContext());
mHttpClient = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
mExecutor = Executors.newFixedThreadPool(4);
return true;
}
/**
* 查询缓存数据
* 如果缓存过期,异步刷新
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
switch (sUriMatcher.match(uri)) {
case CACHE_ITEMS:
return queryAllCache(projection, selection, selectionArgs, sortOrder);
case CACHE_ITEM_ID:
long id = ContentUris.parseId(uri);
return queryCacheById(id, projection);
case CACHE_ITEM_KEY:
String cacheKey = uri.getLastPathSegment();
return queryCacheByKey(cacheKey, projection, uri);
case NETWORK_DATA:
String networkUrl = uri.getQueryParameter("url");
return queryNetworkData(networkUrl, projection, uri);
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
}
/**
* 查询所有缓存
*/
private Cursor queryAllCache(String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = mDbHelper.getReadableDatabase();
return db.query(CacheContract.CacheEntry.TABLE_NAME,
projection, selection, selectionArgs, null, null, sortOrder);
}
/**
* 按ID查询缓存
*/
private Cursor queryCacheById(long id, String[] projection) {
SQLiteDatabase db = mDbHelper.getReadableDatabase();
return db.query(CacheContract.CacheEntry.TABLE_NAME,
projection,
CacheContract.CacheEntry._ID + "=?",
new String[]{String.valueOf(id)},
null, null, null);
}
/**
* 按Key查询缓存,支持自动刷新
*/
private Cursor queryCacheByKey(String cacheKey, String[] projection, Uri notifyUri) {
SQLiteDatabase db = mDbHelper.getReadableDatabase();
Cursor cursor = db.query(CacheContract.CacheEntry.TABLE_NAME,
projection,
CacheContract.CacheEntry.COLUMN_CACHE_KEY + "=?",
new String[]{cacheKey},
null, null, null);
if (cursor != null && cursor.moveToFirst()) {
// 检查是否过期
long expireTime = cursor.getLong(
cursor.getColumnIndex(CacheContract.CacheEntry.COLUMN_EXPIRE_TIME));
if (System.currentTimeMillis() > expireTime) {
// 缓存过期,异步刷新
String sourceUrl = cursor.getString(
cursor.getColumnIndex(CacheContract.CacheEntry.COLUMN_SOURCE_URL));
if (sourceUrl != null) {
refreshCacheAsync(cacheKey, sourceUrl, notifyUri);
}
}
cursor.moveToFirst(); // 重置游标位置
}
if (cursor != null) {
cursor.setNotificationUri(getContext().getContentResolver(), notifyUri);
}
return cursor;
}
/**
* 直接从网络获取数据(同步)
*/
private Cursor queryNetworkData(String url, String[] projection, Uri notifyUri) {
if (url == null) {
return null;
}
String cacheKey = generateCacheKey(url);
// 先查缓存
Cursor cached = queryCacheByKey(cacheKey, projection, notifyUri);
if (cached != null && cached.getCount() > 0) {
return cached;
}
// 缓存未命中,同步获取网络数据
try {
String data = fetchFromNetwork(url);
if (data != null) {
// 存入缓存
saveToCacheSync(cacheKey, url, data, DEFAULT_CACHE_DURATION_MS);
return queryCacheByKey(cacheKey, projection, notifyUri);
}
} catch (IOException e) {
Log.e(TAG, "Network request failed", e);
}
return null;
}
/**
* 异步刷新缓存
*/
private void refreshCacheAsync(String cacheKey, String url, Uri notifyUri) {
mExecutor.execute(() -> {
try {
String data = fetchFromNetwork(url);
if (data != null) {
saveToCacheSync(cacheKey, url, data, DEFAULT_CACHE_DURATION_MS);
// 通知数据变更
getContext().getContentResolver().notifyChange(notifyUri, null);
}
} catch (IOException e) {
Log.e(TAG, "Failed to refresh cache: " + cacheKey, e);
}
});
}
/**
* 从网络获取数据
*/
private String fetchFromNetwork(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = mHttpClient.newCall(request).execute()) {
if (response.isSuccessful() && response.body() != null) {
return response.body().string();
}
}
return null;
}
/**
* 同步保存到缓存
*/
private void saveToCacheSync(String cacheKey, String sourceUrl, String data, long durationMs) {
SQLiteDatabase db = mDbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(CacheContract.CacheEntry.COLUMN_CACHE_KEY, cacheKey);
values.put(CacheContract.CacheEntry.COLUMN_SOURCE_URL, sourceUrl);
values.put(CacheContract.CacheEntry.COLUMN_DATA, data);
values.put(CacheContract.CacheEntry.COLUMN_CREATED_TIME, System.currentTimeMillis());
values.put(CacheContract.CacheEntry.COLUMN_EXPIRE_TIME,
System.currentTimeMillis() + durationMs);
// 使用REPLACE策略
db.replace(CacheContract.CacheEntry.TABLE_NAME, null, values);
}
/**
* 插入或更新缓存
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
if (sUriMatcher.match(uri) != CACHE_ITEMS) {
throw new IllegalArgumentException("Invalid URI for insert: " + uri);
}
// 设置创建时间
if (!values.containsKey(CacheContract.CacheEntry.COLUMN_CREATED_TIME)) {
values.put(CacheContract.CacheEntry.COLUMN_CREATED_TIME, System.currentTimeMillis());
}
// 设置过期时间
if (!values.containsKey(CacheContract.CacheEntry.COLUMN_EXPIRE_TIME)) {
values.put(CacheContract.CacheEntry.COLUMN_EXPIRE_TIME,
System.currentTimeMillis() + DEFAULT_CACHE_DURATION_MS);
}
SQLiteDatabase db = mDbHelper.getWritableDatabase();
long id = db.insert(CacheContract.CacheEntry.TABLE_NAME, null, values);
if (id > 0) {
Uri newUri = ContentUris.withAppendedId(uri, id);
getContext().getContentResolver().notifyChange(newUri, null);
return newUri;
}
return null;
}
/**
* 删除缓存
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = mDbHelper.getWritableDatabase();
int count = 0;
switch (sUriMatcher.match(uri)) {
case CACHE_ITEMS:
count = db.delete(CacheContract.CacheEntry.TABLE_NAME, selection, selectionArgs);
break;
case CACHE_ITEM_ID:
long id = ContentUris.parseId(uri);
count = db.delete(CacheContract.CacheEntry.TABLE_NAME,
CacheContract.CacheEntry._ID + "=?",
new String[]{String.valueOf(id)});
break;
case CACHE_ITEM_KEY:
String cacheKey = uri.getLastPathSegment();
count = db.delete(CacheContract.CacheEntry.TABLE_NAME,
CacheContract.CacheEntry.COLUMN_CACHE_KEY + "=?",
new String[]{cacheKey});
break;
}
if (count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
/**
* 更新缓存
*/
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
SQLiteDatabase db = mDbHelper.getWritableDatabase();
int count = 0;
switch (sUriMatcher.match(uri)) {
case CACHE_ITEM_ID:
long id = ContentUris.parseId(uri);
count = db.update(CacheContract.CacheEntry.TABLE_NAME,
values,
CacheContract.CacheEntry._ID + "=?",
new String[]{String.valueOf(id)});
break;
case CACHE_ITEM_KEY:
String cacheKey = uri.getLastPathSegment();
count = db.update(CacheContract.CacheEntry.TABLE_NAME,
values,
CacheContract.CacheEntry.COLUMN_CACHE_KEY + "=?",
new String[]{cacheKey});
break;
}
if (count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
/**
* call()方法支持额外操作
*/
@Override
public Bundle call(String method, String arg, Bundle extras) {
Bundle result = new Bundle();
switch (method) {
case "clear_expired":
// 清理过期缓存
int deleted = clearExpiredCache();
result.putInt("deleted_count", deleted);
break;
case "clear_all":
// 清空所有缓存
deleted = clearAllCache();
result.putInt("deleted_count", deleted);
break;
case "get_cache_size":
// 获取缓存大小
long size = getCacheSize();
result.putLong("cache_size", size);
break;
case "refresh":
// 强制刷新指定缓存
String cacheKey = arg;
boolean success = forceRefresh(cacheKey);
result.putBoolean("success", success);
break;
}
return result;
}
private int clearExpiredCache() {
SQLiteDatabase db = mDbHelper.getWritableDatabase();
return db.delete(CacheContract.CacheEntry.TABLE_NAME,
CacheContract.CacheEntry.COLUMN_EXPIRE_TIME + "<?",
new String[]{String.valueOf(System.currentTimeMillis())});
}
private int clearAllCache() {
SQLiteDatabase db = mDbHelper.getWritableDatabase();
return db.delete(CacheContract.CacheEntry.TABLE_NAME, null, null);
}
private long getCacheSize() {
File dbFile = getContext().getDatabasePath("cache.db");
return dbFile.exists() ? dbFile.length() : 0;
}
private boolean forceRefresh(String cacheKey) {
SQLiteDatabase db = mDbHelper.getReadableDatabase();
Cursor cursor = db.query(CacheContract.CacheEntry.TABLE_NAME,
new String[]{CacheContract.CacheEntry.COLUMN_SOURCE_URL},
CacheContract.CacheEntry.COLUMN_CACHE_KEY + "=?",
new String[]{cacheKey},
null, null, null);
if (cursor != null && cursor.moveToFirst()) {
String url = cursor.getString(0);
cursor.close();
if (url != null) {
try {
String data = fetchFromNetwork(url);
if (data != null) {
saveToCacheSync(cacheKey, url, data, DEFAULT_CACHE_DURATION_MS);
return true;
}
} catch (IOException e) {
Log.e(TAG, "Force refresh failed", e);
}
}
}
return false;
}
private String generateCacheKey(String url) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(url.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
return String.valueOf(url.hashCode());
}
}
@Override
public String getType(Uri uri) {
return "application/json";
}
/**
* 缓存数据库契约类
*/
public static class CacheContract {
public static class CacheEntry implements BaseColumns {
public static final String TABLE_NAME = "cache";
public static final String COLUMN_CACHE_KEY = "cache_key";
public static final String COLUMN_SOURCE_URL = "source_url";
public static final String COLUMN_DATA = "data";
public static final String COLUMN_CREATED_TIME = "created_time";
public static final String COLUMN_EXPIRE_TIME = "expire_time";
}
}
/**
* 缓存数据库Helper
*/
private static class CacheDatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "cache.db";
private static final int DATABASE_VERSION = 1;
CacheDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + CacheContract.CacheEntry.TABLE_NAME + " ("
+ CacheContract.CacheEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ CacheContract.CacheEntry.COLUMN_CACHE_KEY + " TEXT UNIQUE NOT NULL,"
+ CacheContract.CacheEntry.COLUMN_SOURCE_URL + " TEXT,"
+ CacheContract.CacheEntry.COLUMN_DATA + " TEXT,"
+ CacheContract.CacheEntry.COLUMN_CREATED_TIME + " INTEGER,"
+ CacheContract.CacheEntry.COLUMN_EXPIRE_TIME + " INTEGER"
+ ");");
// 创建索引
db.execSQL("CREATE INDEX idx_cache_key ON "
+ CacheContract.CacheEntry.TABLE_NAME + "("
+ CacheContract.CacheEntry.COLUMN_CACHE_KEY + ");");
db.execSQL("CREATE INDEX idx_expire_time ON "
+ CacheContract.CacheEntry.TABLE_NAME + "("
+ CacheContract.CacheEntry.COLUMN_EXPIRE_TIME + ");");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + CacheContract.CacheEntry.TABLE_NAME);
onCreate(db);
}
}
}
14.3.2 客户端使用示例
java
public class NetworkCacheClient {
private static final Uri CACHE_URI =
Uri.parse("content://com.example.networkcache/cache");
private static final Uri NETWORK_URI =
Uri.parse("content://com.example.networkcache/network");
private final ContentResolver mResolver;
public NetworkCacheClient(Context context) {
mResolver = context.getContentResolver();
}
/**
* 获取数据(优先从缓存,缓存未命中则从网络获取)
*/
public String getData(String url) {
Uri uri = NETWORK_URI.buildUpon()
.appendQueryParameter("url", url)
.build();
Cursor cursor = mResolver.query(uri,
new String[]{"data"}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
String data = cursor.getString(0);
cursor.close();
return data;
}
return null;
}
/**
* 直接从缓存获取(不触发网络请求)
*/
public String getFromCache(String cacheKey) {
Uri uri = Uri.withAppendedPath(CACHE_URI, "key/" + cacheKey);
Cursor cursor = mResolver.query(uri,
new String[]{"data"}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
String data = cursor.getString(0);
cursor.close();
return data;
}
return null;
}
/**
* 手动存入缓存
*/
public Uri saveToCache(String cacheKey, String data, long durationMs) {
ContentValues values = new ContentValues();
values.put("cache_key", cacheKey);
values.put("data", data);
values.put("expire_time", System.currentTimeMillis() + durationMs);
return mResolver.insert(CACHE_URI, values);
}
/**
* 删除缓存
*/
public int deleteCache(String cacheKey) {
Uri uri = Uri.withAppendedPath(CACHE_URI, "key/" + cacheKey);
return mResolver.delete(uri, null, null);
}
/**
* 清理过期缓存
*/
public int clearExpiredCache() {
Bundle result = mResolver.call(CACHE_URI, "clear_expired", null, null);
return result != null ? result.getInt("deleted_count", 0) : 0;
}
/**
* 清空所有缓存
*/
public int clearAllCache() {
Bundle result = mResolver.call(CACHE_URI, "clear_all", null, null);
return result != null ? result.getInt("deleted_count", 0) : 0;
}
/**
* 强制刷新指定缓存
*/
public boolean forceRefresh(String cacheKey) {
Bundle result = mResolver.call(CACHE_URI, "refresh", cacheKey, null);
return result != null && result.getBoolean("success", false);
}
/**
* 监听缓存数据变化
*/
public void registerCacheObserver(String cacheKey, ContentObserver observer) {
Uri uri = Uri.withAppendedPath(CACHE_URI, "key/" + cacheKey);
mResolver.registerContentObserver(uri, false, observer);
}
public void unregisterCacheObserver(ContentObserver observer) {
mResolver.unregisterContentObserver(observer);
}
}
12. ContentProvider 创建流程分析
本节基于AOSP源码深入分析ContentProvider的创建和初始化流程,包括初始化时机、完整调用链以及涉及的关键类和方法。
12.1 ContentProvider 初始化时机
ContentProvider的初始化发生在应用进程启动的早期阶段,具体时机如下:
关键时机点:
- 应用进程创建后: 当Zygote fork出新进程后
- Application对象创建后 : Application实例化完成,但
onCreate()尚未调用 - Application.onCreate()之前 : ContentProvider的
onCreate()在Application的onCreate()之前执行
源码位置 : frameworks/base/core/java/android/app/ActivityThread.java
在handleBindApplication()方法中(行7274-7603),初始化顺序如下:
java
// 1. 创建Application对象 (行7546)
app = data.info.makeApplicationInner(data.restrictedBackupMode, null);
// 2. 设置mInitialApplication (行7555)
mInitialApplication = app;
// 3. 安装ContentProvider (行7568-7571)
// 注意: 这发生在Application.onCreate()之前!
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);
}
}
// 4. 调用Application.onCreate() (行7586)
mInstrumentation.callApplicationOnCreate(app);
重要说明 : ContentProvider的onCreate()在Application的onCreate()之前调用,这意味着:
- 在ContentProvider中不能依赖Application.onCreate()中初始化的资源
- ContentProvider是应用启动时最先被初始化的组件之一
- 过多或耗时的ContentProvider会显著影响应用启动时间
12.2 ContentProvider 创建调用链
12.2.1 完整调用链概览
[系统服务端 - system_server进程]
AMS.attachApplicationLocked()
|
+-> ContentProviderHelper.generateApplicationProvidersLocked()
| 查询该进程需要运行的所有ContentProvider
|
+-> IApplicationThread.bindApplication(providerList)
将Provider列表传递给应用进程
[应用进程]
ActivityThread.handleBindApplication()
|
+-> LoadedApk.makeApplicationInner()
| 创建Application对象
|
+-> ActivityThread.installContentProviders()
| |
| +-> ActivityThread.installProvider() [循环处理每个Provider]
| | |
| | +-> AppComponentFactory.instantiateProvider()
| | | 通过反射创建ContentProvider实例
| | |
| | +-> ContentProvider.attachInfo()
| | |
| | +-> ContentProvider.onCreate()
| | 调用Provider的onCreate()
| |
| +-> AMS.publishContentProviders()
| 通知AMS Provider已就绪
|
+-> Instrumentation.callApplicationOnCreate()
调用Application.onCreate()
12.2.2 系统服务端流程 (AMS)
1. 生成Provider列表
源码位置: frameworks/base/services/core/java/com/android/server/am/ContentProviderHelper.java
java
// 行1245-1316
List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
final List<ProviderInfo> providers;
try {
// 通过PackageManager查询该进程需要运行的所有ContentProvider
providers = AppGlobals.getPackageManager().queryContentProviders(
app.processName, app.uid,
ActivityManagerService.STOCK_PM_FLAGS
| PackageManager.GET_URI_PERMISSION_PATTERNS
| PackageManager.MATCH_DIRECT_BOOT_AUTO,
/*metaDataKey=*/ null)
.getList();
} catch (RemoteException ex) {
return null;
}
// 遍历并处理每个Provider
int numProviders = providers.size();
for (int i = 0; i < numProviders; i++) {
ProviderInfo cpi = providers.get(i);
// 处理单例Provider
boolean singleton = mService.isSingleton(cpi.processName,
cpi.applicationInfo, cpi.name, cpi.flags);
// ...
// 创建ContentProviderRecord并注册到ProviderMap
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, app.userId);
if (cpr == null) {
cpr = new ContentProviderRecord(mService, cpi, app.info, comp, singleton);
mProviderMap.putProviderByClass(comp, cpr);
}
pr.installProvider(cpi.name, cpr);
}
return providers.isEmpty() ? null : providers;
}
2. bindApplication传递Provider列表
源码位置: frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
java
// 行4569-4728
boolean attachApplicationLocked(...) {
// 生成Provider列表
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List<ProviderInfo> providers = normalMode
? mCpHelper.generateApplicationProvidersLocked(app)
: null;
// 设置Provider发布超时
if (providers != null && mCpHelper.checkAppInLaunchingProvidersLocked(app)) {
Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
msg.obj = app;
mHandler.sendMessageDelayed(msg,
ContentResolver.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS);
}
// 封装Provider列表
final ProviderInfoList providerList = ProviderInfoList.fromList(providers);
// 调用bindApplication,传递Provider列表
thread.bindApplication(
processName,
appInfo,
// ... 其他参数 ...
providerList, // Provider列表
// ... 其他参数 ...
);
}
12.2.3 应用进程端流程 (ActivityThread)
1. handleBindApplication() - 应用启动入口
源码位置: frameworks/base/core/java/android/app/ActivityThread.java
java
// 行7274-7623
private void handleBindApplication(AppBindData data) {
// 1. 各种初始化配置...
mBoundApplication = data;
mConfigurationController.setConfiguration(data.config);
// 2. 创建Application对象 (行7546)
app = data.info.makeApplicationInner(data.restrictedBackupMode, null);
mInitialApplication = app;
// 3. 安装ContentProvider (行7568-7571)
// 关键: 这发生在Application.onCreate()之前!
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);
}
}
// 4. 调用Instrumentation.onCreate()
mInstrumentation.onCreate(data.instrumentationArgs);
// 5. 调用Application.onCreate() (行7586)
mInstrumentation.callApplicationOnCreate(app);
}
2. installContentProviders() - 批量安装Provider
java
// 行7876-7903
private void installContentProviders(Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
// 遍历并安装每个Provider
for (ProviderInfo cpi : providers) {
if (DEBUG_PROVIDER) {
StringBuilder buf = new StringBuilder(128);
buf.append("Pub ");
buf.append(cpi.authority);
buf.append(": ");
buf.append(cpi.name);
Log.i(TAG, buf.toString());
}
// 安装单个Provider
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
// 通知AMS所有Provider已发布
try {
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
3. installProvider() - 安装单个Provider
java
// 行8310-8448
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
if (holder == null || holder.provider == null) {
// 需要本地创建Provider
Context c = null;
ApplicationInfo ai = info.applicationInfo;
// 获取正确的Context
if (context != null && context.getPackageName().equals(ai.packageName)) {
c = context;
} else if (mInitialApplication != null &&
mInitialApplication.getPackageName().equals(ai.packageName)) {
c = mInitialApplication;
} else if (context != null) {
c = context.createPackageContext(ai.packageName,
Context.CONTEXT_INCLUDE_CODE);
}
try {
final java.lang.ClassLoader cl = c.getClassLoader();
LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
if (packageInfo == null) {
packageInfo = getSystemContext().mPackageInfo;
}
// 通过AppComponentFactory反射创建ContentProvider实例
localProvider = packageInfo.getAppFactory()
.instantiateProvider(cl, info.name);
// 获取IContentProvider (Binder服务端)
provider = localProvider.getIContentProvider();
// 调用attachInfo()初始化Provider
// 这会触发ContentProvider.onCreate()
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
// 错误处理...
}
} else {
provider = holder.provider;
}
// 注册到本地Provider映射表
synchronized (mProviderMap) {
IBinder jBinder = provider.asBinder();
if (localProvider != null) {
ComponentName cname = new ComponentName(info.packageName, info.name);
ProviderClientRecord pr = mLocalProvidersByName.get(cname);
if (pr != null) {
provider = pr.mProvider;
} else {
holder = new ContentProviderHolder(info);
holder.provider = provider;
holder.noReleaseNeeded = true;
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
mLocalProviders.put(jBinder, pr);
mLocalProvidersByName.put(cname, pr);
}
retHolder = pr.mHolder;
}
// ...
}
return retHolder;
}
4. ContentProvider.attachInfo() - 初始化Provider
源码位置: frameworks/base/core/java/android/content/ContentProvider.java
java
// 行2617-2651
public void attachInfo(Context context, ProviderInfo info) {
attachInfo(context, info, false);
}
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
mNoPerms = testing;
mCallingAttributionSource = new ThreadLocal<>();
// 只在第一次attachInfo时执行
if (mContext == null) {
mContext = context;
if (context != null && mTransport != null) {
mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
Context.APP_OPS_SERVICE);
}
mMyUid = Process.myUid();
if (info != null) {
// 设置Provider权限信息
setReadPermission(info.readPermission);
setWritePermission(info.writePermission);
setPathPermissions(info.pathPermissions);
mExported = info.exported;
mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
setAuthorities(info.authority);
}
if (Build.IS_DEBUGGABLE) {
setTransportLoggingEnabled(Log.isLoggable(getClass().getSimpleName(),
Log.VERBOSE));
}
// 调用ContentProvider.onCreate()
ContentProvider.this.onCreate();
}
}
12.2.4 Provider发布流程 (publishContentProviders)
源码位置: frameworks/base/services/core/java/com/android/server/am/ContentProviderHelper.java
java
// 行731-820
void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
if (providers == null) {
return;
}
mService.enforceNotIsolatedOrSdkSandboxCaller("publishContentProviders");
synchronized (mService) {
final ProcessRecord r = mService.getRecordForAppLOSP(caller);
final long origId = Binder.clearCallingIdentity();
boolean providersPublished = false;
for (int i = 0, size = providers.size(); i < size; i++) {
ContentProviderHolder src = providers.get(i);
if (src == null || src.info == null || src.provider == null) {
continue;
}
// 获取之前创建的ContentProviderRecord
ContentProviderRecord dst = r.mProviders.getProvider(src.info.name);
if (dst == null) {
continue;
}
providersPublished = true;
// 注册Provider到ProviderMap (按类名和authority)
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
mProviderMap.putProviderByClass(comp, dst);
String[] names = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProviderMap.putProviderByName(names[j], dst);
}
// 从mLaunchingProviders移除,取消超时
boolean wasInLaunchingProviders = false;
for (int j = 0, numLaunching = mLaunchingProviders.size();
j < numLaunching; j++) {
if (mLaunchingProviders.get(j) == dst) {
mLaunchingProviders.remove(j);
wasInLaunchingProviders = true;
j--;
numLaunching--;
}
}
if (wasInLaunchingProviders) {
mService.mHandler.removeMessages(
ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG, dst);
mService.mHandler.removeMessages(
ActivityManagerService.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
}
// 设置Provider实例并通知等待者
synchronized (dst) {
dst.provider = src.provider;
dst.setProcess(r);
dst.notifyAll(); // 唤醒等待此Provider的线程
dst.onProviderPublishStatusLocked(true);
}
}
// 更新进程优先级
if (providersPublished) {
mService.updateOomAdjLocked(r, OOM_ADJ_REASON_GET_PROVIDER);
}
Binder.restoreCallingIdentity(origId);
}
}
12.3 ContentProvider创建流程时序图
Application ContentProvider AppComponentFactory LoadedApk ContentProviderHelper ActivityManagerService ActivityThread (App Process) Zygote Application ContentProvider AppComponentFactory LoadedApk ContentProviderHelper ActivityManagerService ActivityThread (App Process) Zygote 进程启动阶段 生成Provider列表 发送bindApplication 应用初始化阶段 ContentProvider初始化 Provider初始化完成 loop [每个Provider] 发布Provider Application初始化 Application初始化完成 fork新进程 attachApplication() generateApplicationProvidersLocked() PackageManager.queryContentProviders() List<ProviderInfo> bindApplication(providerList) handleBindApplication() makeApplicationInner() new Application() Application instance installContentProviders() installProvider() instantiateProvider() new ContentProvider() ContentProvider instance attachInfo(context, info) onCreate() publishContentProviders(holders) publishContentProviders() 注册到ProviderMap notifyAll() 唤醒等待者 onCreate()
12.4 关键类和方法汇总
| 类名 | 源码位置 | 关键方法 | 作用 |
|---|---|---|---|
| ActivityThread | frameworks/base/core/java/android/app/ActivityThread.java |
handleBindApplication() |
应用绑定入口,协调组件初始化顺序 |
installContentProviders() |
批量安装Provider并通知AMS | ||
installProvider() |
安装单个Provider,创建实例并初始化 | ||
| ActivityManagerService | frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java |
attachApplicationLocked() |
处理应用attach请求,发送bindApplication |
| ContentProviderHelper | frameworks/base/services/core/java/com/android/server/am/ContentProviderHelper.java |
generateApplicationProvidersLocked() |
查询进程需要的Provider列表 |
publishContentProviders() |
注册已发布的Provider到ProviderMap | ||
getContentProviderImpl() |
获取Provider,必要时启动Provider进程 | ||
| ContentProvider | frameworks/base/core/java/android/content/ContentProvider.java |
attachInfo() |
初始化Provider,设置权限和Context |
onCreate() |
Provider初始化回调,子类实现 | ||
| LoadedApk | frameworks/base/core/java/android/app/LoadedApk.java |
makeApplicationInner() |
创建Application实例 |
| AppComponentFactory | frameworks/base/core/java/android/app/AppComponentFactory.java |
instantiateProvider() |
通过反射创建Provider实例 |
| ProviderMap | frameworks/base/services/core/java/com/android/server/am/ProviderMap.java |
putProviderByName() |
按authority注册Provider |
getProviderByName() |
按authority查找Provider | ||
| ContentProviderRecord | frameworks/base/services/core/java/com/android/server/am/ContentProviderRecord.java |
- | 存储Provider信息和状态 |
13. 组件启动顺序对比分析
本节分析Application、ContentProvider、Service、Activity等组件的启动时机和顺序,并基于AOSP源码说明其原因。
13.1 组件启动顺序概览
在Android应用进程启动时,各组件的初始化顺序如下:
1. Application对象创建 (构造函数)
|
2. Application.attachBaseContext()
|
3. ContentProvider.onCreate() [所有Provider]
|
4. Application.onCreate()
|
5. [按需启动] Activity.onCreate() / Service.onCreate()
关键点:
- ContentProvider是在Application.onCreate()之前初始化的
- Service和Activity是按需启动的,不会在应用启动时自动创建
- 只有声明在Manifest中且属于当前进程的ContentProvider才会被自动初始化
13.2 各组件启动时机详解
13.2.1 Application 启动时机
创建时机 : 进程启动时,在handleBindApplication()中最先创建
源码位置 : frameworks/base/core/java/android/app/ActivityThread.java
java
// 行7546
app = data.info.makeApplicationInner(data.restrictedBackupMode, null);
实际创建流程 (frameworks/base/core/java/android/app/LoadedApk.java):
java
// 行1407-1505
private Application makeApplicationInner(boolean forceDefaultAppClass,
Instrumentation instrumentation, boolean allowDuplicateInstances) {
if (mApplication != null) {
return mApplication;
}
Application app = null;
String appClass = mApplicationInfo.getCustomApplicationClassNameForProcess(
myProcessName);
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
try {
final java.lang.ClassLoader cl = getClassLoader();
// ...
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
// 创建Application实例
app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
// ...
}
mApplication = app;
// 注意: 这里不会调用onCreate(),instrumentation参数通常为null
// Application.onCreate()在installContentProviders()之后才调用
return app;
}
Application.onCreate()调用时机:
java
// ActivityThread.handleBindApplication() 行7586
mInstrumentation.callApplicationOnCreate(app);
13.2.2 ContentProvider 启动时机
创建时机: 在Application对象创建后、Application.onCreate()之前
触发条件:
- 在AndroidManifest.xml中声明的Provider
- Provider的进程与当前进程匹配
- 非restrictedBackupMode模式
源码依据 (frameworks/base/core/java/android/app/ActivityThread.java):
java
// handleBindApplication() 行7568-7571
// 关键: 这发生在callApplicationOnCreate()之前!
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);
}
}
// 行7586 - 之后才调用Application.onCreate()
mInstrumentation.callApplicationOnCreate(app);
Provider列表来源 (frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java):
java
// 行4569-4572
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List<ProviderInfo> providers = normalMode
? mCpHelper.generateApplicationProvidersLocked(app)
: null;
13.2.3 Service 启动时机
创建时机 : 按需启动,通过startService()或bindService()触发
不会自动启动: Service不会在应用进程启动时自动创建
触发流程:
- 客户端调用
startService()/bindService() - AMS处理请求 ->
ActiveServices.startServiceLocked() - 如果进程已存在,调用
app.thread.scheduleCreateService() ActivityThread.handleCreateService()创建Service实例
源码位置 : frameworks/base/core/java/android/app/ActivityThread.java
java
// 行5011-5074
private void handleCreateService(CreateServiceData data) {
unscheduleGcIdler();
final LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo);
Service service = null;
try {
// 获取或创建Application
Application app = packageInfo.makeApplicationInner(false, mInstrumentation);
// 创建Service实例
final java.lang.ClassLoader cl;
if (data.info.splitName != null) {
cl = packageInfo.getSplitClassLoader(data.info.splitName);
} else {
cl = packageInfo.getClassLoader();
}
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
// 创建Context
ContextImpl context = ContextImpl.getImpl(service
.createServiceBaseContext(this, packageInfo));
// 初始化Service
context.setOuterContext(service);
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
// 调用Service.onCreate()
service.onCreate();
mServicesData.put(data.token, data);
mServices.put(data.token, service);
// 通知AMS Service创建完成
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0, null);
} catch (Exception e) {
// ...
}
}
关键点:
- Service创建前会先确保Application存在 (
makeApplicationInner) - Service.onCreate()在Service实例创建后立即调用
- Service不参与进程启动阶段的组件初始化
13.2.4 Activity 启动时机
创建时机 : 按需启动,通过startActivity()触发
不会自动启动: Activity不会在应用进程启动时自动创建
触发流程:
- 客户端调用
startActivity() - AMS处理请求 ->
ActivityTaskManagerService - 调用
app.thread.scheduleTransaction()->LaunchActivityItem ActivityThread.handleLaunchActivity()创建Activity实例
源码位置 : frameworks/base/core/java/android/app/ActivityThread.java
java
// 行4286-4367
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, int deviceId, Intent customIntent) {
// ...
// Initialize before creating the activity
if (ThreadedRenderer.sRendererEnabled
&& (r.activityInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
HardwareRenderer.preload();
}
// 执行Activity启动
final Activity a = performLaunchActivity(r, customIntent);
// ...
return a;
}
performLaunchActivity流程 (简化):
java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// 1. 创建Activity实例
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
// 2. 获取或创建Application
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
// 3. 创建Context并attach
activity.attach(appContext, this, getInstrumentation(), r.token, ...);
// 4. 调用Activity.onCreate()
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
return activity;
}
13.3 启动顺序对比表
| 组件 | 启动时机 | 触发条件 | onCreate()调用点 | 依赖Application |
|---|---|---|---|---|
| Application | 进程启动时 | 进程创建 | handleBindApplication() |
无 |
| ContentProvider | 进程启动时 | Manifest声明 + 进程匹配 | installContentProviders() |
Application对象已创建,但onCreate()未调用 |
| Service | 按需启动 | startService() / bindService() |
handleCreateService() |
是,Application已完全初始化 |
| Activity | 按需启动 | startActivity() |
performLaunchActivity() |
是,Application已完全初始化 |
13.4 启动顺序的原因分析
13.4.1 为什么ContentProvider在Application.onCreate()之前初始化?
设计考量:
-
跨进程数据共享需求: ContentProvider作为Android的跨进程数据共享机制,可能被其他应用在任何时刻访问。早期初始化确保在第一次访问时就能响应。
-
系统服务依赖: 某些系统级ContentProvider(如SettingsProvider)需要尽早可用,因为很多系统组件依赖它们。
-
数据一致性: 在Application.onCreate()中可能会访问ContentProvider,如果Provider在此之后才初始化,会导致问题。
源码注释说明 (frameworks/base/core/java/android/content/ContentProvider.java 行107-109):
java
/**
* Other methods (such as {@link #onCreate}) are only called from the application
* main thread, and must avoid performing lengthy operations.
*/
13.4.2 为什么Service和Activity是按需启动?
设计考量:
-
资源效率: 不是所有应用的所有Service/Activity都需要启动,按需启动节省资源。
-
用户体验: 快速的应用启动需要最小化初始化工作。
-
生命周期管理: Service和Activity有明确的生命周期,需要由系统或用户操作触发。
-
进程边界: 一个应用可能有多个进程,Service/Activity只在需要时在对应进程中创建。
13.5 启动顺序时序图
Activity Service ContentProvider Application ActivityThread ActivityManagerService 用户/系统 Activity Service ContentProvider Application ActivityThread ActivityManagerService 用户/系统 进程启动阶段 1. Application创建 2. ContentProvider初始化 loop [每个Provider] 3. Application初始化完成 4. Service按需启动 5. Activity按需启动 触发应用启动 bindApplication(providerList) new Application() attachBaseContext() new ContentProvider() attachInfo() ->> onCreate() publishContentProviders() onCreate() startService() scheduleCreateService() new Service() attach() ->> onCreate() startActivity() scheduleLaunchActivity() new Activity() attach() ->> onCreate()
13.6 开发注意事项
基于上述启动顺序分析,开发时需要注意:
-
ContentProvider中不要依赖Application.onCreate()中初始化的资源
- ContentProvider.onCreate()先于Application.onCreate()执行
- 如需使用Application级别的资源,应使用懒加载
-
ContentProvider初始化不宜过重
- 所有声明的Provider都会在启动时初始化
- 耗时操作应延迟到实际访问时
-
避免在ContentProvider.onCreate()中启动Service或Activity
- 此时Application可能尚未完全初始化
-
使用多进程时的Provider注意事项
- 每个进程都会独立初始化其Provider
- Provider的
android:multiprocess="true"会影响实例化行为
-
Service和Activity可以安全地假设Application已完全初始化
- 它们总是在Application.onCreate()之后才会被创建
参考资源
- AOSP源码:
frameworks/base/core/java/android/content/ - AOSP源码:
frameworks/base/core/java/android/app/ActivityThread.java - AOSP源码:
frameworks/base/services/core/java/com/android/server/am/ - SettingsProvider源码:
frameworks/base/packages/SettingsProvider/ - MediaProvider源码:
packages/providers/MediaProvider/ - ContactsProvider源码:
packages/providers/ContactsProvider/ - CalendarProvider源码:
packages/providers/CalendarProvider/ - Android官方文档: Content Providers Guide
致敬前辈,砥砺前行!