热门标签 | HotTags
当前位置:  开发笔记 > 数据库 > 正文

ContentProvider数据库共享之实例讲解

前言:现在这段时间没这么忙了,要抓紧时间把要总结的知识沉淀下来,今年重新分了项目组,在新项目中应该不会那么忙了,看来有时间来学一些自己的东西了。现在而言,我需要的是时间。只要不断的努力,总有一天,你会与从不同。加油。1、《ContentProvider数

前言:现在这段时间没这么忙了,要抓紧时间把要总结的知识沉淀下来,今年重新分了项目组,在新项目中应该不会那么忙了,看来有时间来学一些自己的东西了。现在而言,我需要的是时间。只要不断的努力,总有一天,你会与从不同。加油。 1、《ContentProvider数

前言:现在这段时间没这么忙了,要抓紧时间把要总结的知识沉淀下来,今年重新分了项目组,在新项目中应该不会那么忙了,看来有时间来学一些自己的东西了。现在而言,我需要的是时间。只要不断的努力,总有一天,你会与从不同。加油。



1、《ContentProvider数据库共享之——概述》
2、《ContentProvider数据库共享之——实例讲解》
3、《ContentProvider数据库共享之——MIME类型与getType()》
4、《ContentProvider数据库共享之——读写权限与数据监听》


在上篇文章中,已经给大家初步讲解了有关ContentProvder的整体流程及设计方式,在这篇文章中将通过实例来讲述ContentProvider的操作过程;

一、ContentProvider提供数据库查询接口

在上篇中,我们提到过,两个应用间是通过Content URI来媒介传递消息的,我们的应用在收到URI以后,通过匹配完成对应的数据库操作功能。听的有点迷糊?没关系,下面会细讲,我这里要说的是,匹配成功后要完成的数据库操作!!!那好,我们的第一步:建数据库,数据表

1、利用SQLiteOpenHelper创建数据库、数据表

在这里,我们在一个数据库(“harvic.db”)中创建两个数据表”first”与”second”;每个表都有多出一个字段“table_name”,来保存当前数据表的名称
代码如下:

public class DatabaseHelper extends SQLiteOpenHelper {
    public static final String DATABASE_NAME = "harvic.db";
    public static final int DATABASE_VERSION = 1;
    public static final String TABLE_FIRST_NAME = "first";
    public static final String TABLE_SECOND_NAME = "second";
    public static final String SQL_CREATE_TABLE_FIRST = "CREATE TABLE " +TABLE_FIRST_NAME +"("
            + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
            + "table_name" +" VARCHAR(50) default 'first',"
            + "name" + " VARCHAR(50),"
            + "detail" + " TEXT"
            + ");" ;
    public static final String SQL_CREATE_TABLE_SECOnD= "CREATE TABLE "+TABLE_SECOND_NAME+" ("
            + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
            + "table_name" +" VARCHAR(50) default 'second',"
            + "name" + " VARCHAR(50),"
            + "detail" + " TEXT"
            + ");" ;

    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        Log.e("harvic", "create table: " + SQL_CREATE_TABLE_FIRST);
        db.execSQL(SQL_CREATE_TABLE_FIRST);
        db.execSQL(SQL_CREATE_TABLE_SECOND);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS first");
        db.execSQL("DROP TABLE IF EXISTS second");
        onCreate(db);
    }
}

2、利用ContentProvider提供数据库操作接口

新建一个类PeopleContentProvider,派生自ContentProvider基类,写好之后,就会自动生成query(),insert(),update(),delete()和getType()方法;这些方法就是根据传过来的URI来操作数据库的接口。此时的PeopleContentProvider是这样的:
public class PeopleContentProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        return null;
    }

    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }
}

3、使用UriMatcher匹配URI

我们先不管这几个函数的具体操作,先想想在上节中我提到的当一个URI逐级匹配到了ContentProvider类里以后,会怎么做——利用UriMatcher进行再次匹配!!!UriMatcher匹配成功以后,才会执行对应的操作。所以上面的那些操作是在UriMatcher匹配之后。
好,我们就先看看UriMatcher是怎么匹配的。
public class PeopleContentProvider extends ContentProvider {
    private static final UriMatcher sUriMatcher;
    private static final int MATCH_FIRST = 1;
    private static final int MATCH_SECOnD= 2;
    public static final String AUTHORITY = "com.harvic.provider.PeopleContentProvider";
    public static final Uri CONTENT_URI_FIRST = Uri.parse("content://" + AUTHORITY + "/frist");
    public static final Uri CONTENT_URI_SECOnD= Uri.parse("content://" + AUTHORITY + "/second");

