Android ContentResolve

修改数据

可以通过下面的方法修改content provider控制的数据:

· 增加新记录

· 对存在的记录添加新值

· 批量更新存在的记录

· 删除记录

调用 ContentResolver 的方法可以完成数据的修改。 某些content providers写数据需要比读数据更严格的权限,如果没有写Content Provider的权限, ContentResolver的方法将失效。

添加数据

要想向Content Provider添加一条新纪录,首先要在ContentValues对象中创建一个键-值对的映射,在这个映射中,每个值对应content provider中列的名字,相应的值就是新纪录在对应列中的值;然后,调用以Provider的URI和ContentValues映射作为参数ContentResolver.insert()函数。这个函数返回值是新记录的完整URI——即Provider的RUI加上新纪录的ID。可以通过这个URI查询和得到新纪录的Cursor,并且可以进一步修改这个新增加的记录。下面是一个例子;

import android.provider.Contacts.People;

import android.content.ContentResolver;

import android.content.ContentValues;



ContentValues values = new ContentValues();



// Add Abraham Lincoln to contacts and make him a favorite.

values.put(People.NAME, "Abraham Lincoln");

// 1 = the new contact is added to favorites

// 0 = the new contact is not added to favorites

values.put(People.STARRED, 1);



Uri uri = getContentResolver().insert(People.CONTENT_URI, values);

添加新值

如果一条记录已经存在,我们可以为其增加新信息或者修改现有的信息。例如,上面例子的下一步是为这个新纪录增加联络信息——电话号、IM或电子邮件地址。

为联系人数据库中增加一条新纪录最佳方法是把新数据所去的表明添加到该记录的URI后面,然后使用修正后的RUI来添加新数据值。每个联系人表都开放了一个名字作为CONTENT_DIRECTORY常量。下面的代码接续前面的例子,为刚刚创建的记录增加电话号码和电邮地址。

Uri phoneUri = null;

Uri emailUri = null;



// Add a phone number for Abraham Lincoln. Begin with the URI for

// the new record just returned by insert(); it ends with the _ID

// of the new record, so we don't have to add the ID ourselves.

// Then append the designation for the phone table to this URI,

// and use the resulting URI to insert the phone number.

phoneUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);



values.clear();

values.put(People.Phones.TYPE, People.Phones.TYPE_MOBILE);

values.put(People.Phones.NUMBER, "1233214567");

getContentResolver().insert(phoneUri, values);



// Now add an email address in the same way.

emailUri = Uri.withAppendedPath(uri, People.ContactMethods.CONTENT_DIRECTORY);



values.clear();

// ContactMethods.KIND is used to distinguish different kinds of

// contact methods, such as email, IM, etc.

values.put(People.ContactMethods.KIND, Contacts.KIND_EMAIL);

values.put(People.ContactMethods.DATA, "test@example.com");

values.put(People.ContactMethods.TYPE, People.ContactMethods.TYPE_HOME);

getContentResolver().insert(emailUri, values);

通过调用ContentValues.put()函数可以把一小段二进制数据放到数据表中,ContentValues.put()函数携带一个二进制数组。这种方法适合于小的图标类图片或者一小段音频剪辑。如果要添加大量的二进制数据。例如照片,一整首歌曲,在数据表中保存该数据的 content: URI,然后调用文件URI的ContentResolver.openOutputStream()函数(这会使ContentProvider把数据保存在一个文件里,,把这个文件的路径保存在记录的一个隐含字段中)。

就这方面来说,MediaStore Content Provider是分配图像、视频和音频数据的主要的Provider。MediaStore Content Provider采用特殊的规则:用于query() 或 managedQuery()来获取二进制数据的元信息(如照片的标题或者摄制日期)的URI,同样用于openInputStream()来获取二进制数据本身。类似的,用于insert()函数来把元信息放入MediaStore记录的URI,也用于保存二进制数据本身的 openOutputStream()函数,下面的这段代码展示了这种规则:

import android.provider.MediaStore.Images.Media;

import android.content.ContentValues;

import java.io.OutputStream;



// Save the name and description of an image in a ContentValues map.

ContentValues values = new ContentValues(3);

values.put(Media.DISPLAY_NAME, "road_trip_1");

