文章目录

  • 简介
  • ContentResolver、ContentProvider和URI
  • Uri简介
    • Uri处理
  • 自定义ContentProvider
    • 1. 新建类继承ContentProvider
    • 2. Activity通过通过contentResolver传入Uri调用对应ContentProvider的方法。
    • 其他代码:
      • databaseHelper代码
      • 创建数据库的activity代码

简介

内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访数据的安全性(因为只能访问另一个应用程序暴露出的数据,所以安全)。

ContentResolver、ContentProvider和URI

从ContentResolver、 ContentProvider 和 Uri的关系来看,无论是 ContentResolver,还是ContentProvider,它们所提供的CRUD方法的第一个参数都是 Uri。也就是说, Uri是ContentResolver和 ContentProvider 进行数据交换的标识。如果要访问内容提供器中共享的数据,就一定要记住ContentResolver类,ContentResolver 对指定 Uri 执行 CRUD 等数据操作,这些 CRUD 操作实际会委托给该 Uri 对应的ContentProvider来实现。

通常来说,假如A应用通过ContentResolver执行CRUD操作,这些CRUD操作都需要指定Uri参数, Android系统就根据该Uri 找到对应的ContentProvider(该ContentProvider 通常属于B应用),ContentProvider则负责实现CRUD方法,完成对底层数据的增、删、改、查等操作,这样就可以让A应用访问、修改B应用的数据了。ContentResolver、 Uri、 ContentProvider三者之间的关系如下图所示。

