当前位置: 移动技术网 > IT编程>移动开发>Android > 基于Android ContentProvider的总结详解

基于Android ContentProvider的总结详解

2019年07月24日  | 移动技术网IT编程  | 我要评论

孙菂,贵州信息港游戏中心,黄崇俊

1.适用场景
1) contentprovider为存储和读取数据提供了统一的接口
2) 使用contentprovider,应用程序可以实现数据共享
3) android内置的许多数据都是使用contentprovider形式,供开发者调用的(如视频,音频,图片,通讯录等)
2.相关概念介绍
1)contentprovider简介
当应用继承contentprovider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用sharedpreferences共享数据,需要使用sharedpreferences api读写数据。而使用contentprovider共享数据的好处是统一了数据访问方式。
2)uri类简介
uri uri = uri.parse("content://com.changcheng.provider.contactprovider/contact")
在content provider中使用的查询字符串有别于标准的sql查询。很多诸如select, add, delete, modify等操作我们都使用一种特殊的uri来进行,这种uri由3个部分组成, “content://”, 代表数据的路径,和一个可选的标识数据的id。以下是一些示例uri:
复制代码 代码如下:

content://media/internal/images  这个uri将返回设备上存储的所有图片
content://contacts/people/  这个uri将返回设备上的所有联系人信息
content://contacts/people/45 这个uri返回单个结果(联系人信息中id为45的联系人记录)

尽管这种查询字符串格式很常见,但是它看起来还是有点令人迷惑。为此,android提供一系列的帮助类(在android.provider包下),里面包含了很多以类变量形式给出的查询字符串,这种方式更容易让我们理解一点,因此,如上面content://contacts/people/45这个uri就可以写成如下形式:
uri person = contenturis.withappendedid(people.content_uri,  45);
然后执行数据查询:
cursor cur = managedquery(person, null, null, null);
这个查询返回一个包含所有数据字段的游标,我们可以通过迭代这个游标来获取所有的数据:
复制代码 代码如下:

package com.wissen.testapp;
public class contentproviderdemo extends activity {
    @override
    public void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.main);
displayrecords();
    }
    private void displayrecords() {
 //该数组中包含了所有要返回的字段
     string columns[] = new string[] { people.name, people.number };
uri mcontacts = people.content_uri;
cursor cur = managedquery(
   mcontacts,
   columns,  // 要返回的数据字段
  null,   // where子句
  null,  // where 子句的参数
         null  // order-by子句
     );
if (cur.movetofirst()) {
    string name = null;
    string phoneno = null;
    do {
// 获取字段的值
     name = cur.getstring(cur.getcolumnindex(people.name));
      phoneno = cur.getstring(cur.getcolumnindex(people.number));
      toast.maketext(this, name + ” ” + phoneno, toast.length_long).show();
   } while (cur.movetonext());
}
    }
}

上例示范了一个如何依次读取联系人信息表中的指定数据列name和number。
修改记录:
我们可以使用contentresolver.update()方法来修改数据,我们来写一个修改数据的方法:
复制代码 代码如下:

private void updaterecord(int recno, string name) {
    uri uri = contenturis.withappendedid(people.content_uri, recno);
    contentvalues values = new contentvalues();
    values.put(people.name, name);
    getcontentresolver().update(uri, values, null, null);
}

现在你可以调用上面的方法来更新指定记录:
updaterecord(10, ”xyz”);   //更改第10条记录的name字段值为“xyz”
添加记录:
要增加记录,我们可以调用contentresolver.insert()方法,该方法接受一个要增加的记录的目标uri,以及一个包含了新记录值的map对象,调用后的返回值是新记录的uri,包含记录号。
上面的例子中我们都是基于联系人信息簿这个标准的content provider,现在我们继续来创建一个insertrecord() 方法以对联系人信息簿中进行数据的添加:
复制代码 代码如下:

