欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

基于Android ContentProvider的总结详解

程序员文章站 2023-11-20 17:40:40
1.适用场景1) contentprovider为存储和读取数据提供了统一的接口2) 使用contentprovider,应用程序可以实现数据共享3) android内置的...
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());
}
    }
}