从图可以看出,以指定Uri为标识, ContentResolver可以实现“间接调用” ContentProvider的CRUD方法。

  • 当A应用调用ContentResolver的 insert()方法时,实际上相当于调用了该Uri对应的ContentProvider (该ContentProvider 属于 B应用)的 insert()方法。
  • 当A应用调用ContentResolver 的 update()方法时,实际上相当于调用了该Uri对应的ContentProvider (该ContentProvider 属于 B应用)的 update()方法。
  • 当A应用调用ContentResolver的delete ()方法时,实际上相当于调用了该Uri对应的ContentProvider (该ContentProvider 属于 B应用)的 delete(方法。
  • 当A应用调用ContentResolver 的 query ()方法时,实际上相当于调用了该Uri对应的ContentProvider (该ContentProvider 属于 B应用)的query(方法。通过上面这种关系,即可实现让A应用访问、使用B应用底层的数据。

Uri简介

ContentProvider 要求的Uri与http (httt://localhost/index.jsp)的类似,例如:content://com.example.demo.MyContentProvider/word

它可以分为如下三个部分:

  • content://: 这个部分是 Android的ContentProvider规定的,就像上网的协议默认是http:/一样。暴露 ContentProvider、访问 ContentProvider 的协议默认是 content://。
  • com.example.demo.MyContentProvider: 这个部分就是 ContentProvider 的authorities。authority是用于对不同的应用程序做区分的,一般为了避免冲突,都会采用程序包名的方式来进行命名。比如某个程序的包名是com.example.app,那么该程序对应的authority就可以命名为com.example.app. provider。系就是由这个部分来找到操作哪个ContentProvider的。只要访问指定的ContentProvider,这个部分就是固定的。
  • words: 资源部分(或者说数据部分)。当访问者需要访问不同资源时,这个部分是动态改变的。相当于就是Uri中的Path,path则是用于对同一应用程序中不同的表做区分的,通常都会添加到authority的后面。比如某个程序的数据库里存在两张表: tablel和table2,这时就可以将path分别命名为/tablel和/table2,然后把authority和path进行合,内容URI就变成了com.example.app.provider/tablel和 com.example.app.provider/table2

上面的Uri只是一个字符串,还需要将它解析成Uri对象才可以作为参数传入。解析的代码如 下:

Uri uri = Uri.parse(" content:// com.example.demo.MyContentProvider/table")

Uri处理

一个标准的内容URI写法是这样的:
content://com.example.app.provider/tablel

这就表示调用方期望访问的是com.example.app这个应用的tablel表中的数据。除此之外,我们还可以在这个内容URI的后面加上一个id,如下所示:
content://com.example.app.provider/tablel/1
这就表示调用方期望访问的是com.example.app这个应用的tablel表中id为1的数据。
内容URI的格式主要就只有以上两种,以路径结尾就表示期望访问该表中所有的数据, 以id结尾就表示期望访问该表中拥有相应id的数据。 我们可以使用通配符的方式来分别匹配这两种格式的内容URI,规则如下。

  • *: 表示匹配任意长度的任意字符。
  • #:表示匹配任意长度的数字。所以,一个能够匹配任意表的内容URI格式就可以写成:

content://com.example.app.provider/*

而一个能够匹配tablel表中任意一行数据的内容URI格式就可以写成:

content://com.example.app.provider/tablel/#

Android系统提供了UriMatcher工具类可以轻松地实现匹配内容URI的功能。URI工具类主要提供了两个方法:

  • void addURI(String authority, String path, int code): 该方法用于向 UriMatcher 对象注册Uri.其中 authority 和 path 组合成一个 Uri,而 code 则代表该Uri 对应的标识码。
  • int match(Uri uri): 根据前面注册的Uri 来判断指定Uri对应的标识码。如果找不到匹配的标识码,该方法将会返回-1。

例如,可以通过如下代码来创建UriMatcher 对象。

UriMatcher matcher new UriMatcher(UriMatcher.No MATcH);
matcher.adduRi("com.example.app.provider", "table", );
matcher.adduRi ("com.example.app.provider", "table/#", 2);

上面创建的UriMatcher 对象注册了两个Uri,其中 com.example.app.provider/table 对应的标识码为1; com.example.app.provider/table/#对应的标识码为2, #为通配符。
这就意味着如下匹配结果

matcher.match (Uri.parse ("content://com.example.app.provider/table"));//返回标识码1
matcher.match (Uri.parse ("content://com.example.app.provider/table/2));//返回标识码2
matcher.match (Uri.parse("content://com.example.app.provider/table/10"));//返回标识码2

到底需要为UriMatcher对象注册多少个Uri,则取决于系统的业务需求。对于content://com.example.app.provider/table这个Uri,它的资源部分为table,这种资源为table,这种资源通常代表访问指定数据项,其中最后一个数据代往往代表了数据的ID。

除此之外,Android还提供了一个ContentUris工具类,它是一个操作Uri字符串的工具类,提供了如下两个工具方法。

  • withAppendedId(uri, id): 用于为路径加上 ID 部分。例如:
Uri uri = uri.parse("content://com.example.app.provider/table");
Uri resulturi = Contentüris.withAppendedId (uri, 2);// 成i的uri为: "content://com.example.app.provider/table/2"
  • parseld(uri): 用于从指定Uri 中解析出所包含的ID值。例如:
Uri uri Uri.parse("content://com.example.app.provider/table/2");
int wordId = ContentUris.parseId(uri); // 获取的结果为2

自定义ContentProvider

1. 新建类继承ContentProvider

ContentProvider类中有6个抽象方法,子类继承它的时候要全部重写


public class DatabaseProvider extends ContentProvider {public static final int BOOK_DIR = 0;public static final int BOOK_ITEM = 1;public static final int CATEGORY_DIR = 2;public static final int CATEGORY_ITEM = 3;public static final String AUTHORITY = "com.example.androidlearn";private static UriMatcher uriMatcher;private MyDatabaseHelper dbHelper;static {uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);}public DatabaseProvider() {}@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {SQLiteDatabase db = dbHelper.getWritableDatabase();int deletedRows = 0;switch (uriMatcher.match(uri)) {case BOOK_DIR:deletedRows = db.delete("Book", selection, selectionArgs);break;case BOOK_ITEM:String bookId = uri.getPathSegments().get(1);deletedRows = db.delete("Book", "id = ?", new String[] { bookId });break;case CATEGORY_DIR:deletedRows = db.delete("Category", selection, selectionArgs);break;case CATEGORY_ITEM:String categoryId = uri.getPathSegments().get(1);deletedRows = db.delete("Category", "id = ?", new String[] { categoryId });break;default:break;}return deletedRows;}/*** 根据传入的内容URI来返回相应的MIME类型* @param uri* @return*/@Overridepublic String getType(Uri uri) {switch (uriMatcher.match(uri)) {case BOOK_DIR:return "vnd.android.cursor.dir/vnd.com.example.databasetest. provider.book";case BOOK_ITEM:return "vnd.android.cursor.item/vnd.com.example.databasetest. provider.book";case CATEGORY_DIR:return "vnd.android.cursor.dir/vnd.com.example.databasetest. provider.category";case CATEGORY_ITEM:return "vnd.android.cursor.item/vnd.com.example.databasetest. provider.category";}return null;}// 插入数据@Overridepublic Uri insert(Uri uri, ContentValues values) {SQLiteDatabase db = dbHelper.getWritableDatabase();Uri uriReturn = null;switch (uriMatcher.match(uri)) {case BOOK_DIR:case BOOK_ITEM:long newBookId = db.insert("Book", null, values);uriReturn = Uri.parse("content://" + AUTHORITY + "/book/" + newBookId);break;case CATEGORY_DIR:case CATEGORY_ITEM:long newCategoryId = db.insert("Category", null, values);uriReturn = Uri.parse("content://" + AUTHORITY + "/category/" + newCategoryId);break;default:break;}return uriReturn;}// contentProvider第一次被创建的时候执行@Overridepublic boolean onCreate() {dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null ,1);return true;}/*** 查询数据* @param uri* @param projection* @param selection* @param selectionArgs* @param sortOrder* @return*/@Overridepublic Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {SQLiteDatabase db = dbHelper.getReadableDatabase();Cursor cursor = null;switch (uriMatcher.match(uri)){case BOOK_DIR:cursor = db.query("Book", projection, selection, selectionArgs, null, null, sortOrder);break;case BOOK_ITEM:String bookId = uri.getPathSegments().get(1);cursor = db.query("Book", projection, "id = ?", new String[] { bookId }, null, null, sortOrder);break;case CATEGORY_DIR:cursor = db.query("Category", projection, selection, selectionArgs, null, null, sortOrder);break;case CATEGORY_ITEM:// String categoryId = ContentUris.parseId(uri);String categoryId = uri.getPathSegments().get(1);cursor = db.query("Category", projection, "id = ?", new String[] { categoryId }, null, null, sortOrder);break;default:break;}return cursor;}//更新数据@Overridepublic int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {SQLiteDatabase db = dbHelper.getWritableDatabase();int updatedRows = 0;switch (uriMatcher.match(uri)) {case BOOK_DIR:updatedRows = db.update("Book", values, selection, selectionArgs);break;case BOOK_ITEM:String bookId = uri.getPathSegments().get(1);updatedRows = db.update("Book", values, "id = ?", new String[] { bookId });break;case CATEGORY_DIR:updatedRows = db.update("Category", values, selection, selectionArgs);break;case CATEGORY_ITEM:String categoryId = uri.getPathSegments().get(1);updatedRows = db.update("Category", values, "id = ?", new String[] { categoryId });break;default:break;}return updatedRows;}
}

说一下抽象方法getType()除此之外,还有一个方法你会比较陌生,即getType()方法。它是所有的内容提供器都必须提供的一个方法,用于获取Uri对象所对应的MIME类型。一个内容URI所对应的MIME字符串主要由3部分组成, Android对这3个部分做了如下格式规定。

  • 必须以vnd开头。
  • 如果内容URI 以路径结尾,则后接 android.cursor.dir/,如果内容 URI以 id结尾,则后接 android.cursor.item/
  • 最后接上 vnd.<authority>. 。

所以,对于content://com.example.app.provider/table1这个内容URI,它所对应的MIME类型就可以写成:

vnd.android.cursor.dir/vnd.com.example.app.provider.tablel

对于content://com.example.app.provider/table1/1这个内容URI,它所对应的MIME类型就可以写成:

vnd.android.cursor.item / vnd.com.example.app.provider.tablel

2. Activity通过通过contentResolver传入Uri调用对应ContentProvider的方法。

public class ContentProviderActivity extends AppCompatActivity {private String newId;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_content_provider_test);Button addData = (Button) findViewById(R.id.add_data);addData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 添加数据Uri uri = Uri.parse("content://com.example.androidlearn/book");ContentValues values = new ContentValues();values.put("name", "A Clash of Kings");values.put("author", "George Martin");values.put("pages", 1040);values.put("price", 55.55);Uri newUri = getContentResolver().insert(uri, values);newId = newUri.getPathSegments().get(1);}});Button queryData = (Button) findViewById(R.id.query_data);queryData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 查询数据Uri uri = Uri.parse("content://com.example.androidlearn/book");Cursor cursor = getContentResolver().query(uri, null, null, null, null);if (cursor != null) {while (cursor.moveToNext()) {@SuppressLint("Range") String name = cursor.getString(cursor. getColumnIndex("name"));@SuppressLint("Range") String author = cursor.getString(cursor. getColumnIndex("author"));@SuppressLint("Range") int pages = cursor.getInt(cursor.getColumnIndex ("pages"));@SuppressLint("Range") double price = cursor.getDouble(cursor. getColumnIndex("price"));Log.d("MainActivity", "book name is " + name);Log.d("MainActivity", "book author is " + author);Log.d("MainActivity", "book pages is " + pages);Log.d("MainActivity", "book price is " + price);}cursor.close();}}});Button updateData = (Button) findViewById(R.id.update_data);updateData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 更新数据Uri uri = Uri.parse("content://com.example.androidlearn/book/" + newId);ContentValues values = new ContentValues();values.put("name", "A Storm of Swords");values.put("pages", 1216);values.put("price", 24.05);getContentResolver().update(uri, values, null, null);}});Button deleteData = (Button) findViewById(R.id.delete_data);deleteData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 删除数据Uri uri = Uri.parse("content://com.example.androidlearn/book/" + newId);getContentResolver().delete(uri, null, null);}});}}