private void insertrecords(string name, string phoneno) {
    contentvalues values = new contentvalues();
    values.put(people.name, name);
    uri uri = getcontentresolver().insert(people.content_uri, values);
    log.d(”android”, uri.tostring());
    uri numberuri = uri.withappendedpath(uri, people.phones.content_directory);
    values.clear();
    values.put(contacts.phones.type, people.phones.type_mobile);
    values.put(people.number, phoneno);
    getcontentresolver().insert(numberuri, values);
}

这样我们就可以调用insertrecords(name, phoneno)的方式来向联系人信息簿中添加联系人姓名和电话号码。
删除记录:
content provider中的getcontextresolver.delete()方法可以用来删除记录,下面的记录用来删除设备上所有的联系人信息:
复制代码 代码如下:

private void deleterecords() {
    uri uri = people.content_uri;
    getcontentresolver().delete(uri, null, null);
}

你也可以指定where条件语句来删除特定的记录:
getcontentresolver().delete(uri, “name=” + “‘xyz xyz'”, null);
这将会删除name为‘xyz xyz'的记录。
3. 创建contentprovider
要创建我们自己的content provider的话,我们需要遵循以下几步:
a. 创建一个继承了contentprovider父类的类
b. 定义一个名为content_uri,并且是public static final的uri类型的类变量,你必须为其指定一个唯一的字符串值,最好的方案是以类的全名称, 如:
public static final uri content_uri = uri.parse( “content://com.google.android.mycontentprovider”);
c. 定义你要返回给客户端的数据列名。如果你正在使用android数据库,必须为其定义一个叫_id的列,它用来表示每条记录的唯一性。
d. 创建你的数据存储系统。大多数content provider使用android文件系统或sqlite数据库来保持数据,但是你也可以以任何你想要的方式来存储。
e. 如果你要存储字节型数据,比如位图文件等,数据列其实是一个表示实际保存文件的uri字符串,通过它来读取对应的文件数据。处理这种数据类型的content provider需要实现一个名为_data的字段,_data字段列出了该文件在android文件系统上的精确路径。这个字段不仅是供客户端使用,而且也可以供contentresolver使用。客户端可以调用contentresolver.openoutputstream()方法来处理该uri指向的文件资源;如果是contentresolver本身的话,由于其持有的权限比客户端要高,所以它能直接访问该数据文件。
f. 声明public static string型的变量,用于指定要从游标处返回的数据列。
g. 查询返回一个cursor类型的对象。所有执行写操作的方法如insert(), update() 以及delete()都将被监听。我们可以通过使用contentresover().notifychange()方法来通知监听器关于数据更新的信息。
h. 在androidmenifest.xml中使用<provider>标签来设置content provider。
i. 如果你要处理的数据类型是一种比较新的类型,你就必须先定义一个新的mime类型,以供contentprovider.getype(url)来返回。mime类型有两种形式:一种是为指定的单个记录的,还有一种是为多条记录的。这里给出一种常用的格式:
复制代码 代码如下:

  vnd.android.cursor.item/vnd.yourcompanyname.contenttype (单个记录的mime类型)
  比如, 一个请求列车信息的uri如content://com.example.transportationprovider/trains/122 可能就会返回typevnd.android.cursor.item/vnd.example.rail这样一个mime类型。
  vnd.android.cursor.dir/vnd.yourcompanyname.contenttype (多个记录的mime类型)
  比如, 一个请求所有列车信息的uri如content://com.example.transportationprovider/trains 可能就会返回vnd.android.cursor.dir/vnd.example.rail这样一个mime 类型。

下列代码将创建一个content provider,它仅仅是存储用户名称并显示所有的用户名称(使用 sqllite数据库存储这些数据):
复制代码 代码如下:

public class myusers {
    public static final string authority  = “com.wissen.mycontentprovider”;
    // basecolumn类中已经包含了 _id字段
   public static final class user implements basecolumns {
 public static final uri content_uri  = uri.parse(”content://com.wissen.mycontentprovider”);
 // 表数据列
 public static final string  user_name  = “user_name”;
    }
}

上面的类中定义了content provider的content_uri,以及数据列。下面我们将定义基于上面的类来定义实际的content provider类:
复制代码 代码如下:

public class mycontentprovider extends contentprovider {
    private sqlitedatabase     sqldb;
    private databasehelper    dbhelper;
    private static final string  database_name = “users.db”;
    private static final int  database_version= 1;
    private static final string table_name= “user”;
    private static final string tag = “mycontentprovider”;
    private static class databasehelper extends sqliteopenhelper {
 databasehelper(context context) {
     super(context, database_name, null, database_version);
 }
 @override
 public void oncreate(sqlitedatabase db) {
     //创建用于存储数据的表
 db.execsql(”create table ” + table_name + “( _id integer primary key autoincrement, user_name text);”);
 }
 @override
 public void onupgrade(sqlitedatabase db, int oldversion, int newversion) {
     db.execsql(”drop table if exists ” + table_name);
     oncreate(db);
 }
    }
    @override
    public int delete(uri uri, string s, string[] as) {
 return 0;
    }
    @override
    public string gettype(uri uri) {
 return null;
    }
    @override
    public uri insert(uri uri, contentvalues contentvalues) {
 sqldb = dbhelper.getwritabledatabase();
 long rowid = sqldb.insert(table_name, “”, contentvalues);
 if (rowid > 0) {
     uri rowuri = contenturis.appendid(myusers.user.content_uri.buildupon(), rowid).build();
     getcontext().getcontentresolver().notifychange(rowuri, null);
     return rowuri;
 }
 throw new sqlexception(”failed to insert row into ” + uri);
    }
    @override
    public boolean oncreate() {
 dbhelper = new databasehelper(getcontext());
 return (dbhelper == null) ? false : true;
    }
    @override
    public cursor query(uri uri, string[] projection, string selection, string[] selectionargs, string sortorder) {
 sqlitequerybuilder qb = new sqlitequerybuilder();
 sqlitedatabase db = dbhelper.getreadabledatabase();
 qb.settables(table_name);
 cursor c = qb.query(db, projection, selection, null, null, null, sortorder);
 c.setnotificationuri(getcontext().getcontentresolver(), uri);
 return c;
    }
    @override
    public int update(uri uri, contentvalues contentvalues, string s, string[] as) {
 return 0;
    }
}

一个名为mycontentprovider的content provider创建完成了,它用于从sqlite数据库中添加和读取记录。
content provider的入口需要在androidmanifest.xml中配置:
复制代码 代码如下:

<provider android:name=”mycontentprovider” android:authorities=”com.wissen.mycontentprovider” />

之后,让我们来使用这个定义好的content provider:
1)为应用程序添加contentprovider的访问权限。
2)通过getcontentresolver()方法得到contentresolver对象。
3)调用contentresolver类的query()方法查询数据,该方法会返回一个cursor对象。
4)对得到的cursor对象进行分析,得到需要的数据。
5)调用cursor类的close()方法将cursor对象关闭。
复制代码 代码如下:

public class mycontentdemo extends activity {
    @override
    protected void oncreate(bundle savedinstancestate) {
 super.oncreate(savedinstancestate);
 insertrecord(”myuser”);
 displayrecords();
    }

    private void insertrecord(string username) {
 contentvalues values = new contentvalues();
 values.put(myusers.user.user_name, username);
 getcontentresolver().insert(myusers.user.content_uri, values);
    }
    private void displayrecords() {
 string columns[] = new string[] { myusers.user._id, myusers.user.user_name };
 uri myuri = myusers.user.content_uri;
 cursor cur = managedquery(myuri, columns,null, null, null );
 if (cur.movetofirst()) {
     string id = null;
     string username = null;
     do {
  id = cur.getstring(cur.getcolumnindex(myusers.user._id));
  username = cur.getstring(cur.getcolumnindex(myusers.user.user_name));
  toast.maketext(this, id + ” ” + username, toast.length_long).show();
    } while (cur.movetonext());
}
    }
}

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网