一、概述

  ContentProvider 是 Android 中提供的专门用于不同应用间进行数据共享的方式。和 Messenger 一样,ContentProvider 的底层实现同样也是 Binder 。ContentProvider 的数据源不止包括 SQLite 数据库,还可以是文件数据。通过将数据储存层和应用层分离,ContentProvider 为各种数据源提供了一个通用的接口。系统预置了许多 ContentProvider ,比如通讯录信息、日程表信息等,要跨进程访问这些信息,只需要通过 ContentResolver 的 query 、update 、insert 和 delete 方法即可。ContentProvider 主要以表格的形式来组织数据,并且可以包含多个表。对于表的定位示例工程也会展示。

  下面内容:

    ① 创建、注册以及访问 ContentProvider

    ② 如何定位到具体的数据?

    ③ 通过 openFile() 方法实现文件数据的共享

    ④ ContentProvider 安全防范

    ⑤ getType() 方法说明

    ⑥ 示例工程代码(双应用)------》 数据源 SQLite 、实现文件共享、多表操作

1、创建、注册以及访问 ContentProvider

  创建一个 SLProvider 继承 ContentProvider , 并实现 onCreate()、query()、insert()、update()、delete()、getType()六个抽象方法 。其中 onCreate 方法是在 UI 线程中执行,所以不能在该方法中做耗时操作。

public class SLProvider extends ContentProvider {
@Override
public boolean onCreate() {
return false;
} @Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
return null;
} @Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
} @Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
return null;
} @Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
} @Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
}

  注册 SLProvider :为了访问安全,用到了自定义权限 com.sl.Provider 。

<provider
android:name=".SLProvider"
android:authorities="com.sl.provider"
android:exported="true"
android:permission="com.sl.Provider" />

  访问 ContentProvider :(注意如果声明自定义权限,那么访问端就需要声明该权限)

Uri uri = Uri.parse("content://com.sl.provider");
Cursor bookCursor = getContentResolver().query(uri, null, null, null, null);

2. 如何定位到具体的数据?

  采用Content Uri,一个Content Uri如下所示:

content://com.sl.provider/book

  它的组成一般分为三部分:

    (1)content://: 作为 content Uri的特殊标识(必须);

    (2)权(authority):用于唯一标识这个 ContentProvider,外部访问者可以根据这个标识找到它;在AndroidManifest中也配置的有 ----->"com.sl.provider"

    (3)路径(path): 所需要访问数据的路径,根据业务而定。 ------>  "/book"

  • 3. 通过 openFile() 方法实现文件数据的共享

  根据 Uri 提供文件的句柄:

@Nullable
@Override
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
Log.i("openFile", uri.getPath());
if (getContext() != null) {
String filePath = getFilePath(uri, getContext());
File file = new File(filePath);
// ParcelFileDescriptor.MODE_READ_ONLY:只可读
// ParcelFileDescriptor.MODE_WRITE_ONLY:只可写
// ParcelFileDescriptor.MODE_READ_WRITE:可读可写
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}
return super.openFile(uri, mode);
} private String getFilePath(Uri uri, Context context) {
String path = null;
switch (matcher.match(uri)) {
case A_FILE_URI_CODE:
path = context.getFilesDir().getParent() + "/a.txt";
break;
case B_FILE_URI_CODE:
path = context.getFilesDir().getParent() + "/b.txt";
break;
}
return path;
}

  在访问端访问文件数据:在访问的时候就会调用 ContentProvider 的 openFile 方法获取 文件句柄,进行访问文件。

            String uri = "content://com.sl.provider/b";
