[TOC]

1. 什么是内容提供器?

​ 内容提供器(Content Provider) 主要用于在不同的应用程序之间实现数据的共享功能,他提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还可以保证被访问的数据的安全性,目前使用内容提供器十Android实现跨程序共享数据的标准方式. 内容提供器可以选择只对哪一部分的数据进行共享,这样就可以保证我们数据的安全性.

2. 如何使用内容提供器获取其他应用的数据

2.1 权限声明

​ 运行时权限:6.0系统中引入的新功能,为了能更好的保护用户的隐私.

​ 通常的权限声明只需要在MainFest中添加要使用的权限就可以了,6.0及以后对于某些权限还需要在运行的时候在代码中检测是否有这个权限否则弹出对话框申请,拒绝的话是无法使用的.

​ **并不是所有的权限都需要用到运行时权限,只有关系到用户隐私的才需要.除了在ManiFest中声明以外,还要在代码中重新请求一 遍,如果没有使用运行时权限则会导致这个应用抛出异常SecurityException() **

public class MainActivity extends AppCompatActivity {private String TAG = "TAG";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);checkSelf();//检查权限}private void checkSelf() {//如果检查多个权限的话,可以将要检查的权限放入数组或者集合当中,遍历检查即可if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {/*** 通过checkSelfPermission可以检查当前这个应用有没有获取到指定的那个权限,没有的话就调用请求权限的那个方法.* 根据返回值来判断状态  0表示权限已给予,-1表示没有获取到权限.*/Log.d(TAG, "checkSelf: 权限允许");} else {/*** 通过ActivityCompat.requestPermissions动态的申请权限.* 第一个参数:当前的上下文* 第二个参数:需要申请的权限的字符串,保存在数组中.* 第三个参数:查询码,请求权限的最终结果会通过回调的方式->onRequestPermissionsResult();在这个方法中,告诉你最后请求成功了没有*/ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_CALENDAR,Manifest.permission.CAMERA}, 1);}}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {/*** 通过请求码来判断具体是申请了什么权限,以及结果;* grantResult[]  保存着申请权限后,根据申请权限的先后顺序保存*/switch (requestCode) {case 1:if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {Log.d(TAG, "onRequestPermissionsResult: 已授权");for (int i = 0; i < grantResults.length; i++) {Log.d(TAG, "onRequestPermissionsResult: 授权情况:"+grantResults[i]);}} else {Log.d(TAG, "onRequestPermissionsResult: 您拒绝了授权");}break;case 2:break;default:break;}}
}复制代码

以上就是运行时权限的基本用法

2.2 内容提供器的基本用法及原理

​ 内容提供器的用法一般有两种,一是使用现有的内容提供器来读取和操作相应程序中的数据,二是自己创建一个内容提供器给我们的程序的数据提供外部访问接口 (就是通过别人(程序)提供的内容提供器来获取别人(程序)想要给我们使用的数据),系统自带的短信,电话簿,媒体库等程序都提供了类似的访问接口,我们就可以利用这个来进行再次开发和使用了.

​ 如果想要获取内容提供器中的数据,那么就需要借助Content-Resolver类,Context中的GetContentResolver方法可以获取到该类的实例. Content-Resolver提供了类似SqLiteDatabase类的方法,可以对共享的数据进行CRUD 操作,只是参数略微有点不同罢了.

​ 内容提供器是通过Uri来寻找数据源的 Uri由authority和path组成;authority用于区分不同的程序,通常使用包名,path是对同一程序不同的表做区分用的.

​ 比如某个包名为 com.example.test 有两张表table1,table2;