其他代码:

databaseHelper代码


public class MyDatabaseHelper extends SQLiteOpenHelper {public static final String CREATE_BOOK = "create table Book ("+ "id integer primary key autoincrement, "+ "author text, "+ "price real, "+ "pages integer, "+ "name text)";public static final String CREATE_CATEGORY = "create table Category ("+ "id integer primary key autoincrement, "+ "category_name text, "+ "category_code integer)";private Context mContext;public MyDatabaseHelper(Context context, String name,SQLiteDatabase.CursorFactory factory, int version) {super(context, name, factory, version);mContext = context;}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL(CREATE_BOOK);db.execSQL(CREATE_CATEGORY);// Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_SHORT).show();}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {db.execSQL("drop table if exists Book");db.execSQL("drop table if exists Category");onCreate(db);}}

创建数据库的activity代码

package com.example.androidlearn.contentProvider;import androidx.appcompat.app.AppCompatActivity;import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;import com.example.androidlearn.R;public class DatabaseActivity extends AppCompatActivity {private MyDatabaseHelper dbHelper;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_content_provider);dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 1);Button createDatabase = (Button) findViewById(R.id.create_database);createDatabase.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {dbHelper.getWritableDatabase();}});Button addData = (Button) findViewById(R.id.add_data);addData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {SQLiteDatabase db = dbHelper.getWritableDatabase();ContentValues values = new ContentValues();// 开始组装第一条数据values.put("name", "The Da Vinci Code");values.put("author", "Dan Brown");values.put("pages", 454);values.put("price", 16.96);db.insert("Book", null, values); // 插入第一条数据values.clear();// 开始组装第二条数据values.put("name", "The Lost Symbol");values.put("author", "Dan Brown");values.put("pages", 510);values.put("price", 19.95);db.insert("Book", null, values); // 插入第二条数据}});Button updateData = (Button) findViewById(R.id.update_data);updateData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {SQLiteDatabase db = dbHelper.getWritableDatabase();ContentValues values = new ContentValues();values.put("price", 10.99);db.update("Book", values, "name = ?", new String[] { "The Da Vinci Code" });}});Button deleteButton = (Button) findViewById(R.id.delete_data);deleteButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {SQLiteDatabase db = dbHelper.getWritableDatabase();db.delete("Book", "pages > ?", new String[] { "500" });}});Button queryButton = (Button) findViewById(R.id.query_data);queryButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {SQLiteDatabase db = dbHelper.getWritableDatabase();// 查询Book表中所有的数据Cursor cursor = db.query("Book", null, null, null, null, null, null);if (cursor.moveToFirst()) {do {// 遍历Cursor对象,取出数据并打印@SuppressLint("Range") String name = cursor.getString(cursor.getColumnIndex("name"));@SuppressLint("Range") String author = cursor.getString(cursor.getColumnIndex("author"));@SuppressLint("Range") int pages = cursor.getInt(cursor.getColumnIndex("pages"));@SuppressLint("Range") double price = cursor.getDouble(cursor.getColumnIndex("price"));Log.d("MainActivity", "book name is " + name);Log.d("MainActivity", "book author is " + author);Log.d("MainActivity", "book pages is " + pages);Log.d("MainActivity", "book price is " + price);} while (cursor.moveToNext());}cursor.close();}});}
}

