zoukankan      html  css  js  c++  java
  • 数据共享 ContentProvider,ContentResolver

    代码示例在:360云盘:自己的学习资料----Android总结过的项目----ContentProviderExample.rar

    一、前言

    前面介绍过了数据存储操作的方式可知,每个程序都是线程控制,并且数据无法共享,而 ContentProvider 来解决这个问题,ContentProvider 是所有应用程序之间数据存储与检索的一个桥梁,他的作用就是使得各个程序之间实现数据共享。
    可以把 ContentProvider 理解为一种特殊的存储数据的类型,因为 ContentProvider 是个抽象类,继承自该类必须实现:getType,insert,delete,update,query方法,所以它提供了一套上述接口来获取,操作数据。
     
    就像我们学习 web 开发刚接触 Model1 的 MVC 时,数据层方面实现了针对实体的增删改查,但是在业务层方面最好封装一个数据操作的 Service,这样的话就避免了业务层直接操作数据层,而把这个工作交给对应的 Service 的实现,更好的管理。这里就引出了ContentResolver,它的作用就可以理解为对应 Service 的实现来操作对应 ContentProvider。

    ContentResolver 对象的获取可以通过 getContentResolver()方法来取得
          
    这里需要说明的是,每个 ContentProvider 都会对外提供一个公共的 URI(包装成 Uri 对象),如果应用程序有数据需要共享,就需要使用 ContentProvider 为这些数据定义一个 URI,然后其他应用程序就可以通过 ContentProvider 传入这个URI来对数据进行操作。

    --------------------------------------------------------------------------------------------
    二、URI简介:

    http://www.dubblogs.cc:8751/H264.mp4

    URI 的作用是为互联网上所涉及到的所有资源比方说 HTML 文档、图像等等,提供一个唯一的标识。

    URI 由3部分组成:
    第一部分是:协议,如:http。第一部分与第二部分用“://”符号隔开。
    第二部分是:存有该资源的主机域名或IP地址,如:www.dubblogs.cc:8751。第二部分与第三部分用“/”符号隔开。
    第三部分是:主机资源的具体地址,如:H264.mp4。

    (第三部分可以忽略不写,但第一和第二部分必须有)

    --------------------------------------------------------------------------------------------
    三、ContentProvider 中的 URI:

    content://com.prd.contactprovider/contact/20

    ContentProvider 中的 URI 也由3个部分组成:

    1.协议部分(content://):这个是 android 规定的 content 协议

    2.主机名部分(com.prd.contactprovider):需要在 AndroidManifest.xml 中声明用来唯一标识这个ContentProvider,外部调用者就可以根据这个标识来找到他。

    3.路径部分(contact/20):用来表示我们要操作的数据,这个就是操作 contact 表中 id 为 20 的记录。contact/20/phone 代表 contact 表中 id 为 20 的记录的 phone 字段。当然,要操作的数据还可以是文件、xml或网络等其他存储方式。

    content://media/internal/images   这个URI返回设备上存储的所有图片
    content://contacts/people/5       联系人信息中ID为5的联系人记录
    content://contacts:/people/       这个URI返回设备上得所有联系人信息。

    --------------------------------------------------------------------------------------------
    四、将字符串转换成 Uri

    android 提供了两个操作 Uri 的工具类,分别是 ContentUris 和 UriMatcher。我们先来看 ContentUris。

    1.ContentUris

    我们先看他常用的两个方法

    1.第一个方法 withAppendedId(uri,id) 这个方法用于为路径加上 ID 部分。

    Uri uri = Uri.parse("content://com.test.provider.personprovider/person")
    Uri resultUri = ContentUris.withAppendedId(uri, 5);
    //生成后的Uri为:content://com.test.provider.personprovider/person/5

    其结果等价于 Uri.withAppendedPath(Uri baseUri, String pathSegment)
    Uri resultUri = Uri.withAppendedPath(uri, "5");

    2.第二个方法:parseId(Uri contentUri) 这个方法用于从路径中获取 ID 部分。

    Uri uri = Uri.parse("content://com.test.provider.personprovider/person/5")
    long personid = ContentUris.parseId(uri);
    //获取的结果 personid 为:5
     
    2.UriMatcher,他的作用是来匹配 Uri

    1.首先创建一个 UriMatcher 对象,参数 UriMatcher.NO_MATCH 表示不匹配任何路径的返回码(-1)
    UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    2.接着定义 2 个整形常量(当然可以是多个,要看你注册多少个 Uri 了)
    private static final int CONTACTS = 1;
    private static final int CONTACT = 2;

    3.然后定义一个 static 代码块,他的作用是在项目启动的时候就会执行这个静态代码块,通过 addURI 注册所有 Uri,注意 addURI 里第一个参数是域名部分,不要加协议部分的 content,第二个参数是路径部分,第三个参数是返回的是匹配码也就是 1,第二个 addURI() 方法,如果 Uri 的路径符合第二个方法里的注册路径,就会返回 2 。其中 # 号是通配符用来匹配数字,还可以是 * 用来匹配任意的字符。

    static {

    // 注册URI路径content://com.prd.contactprovider/contactinfo
    matcher.addURI("com.prd.contactprovider", "contactinfo", CONTACTS);

    // 注册URI路径content://com.prd.contactprovider/contactinfo/#
    matcher.addURI("com.prd.contactprovider", "contactinfo/#", CONTACT);
    }

    4.使用 uriMatcher.match()方法匹配 Uri 地址并返回匹配值,一般用在 getType(Uri uri)中。


    //如果match()方法匹配content://com.changcheng.sqlite.provider.contactprovider/contact路径,返回匹配码为1
    uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact”, 1);//添加需要匹配uri,如果匹配就会返回匹配码

    //如果match()方法匹配content://com.changcheng.sqlite.provider.contactprovider/contact/230路径,返回匹配码为2
    uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact/#”, 2);//#号为通配符

    --------------------------------------------------------------------------------------------
    五、ContentProvider 的实现

    继承 ContentProvider 类,就会实现 onCreate()、getType()、delete()、insert()、query()、update(),六个方法。

    1.onCreate()方法,这个方法是 ContentProvider 创建后就会被调用,当程序第一次访问这个 ContentProvider 时 ContentProvider 就会被创建。

    2.getType(Uri uri)方法,这个方法用于返回当前 Uri 所代表数据的 MIME 类型。

    问:MIME类型是什么?

    答:当我们使用浏览器的时候,向服务器请求数据,服务器会返回很多类型的数据,可能有的是 mp3,有的是 excle文件,也有可能是 word 文件,服务器需要将这些数据的类型告诉浏览器,怎么来告诉呢,就是说明这些数据的 MIME 类型,这样浏览器就会根据 MIME 类型来选择相应的插件,来读取相关文件,比如 word 文件的 MIME 类型就是 .word application/msword,假如我们设定 word 程序,为处理这种 MIME 类型的程序,那么 word 文件就会交给 word 程序处理,这就是 MIME 类型,那么 android 所代表数据的 MIME 类型是什么呢?如果操作的数据属于集合类型,比如 URI 代表数据是一张表,那么 MIME 类型应该以 vnd.android.cursor.dir/ 开头,例如:要得到所有 contact 表也就是联系人表中的数据,那么他的 Uri 就是 content://com.prd.contactprovier/contact ,那么返回的 MIME 类型字符串应该为 vnd.android.cursor.dir/contact 如果要操作的数据属于非集合类型数据,比方说 contact 表中的一条数据,那么 MIME 类型字符串应该以 vnd.android.cursor.item/ 开头,例如得到 id 为 10 的 contact 记录,Uri为 content://com.prd.contactprovier/contact/10 那么返回的 MIME 类型字符串应该为 vnd.android.cursor.item/contact 我们可以对 URI 地址进行 MIME类型分类,相同 MIME 类型的 URI 可以执行相同的操作,方便代码的编写。

    3.insert(Uri uri,ContentValues values)方法,用于当外部程序向 ContentProvider 中增加数据的时候调用。他的第一参数就是 Uri 对象,用于指定要操作的数据。第二个参数是 ContentValues,是要增加的数据的内容,我们看 ContentValues 的用法和完整的 insert() 操作。

    //声明 ContentValues 保存数据
    ContentValues tValues=new ContentValues();
    tValues.put("name", "wang"); //name 对应数据库表中的字段 name,值为wang
    tValues.put("icon", 123); // icon 对应数据库表中的字段 icon,值为 123,是 int 型,当然,也可以是其他类型。

    Uri url=Uri.parse("content://xjl.prd.contactprovider/contactinfo"); //构建 ContentProvider Uri,这里对应 contactinfo 表
    Uri tResolver=getContentResolver().insert(url, tValues); //根据 Uri地址保存数据,并返回拼接 id 后的 Uri地址
    long id=ContentUris.parseId(tResolver); //返回刚才保存数据的 id

    4.delete(Uri uri, String selection, String[] selectionArgs)方法,此方法用于 ContentProvider 删除操作,第一个参数是 Uri 对象,即:要操作的数据表。第二个参数 selection 相当于 sql 语句中的 where 部分。第三个参数 selectionArgs 相当于 where 的值,比如有一个 SQL 语句是用来删除 name 列为 tom 的数据,就是 delete from contact where name="tom",那么 selection 对应着 name 列,selectionArgs 对应着 tom。传参的时候就可以这样,定义一个字符串他的值是 name=?,?是占位符,然后定义一个字符串数组占位符的值在数组里定义。如下边的示例。

    String where = "id=?";
    String[] selectionArgs = new String[]{"1"};

    getContentResolver().delete(url, where, selectionArgs);//删除 id 为 1 的记录

    5.update(Uri uri, ContentValues values, String selection, String[] selectionArgs)方法,用于更新,这四个参数前面都介绍过了,uri 指定要操作的数据。values 用来保存要更新的数据。第三个参数和第四个参数用来限定更新条件,相当于 sql 中的 where 部分,用于检索要更新的数据。

    ContentValues tUpdateValues=new ContentValues();
    tUpdateValues.put("msn", "99999");

    String tUpdateWhere = "name=?";
    String[] tUpdateSelectionArgs = new String[]{"san1"};

    getContentResolver().update(url, tUpdateValues, tUpdateWhere, tUpdateSelectionArgs);

    6.query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)方法,用于查询,他的返回值是一个 Cursor 对象。我们看他的参数,第一三四个参数介绍过了。第二个参数 projection 是指定返回查询的列,是一个字符串数组,比方说定义一个字符串数组值是name和phone,那么就返回name和phone列。第五个参数是指定排列方式,比方升序或者降序,如下示例:

    Cursor tCursor=getContentResolver().query(url, null, null, null, null);

    while (tCursor.moveToNext()) {

    String id=tCursor.getString(tCursor.getColumnIndex("id"));
    String name=tCursor.getString(tCursor.getColumnIndex("name"));
    String msn=tCursor.getString(tCursor.getColumnIndex("msn"));

    Log.e(TAG, "id为:"+id+" name为:"+name+" msn为:"+msn);
    }

    --------------------------------------------------------------------------------------------
    六、完整代码

    1.先定义数据库,这里不说了。

    2.自定义 ContentProvider 类

    public class ContactProvider extends ContentProvider {

    private DBOpenHelper dbOpenHelper;

    private static final UriMatcher matcher = new UriMatcher(
    UriMatcher.NO_MATCH);

    private static final int CONTACTS = 1;
    private static final int CONTACT = 2;
    private static final String TABLENAME = "contactinfo";

    static {

    // 注册URI路径content://com.prd.contactprovider/contactinfo
    matcher.addURI("xjl.prd.contactprovider", "contactinfo", CONTACTS);

    // 注册URI路径content://com.prd.contactprovider/contactinfo/#
    matcher.addURI("xjl.prd.contactprovider", "contactinfo/#", CONTACT);
    }

    @Override
    public boolean onCreate() {

    dbOpenHelper = new DBOpenHelper(this.getContext());
    return true;
    }

    /**
    * 获得 URI 的 MIME 类型
    */
    @Override
    public String getType(Uri uri) {

    switch (matcher.match(uri)) {

    case CONTACTS:

    return "vnd.android.cursor.dir/contactinfo";
    case CONTACT:

    return "vnd.android.cursor.item/contactinfo";

    default:
    throw new IllegalArgumentException("URI" + uri + "无法解析!");
    }
    }

    // 对数据库进行删除操作
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {

    SQLiteDatabase sqLiteDatabase = dbOpenHelper.getWritableDatabase();
    int rowNum = 0;

    switch (matcher.match(uri)) {

    case CONTACTS:

    rowNum = sqLiteDatabase.delete(TABLENAME, selection, selectionArgs);
    return rowNum;
    case CONTACT:

    long id = ContentUris.parseId(uri);
    String whereClause = "id=" + id;
    if (selection != null) {
    whereClause += "and" + selection;
    }
    rowNum = sqLiteDatabase.delete(TABLENAME, whereClause,
    selectionArgs);
    return rowNum;

    default:
    throw new IllegalArgumentException("URI" + uri + "无法解析!");
    }
    }

    /**
    * 向数据库中添加数据
    */
    @Override
    public Uri insert(Uri uri, ContentValues values) {

    SQLiteDatabase sqLiteDatabase = dbOpenHelper.getWritableDatabase();

    switch (matcher.match(uri)) {

    case CONTACTS:

    long id = sqLiteDatabase.insert(TABLENAME, "name", values);
    Uri insertUri = ContentUris.withAppendedId(uri, id);

    return insertUri;

    default:
    throw new IllegalArgumentException("URI" + uri + "无法解析!");
    }
    }

    /**
    * 查询数据库表中的数据
    */
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
    String[] selectionArgs, String sortOrder) {

    SQLiteDatabase sqLiteDatabase = dbOpenHelper.getWritableDatabase();
    switch (matcher.match(uri)) {

    case CONTACTS:

    return sqLiteDatabase.query(TABLENAME, projection, selection,
    selectionArgs, null, null, sortOrder);
    case CONTACT:

    long id = ContentUris.parseId(uri);
    String whereClause = "id=" + id;

    if (selection != null) {

    whereClause += "and" + selection;
    }
    return sqLiteDatabase.query(TABLENAME, projection, whereClause,
    selectionArgs, null, null, sortOrder);

    default:
    throw new IllegalArgumentException("URI" + uri + "无法解析!");
    }
    }

    /**
    * 更新数据库表的记录
    */
    @Override
    public int update(Uri uri, ContentValues values, String selection,
    String[] selectionArgs) {

    SQLiteDatabase sqLiteDatabase = dbOpenHelper.getWritableDatabase();
    int rowNum = 0;

    switch (matcher.match(uri)) {

    case CONTACTS:

    rowNum = sqLiteDatabase.update(TABLENAME, values, selection,
    selectionArgs);
    return rowNum;

    case CONTACT:

    long id = ContentUris.parseId(uri);
    String whereClause = "id=" + id;

    if (selection != null) {

    whereClause += "and" + selection;
    }
    rowNum = sqLiteDatabase.update(TABLENAME, values, whereClause,
    selectionArgs);
    return rowNum;

    default:
    throw new IllegalArgumentException("URI" + uri + "无法解析!");
    }
    }
    }

    /**
    * @Description:主页面
    */
    public class MainActivity extends Activity {

    private static final String TAG = "@@@MainActivity";

    /** 构建 ContentProvider Uri,这里对应 contactinfo 表 */
    Uri url = Uri.parse("content://xjl.prd.contactprovider/contactinfo");

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    /**
    * 添加操作
    */
    for (int i = 0; i <= 2; i++) {

    // 声明 ContentValues 保存数据
    ContentValues tValues = new ContentValues();
    tValues.put("name", "san" + i); // name 对应数据库表中的字段 name,值为wang
    tValues.put("icon", 123 + i); // icon 对应数据库表中的字段 icon,值为 123,是 int
    // 型,当然,也可以是其他类型。

    /* 根据 Uri 地址保存数据,并返回拼接 id 后的 Uri地址 */
    Uri tResolver = getContentResolver().insert(url, tValues);
    long id = ContentUris.parseId(tResolver); // 返回刚才保存数据的 id

    Log.e(TAG, "刚插入数据的 id 为:" + id);
    }

    showContact("添加操作完成后");

    /**
    * 删除操作
    */
    String tDelWhere = "id=?";
    String[] tDelSelectionArgs = new String[] { "1" };

    getContentResolver().delete(url, tDelWhere, tDelSelectionArgs); // 删除 id 为 1 的记录

    showContact("删除操作完成后");

    /**
    * 修改操作
    */
    ContentValues tUpdateValues = new ContentValues();
    tUpdateValues.put("msn", "99999");

    String tUpdateWhere = "name=?";
    String[] tUpdateSelectionArgs = new String[] { "san1" };

    getContentResolver().update(url, tUpdateValues, tUpdateWhere,
    tUpdateSelectionArgs);

    showContact("修改操作完成后");
    }

    public void showContact(String manipulate) {

    /**
    * 查看操作
    */
    Cursor tCursor = getContentResolver()
    .query(url, null, null, null, null);

    while (tCursor.moveToNext()) {

    String id = tCursor.getString(tCursor.getColumnIndex("id"));
    String name = tCursor.getString(tCursor.getColumnIndex("name"));
    String msn = tCursor.getString(tCursor.getColumnIndex("msn"));

    Log.e(TAG, manipulate+" id为:" + id + " name为:" + name + " msn为:" + msn);
    }
    }
    }

    3.在 AndroidManifest.xml 配置 provider(与 activity 同级)
    <provider
    android:name="com.xjl.contentprovider.provider.ContactProvider"
    android:authorities="xjl.prd.contactprovider" />

    注:android:authorities="xjl.prd.contactprovider"

    这里 authorities 值是注册的 URI:matcher.addURI("xjl.prd.contactprovider", "contactinfo", CONTACTS); 里的 xjl.prd.contactprovider ,此处必须对应,否则不能操作数据。

    此属性在官方文档中的解释:
    A list of one or more URI authorities that identify data offered by the content provider. Multiple authorities are listed by separating their names with a semicolon. To avoid conflicts, authority names should use a Java-style naming convention (such ascom.example.provider.cartoonprovider). Typically, it's the name of the ContentProvidersubclass that implements the provider
    There is no default. At least one authority must be specified
    【在同一项目中运行结果】
    @@@MainActivity
    刚插入数据的 id 为:1
    @@@MainActivity
    刚插入数据的 id 为:2
    @@@MainActivity
    刚插入数据的 id 为:3
    @@@MainActivity
    ======添加操作完成后======
    @@@MainActivity
    id为:1 name为:san0 msn为:null
    @@@MainActivity
    id为:2 name为:san1 msn为:null
    @@@MainActivity
    id为:3 name为:san2 msn为:null
    @@@MainActivity
    ======删除操作完成后======
    @@@MainActivity
    id为:2 name为:san1 msn为:null
    @@@MainActivity
    id为:3 name为:san2 msn为:null
    @@@MainActivity
    ======修改操作完成后======
    @@@MainActivity
    id为:2 name为:san1 msn为:99999
    @@@MainActivity
    id为:3 name为:san2 msn为:null


    4.在其他程序中要访问自定义的 ContentProvider ,需要在共享的 ContentProvider 中设置 android:exported="true"

    <provider
    android:name="com.xjl.contentprovider.provider.ContactProvider"
    android:authorities="com.xjl.contactprovider"
    android:exported="true" />

    这样其他程序就可以访问自定义的 ContentProvider 了,其中,exported 在 sdk 16 或以下默认值为 true,17或以上默认值为 false;

    代码示例在:360云盘:自己的学习资料----Android总结过的项目----ContentProviderExample.rar

  • 相关阅读:
    高阶函数 map
    高阶函数_filter
    sort和sorted方法的使用
    一个函数作为另外一个函数的参数
    匿名函数
    jenkins+Xcode+蒲公英实现ipa自动打包发布全攻略
    iOS 画贝塞尔曲线 连续曲线 平滑曲线 曲线图表
    基于WebRTC实现iOS端音频降噪功能
    苹果ios音频的回声消除处理
    iOS实现录音功能
  • 原文地址:https://www.cnblogs.com/zx-blog/p/11835723.html
Copyright © 2011-2022 走看看