​ 则标准的Uri格式为(://前面的内容为协议):

​ content://com.example.test/table1

​ content://com.example.test/table2

​ 这样就可以明确的表达出我们想要访问哪个程序的哪个表里面的数据了. 所以内容提供器的CRUD都只接受Uri参数来确定位置.

查找:

Uri uri=Uri.parse("content://com.example.test/table2");

Cursor cursor=getContentResolver().quert(uri,projection,selection,selectionArgs,sortOrder);

quert()方法的参数 对应SQL部分 描述
uri from table_name 指定查询某个应用程序下的某一张表
projection select column1,column2 指定查询的列名
selection where column =value 指定where的约束条件
selectionArgs - 为where中的占位符提供具体的值
sortOrder order by column1,column2 指定查询结果的排序方式

从cursor中取出值也和数据库的操作一样,选择想要获取的数据类型,再选择列名即可得到

    ...String data=cursor.getString(cursor.getColumnIndex("columnName"));...复制代码

插入:

​ 和数据库的操作差不多,都是讲数据组装到ContentValues中

ContentValues values=new ContentValues();values.put("columnName1","value1");values.put("columnName2","value2");getContentResolver().insert(uri,values);
复制代码

更新:

ContentValues values=new ContentValues();
values.put("columnName1","value1");
getContentResolver().update(Uri.parse(""),contentValues,"where column1=?",new String[]{"1"});
复制代码

update()中的参数解释:第一个参数指定数据的位置;第二个参数指定要更新成什么值;第三个参数指定条件;第四个参数指定where条件语句中的缺省值;

删除:

getContentResolver().delete(Uri.parse(""),"column=?",new String[]{"1"});
复制代码

​ 参数的意思和上面几个差不多.就不过多的解释了.

2.3 使用内容提供器获取数据

​ 下面使用系统已经给我们提供好了的内容提供器来获取通讯簿中的姓名的电话号码吧,先确保的确保存了几个电话号码在通讯簿中.

1.首先需要获取READ_CANTACTS权限否则是不能读取数据的 并且会报错.

2.查询语句和数据库的用法相似

public class MainActivity extends AppCompatActivity {private String TAG = "TAG";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_CONTACTS}, 1);} else {getData();}}private void getData() {Cursor cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);//传入的Uri是ContactsContract.CommonData-Kinds.Phone类已经帮我们封装好了的一个常量//点开源代码可以看到 : //public static final Uri CONTENT_URI = Uri.withAppendedPath(Data.CONTENT_URI,"phones");if (cursor != null) {while (cursor.moveToNext()) {String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));//ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME  也是一个常量,为保存该数据的类名.下同String phone = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));Log.d(TAG, "getData: " + name + "-" + phone);}}}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {switch (requestCode) {case 1:if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {getData();} else {Log.d(TAG, "onRequestPermissionsResult: 你点击了拒绝!");}break;default:break;}}
}复制代码

运行程序就可以看到姓名和电话一起打印出来了.

主要的内容都在getData();方法中,具体的解释都写在注释中,只要知道原理 那么使用起来就变得十分的方便了.

3. 创建自己的内容提供器

基础知识:

通过新建一个类去继承ContentProvider的方式来创建一个内容提供器,但是必须重写里面的六个方法;

重写的方法: 返回值 用途
onCreate(); boolean 初始化内容提供器的时候调用.通常会在这里完成对数据库的创建和升级操作.返回true表明内容提供器初始化成功,false失败.
query(uri,projection, selection, selectionArgs, sortOrder); Cursor 从内容提供器中查找数据,使用uri参数来确定要查找哪一张表格,projection用于确定查找哪些列,selection用于确定查找的条件,selectionArgs用于填充selection中的条件的缺省值,sortOrder用于对查询结果排序 ; 查询结果放入Cursor中返回;
insert(uri, values); Uri 向内容提供器中添加一条数据,使用uri来确定要添加到的表,待添加的数据放在values参数中; 添加成功以后 返回一个用于表示这条新纪录的Cursor;
update(uri, values, selection, selectionArgs); int 更新内容提供器中已有的数据,使用Uri参数来确定更新哪一张表中的数据,新的数据保存在values中(只更新这里有写出来的值),selection和selectIonArgs参数用于约束更新哪些行;受影响的行数将作为返回值 返回
delete(uri, selection, selectionArgs) int 删除内容提供器中的数据,uri参数用来确定删除哪张表中的数据,selection和selectionArgs参数用于约束删除哪些行;被删除的行数将作为返回值返回;
getType(uri)(); String 根据传入的Uri来返回相对应的MIME类型字符串

Uri两种格式的说明:

​ content://com.example.test/table1/1;

表示调用方期望访问的是com.exampke.test应用里面表table1中id为1的数据

​ content://com.example.test/table1;

表示调用方期望访问的是com.exampke.test应用里面表table1的所有数据

不过通常都使用通配符的方式来匹配这两种格式:

:表示匹配任意长度的数字

#* :表示匹配任意长度的字符

上面的内容就可以写成: content://com.example.test/* 或 content://com.example.test/table1/#

getType();返回数据说明

​ content://com.example.test/table1/1;

​ content://com.example.test/table1;

还是以上面这两个为例子.

MIME字符串: 以vnd开头 + . + android.cursor.dir/(或android.cursor.item/) + vnd. + AUTHORITY + . + PATH

​ vnd.android.cursor.dir/vnd.com.example.test.table1;