    static {
        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        sUriMatcher.addURI(AUTHORITY, "first", MATCH_FIRST);
        sUriMatcher.addURI(AUTHORITY, "second", MATCH_SECOND);
    }

    private DatabaseHelper mDbHelper;

    @Override
    public boolean onCreate() {
        mDbHelper = new DatabaseHelper(getContext());
        return false;
    }
    …………
}
上面的代码是最重要的一句话:
sUriMatcher.addURI(AUTHORITY, "first", MATCH_FIRST);
addUri的官方声明为:
public void addURI (String authority, String path, int code)
  • authority:这个参数就是ContentProvider的authority参数,这个参数必须与AndroidManifest.xml中的对应provider的authorities值一样;
  • path:就匹配在URI中authority后的那一坨,在这个例子中,我们构造了两个URI
    (1)、content://com.harvic.provider.PeopleContentProvider/frist
    (2)、content://com.harvic.provider.PeopleContentProvider/second
    而path匹配的就是authority后面的/first或者/second
  • code:这个值就是在匹配path后,返回的对应的数字匹配值;
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
句话的意思是,如果匹配不成功,返回UriMatcher.NO_MATCH
我们要做一个应用:外部可以使用两个URI传来给我们,当传来content://com.harvic.provider.PeopleContentProvider/frist时,就操作first数据库;如果传来content://com.harvic.provider.PeopleContentProvider/second时,就操作second数据库

4、insert方法

下面先看看insert方法,主要功能为:当URI匹配content://com.harvic.provider.PeopleContentProvider/frist时,将数据插入first数据库
当URI匹配content://com.harvic.provider.PeopleContentProvider/second时,将数据插入second数据库

@Override
public Uri insert(Uri uri, ContentValues values) {
    SQLiteDatabase db = mDbHelper.getWritableDatabase();
    switch (sUriMatcher.match(uri)){
        case MATCH_FIRST:{
            long rowID = db.insert(DatabaseHelper.TABLE_FIRST_NAME, null, values);
            if(rowID > 0) {
                Uri retUri = ContentUris.withAppendedId(CONTENT_URI_FIRST, rowID);
                return retUri;
            }
        }
        break;
        case MATCH_SECOND:{
            long rowID = db.insert(DatabaseHelper.TABLE_SECOND_NAME, null, values);
            if(rowID > 0) {
                Uri retUri = ContentUris.withAppendedId(CONTENT_URI_SECOND, rowID);
                return retUri;
            }
        }
        break;
        default:
            throw new IllegalArgumentException("Unknown URI " + uri);
    }
    return null;
}
下面对上面这段代码进行讲解:
首先,利用UriMatcher.match(uri)来匹配到来的URI,如果这个URI与content://com.harvic.provider.PeopleContentProvider/frist匹配,就会返回1即MATCH_FIRST;
即:当与"/first"匹配时,就将数据键值对(values)插入到first表中:
long rowID = db.insert(DatabaseHelper.TABLE_FIRST_NAME, null, values);
插入之后,会返回新插入记录的当前所在行号,然后将行号添加到URI的末尾,做为结果返回
Uri retUri = ContentUris.withAppendedId(CONTENT_URI_FIRST, rowID);
当匹配content://com.harvic.provider.PeopleContentProvider/second时,同理,不再赘述。

5、update()方法

在看了insert方法之后,update方法难度也不大,也是根据UriMatcher.match(uri)的返回值来判断当前与哪个URI匹配,根据匹配的URI来操作对应的数据库,代码如下:

public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    SQLiteDatabase db = mDbHelper.getWritableDatabase();
    int count = 0;
    switch(sUriMatcher.match(uri)) {
        case MATCH_FIRST:
            count = db.update(DatabaseHelper.TABLE_FIRST_NAME, values, selection, selectionArgs);
            break;
        case MATCH_SECOND:
            count = db.update(DatabaseHelper.TABLE_SECOND_NAME, values, selection, selectionArgs);
            break;

        default:
            throw new IllegalArgumentException("Unknow URI : " + uri);
    }
    this.getContext().getContentResolver().notifyChange(uri, null);
    return count;
}
在最后调用getContentResolver().notifyChange(uri, null);来通知当前的数据库有改变,让监听这个数据库的所有应用执行对应的操作,有关共享数据库的变量监听与响应的问题,我们会在最后一篇中讲述。这里就先不提,这里不加这一句也是没问题的。我这里只是一个引子,大家知道这个事就行了。