values.put(Media.DESCRIPTION, "Day 1, trip to Los Angeles");

values.put(Media.MIME_TYPE, "image/jpeg");



// Add a new record without the bitmap, but with the values just set.

// insert() returns the URI of the new record.

Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values);



// Now get a handle to the file for that record, and save the data into it.

// Here, sourceBitmap is a Bitmap object representing the file to save to the database.

try {

OutputStream outStream = getContentResolver().openOutputStream(uri);

sourceBitmap.compress(Bitmap.CompressFormat.JPEG, 50, outStream);

outStream.close();

} catch (Exception e) {

Log.e(TAG, "exception while writing image", e);

}

批量更新数据

想要批量更新一组记录,例如把所有字段的"NY"改成 "New York",就要调用ContentResolver.update()函数,用想要改变的列和值作为参数。

删除记录

调用ContentResolver.delete()函数,以特定行的 Uri为参数,可以删除一条记录。

调用ContentResolver.delete()函数,以需要删除的记录类型的Uri(如android.provider.Contacts.People.CONTENT_URI)为参数,可以删除多行数据,SQL的 WHERE语句可以指定删除哪儿些行。

注意:如果要删除一个通用类型,一定要确定WHERE语句是有效的,否则将会面临删掉比预计更多的记录的风险。

创建Content Provider

要创建一个Content Provider,必须:

· 建立保存数据的系统。大多数content providers使用Android的文件或SQLite数据库来存储数据,但是我们可以以我们想要的任何方式存储数据。 Android提供了 SQLiteOpenHelper来帮助我们创建数据库,提供了 SQLiteDatabase类来管理数据库。

· 扩展ContentProvider类来提供访问数据的方法。.

· 在应用程序的AndroidManifest.xml文件中声明这个Content Provider。

下面各节将讨论上面任务的后两项

定义ContentProvider类的子类把数据开放给使用ContentResolver和Cursor期望规则的其它应用程序,这主要是要实现在ContentProvider类中定义的六个虚函数:

query()

insert()

update()

delete()

getType()

onCreate()

query()方法必须返回一个能够遍历所求数据的Cursor对象。 Cursor本身也是接口,但是Android一些可用的Cursor对象,例如,SQLiteCursor可以遍历保存在SQLite数据库中的数据。通过调用任何SQLiteDatabase类的query()方法就可以得到Cursor对象,还有其它为不是保存在数据库中的数据而设的Cursor实现,诸如MatrixCursor。

由于在不同的进程和线程中的ContentResolver 对象都可以调用ContentProvider的方法,必须在线程安全模式下使用。

当对数据修改时,处于善意,会调用call ContentResolver.notifyChange()函数来通知监听者。

除了定义子类,还要采取其他步骤来简化客户端的工作,使这个类更容易使用:

· 定义 public static final Uri命名为 CONTENT_URI,它是一个字符串,代表了content provider能够处理的全部内容。 必须为这个值定义唯一的字符串。最佳的方法是使用content provider的完全类名 (小写)。例如, TransportationProvider类的定义如下的URI

* public static final Uri CONTENT_URI = Uri.parse("content://com.example.codelab.transporationprovider");

如果Provider有子表,为每一个子表都要定义CONTENT_URI常量。这些URIs 应该具备相同的权限,只能通过它们的路径来区分。例如:

content://com.example.codelab.transporationprovider/train
content://com.example.codelab.transporationprovider/air/domestic
content://com.example.codelab.transporationprovider/air/international

· 定义Content Provider返回给客户端的列名。 如果使用数据库,这些列名通常与SQL数据库类名同名。还要定义public static字符串常量,客户端可以在查询或其它指令中指定列

要确保为记录的ID提供一个命名为"_id" (常量 _ID)的整数列。无论是否还有其它字段(如URI)能够唯一标识记录,都应该定义这个ID字段。如果使用SQLite数据库,_ID应为下面的类型:

INTEGER PRIMARY KEY AUTOINCREMENT

AUTOINCREMENT 描述符是可选的。 如果没有它,SQLite载ID字段添加比现存ID列中最大的数大一的数。如果删除了最后一行,那么增加的下一行将使用与被删除行相同的ID。 AUTOINCREMENT使SQLite增加到目前最大值的下一个值,而无论这个最大值是否被删除。