​ vnd.android.cursor.item/vnd.com.example.test.table1

3.1 创建的基本步骤

创建一个继承自ContentProvider的类,并重写里面的六个方法

其中内容提供器必须在ManiFest.xml中进行注册,否则无法使用

<providerandroid:name=".MyProvider" android:authorities="com.example.h.content_demo_provider"android:enabled="true"android:exported="true" />
复制代码

第一个参数:类名

第二个参数:通常使用包名来使用, 可以区分 不同的程序之间的内容提供器

第三个参数:启用

第四个参数:表示允许被其他的应用程序进行访问

新建一个内容提供器:


public class MyProvider extends ContentProvider {public static final int BOOK_DIT = 0;public static final int BOOK_ITEM = 1;public static final int CATEGORY_DIR = 2;public static final int CATEGORT_ITEM = 3;public static UriMatcher sUriMatcher;public static final String AUTHORITY = "com.example.h.content_demo_provider";private MyDatabaseHelper mMyDatabaseHelper;private SQLiteDatabase db;{sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);sUriMatcher.addURI(AUTHORITY, "book", BOOK_DIT);sUriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);sUriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);sUriMatcher.addURI(AUTHORITY, "category/#", CATEGORT_ITEM);//UriMatcher 可以匹配uri  通过调用他的match()方法 匹配到就会返回我们在上面添加uri时填入的第三个参数}@Override/*** 初始化内容提供器的时候调用,通常会在这里完成对数据库的创建和升级等操作* 返回true表示内容提供器初始化成功,返回false则表示失败.*/public boolean onCreate() {//对当前内容提供器需要的资源进行初始化mMyDatabaseHelper = new MyDatabaseHelper(getContext(), "info.db", null, 1);db = mMyDatabaseHelper.getWritableDatabase();Log.d(TAG, "onCreate: 内容提供器初始化完成");return true;}@Nullable@Overridepublic Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable Stringselection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {//查询方法,通过解析uri来判断想要查询哪个程序的哪个表.通过UriMatchder进行匹配 如果有就返回前面addUri()中填入的codeCursor cursor = null;switch (sUriMatcher.match(uri)) {case BOOK_DIT:cursor = db.query("book", projection, selection, selectionArgs, null, null,sortOrder);Log.d(TAG, "query: 查询整个表" + cursor);return cursor;case BOOK_ITEM:String itemId = uri.getPathSegments().get(1);cursor = db.query("book", projection, "id=?", new String[]{itemId}, null, null,sortOrder);/*** .getPathSegments()它会将内容URI权限之后的部分以 / 进行分割,并把分割后的结果放入到一个字符串列表中,* 返回的列表[0]存放的就是路径,[1]存放的就是id*/return cursor;case CATEGORT_ITEM:String itemId2 = uri.getPathSegments().get(1);cursor = db.query("category", projection, "id=?", new String[]{itemId2}, null, null,sortOrder);return cursor;case CATEGORY_DIR:cursor = db.query("category", projection, selection, selectionArgs, null, null,sortOrder);return cursor;default:return cursor;}}@Nullable@Overridepublic String getType(@NonNull Uri uri) {switch (sUriMatcher.match(uri)) {case BOOK_DIT:return "vnd.android.cursor.dir/vnd.com.example.h.content_demo_provider.book";case BOOK_ITEM:return "vnd.android.cursor.item/vnd.com.example.h.content_demo_provider.book";case CATEGORT_ITEM:return "vnd.android.cursor.item/vnd.com.example.h.content_demo_provider.category";case CATEGORY_DIR:return "vnd.android.cursor.dir/vnd.com.example.h.content_demo_provider.category";default:return null;}}@Nullable@Overridepublic Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {Uri uriReturn = null;switch (sUriMatcher.match(uri)) {case BOOK_DIT:case BOOK_ITEM:long value = db.insert("book", null, values);uriReturn = Uri.parse("content://" + AUTHORITY + "/book/" + value);return uriReturn;//返回新插入行的行id,如果发生错误则返回-1case CATEGORT_ITEM:case CATEGORY_DIR:long value2 = db.insert("category", null, values);uriReturn = Uri.parse("content://" + AUTHORITY + "/category/" + value2);return uriReturn;default:return uriReturn;}}@Overridepublic int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[]selectionArgs) {int deleteRows = 0;switch (sUriMatcher.match(uri)) {case BOOK_DIT:deleteRows = db.delete("book", selection, selectionArgs);return deleteRows;case BOOK_ITEM:String itemId1 = uri.getPathSegments().get(1);deleteRows = db.delete("book", "id=?", new String[]{itemId1});return deleteRows;case CATEGORT_ITEM:String itemId2 = uri.getPathSegments().get(1);deleteRows = db.delete("category", "id=?", new String[]{itemId2});return deleteRows;case CATEGORY_DIR:deleteRows = db.delete("category", selection, selectionArgs);return deleteRows;default:return deleteRows;}}@Overridepublic int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable Stringselection, @Nullable String[] selectionArgs) {int updateRows = 0;switch (sUriMatcher.match(uri)) {case BOOK_DIT:updateRows = db.update("book", values, selection, selectionArgs);return updateRows;case BOOK_ITEM:String itemId1 = uri.getPathSegments().get(1);updateRows = db.update("book", values, "id=?", new String[]{itemId1});return updateRows;case CATEGORT_ITEM:String itemId2 = uri.getPathSegments().get(1);updateRows = db.update("category", values, "id=?", new String[]{itemId2});return updateRows;case CATEGORY_DIR:updateRows = db.update("category", values, selection, selectionArgs);return updateRows;default:return updateRows;}}
}复制代码