6、query() 方法

至于query()方法就不再细讲了,跟上面的一样,根据不同的URI来操作不同的查询操作而已,代码如下:

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
    switch (sUriMatcher.match(uri)) {
        case MATCH_FIRST:
            // 设置查询的表
            queryBuilder.setTables(DatabaseHelper.TABLE_FIRST_NAME);
            break;

        case MATCH_SECOND:
            queryBuilder.setTables(DatabaseHelper.TABLE_SECOND_NAME);
            break;

        default:
            throw new IllegalArgumentException("Unknow URI: " + uri);
    }

    SQLiteDatabase db = mDbHelper.getReadableDatabase();
    Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, null);
    return cursor;
}

7、delete()方法

delete()方法如下:
public int delete(Uri uri, String selection, String[] selectionArgs) {
    SQLiteDatabase db = mDbHelper.getWritableDatabase();
    int count = 0;
    switch(sUriMatcher.match(uri)) {
        case MATCH_FIRST:
            count = db.delete(DatabaseHelper.TABLE_FIRST_NAME, selection, selectionArgs);
            break;

        case MATCH_SECOND:
            count = db.delete(DatabaseHelper.TABLE_SECOND_NAME, selection, selectionArgs);
            break;
        default:
            throw new IllegalArgumentException("Unknow URI :" + uri);

    }
    return count;
}

8、getType()

这个函数,我们这里暂时用不到,直接返回NULL,下篇我们会专门来讲这个函数的作用与意义。
    public String getType(Uri uri) {
        return null;
    }

9、AndroidManifest.xml中声明provider

在AndroidManifest.xml中要声明我们创建的Provider:

二、第三方应用通过URI操作共享数据库

1、ContentResolver操作URI

在第三方应用中,我们要如何利用URI来执行共享数据数的操作呢,这是利用ContentResolver这个类来完成的。
获取ContentResolver实例的方法为:

ContentResolver cr = this.getContentResolver();
ContentResolver有下面几个数据库操作:查询、插入、更新、删除
public final Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
public final Uri insert (Uri url, ContentValues values)
public final int update (Uri uri, ContentValues values, String where, String[] selectionArgs)
public final int delete (Uri url, String where, String[] selectionArgs)
第一个参数都是传入要指定的URI,然后再后面的几个参数指定数据库条件——where语句和对应的参数;下面我们就用户实例来看看这几个函数到底是怎么用的。

2、全局操作

新建一个工程,命名为“UseProvider”,界面长这个样子:


最上头有两个按钮,用来切换当前使用哪个URI来增、删、改、查操作;由于不同的URI会操作不同的数据表,所以我们使用不同的URI,会在不同的数据表中操作;
先列出来那两个要匹配的URI,以及全局当前要使用的URI(mCurrentURI ),mCurrentURI 默认是/first对应的URI,如果要切换,使用界面上最上头的那两个按钮来切换当前所使用的URI。

public static final String AUTHORITY = "com.harvic.provider.PeopleContentProvider";
public static final Uri CONTENT_URI_FIRST = Uri.parse("content://" + AUTHORITY + "/first");
public static final Uri CONTENT_URI_SECOnD= Uri.parse("content://" + AUTHORITY + "/second");
public static Uri mCurrentURI = CONTENT_URI_FIRST;

3、query()——查询操作

下面先来看看查询操作的过程:
private void query() {
    Cursor cursor = this.getContentResolver().query(mCurrentURI, null, null, null, null);
    Log.e("test ", "count=" + cursor.getCount());
    cursor.moveToFirst();
    while (!cursor.isAfterLast()) {
        String table = cursor.getString(cursor.getColumnIndex("table_name"));
        String name = cursor.getString(cursor.getColumnIndex("name"));
        String detail = cursor.getString(cursor.getColumnIndex("detail"));
        Log.e("test", "table_name:" + table);
        Log.e("test ", "name: " + name);
        Log.e("test ", "detail: " + detail);
        cursor.moveToNext();
    }
    cursor.close();
}
其实只有一句话,就完成的查询操作:(由于没有加任何的限定语句,所以是查询出此数据表中的所有记录)
Cursor cursor = this.getContentResolver().query(mCurrentURI, null, null, null, null);
然后将返回的数据库记录指针Cursor,逐个读出所有的记录;