· 仔细考证每个列的数据类型,客户端将使用这个信息来读数据。

· 如果要处理新的数据类型,必须在ContentProvider.getType()函数的实现中定义一种新的MIME类型返回。这种数据类型部分依赖于提交给getType()函数的content: URI 是否局限于请求某一特定记录。针对单一记录有一种MIME类型的格式,针对多记录则是另外一种格式。利用 Uri 方法有助于确定请求的是什么。下面是每种类型的通用格式:

o 对单一记录:

vnd.android.cursor.item/vnd.yourcompanyname.contenttype

例如,请求train记录122的URI格式如下:

content://com.example.transportationprovider/trains/122

返回的MIME类型:

vnd.android.cursor.item/vnd.example.rail

o 对多记录:vnd.android.cursor.dir/vnd.yourcompanyname.contenttype

例如,请求所有train记录的URI格式如下

content://com.example.transportationprovider/trains

返回的MIME类型:

vnd.android.cursor.dir/vnd.example.rail

· 如果输出的字节数据太大以至于不能放在数据表中,如大的bitmap文件,那么向客户端输出的字段实际上应该包含一个content: URI字符串。这个字段提供给客户端访问数据文件的方式。记录中也要包含名为“_data”的字段。 “_data”列出了该文件在设备上的确切的文件路径,它不是提供给客户端访问的,而是由ContentResolver读取。客户端可以在面向用户字段上调用 ContentResolver.openInputStream() ,这个字段保存条目的URI。 ContentResolver会请求记录的"_data"字段。由于ContentResolver比起客户端来说有更高的权限,所以 ContentResolver 可以直接访问文件,向客户端返回一个read wrapper。

content provider实现的例子,参考随同SDK一同发布的Notepad 示例应用程序中NodePadProvider 类。
声明content provider

为了让Android系统知道我们开发的content provider,需要在应用程序的AndroidManifest.xml文件中声明一个<provider>元素。没有在 mainfest中声明的Content providers对Android系统来说是不可见的。

Name属性是能够完全确定ContentProvider子类的属性;authorities属性是标识provider的 content: URI的权限部分。例如,ContentProvider的子类AutoInfoProvider的<provider>元素定义如下:

<provider name="com.example.autos.AutoInfoProvider"

authorities="com.example.autos.autoinfoprovider"

. . . />

</provider>

注意:authorities属性忽略了content: URI的路径部分。例如,如果AutoInfoProvider为不同类型的汽车或制造商管理不同的子表

content://com.example.autos.autoinfoprovider/honda
content://com.example.autos.autoinfoprovider/gm/compact
content://com.example.autos.autoinfoprovider/gm/suv

这些路径不要在manifest中声明,authority是标识provider的,provider能够解释所选择的URI 的路径部分。

其它<provider>属性可以设置读写数据的权限,提供可以显示给用户的图标或文本,使provider有效或无效等等。如果在content provider多个运行版本之间不需要数据的同步,把multiprocess属性设置为“true”,这允许在每个客户进程中创建provider实例,消除执行IPC的需求。
Content URI 总结

下面重温一下content URI的重要部分:

content://com.example.transportationprovider/trains/122
v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);} Normal 0 7.8 磅 0 2 false false false MicrosoftInternetExplorer4 /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;}

A: 标准前缀,指明数据是由一个content provider控制的,永远不要修改这个前缀。

B: URI的权柄部分,它指明了content provider。对第三方应用程序来说,这应当是一个完全限定类名(小写),以确保其唯一性。在<provider>元素的 authorities属性中声明这个权柄。

<provider name=".TransportationProvider"

authorities="com.example.transportationprovider"

. . . >

C: content provider用来确定请求哪种类型数据的路径。这部分可能有零个或多个段长。如果content provider只输出一种类型的数据(例如,只有trains),这部分是可以不要的;如果provider输出几种类型的数据,包括子类型,那么这部分可能是几个段长——例如,"land/bus", "land/train", "sea/ship",和 "sea/submarine"提供了四种可能性。

D: 如果有的话,这部分是被请求的具体记录的ID,它是被请求记录的_ID值。如果不是请求单一的记录,这部分以及斜杠线都可以省略,如:

content://com.example.transportationprovider/trains
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值