所有进程共用1个内核空间
- copy_from_user() : 将用户空间的数据拷贝到内核空间
- copy_to_user() : 将内核空间的数据拷贝到用户空间
进程隔离
为了保证安全性、独立性,一个进程不能直接操作或者访问另一个进程,即Android的进程是相互独立、隔离的
跨进程通信(IPC)
即进程间需要进行数据交互、通信
a.Binder的作用:连接两个进程,实现了mmap()系统调用,主要负责创建数据接收的缓存空间和管理数据接收缓存
b.传统的跨进程通信需要拷贝数据两次,但Binder机制只需要1次,主要是使用了内存映射
client进程、server进程、service Manager进程之间的交互都必须通过Binder驱动而非直接交互
原因: client、server、service manager进程属于用户空间,不可进行进程间交互;Binder驱动属于内核空间,可以进行进程间的交互
Binder驱动、Service Manager进程属于Android基础架构(已经实现好的);而Client进程和Server进程属于Android应用层(需要开发者自己实现)。所以,在进行跨进程通信时,开发者只需要自定义Client、Server进程并显示使用上述3个步骤,最终借助Android的基本架构功能就可完成进程间通信
Binder请求的线程管理
Binder的接收缓存区
binder实际上并没有在内核区中创建接收缓存区,实际的接收缓存区是创建在了接收方进程中,只是在发送方产生了一个映射,在接收方也产生了一个映射。本质上是内存映射原理。不然的话假如接收缓存区是创建在内核区中的话那么依然是两次拷贝,也就是说内核区中的只是一个映射
它是Android是四大组件之一,是用来在进程间进行数据交互和共享的,也就是跨进程通信,底层是采用的Android中的Binder机制
大体把握:
ContentProvider主要以表格的形式组织数据,同时也支持文件数据,不过表格形式用得比较多。每个表格中包含多张表,每张表的行、列分别对应记录、字段
进程间共享数据的本质:添加、删除、获取、修改数据
ContentProvider的核心方法也是上述四个作用:
// 外部进程向 ContentProvider 中添加数据
public int delete(Uri uri, String selection, String[] selectionArgs)
// 外部进程 删除 ContentProvider 中的数据
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
// 外部进程更新 ContentProvider 中的数据
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
// 外部应用 获取 ContentProvider 中的数据
// 注:
// 1. 上述4个方法由外部进程回调,并运行在ContentProvider进程的Binder线程池中(不是主线程)
// 2. 存在多线程并发访问,需要实现线程同步
// a. 若ContentProvider的数据存储方式是使用SQLite & 一个,则不需要,因为SQLite内部实现好了线程同步,若是多个SQLite则需要,因为SQL对象之间无法进行线程同步
// b. 若ContentProvider的数据存储方式是内存,则需要自己实现线程同步
<-- 2个其他方法 -->
public boolean onCreate()
// ContentProvider创建后 或 打开系统后其它进程第一次访问该ContentProvider时 由系统进行调用
// 注:运行在ContentProvider进程的主线程,故不能做耗时操作
public String getType(Uri uri)
// 得到数据类型,即返回当前 Url 所代表数据的MIME类型
Android为常见的数据(通讯录、日程表等)提供了内置的默认的ContentProvider
ContentProvider类并不会直接与外部进程交互,而是通过ContentResolver类
统一管理不同ContentProvider间的操作
- 即通过URI即可操作不同的ContentProvider中的数据
- 外部进程通过ContentResolver类从而与ContentProvider类进行交互
一般来说,一款应用要使用多个ContentProvider,若需要了解每个ContentProvider的不同实现从而完成数据交互,操作成本高、难度大。所以就在ContentProvider类上加了一个ContentResolver类对所有的ContentProvider进行统一管理
ContentResolver类提供了与ContentProvider类相同名字和作用的4个方法
public Uri insert(Uri uri, ContentValues values)
// 外部进程 删除 ContentProvider 中的数据
public int delete(Uri uri, String selection, String[] selectionArgs)
// 外部进程更新 ContentProvider 中的数据
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
// 外部应用 获取 ContentProvider 中的数据
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
实例说明:
// 可通过在所有继承Context的类中 通过调用getContentResolver()来获得ContentResolver
ContentResolver resolver = getContentResolver();
// 设置ContentProvider的URI
Uri uri = Uri.parse("content://cn.scu.myprovider/user");
// 根据URI 操作 ContentProvider中的数据
// 此处是获取ContentProvider中 user表的所有记录
Cursor cursor = resolver.query(uri, null, null, null, "userid desc");
Android提供了3个用于辅助ContentPRovider的工具类:
Uri uri = Uri.parse("content://cn.scu.myprovider/user")
Uri resultUri = ContentUris.withAppendedId(uri, 7);
// 最终生成后的Uri为:content://cn.scu.myprovider/user/7
// parseId()作用:从URL中获取ID
Uri uri = Uri.parse("content://cn.scu.myprovider/user/7")
long personid = ContentUris.parseId(uri);
//获取的结果为:7
作用
具体使用
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
//常量UriMatcher.NO_MATCH = 不匹配任何路径的返回码
// 即初始化时不匹配任何东西
// 步骤2:在ContentProvider 中注册URI(addURI())
int URI_CODE_a = 1;
int URI_CODE_b = 2;
matcher.addURI("cn.scu.myprovider", "user1", URI_CODE_a);
matcher.addURI("cn.scu.myprovider", "user2", URI_CODE_b);
// 若URI资源路径 = content://cn.scu.myprovider/user1 ,则返回注册码URI_CODE_a
// 若URI资源路径 = content://cn.scu.myprovider/user2 ,则返回注册码URI_CODE_b
// 步骤3:根据URI 匹配 URI_CODE,从而匹配ContentProvider中相应的资源(match())
@Override
public String getType(Uri uri) {
Uri uri = Uri.parse(" content://cn.scu.myprovider/user1");
switch(matcher.match(uri)){
// 根据URI匹配的返回码是URI_CODE_a
// 即matcher.match(uri) == URI_CODE_a
case URI_CODE_a:
return tableNameUser1;
// 如果根据URI匹配的返回码是URI_CODE_a,则返回ContentProvider中的名为tableNameUser1的表
case URI_CODE_b:
return tableNameUser2;
// 如果根据URI匹配的返回码是URI_CODE_b,则返回ContentProvider中的名为tableNameUser2的表
}
}
内容观察者,观察Uri引起ContentProvider中的数据变化、通知外界(即访问该数据访问者)
具体使用:
getContentResolver().registerContentObserver(uri);
// 通过ContentResolver类进行注册,并指定需要观察的URI
// 步骤2:当该URI的ContentProvider数据发生变化时,通知外界(即访问该ContentProvider数据的访问者)
public class UserContentProvider extends ContentProvider {
public Uri insert(Uri uri, ContentValues values) {
db.insert("user", "userid", values);
getContext().getContentResolver().notifyChange(uri, null);
// 通知访问者
}
}
// 步骤3:解除观察者
getContentResolver().unregisterContentObserver(uri);
// 同样需要通过ContentResolver类进行解除
ContentProvider为应用间的数据交互提供了一个安全的环境:允许把自己的应用数据根据需求开放给其他应用进行增、删、改、查,而不用担心因为直接开放数据库权限而带来的安全问题
对比于其他对外共享数据的方式,数据访问方式会因数据存储的方式而不同