ContentProvider(外共享数据)

ContentProvider在Android中的作用是对外共享数据,也就是说通过ContentProvider可以吧应用中的数据共享给其他应用访问,其他的应用可以通过ContentProvider对应用中的数据进行增、删、改、查的操作。使用ContentProvider 的好处是统一了数据访问方式。它实际上是对SqliteOpenHelper的进一步封装,通过Uri映射来判断选择需要操作数据库中的哪个表并且进行想要的处理。

  • Uri
    首先我们了解下Uri,Uri代表操作数据表的绝对路径,它主要包含两部分信息。一个是需要操作的ContentProvider,二是对ContentProvider中的哪个表进行操作。对于ContentProvider来说,一个Uri由以下几部分组成。
image.png
  • 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

更多相关文章

  1. SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
  2. 一句话锁定MySQL数据占用元凶
  3. Android(安卓)Room牛刀小试
  4. Android(安卓)registerContentObserver 接口监视数据库变化
  5. SharedPreferences 简介
  6. 安卓数据库连接解决办法 ,避免 sqlite3 database is locked
  7. Android中进入页面默认定位到ListView的第一条数据解决方法
  8. android Spinner setSelection无效 总显示第一项
  9. 学习ContentProvider---之一:查询数据库

随机推荐

  1. android之 ExpandableListView的使用
  2. Android(安卓)S5PV210 创建的video设备
  3. android 中 SQLiteOpenHelper的封装使用
  4. No 123 · Android(安卓)【常见问题】个
  5. 使用xmlpull解析xml
  6. Android(安卓)AnimationDrawable运行的几
  7. Android使用AudioManager修改系统音量的
  8. MD2模型結構解釋
  9. Android(安卓)LruCache和DiskLruCache相
  10. Android中的Binder机制