新出现的方法:

uri.getPathSegments().get(1); //getPathSegments()返回一个集合,它将uri中authority后面的内容进行分割 即get(0)表名和id的值get(1) 这样取出来的id就可以当作条件对数据库进行条件查询了 .

3.2 跨程序获取数据

public class MainActivity extends AppCompatActivity implements View.OnClickListener {private Button btn1, btn2, btn3, btn4;private Uri mUriBook = Uri.parse("content://com.example.h.content_demo_provider/book");private Uri mUriCategory = Uri.parse("content://com.example.h.content_demo_provider/category");@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();//randomInsert();iQuery();}private void initView() {btn1 = findViewById(R.id.button);btn2 = findViewById(R.id.button2);btn3 = findViewById(R.id.button4);btn4 = findViewById(R.id.button5);btn4.setOnClickListener(this);btn3.setOnClickListener(this);btn2.setOnClickListener(this);btn1.setOnClickListener(this);}private void iQuery() {Cursor cursor = getContentResolver().query(mUriBook, null, null, null, null);if (cursor != null) {while (cursor.moveToNext()) {String id = cursor.getString(cursor.getColumnIndex("id"));String name = cursor.getString(cursor.getColumnIndex("name"));String author = cursor.getString(cursor.getColumnIndex("author"));System.out.println(id + name + author);}} else {System.out.println("为空");}}private void randomInsert() {ContentValues contentValues1 = new ContentValues();ContentValues contentValues2 = new ContentValues();for (int i = 0; i < 5; i++) {contentValues1.put("name", "name" + i);contentValues1.put("author", "author" + i);System.out.println(getContentResolver().insert(mUriBook, contentValues1));contentValues2.put("type", "type" + i);contentValues2.put("code", "code" + i);System.out.println(getContentResolver().insert(mUriCategory, contentValues2)); ;}//这个方法只是通过内容提供器对另一个程序中的数据库写一点数据进去方便我们进行后续的CRUD.}private void iDelete(String value) {ContentValues contentValues = new ContentValues();int x = 0;Uri uri = Uri.parse("content://com.example.h.content_demo_provider/book/" + value);x = getContentResolver().delete(uri, null, null);Log.d("TAG", "iDelete: 删除后的返回值:" + x);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.button:iQuery();break;case R.id.button2:break;case R.id.button4:iDelete("1");break;case R.id.button5:break;default:break;}}
}
复制代码

主要还是通过定义好Uri 然后传递给内容提供器,告诉它你想做什么,最后他会将执行结果通过返回值告诉你,更新功能和其他几个差不多就不展开解析了,

3.3 总结

经过上面的实践,给我的感觉就是, 某个程序A提供了一个内容提供器(这个内容提供器本质上就是对本程序内的数据库进行CRUD 不过可以对他进行限制一些权限 只给想给的数据), 然后程序B想要访问程序A中的某些数据, 程序B可以通过Uri对程序A允许的范围内进行CRUD

