Android《第五章:ContentProvider》
ContentProvider(外共享数据)
ContentProvider在Android中的作用是对外共享数据,也就是说通过ContentProvider可以吧应用中的数据共享给其他应用访问,其他的应用可以通过ContentProvider对应用中的数据进行增、删、改、查的操作。使用ContentProvider 的好处是统一了数据访问方式。它实际上是对SqliteOpenHelper的进一步封装,通过Uri映射来判断选择需要操作数据库中的哪个表并且进行想要的处理。
- Uri
首先我们了解下Uri,Uri代表操作数据表的绝对路径,它主要包含两部分信息。一个是需要操作的ContentProvider,二是对ContentProvider中的哪个表进行操作。对于ContentProvider来说,一个Uri由以下几部分组成。
- Scheme:是系统定好的 content://
- Authority:是ContentProvider的唯一标识
- Path:是该ContentProvider下要操作的数据库表
- Id: 是指表中的数据项ID
举例:"content://com.bsoft.hs/userinfo/100"
好的既然我们理解了其中的概念那么我们来带大家实践下!
创建两个项目FactoryProvider:是用来共享商品数据。ProviderTest:用来对FactoryProvider所提供的数据进行修改操作。
- FactoryProvider(工厂项目)
首先我们来设计数据结构,假设我们工厂用来提供商品,那么我们需要创建商品数据库(Factory),其次我们需要创建一个水果类型的表用来提供水果类型的商品(Goods)表设计如下。
Goods |
---|
_ID | _Name | _Desc | _Url |
---|---|---|---|
0 | 苹果 | 花牛苹果(正品秦安) | http://shop.bytravel.cn/produce/82B1725B82F9679C/ |
好的我们数据表建立完成,我们都知道Android 内部提供了SQlite数据库,是一种关系型轻量级的数据库,那么我们就用它了。
/** * Created by 泅渡者 * Created on 2017/9/28. * */public class Goods { /** * 数据字段 **/ public static final String ID = "_id"; public static final String NAME = "_name"; public static final String DESC = "_desc"; public static final String URL = "_url"; /** * 数据默认排序 **/ public static final String DEFAULT_SORT_ORDER = "_id asc"; /** * ContentProvider的唯一标识 **/ public static final String AUTHORITY = "com.bsoft.factoryprovider"; /** * METHOD_GET_ITEM_COUNT和KEY_ITEM_COUNT两个常量是调用ContentProvider接口的一个未公开函数call来查询数据时用的, * 使用这个call函数时,传入参数METHOD_GET_ITEM_COUNT表示我们要调用我们自定义的ContentProvider子类中的 * getItemCount函数来获取数据库中的文章信息条目的数量, * 结果放在一个Bundle中以KEY_ITEM_COUNT为关键字的域中。 **/ public static final String METHOD_GET_ITEM_COUNT = "METHOD_GET_ITEM_COUNT"; public static final String KEY_ITEM_COUNT = "KEY_ITEM_COUNT"; /** * CONTENT_URI:表示通过ID来访问数据 * CONTENT_POS_URI:表示是通过位置来访问数据(这里的位置并比一定和Id 相同) **/ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/item"); public static final Uri CONTENT_POS_URI = Uri.parse("content://" + AUTHORITY + "/item"); /** * MIME类型 **/ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/com.bsoft.factoryprovider.goods"; public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/com.bsoft.factoryprovider.goods"; /** * URI匹配规则的匹配码 **/ public static final int ITEM = 1; public static final int ITEM_ID = 2; public static final int ITEM_POS = 3;}
ID、NAME、DESC 、URL :是我们的数据表字段
DEFAULT_SORT_ORDER :是数据的排序方式
AUTHORITY:我们前面提过事唯一标识一般情况下用包名
MIME: 每个MIME类型由两部分组成,前面是数据的大类别,后面定义具体的种类。在Content Provider中,URI所对应的资源的MIME类型的大类别根据同时访问的资源的数量分为两种,对于访问单个资源的URI,它的大类别就为vnd.android.cursor.item,而对于同时访问多个资源的URI,它的大类别就为vnd.android.cursor.dir。Content Provider的URI所对应的资源的MIME类型的具体类别就需要由Content Provider的提供者来设置了,它的格式一般为vnd.[company name].[resource type]的形式。
接下来我们需要创建一个数据库助手类:
/** * Created by 泅渡者 * Created on 2017/9/28. */public class DBHelper extends SQLiteOpenHelper { public static final String DB_NAME = "Factory.db"; public static final String DB_TABLE = "Goods"; public static final int DB_VERSION = 1; /** * 创建数据库SQL **/ private static final String DB_CREATE = "CREATE TABLE " + DB_TABLE + "(" + Goods.ID + " INTEGER PRIMARY KEY, " + Goods.NAME + " text not null, " + Goods.DESC + " text not null, " + Goods.URL + " text not null " + ")"; public DBHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(DB_CREATE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE); onCreate(db); }}
这里有个地方需要注意下 Goods.ID + " INTEGER PRIMARY KEY, "这句话别用 "integer PRIMARY KEY autoincrement"会出现报错具体的原因网上很多。
这些都创建完成后我们来创建我们的利器 GoodsProvider:
/** * Created by 泅渡者 * Created on 2017/9/28. */public class GoodsProvider extends ContentProvider { /** * 定义Uri匹配规则,如果不符合则会返回 (UriMatcher.NO_MATCH) **/ private static final UriMatcher uriMatcher; static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(Goods.AUTHORITY, "item", Goods.ITEM); uriMatcher.addURI(Goods.AUTHORITY, "item/#", Goods.ITEM_ID); uriMatcher.addURI(Goods.AUTHORITY, "pos/#", Goods.ITEM_POS); } /** * 这里的主要作用是隐藏数据库表结构,防止将数据库中的表字段暴露出来 **/ private static final HashMap goodsProjectionMap; static { goodsProjectionMap = new HashMap(); goodsProjectionMap.put(Goods.ID, Goods.ID); goodsProjectionMap.put(Goods.NAME, Goods.NAME); goodsProjectionMap.put(Goods.DESC, Goods.DESC); goodsProjectionMap.put(Goods.URL, Goods.URL); } private DBHelper dbHelper = null; private ContentResolver resolver = null; @Override public boolean onCreate() { Context context = getContext(); resolver = context.getContentResolver(); dbHelper = new DBHelper(context); KLog.d("GoodsProvider onCreate()"); return true; } @Nullable @Override public String getType(@NonNull Uri uri) { switch (uriMatcher.match(uri)) { case Goods.ITEM: return Goods.CONTENT_TYPE; case Goods.ITEM_ID: case Goods.ITEM_POS: return Goods.CONTENT_ITEM_TYPE; default: throw new IllegalArgumentException("Error Uri: " + uri); } } @Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { if (uriMatcher.match(uri) != Goods.ITEM) { throw new IllegalArgumentException("Error Uri: " + uri); } SQLiteDatabase db = dbHelper.getWritableDatabase(); long id = db.insert(DB_TABLE, Goods.ID, values); if (id < 0) { throw new SQLiteException("Unable to insert " + values + " for " + uri); } Uri newUri = ContentUris.withAppendedId(uri, id); resolver.notifyChange(newUri, null); return newUri; } @Override public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { SQLiteDatabase db = dbHelper.getWritableDatabase(); int count = 0; switch (uriMatcher.match(uri)) { case Goods.ITEM: { count = db.update(DB_TABLE, values, selection, selectionArgs); break; } case Goods.ITEM_ID: { String id = uri.getPathSegments().get(1); count = db.update(DB_TABLE, values, Goods.ID + "=" + id + (!TextUtils.isEmpty(selection) ? " and (" + selection + ')' : ""), selectionArgs); break; } default: throw new IllegalArgumentException("Error Uri: " + uri); } resolver.notifyChange(uri, null); return count; } @Nullable @Override public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { SQLiteDatabase db = dbHelper.getReadableDatabase(); SQLiteQueryBuilder sqlBuilder = new SQLiteQueryBuilder(); String limit = null; switch (uriMatcher.match(uri)) { case Goods.ITEM: { sqlBuilder.setTables(DB_TABLE); sqlBuilder.setProjectionMap(goodsProjectionMap); break; } case Goods.ITEM_ID: { String id = uri.getPathSegments().get(1); sqlBuilder.setTables(DB_TABLE); sqlBuilder.setProjectionMap(goodsProjectionMap); sqlBuilder.appendWhere(Goods.ID + "=" + id); break; } case Goods.ITEM_POS: { String pos = uri.getPathSegments().get(1); sqlBuilder.setTables(DB_TABLE); sqlBuilder.setProjectionMap(goodsProjectionMap); limit = pos + ", 1"; break; } default: throw new IllegalArgumentException("Error Uri: " + uri); } Cursor cursor = sqlBuilder.query(db, projection, selection, selectionArgs, null, null, TextUtils.isEmpty(sortOrder) ? Goods.DEFAULT_SORT_ORDER : sortOrder, limit); cursor.setNotificationUri(resolver, uri); return cursor; } @Override public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { SQLiteDatabase db = dbHelper.getWritableDatabase(); int count = 0; switch (uriMatcher.match(uri)) { case Goods.ITEM: { count = db.delete(DB_TABLE, selection, selectionArgs); break; } case Goods.ITEM_ID: { String id = uri.getPathSegments().get(1); count = db.delete(DB_TABLE, Goods.ID + "=" + id + (!TextUtils.isEmpty(selection) ? " and (" + selection + ')' : ""), selectionArgs); break; } default: throw new IllegalArgumentException("Error Uri: " + uri); } resolver.notifyChange(uri, null); return count; } @Override public Bundle call(String method, String request, Bundle args) { KLog.i("FactoryProvider.call:" + method); if (method.equals(Goods.METHOD_GET_ITEM_COUNT)) { return getItemCount(); } throw new IllegalArgumentException("Error method call: " + method); } private Bundle getItemCount() { KLog.i("FactoryProvider.getItemCount:"); SQLiteDatabase db = dbHelper.getReadableDatabase(); Cursor cursor = db.rawQuery("select count(*) from " + DB_TABLE, null); int count = 0; if (cursor.moveToFirst()) { count = cursor.getInt(0); } Bundle bundle = new Bundle(); bundle.putInt(Goods.KEY_ITEM_COUNT, count); cursor.close(); db.close(); return bundle; }}
主要介绍下里面的几个常量:
UriMatcher:在创建UriMatcher对象uriMatcher时,我们传给构造函数的参数为UriMatcher.NO_MATCH,它表示当uriMatcher不能匹配指定的URI时,就返回代码UriMatcher.NO_MATCH。符号#表示匹配任何数字。
articleProjectionMap: 第三点是SQLiteQueryBuilder的使用。在query函数中,我们使用SQLiteQueryBuilder来辅助数据库查询操作,使用这个类的好处是我们可以不把数据库表的字段暴露出来,而是提供别名给第三方应用程序使用,这样就可以把数据库表内部设计隐藏起来,方便后续扩展和维护。第一个参数表示列的别名,第二个参数表示列的真实名称。在这个例子中,我们把列的别名和和真实名称都设置成一样的。
以上就是 我们创建好的ContentProvider,这里我们需要再进一步操作将我们所编写的Class文件进行打包,来供ProviderTest使用。
要看怎么进行打包成Jar的请移步:http://www.jianshu.com/p/5cc512156b07
最后我们需要来进行注册:
<?xml version="1.0" encoding="utf-8"?>
这里由于7.0系统问题需要配置文件存储路径:
image.png<?xml version="1.0" encoding="utf-8"?>
- ProviderTest
首先我们需要将Jar包导入项目并进行依赖。其次我们来创建一个实体类:
/** * Created by 泅渡者 * Created on 2017/9/28. */public class Fruits { public Fruits(int id, String name, String desc, String url) { this.id = id; this.name = name; this.desc = desc; this.url = url; } public int id; public String name; public String desc; public String url;}
FruitsHelper.class 是我们用来操作Contentprovider的“工具类”
/** * Created by 泅渡者 * Created on 2017/9/28. * 数据库助手类 */public class FruitsHelper { private ContentResolver resolver = null; private GoodsProvider goodsProvider; public FruitsHelper(Context context) { resolver = context.getContentResolver(); goodsProvider = new GoodsProvider(); } /** * 向ContentProvider添加数据 并返回ID **/ public long insertFruits(Fruits fruits) { ContentValues values = new ContentValues(); values.put(Goods.NAME, fruits.name); values.put(Goods.DESC, fruits.desc); values.put(Goods.URL, fruits.url); Uri uri = resolver.insert(Goods.CONTENT_URI, values); String itemId = uri.getPathSegments().get(1); return Integer.valueOf(itemId).longValue(); } /** * 对ContentProvider更新数据并 返回状态 **/ public boolean updateFruits(Fruits fruits) { Uri uri = ContentUris.withAppendedId(Goods.CONTENT_URI, fruits.id); ContentValues values = new ContentValues(); values.put(Goods.NAME, fruits.name); values.put(Goods.DESC, fruits.desc); values.put(Goods.URL, fruits.url); int count = resolver.update(uri, values, null, null); return count > 0; } public boolean removeFruits(int id) { Uri uri = ContentUris.withAppendedId(Goods.CONTENT_URI, id); int count = resolver.delete(uri, null, null); return count > 0; } /** * 对Contentprovider的Fruits 表进行遍历 **/ public LinkedList getAllFruits() { LinkedList fruitses = new LinkedList(); String[] projection = new String[]{ Goods.ID, Goods.NAME, Goods.DESC, Goods.URL }; Cursor cursor = resolver.query(Goods.CONTENT_URI, projection, null, null, Goods.DEFAULT_SORT_ORDER); if (cursor.moveToFirst()) { do { int id = cursor.getInt(0); String name = cursor.getString(1); String desc = cursor.getString(2); String url = cursor.getString(3); Fruits fruits = new Fruits(id, name, desc, url); fruitses.add(fruits); } while (cursor.moveToNext()); } return fruitses; } public int getFruitsCount() { int count = 0; Bundle bundle = resolver.call(Goods.CONTENT_URI, Goods.METHOD_GET_ITEM_COUNT, null, null); count = bundle.getInt(Goods.KEY_ITEM_COUNT, 0); return count; } public Fruits getArticleById(int id) { Uri uri = ContentUris.withAppendedId(Goods.CONTENT_URI, id); String[] projection = new String[]{ Goods.ID, Goods.NAME, Goods.DESC, Goods.URL }; Cursor cursor = resolver.query(uri, projection, null, null, Goods.DEFAULT_SORT_ORDER); if (!cursor.moveToFirst()) { return null; } String title = cursor.getString(1); String abs = cursor.getString(2); String url = cursor.getString(3); return new Fruits(id, title, abs, url); } public Fruits getArticleByPos(int pos) { Uri uri = ContentUris.withAppendedId(Goods.CONTENT_POS_URI, pos); String[] projection = new String[]{ Goods.ID, Goods.NAME, Goods.DESC, Goods.URL }; Cursor cursor = resolver.query(uri, projection, null, null, Goods.DEFAULT_SORT_ORDER); if (!cursor.moveToFirst()) { return null; } int id = cursor.getInt(0); String title = cursor.getString(1); String abs = cursor.getString(2); String url = cursor.getString(3); return new Fruits(id, title, abs, url); }}
我们这里只做引导入门所以测试大多是日志:
package com.bsoft.providertest;import android.content.ContentResolver;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import com.socks.library.KLog;public class MainActivity extends AppCompatActivity { private ContentResolver resolver = null; private FruitsHelper fruitsHelper = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); fruitsHelper = new FruitsHelper(this); /**增加数据**/ Fruits fruits = new Fruits(4, "大苹果", "巨大", "wwww.baidu.com"); long flag = fruitsHelper.insertFruits(fruits); KLog.i(flag); /**删除数据**/ /**修改数据**/ /**查找数据**/ }}
我们看下我的运行结果:
MainActivity.java: [ (MainActivity.java:23)#onCreate ] 5
这里我只做了个插入操作,剩余的删改,查。大家可以亲自试试。
代码地址:https://gitee.com/13102169005/Android_Projects.git
更多相关文章
- SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
- 一句话锁定MySQL数据占用元凶
- Android(安卓)Room牛刀小试
- Android(安卓)registerContentObserver 接口监视数据库变化
- SharedPreferences 简介
- 安卓数据库连接解决办法 ,避免 sqlite3 database is locked
- Android中进入页面默认定位到ListView的第一条数据解决方法
- android Spinner setSelection无效 总显示第一项
- 学习ContentProvider---之一:查询数据库