【9】Android组件系列----ContentProvider内容提供者+ 原生数据库Sqlite操作

一、ContentProvider简介:

ContentProvider内容提供者(四大组件之一)主要用于在不同的应用程序之间实现数据共享的功能

ContentProvider可以理解为一个Android应用对外开放的接口,只要是符合它所定义的Uri格式的请求,均可以正常访问执行操作。其他的Android应用可以使用ContentResolver对象通过与ContentProvider同名的方法请求执行,被执行的就是ContentProvider中的同名方法。所以ContentProvider有很多对外可以访问的方法,在ContentResolver中均有同名的方法,是一一对应的,来看 下面这一张图:

12154217-a98af1e70c7e46cca16299e42ee30fb3

Android附带了许多有用的ContentProvider,但是本文暂时不涉及到这么多(本文将学习如何创建自己的ContentProvider)。Android中自带的ContentProvider包括:

  • Browser:存储如浏览器的信息。
  • CallLog:存储通话记录等信息。
  • Contacts Provider:存储联系人(通讯录)等信息。
  • MediaStore:存储媒体文件的信息。
  • Settings:存储设备的设置和首选项信息。

此外,还有日历、

ContentProvider的方法:

如果要创建自己的内容提供者,需要新建一个类继承抽象类ContentProvider,并重写其中的抽象方法。抽象方法如下:

 

boolean onCreate()   
初始化提供者


Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)  
查询数据,返回一个数据Cursor对象。其中参数selection和selectionArgs是外部程序提供的查询条件


Uri insert(Uri uri, ContentValues values) 
插入一条数据。参数values是需要插入的值


int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) 
根据条件更新数据


int delete(Uri uri, String selection, String[] selectionArgs) 
 
根据条件删除数据


String getType(Uri uri)   
返回MIME类型对应内容的URI

 

除了onCreate()和getType()方法外,其他的均为CRUD操作,这些方法中,Uri参数为与ContentProvider匹配的请求Uri,剩下的参数可以参见SQLite的CRUD操作,基本一致。 

备注:还有两个非常有意思的方法,必须要提一下,call()和bulkInsert()方法,使用call,理论上可以在ContentResolver中执行ContentProvider暴露出来的任何方法,而bulkInsert()方法用于插入多条数据。

Uri:

在Android中,Uri是一种比较常见的资源访问方式。而对于ContentProvider而言,Uri也是有固定格式的:<srandard_prefix>://<authority>/<data_path>/<id>

  • <srandard_prefix>:ContentProvider的srandard_prefix始终是content://。
  • <authority>:ContentProvider的名称。
  • <data_path>:请求的数据类型。
  • <id>:指定请求的特定数据。

在ContentProvider的CRUD操作,均会传递一个Uri对象,通过这个对象来匹配对应的请求。那么如何确定一个Uri执行哪项操作呢?需要用到一个UriMatcher对象,这个对象用来帮助内容提供者匹配Uri。它所提供的方法非常简单,仅有两个:

  • void addURI(String authority,String path,int code):添加一个Uri匹配项,authority为AndroidManifest.xml中注册的ContentProvider中的authority属性;path为一个路径,可以设置通配符,#表示任意数字,*表示任意字符;code为自定义的一个Uri代码。
  • int match(Uri uri):匹配传递的Uri,返回addURI()传递的code参数。

 

二、代码举例:

最终所有工程文件的目录结构如下:

如图所示

PersonDao是增删改查数据库的工具类,并在PersonContentProvider中得到调用。DBHelper用于初始化SQLite数据库。

PersonContentProvider用于向外提供增删改查的接口。并最终在ContentResolverTest的MyTest.java中进行单元测试,实现CRUD。

本文的核心类是:PersonContentProvider和MyTest

下面来看一下具体的实现步骤。

新建工程文件ContetProviderTest01。

(1)新建类PersonDao:用于进行对SQLite的CRUD操作。代码如下:

package com.boradcasst.liuan.mycontentprovider;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

public class PersonDao {
    private DBHelper helper = null;

    public PersonDao(Context context) {
        helper = new DBHelper(context);
    }

    /**
     * 插入操作 ,返回的long的类型为:插入当前行的行号
     * @param values
     * @return
     */
    public long insertPerson(ContentValues values) {
        long id = -1;
        SQLiteDatabase database = null;
        try {
            database = helper.getWritableDatabase();
            id = database.insert("person", null, values);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (database != null) {
                database.close();
            }
        }
        return id;
    }