内容提供器(Content-Provider)完整使用指南相关推荐

  1. 第一行代码学习笔记第七章——探究内容提供器

    知识点目录 7.1 内容提供器简介 7.2 运行权限 * 7.2.1 Android权限机制详解 * 7.2.2 在程序运行时申请权限 7.3 访问其他程序中的数据 * 7.3.1 ContentRe ...

  2. 创建自己的内容提供器

    如果想要实现跨程序共享数据的功能,官方推荐的方式就是使用内容提供器,可以通过新建一个类去继承ContentProvider的方式来创建一个自己的内容提供器.ContentProvider类中有6个抽象 ...

  3. android 提供的方法,Android编程之创建自己的内容提供器实现方法

    本文实例讲述了Android编程之创建自己的内容提供器实现方法.分享给大家供大家参考,具体如下: 我们学习了如何在自己的程序中访问其他应用程序的数据.总体来说思 路还是非常简单的,只需要获取到该应用程 ...

  4. android内容提供器读取图片,android实现拍照或从相册选取图片

    从相册或拍照更换图片功能的实现:(取图无裁剪功能) 获取图片方式: (类似更换头像的效果) 1.手机拍照 选择图片: 2.相册选取图片: 本文只是简单实现该功能,页面展示有些简陋,运行效果图如下: 创 ...

  5. android--创建自己的内容提供器

    想要实现跨程序共享数据需要用到内容提供器,内容提供器给别的应用提供了访问接口,可以新建一个类去继承ContentProvider来创建一个自己的内容提供器.ContentProvider类中有6种抽象 ...

  6. Android入门(13)| Android权限 与 内容提供器

    文章目录 普通权限与危险权限 运行时申请权限 内容提供器 运用安卓封装好的内容提供器 自实现的内容提供器 概念 实现 普通权限与危险权限 主要用于不同应用程序之间在保证被访数据的安全性的基础上,实现数 ...

  7. 系出名门Android(9) - 数据库支持(SQLite), 内容提供器(ContentProvider)

    [索引页] [×××] 系出名门Android(9) - 数据库支持(SQLite), 内容提供器(ContentProvider) 作者:webabcd 介绍 在 Android 中使用 SQLit ...

  8. Android 内容提供器---简介

    内容提供器管理结构化的数据集的访问.它们封装数据.提供定义数据安全的机制.内容提供器是用运行在另一个进程中的代码连接另一个进程中的数据的标准接口. 当你想要访问内容提供器中的数据时,使用应用程序的Co ...

  9. 第七章:跨程序共享数据-探究内容提供器

    运行时权限 Android 6.0 之后的系统提供了两种权限: 普通权限.例如:访问网络状态.监控开机是否完成等.这种权限会在安装 APP 时询问用户是否同意. 危险权限,也就是说运行时权限,需要在程 ...

最新文章

  1. pyqt5讲解5:窗口绘图类控件QPainter,QPen,QBrush,QPixmap
  2. python访问序列元素的编号用什么括起来_python-重新编号数组中元素的有效方法...
  3. c语言如何编写mysql客户端_【C/C++学院】(23)Mysql数据库编程--C语言编程实现mysql客户端...
  4. 增量导出_[华为]一种实用的增量式深度CTR模型训练方法
  5. 安卓系统为何这么容易被黑客入侵
  6. 深度剖析Java数据结构之队列(一)——双端队列(ArrayDeque)
  7. win7旗舰版系统如何重装系统win10
  8. C++引用,常量优化,四种类型转换符
  9. 夜弦网页游戏专用浏览器(支持最小化隐藏后台多开挂机)
  10. WPS表格简单入门_我的笔记_一些常用操作
  11. 第2章第6节:使用Slider滑杆在指定的范围内选择一个数值 [SwiftUI快速入门到实战]
  12. Python小工具:批量给视频加水印!
  13. 八个设计师接私活的网站,你有技术就有钱
  14. No matching provisioning profile found: Your build settings specify a provisioning profile with the
  15. 什么是美颜SDK?怎样选择美颜SDK?
  16. 可ping通主机而不能连上主机端口的一般原因
  17. 推荐几本纳米级光刻机书籍
  18. vue生命周期(超详解!)
  19. Paper pusher 摆弄文件的人
  20. PAT日志 1011

热门文章

  1. myeclipse mysql连接_MyEclipse连接MySQL数据库图文教程
  2. liunx mysql模块_linux下安装MySQLdb模块_MySQL
  3. json文件中的双引号隐藏
  4. 帆软日期格式转换_时间转换为年月日
  5. php 获取html中图片不显示,HTML中img标签src属性用PHP设置后不显示
  6. 字符串相加/大数相加(代码极短)
  7. 防止程序多开的两种方法
  8. Android中使用SeekBar拖动条实现改变图片透明度
  9. C#中全局作用域的常量、字段、属性、方法的定义与使用
  10. Python中使用pip install ncmbot时各种错误解决办法