Android之ContentProvider
文章目录
- 简介
- 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相关推荐
- 009 Android之ContentProvider
文章目录 Android文件权限简述 ContentProvider 内容提供者 ContentResolver URI 什么是URI URI示例 URI和URL ContentProvider实例 ...
- provider android简书,Android 使用ContentProvider在应用间共享数据
Android 使用ContentProvider在应用间共享数据 题图 www.gratisography.com 在开发的过程中,有时会有需求要实现应用程序之间实现数据共享,在Android系统中 ...
- Android之ContentProvider数据存储
一.ContentProvider保存数据介绍 一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据完全暴露出去,而且ContentProvider是以类似数据库中表的方式将数 ...
- Android About ContentProvider
Contents: ContentProvider Structural Analysis What is URI? Query Attribute,SQL UNION ContentProvid ...
- android使用ContentProvider初始化sdk,初始化时机
文章目录 系列目录 前言 什么是ContentProvider sdk初始化 常规初始化方案 使用ContentProvider初始化sdk 为什么ContentProvider可以作为sdk初始化 ...
- Android【ContentProvider案例】
ContentProvider实现跨程序数据共享,实例如下: 数据库基于数据存储中的使用的数据库,点击Here 1]ContentProvider类 public class ContentProvi ...
- android中contentProvider及ContentResolver
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程 这个技术是解决应用之间的一个调用,如常见的应用间数据库查询,内容提供者暴露接口,内容解析器Conten ...
- android 53 ContentProvider内容提供者
ContentProvider内容提供者:像是一个中间件一样,一个媒介一样,可以以标准的增删改差操作对手机的文件.数据库进行增删改差.通过ContentProvider查找sd卡的音频文件,可以提供标 ...
- Android 中ContentProvider和Uri详解
一.使用ContentProvider(内容提供者)共享数据 ContentProvider在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给 ...
最新文章
- 离线安装Visual Studio Code插件
- Android 音视频深入 十一 FFmpeg和AudioTrack播放声音(附源码下载)
- 分形之龙形曲线(Dragon Curve)
- boost::geometry模块实现自定义Linestring示例
- 任务管理器在右下角的图标不显示
- classcastexception异常_优雅的异常处理
- tomcat 设置java_为tomcat配置java环境变量
- c语言题目1120,九度题目1120:全排列
- python中文叫什么-python中文别名
- 华硕主板如何设置开机自启_华硕主板自动开机的设置方法.doc
- 计算机输出科学计数法,python不用科学计数法
- KC伺服舵机带参四方向Demo程序
- 计算机网络——IPv4地址概述
- 数据结构:图:图形和图形模型(Graphs and Graph Models)
- 2022年全球市场颈椎按摩仪总体规模、主要生产商、主要地区、产品和应用细分研究报告
- android studio seekbar 简单音乐播放器
- 止疼药-盐酸羟考酮缓释片//2021-2-20
- 超详细修改C盘下的用户名(适合win10和win11)
- python爬取微博图片教程_Python爬取微博实例分析
- 【jmeter性能测试】模拟多个IP同时登录