try {
// 处理方式1:直接通过uri读文件
InputStream inputStream = context.getContentResolver()
.openInputStream(Uri.parse(uri));
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(inputStream));
String lineStr = null;
while ((lineStr = bufferedReader.readLine()) != null) {
System.out.println("lineStr:" + lineStr);
}
bufferedReader.close(); // 处理方式2:直接通过uri写文件
context.getContentResolver().openOutputStream(Uri.parse(uri),
"w"); // 处理方式3:把文件拷贝到本地处理
AssetFileDescriptor descriptor = context.getContentResolver()
.openAssetFileDescriptor(Uri.parse(uri), "r");
InputStream is = descriptor.createInputStream();
// 根据uri解析文件名
int start = uri.lastIndexOf('/');
String fileName = uri.substring(start + 1);
// 把文件拷贝到本地data/data/包名/files 目录下
fileName = context.getFilesDir().getAbsolutePath() + "/"
+ fileName;
OutputStream os = new FileOutputStream(new File(fileName));
byte[] b = new byte[1024];
int len;
while ((len = is.read(b)) != -1) {
os.write(b, 0, len);
}
os.flush();
is.close();
os.close();
// 读取文件
File file = new File(fileName);
if (file.isFile() && file.exists()) {
InputStreamReader reader = new InputStreamReader(
new FileInputStream(file));
BufferedReader buffered = new BufferedReader(reader);
String lineTxt = null;
while ((lineTxt = buffered.readLine()) != null) {
System.out.println("lineTxt:" + lineTxt);
}
reader.close();
buffered.close();
} } catch (Exception e) {
e.printStackTrace();
}

4. ContentProvider 安全防范

  (1)正确定义私有权限,设置权限等级,自定义权限可以看这里

  (2)防止本地SQL注入,注意:一定不要使用拼接来组装SQL语句。 如果Content Provider的数据源是 SQLite 数据库,如果使用拼接字符串的形式组成原始SQL语句执行,则会导致SQL注入。

  (3)防止目录遍历:

      a、去除Content Provider中没有必要的openFile()接口。

      b、过滤限制跨域访问,对访问的目标文件的路径进行有效判断:
  (4)通过检测签名来授权合作方应用访问

    如果必须给合作方的APP提供Provider的访问权限,而合作方的APP签名证书又于自己公司的不同,可将合作方的APP的签名哈希值预埋在提供Provider的APP中,提供Provider的APP要检查请求访问此Provider的APP的签名,签名匹配通过才让访问。

5. getType() 方法说明

  getType 的作用应该是这样的,以指定的两种方式开头,android可以顺利识别出这是单条数据还是多条数据,query 方法查询结果是一个 Cursor,我们可以根据 getType 方法中传进来的 Uri 判断出query 方法要返回的 Cursor 中只有一条数据还是有多条数据,这个有什么用呢?如果我们在 getType 方法中返回一个 null 或者是返回一个自定义的 android 不能识别的MIME类型,那么当我们在 query 方法中返回 Cursor 的时候,系统要对 Cursor 进行分析,进而得出结论,知道该Cursor只有一条数据还是有多条数据,但是如果我们按照 Google 的建议,手动的返回了相应的 MIME ,那么系统就不会自己去分析了,这样可以提高一丢点的系统性能。基于此,我们自定义的 ContentProvider 中的 getType 方法可以这么写:

    @Override
public String getType(Uri uri) {
int code = matcher.match(uri);
switch (code) {
case 1:
// 查询多条数据
return "vnd.android.cursor.dir/multi";
case 2:
case 3:
// 根据id或者姓名查询一条数据
return "vnd.android.cursor.item/single";
}
return null;
}

  MIME 前面的一部分我们按照 Google 的要求来写,后面一部分就可以根据我们自己的实际需要来写。还有一种我们可能会很少遇到的情况,我们有可能不知道 ContentProvider 返回给我们的是什么,这个时候我们可以先调用 ContentProvider 的 getType,根据 getType 的不同返回值做相应的处理。

6. 示例工程

  链接: https://pan.baidu.com/s/1TU5-NzTxRo0NW6hbW1Mu5A  密码: 25pp

参考:

Android之通过ContentProvider共享文件

Android安全开发之Provider组件安全

对ContentProvider中getType方法的一点理解