    public int deletePerson(String whereCaluse,String[] whereArgs){
        int count=-1;
        SQLiteDatabase database = null;
        try {
            database=helper.getWritableDatabase();
            count=database.delete("person",whereCaluse,whereArgs);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (database != null) {
                database.close();
            }
        }

        return count;
    }


    public int updatePerson(ContentValues values,String whereClause,String[] whereArgs){
        SQLiteDatabase database=null;
        int count=-1;
        try {
            database = helper.getWritableDatabase();
            database.update("person",values,whereClause,whereArgs);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (database != null) {
                database.close();
            }
        }
    return count;
    }
    public Cursor queryPersons(String selection,String[] selectionArgs){
        SQLiteDatabase database=null;
        Cursor cursor=null;

        try {
            database=helper.getReadableDatabase();
            cursor=database.query(true,"person",null,selection,selectionArgs,null,null,null,null);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {

        }
        return cursor;

    }


}

(2)新建类DBHelper:用于初始化SQLiate数据库

DBHelper.java:

package com.boradcasst.liuan.mycontentprovider;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DBHelper extends SQLiteOpenHelper {
    //数据库的名字
    private static String name = "mydb.db";
    //数据库的版本
    private static int version = 1;

    public DBHelper(Context context) {
        super(context, name, null, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
    //只能支持基本数据类型:varchar int long float boolean text blob clob
        //建表语句执行
        String sql="create table person(id integer primary key autoincrement,name varchar(64),address varchar(64))";
        db.execSQL(sql);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        String sql="alter table person add sex varchar(8)";
        db.execSQL(sql);
    }
}

(3)【核心】新建类PersonContentProvider,继承ContetProvider

PersonContentProvider.java:

package com.boradcasst.liuan.mycontentprovider;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;

public class PersonContentProvider extends ContentProvider {

    private PersonDao personDao;
    //默认规则是不匹配的
    public static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
    //操作单行记录
    private static final int PERSON = 1;
    //操作多行记录S2
    private static final int PERSONS = 2;

    /**
     * 添加两个URL筛选
     */
    static {
        URI_MATCHER.addURI("com.boradcasst.liuan.mycontentprovider", "person", PERSONS);
        /**
         * 匹配任意数字
         */
        URI_MATCHER.addURI("com.boradcasst.liuan.mycontentprovider", "person/#", PERSONS);
    }

    @Nullable
    @Override
    public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
        Log.e(TAG, "call: ---->>" + method);
        Bundle bundle = new Bundle();
        bundle.putString("returnCell", "call 被执行了");
        return bundle;
    }

    @Override
    public boolean onCreate() {
        //初始化一个数据持久层
        personDao = new PersonDao(getContext());

        return true;
    }

    public PersonContentProvider() {
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        Uri resultUri = null;
        //解析Uri,返回code
        int flag = URI_MATCHER.match(uri);
        switch (flag) {
            case PERSONS:
                //调用数据库方法
                long id = personDao.insertPerson(values);
                resultUri = ContentUris.withAppendedId(uri, id);
                Log.e(TAG, "insert: --->插入成功,id = " + id);
                Log.e(TAG, "insert: --->插入成功,resultUri = " + resultUri.toString());
                System.out.println("inssert success");
                break;
        }
        return resultUri;
    }

    private static final String TAG = "PersonContentProvider";

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        int count = -1; //影响数据库的行数

        try {
            int flag = URI_MATCHER.match(uri);
            switch (flag) {
                case PERSON:
                    //delete from student where id=?
                    //单条数据,使用COntentUris工具类解析出结尾Id
                    long id = ContentUris.parseId(uri);
                    String where_value = "id = ?";
                    String[] args = {String.valueOf(id)};
                    count = personDao.deletePerson(where_value, args);
                    break;
                case PERSONS:
                    count = personDao.deletePerson(selection, selectionArgs);
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        Log.e(TAG, "delete: ----> 删除成功,count=" + count);
        return count;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        int count = -1;

        try {
            int flag = URI_MATCHER.match(uri);
            switch (flag) {
                case PERSON:
                    long id = ContentUris.parseId(uri);
                    String where_value = " id = ?";
                    String[] args = {String.valueOf(id)};
                    count = personDao.updatePerson(values, where_value, args);
                    break;
                case PERSONS:
                    count = personDao.updatePerson(values, selection, selectionArgs);
                    break;

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        Log.e(TAG, "--->>更新成功,count=" + count);
        return count;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        Cursor cursor = null;
        try {
            int flag = URI_MATCHER.match(uri);
            switch (flag) {
                case PERSON:
                    long id = ContentUris.parseId(uri);
                    String where_value = " id = ?";
                    String[] args = {String.valueOf(id)};
                    cursor = personDao.queryPersons(where_value, args);
                    break;
                case PERSONS:
                    cursor = personDao.queryPersons(selection, selectionArgs);
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        Log.i(TAG, "--->>查询成功,Count=" + cursor.getCount());
        return cursor;
    }

    @Override
    public String getType(Uri uri) {
        int flag = URI_MATCHER.match(uri);
        switch (flag) {
            case PERSON:
                // 如果是单条记录,则为vnd.android.cursor.item + path
                return "vnd.android.cursor.item/person";
            case PERSONS:
                // 如果是多条记录,则为vnd.android.cursor.dir/ + path
                return "vnd.android.cursor.dir/persons";
        }
        return null;
    }


}

 

UriMatcher类的作用是:匹配内容uri,默认的规则是不匹配的。UriMatcher提供了一个addURI方法:

  • void android.content.UriMatcher.addURI(String authority, String path, int code)

     

这三个参数分别代表:权限、路径、和一个自定义代码。一般第一个参数是uri(包名.内容提供者的类名),第二个参数一般是数据库的表名。

27行:匹配规则的解释:*表示匹配任意字符,#表示匹配任意数字。注:如果内部的匹配规则越多,越容易访问。

138行的getType(Uri uri)方法:所有的内容提供者都必须提供的一个方法。用于获取uri对象所对应的MIME类型。

然后,每编写一个内容提供者,都必须在清单文件中进行声明。在AndroidManifest.xml中<application>节点中增加,格式如下:

<provider
    android:name=".内容提供者的类名"
    android:authorities="包名.内容提供者的类名" >
</provider>

第3行表示的是uri路径,毕竟Contet Provider是通过路径来访问的。

所以在本程序中,在AndroidManifest.xml的<application>节点中增加如下代码:

        <provider
            android:name=".PersonContentProvider"
            android:authorities="com.boradcasst.liuan.mycontentprovider"
            android:enabled="true"
            android:exported="true"></provider>

随便操作操作可以看到效果

09-20 10:31:21.152 11114-11114/com.boradcasst.liuan.mycontentprovider E/PersonContentProvider: call: ---->>method
09-20 10:32:26.808 11114-11114/com.boradcasst.liuan.mycontentprovider E/PersonContentProvider: insert: --->插入成功,id = 22
09-20 10:32:26.809 11114-11114/com.boradcasst.liuan.mycontentprovider E/PersonContentProvider: insert: --->插入成功,resultUri = content://com.boradcasst.liuan.mycontentprovider/person/22
09-20 10:32:50.849 11114-11114/com.boradcasst.liuan.mycontentprovider E/PersonContentProvider: insert: --->插入成功,id = 23
    insert: --->插入成功,resultUri = content://com.boradcasst.liuan.mycontentprovider/person/23
09-20 10:32:51.991 11114-11114/com.boradcasst.liuan.mycontentprovider I/PersonContentProvider: --->>查询成功,Count=1
09-20 10:32:58.559 11114-11114/com.boradcasst.liuan.mycontentprovider E/PersonContentProvider: insert: --->插入成功,id = 24
09-20 10:32:58.560 11114-11114/com.boradcasst.liuan.mycontentprovider E/PersonContentProvider: insert: --->插入成功,resultUri = content://com.boradcasst.liuan.mycontentprovider/person/24
09-20 10:32:59.229 11114-11114/com.boradcasst.liuan.mycontentprovider I/PersonContentProvider: --->>查询成功,Count=2
09-20 10:33:21.738 11114-11114/com.boradcasst.liuan.mycontentprovider I/PersonContentProvider: --->>查询成功,Count=4
09-20 10:33:25.007 11114-11114/com.boradcasst.liuan.mycontentprovider I/PersonContentProvider: --->>查询成功,Count=4

具体是看数据库的数据 

as 3.0+  和2.0+版本 查看数据方法不一致

具体数据库放在文件的

data/data/packageName/databases/目录下

可使用工具 SQLiteStudio.exe查看

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安果移不动

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值