Android之ContentProvider相关推荐

  1. 009 Android之ContentProvider

    文章目录 Android文件权限简述 ContentProvider 内容提供者 ContentResolver URI 什么是URI URI示例 URI和URL ContentProvider实例 ...

  2. provider android简书,Android 使用ContentProvider在应用间共享数据

    Android 使用ContentProvider在应用间共享数据 题图 www.gratisography.com 在开发的过程中,有时会有需求要实现应用程序之间实现数据共享,在Android系统中 ...

  3. Android之ContentProvider数据存储

    一.ContentProvider保存数据介绍 一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据完全暴露出去,而且ContentProvider是以类似数据库中表的方式将数 ...

  4. Android About ContentProvider

    Contents: ContentProvider Structural Analysis What is URI? Query Attribute,SQL UNION   ContentProvid ...

  5. android使用ContentProvider初始化sdk,初始化时机

    文章目录 系列目录 前言 什么是ContentProvider sdk初始化 常规初始化方案 使用ContentProvider初始化sdk 为什么ContentProvider可以作为sdk初始化 ...

  6. Android【ContentProvider案例】

    ContentProvider实现跨程序数据共享,实例如下: 数据库基于数据存储中的使用的数据库,点击Here 1]ContentProvider类 public class ContentProvi ...

  7. android中contentProvider及ContentResolver

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程 这个技术是解决应用之间的一个调用,如常见的应用间数据库查询,内容提供者暴露接口,内容解析器Conten ...

  8. android 53 ContentProvider内容提供者

    ContentProvider内容提供者:像是一个中间件一样,一个媒介一样,可以以标准的增删改差操作对手机的文件.数据库进行增删改差.通过ContentProvider查找sd卡的音频文件,可以提供标 ...

  9. Android 中ContentProvider和Uri详解

    一.使用ContentProvider(内容提供者)共享数据 ContentProvider在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给 ...