IPC 之 ContentProvider 的使用的更多相关文章

  1. Android艺术开发探索——第二章:IPC机制(下)

    Android艺术开发探索--第二章:IPC机制(下) 我们继续来讲IPC机制,在本篇中你将会学习到 ContentProvider Socket Binder连接池 一.使用ContentProvi ...

  2. Android进程间通信IPC

    一.IPC的说明 IPC是Inter-Process Communication的缩写,含义为进程间通信或跨进程通信,是指两个进程之间进行数据交换的过程. IPC不是Android独有的,任何一个操作 ...

  3. Android IPC 结篇

    一.概述 Android 的 IPC 方式有 Bundle .共享文件.AIDL .Messenger .ContentProvider .Socket ,我们在实现进程间通信时要选择哪一种方式来实现 ...

  4. MIUI通过xposed自动设置root权限

    在小米手机上,每次安装一个自己的插件总需要打开安全中心进行root权限授权,非常的麻烦,总共需要电5次确认,每次需要等5秒 因为插件开发的需求,希望重启计算机时候判断是否已经root,未root则自动 ...

  5. Android查缺补漏(IPC篇)-- Bundle、文件共享、ContentProvider、Messenger四种进程间通讯介绍

    本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8387752.html 进程间通讯篇系列文章目录: Android查缺补漏(IP ...

  6. 【Android - IPC】之ContentProvider简介

    1.Content Provider简介 Content Provider是Android中提供的一种专门用于不同应用之间进行数据共享的方式,从这一点来看,它天生就适合IPC(Inter-Proces ...

  7. Android IPC机制之ContentProvider

    ContentProvider:即内容提供者,用来管理数据,并对外暴露一个uri,外部可以通过uri和数据建立联系并获取或操作数据: 服务端:1.首先创建一个数据库类,并创建一个表:2.创建一个Con ...

  8. Android IPC机制(四)用ContentProvider进行进程间通信

    前言 ContentProvider为存储和获取数据提供统一的接口,它可以在不同的应用程序之间共享数据,本身就是适合进程间通信的.ContentProvider底层实现也是Binder,但是使用起来比 ...

  9. 四大组件之ContentProvider

    前言 ContentProvider作为Android的四大组件之一,是属于需要掌握的基础知识,可能在我们的应用中,对于Activity和Service这两个组件用的很常见,了解的也很多,但是对Con ...

随机推荐

  1. jQuery实现广告弹窗

    首先设置一个固定的窗口位于右下角,效果如下: 代码: jQuery实现广告弹窗.html 之后将该窗口初始设为隐藏,通过代码实现3秒自动显示,5秒自动隐藏,其效果如下: <!DOCTYPE ht ...

  2. HTTP小幺鸡接口管理工具安装与配置说明

    http://www.xiaoyaoji.cn/doc/TxybXPTdx 小幺鸡接口管理工具安装说明 使用可以参考:https://blog.csdn.net/qincidong/article/d ...

  3. mysql服务器iowait高优化一例完整深入解析

    我们有一服务器,上面运行着两个mysql实例,这几天iowait一直很高,在20-30%,下午特地专门排查和解决了下,相关过程整理如下. 该服务器有两个挂载盘,服务器在阿里云上,一个系统盘,一个数据盘 ...

  4. windows10 hyper-v安装配置centos6.8

    27.PNG 选择最小化安装,并在界面下方选中自定义额外的安装包 28.PNG 选择额外的安装包 29.PNG 选择额外的安装包 30.PNG 终于开始安装了 31.PNG 安装完毕,重启 32.PN ...

  5. docker 实践

    https://doc.yonyoucloud.com/doc/docker_practice/etcd/etcdctl.html 启动http restful API docker批量映射端口 怎么 ...

  6. Android之udp传输

    注意除了添加Internet权限外,还要添加两行代码 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectDi ...

  7. 汉诺塔问题(Hanoi Tower)递归算法解析(Python实现)

    汉诺塔问题 1.问题来源:汉诺塔来源于印度传说的一个故事,上帝创造世界时作了三根金刚石柱子,在一根柱子上从上往下从小到大顺序摞着64片黄金圆盘.上帝命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根 ...

  8. [error] 2230#2230: *84 client intended to send too large body: 1711341 bytes

    centos7 lnmp部署知乎上传主体报错 2019/01/17 18:55:27 [error] 2230#2230: *89 open() "/code/wordpress/favic ...

  9. C# 文件与二进制之间的转换

    /// <summary> /// 工具类:文件与二进制流间的转换 /// </summary> public class FileBinaryConvertHelper { ...

  10. Bootstrap3基础 form-horizontal 表单元素横向布局 简单示例

      内容 参数   OS   Windows 10 x64   browser   Firefox 65.0.2   framework     Bootstrap 3.3.7   editor    ...