4、insert()插入操作

public final Uri insert (Uri url, ContentValues values)
插入操作有点特殊,第二个函数要求传入要插入的ContentValues的键值对;其实也没什么难度,跟普通的数据库操作一样;
private void insert() {
    ContentValues values = new ContentValues();
    values.put("name", "hello");
    values.put("detail", "my name is harvic");
    Uri uri = this.getContentResolver().insert(mCurrentURI, values);
    Log.e("test ", uri.toString());
}

5、update()更新操作

public final int update (Uri uri, ContentValues values, String where, String[] selectionArgs)
这个函数的意思就是,先用where语句找出要更新的记录,然后将values键值对更新到对应的记录中;
  • uri:即要匹配的URI
  • values:这是要更新的键值对
  • where:SQL中对应的where语句
  • selectionArgs:where语句中如果有可变参数,可以放在selectionArgs这个字符串数组中;这些与数据库中的用法一样,不再细讲。
private void update() {
    ContentValues values = new ContentValues();
    values.put("detail", "my name is harvic !!!!!!!!!!!!!!!!!!!!!!!!!!");
    int count = this.getContentResolver().update(mCurrentURI, values, "_id = 1", null);
    Log.e("test ", "count=" + count);
    query();
}
在这里,我们将_id = 1的记录的detail字符更新,在后面多加了N个感叹号。

6、delete()删除操作

public final int delete (Uri url, String where, String[] selectionArgs)
删除操作的参数比较好理解,第二个参数是SQL中的WHERE语句的过滤条件,selectionArgs同样是Where语句中的可变参数;
private void delete() {
    int count = this.getContentResolver().delete(mCurrentURI, "_id = 1", null);
    Log.e("test ", "count=" + count);
    query();
}
删除_id = 1的记录;

三、结果

1、我们先运行ContentProvider对应的APP:ContentProviderBlog,然后再运行UseProvider;
2、然后先用content://com.harvic.provider.PeopleContentProvider/frist 来操作ContentProviderBlog的数据库:
3、点两个insert操作,看返回的URI,在每个URI后都添加上了当前新插入记录的行号


4、然后做下查询操作——query()
由于我们的URI是针对first记录的,所以在这里的table_name,可以看到是“first”,即我们操作的是first表,如果我们把URI改成second对应的URI,那操作的就会变成second表


5、更新操作——update()

执行Update()操作,会将_id = 1的记录的detail字段更新为“my name is harvic !!!!!!!!!!!!!!!!!!!!!!!!!!”;其它记录的值不变,结果如下:


6、删除操作——delete()

同样,删除操作也会只删除_id = 1的记录,所以操作之后的query()结果如下:


总结:在这篇文章中,我们写了两个应用ContentProviderBlog和UseProvider,其中ContentProviderBlog派生自ContentProvider,提供第三方操作它数据库的接口;而UseProvider就是所谓第三方应用,在UseProvider中通过URI来操作ContentProviderBlog的数据库;

好了,到这里,整个文章就结束了,大家在看完这篇文章以后再回过头来看第一篇,应该对应用间数据库共享的整体流程已经有清晰的了解了。


源码来了,源码包含两部分内容:

1、《ContentProviderBlog》:这个是提供共享数据库接口的APP;
2、《UseProvider》:第三方通过URI来操作数据库的APP;


如果本文有帮到你,记得关注哦

源码下载地址:http://download.csdn.net/detail/harvic880925/8528507

http://blog.csdn.net/harvic880925/article/details/44591631 谢谢!

推荐阅读
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文讲述了如何通过代码在Android中更改Recycler视图项的背景颜色。通过在onBindViewHolder方法中设置条件判断,可以实现根据条件改变背景颜色的效果。同时,还介绍了如何修改底部边框颜色以及提供了RecyclerView Fragment layout.xml和项目布局文件的示例代码。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
author-avatar
欧阳俊俊2502921727
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有