最新文章

  1. 离线安装Visual Studio Code插件
  2. Android 音视频深入 十一 FFmpeg和AudioTrack播放声音(附源码下载)
  3. 分形之龙形曲线(Dragon Curve)
  4. boost::geometry模块实现自定义Linestring示例
  5. 任务管理器在右下角的图标不显示
  6. classcastexception异常_优雅的异常处理
  7. tomcat 设置java_为tomcat配置java环境变量
  8. c语言题目1120,九度题目1120:全排列
  9. python中文叫什么-python中文别名
  10. 华硕主板如何设置开机自启_华硕主板自动开机的设置方法.doc
  11. 计算机输出科学计数法,python不用科学计数法
  12. KC伺服舵机带参四方向Demo程序
  13. 计算机网络——IPv4地址概述
  14. 数据结构:图:图形和图形模型(Graphs and Graph Models)
  15. 2022年全球市场颈椎按摩仪总体规模、主要生产商、主要地区、产品和应用细分研究报告
  16. android studio seekbar 简单音乐播放器
  17. 止疼药-盐酸羟考酮缓释片//2021-2-20
  18. 超详细修改C盘下的用户名(适合win10和win11)
  19. python爬取微博图片教程_Python爬取微博实例分析
  20. 【jmeter性能测试】模拟多个IP同时登录

热门文章

  1. 多变量干扰事件发生下的地铁客流预测
  2. 有关卡方检验(chi-square test )
  3. 四、点对点的传输层【网络全景图分析系列】
  4. 云服务器共享文件格式设置,云服务器文件共享设置
  5. 怎么把java源代码封装,如何把JAVA程序封装成EXE文件
  6. 虎年降至.一款2022虎年为主的一款头像制作小程序源码。
  7. SegmentFault 思否发布开源问答社区软件 Answer
  8. vxworks培训笔记
  9. ucenter通信实现同步登录、同步退出(详细)
  10. 如何批量将多个 PDF 文档快速合并成一个文档