您的位置:首页 > 移动开发 > Android开发

Android开发之数据保存技术(一)

2016-09-02 09:29 459 查看
本文主要讲解Android开发的数据保存技术。Android的数据保存技术主要有preference,本地文件,SQLite轻量级数据库,和Content Provider。本文只要讲SQLite和Conent Provider。preference和本地文件,将放在后面讨论。

SQLite

Android通过SQLite库提供了完善的关系数据库功能,而没有强加任何额外的限制。通过使用SQLite,可以为每一个应用程序创建独立的关系数据库。

所有的Android数据库都存储在/data/data/<package_name>/databases文件夹下。默认条件下,所有的数据库都是私有的,并且只能被创建它们的应用程序访问。要跨应用程序共享数据库,可以使用内容提供器。

SQLite是一个关系数据管理系统。它被普遍认为是:开源,兼容标准,轻量级,Single-tier。

可以用一个例子来演示SQLite。该例子将记录存储在一个数据库中,然后显示出来。

新建一个HelloSQLite的程序。

需要在某个地方放置该数据库中描述的一些常量,所以要创建一个Constants接口。

代码如下:

/*

* Android开发之数据保存技术(一)

* Constants.java

* Created on: 2011-8-15

* Author: blueeagle

* Email: liujiaxiang@gmail.com

*/

view sourceprint?

01.
package com.blueeagle;

02.

03.

04.

05.
import android.provider.BaseColumns;

06.

07.
public

interface Constants extends BaseColumns {

08.

09.
/** Called when the activity is first created. */

10.

11.
public

static
finalString TABLE_NAME =
"HelloSQLite"
;

12.

13.
public

static
finalString TIME =

"time"
;

14.

15.
public

static
finalString TITLE =
"title"
;

16.

17.
}


每个事件都将作为HeloSQLite表中的一行进行存储。每行都包含一个_id、time和title列。_id是主键,在扩展的BaseColums接口中声明。time和title分别作为时间和事件标记。

使用SQliteOpenHelper。接下来,创建一个名为SQLiteData的帮助器来表示数据库本身。这个类扩展自Android的SQLiteOpenHelper类,它负责管理数据库的创建和版本。需要做的就是提供一个构造方法并且覆写两个方法。

代码如下:

view sourceprint?

01.
/*

02.

03.
*  Android开发之数据保存技术(一)

04.

05.
*  MySQLite.java

06.

07.
*  Created on: 2011-8-16

08.

09.
*  Author: blueeagle

10.

11.
*  Email: liujiaxiang@gmail.com

12.

13.
*/

14.

15.
package com.blueeagle;

16.

17.

18.

19.
import android.content.Context;

20.

21.
import android.database.sqlite.SQLiteDatabase;

22.

23.
import android.database.sqlite.SQLiteDatabase.CursorFactory;

24.

25.
import android.database.sqlite.SQLiteOpenHelper;

26.

27.
import
static
com.blueeagle.Constants.TABLE_NAME;

28.

29.
import
static
com.blueeagle.Constants.TIME;

30.

31.
import
static
com.blueeagle.Constants.TITLE;

32.

33.
import
static
android.provider.BaseColumns._ID;

34.

35.

36.

37.
public

class
MySQLite extends SQLiteOpenHelper {

38.

39.

40.

41.
private

static
finalString DATABASE_NAME =
"MySQLite.db"
;

42.

43.
private

static
final
int

DATABASE_VERSION = 1;

44.

45.

46.

47.
public

MySQLite(Context context,String name,CursorFactory factory,

48.

49.
int

version) {

50.

51.
super(context,DATABASE_NAME,factory,DATABASE_VERSION);

52.

53.
// TODO Auto-generated constructor stub

54.

55.
}

56.

57.

58.

59.
@Override

60.

61.
public

void
onCreate(SQLiteDatabase db) {

62.

63.
// TODO Auto-generated method stub

64.

65.
db.execSQL(
"CREATE TABLE "
+ TABLE_NAME+
"("
+_ID+
"INTEGER
PRIMARY KEY AUTOINCREMENT,"
+TIME+
"INTEGER,"
+TITLE+
"TEXT NOT NULL);"
);

66.

67.
}

68.

69.

70.

71.
@Override

72.

73.
public

void
onUpgrade(SQLiteDatabase db,
int
oldVersion,

int
newVersion) {

74.

75.
// TODO Auto-generated method stub

76.

77.
db.execSQL(
"DROP TABLE IF EXISTS"

+ TABLE_NAME);

78.

79.
onCreate(db);

80.

81.

82.

83.
}

84.

85.
}


首次访问数据库时,SQLiteOpenHelper将注意到该数据库不存在,并调用onCreate()方法来创建它。

定义Activity主程序。

在HelloSQLite程序中做的第一次尝试是使用本地的SQLite数据库来存储事件,并将这些事件显示为TextView中的一个字符串。

布局xml文件如下:

view sourceprint?

01.
main.xml

02.

03.
<?
xml

version
=
"1.0"

encoding
=
"utf-8"
?>

04.

05.
<
ScrollView

xmlns:android
=
"http://schemas.android.com/apk/res/android"

06.

07.
android:layout_width
=
"fill_parent"

08.

09.
android:layout_height
=
"fill_parent"

10.

11.
>

12.

13.
<
TextView

14.

15.
android:id
=
"@+id/myText"

16.

17.
android:layout_width
=
"fill_parent"

18.

19.
android:layout_height
=
"wrap_content"

20.

21.
/>

22.

23.
</
ScrollView
>


这段代码使用一个ScrollView,以防太多的时间沾满了屏幕。

对于HelloSQLite.java的实现如下所示:相关代码有注释说明。

view sourceprint?

001.
package

com.blueeagle;

002.

003.
/*

004.

005.
*  Android开发之数据保存技术(一)

006.

007.
*  HelloSQLite.java

008.

009.
*  Created on: 2011-8-16

010.

011.
*  Author: blueeagle

012.

013.
*  Email: liujiaxiang@gmail.com

014.

015.
*/

016.

017.

018.

019.
import

android.app.Activity;

020.

021.
import

android.content.ContentValues;

022.

023.
import

android.database.Cursor;

024.

025.
import

android.database.sqlite.SQLiteDatabase;

026.

027.
import

android.os.Bundle;

028.

029.
import

android.widget.TextView;

030.

031.
import

static
com.blueeagle.Constants.TABLE_NAME;

032.

033.
import

static
com.blueeagle.Constants.TIME;

034.

035.
import

static
com.blueeagle.Constants.TITLE;

036.

037.
import

static
android.provider.BaseColumns._ID;

038.

039.

040.

041.
public

class
HelloSQLite
extends

Activity {

042.

043.
/** Called when the activity is first created. */

044.

045.
private

MySQLite mySqlite;

046.

047.
private

static
String[] FROM={_ID,TIME,TITLE};

048.

049.
private

static
String ORDER_BY = TIME+
" DESC"
;

050.

051.
@Override

052.

053.
public

void
onCreate(Bundle savedInstanceState) {

054.

055.
super
.onCreate(savedInstanceState);

056.

057.
setContentView(R.layout.main);

058.

059.

060.

061.
mySqlite =
new
MySQLite(
this
);

062.

063.
try
{

064.

065.
addItem(
"Hello,Android!"
);

066.

067.
Cursor cursor = getItems();

068.

069.
showItems(cursor);

070.

071.
}

072.

073.
finally
{

074.

075.
mySqlite.close();

076.

077.
}

078.

079.
}

080.

081.
//显示查询结果

082.

083.
private

void
showItems(Cursor cursor) {

084.

085.
// TODO Auto-generated method stub

086.

087.
StringBuilder builder =
new
StringBuilder(
"Saved items:\n"
);

088.

089.
while
(cursor.moveToNext()){

090.

091.
long

id = cursor.getLong(
0
);

092.

093.
long

time = cursor.getLong(
1
);

094.

095.
String title = cursor.getColumnName(
2
);

096.

097.
builder.append(id).append(
": "
);

098.

099.
builder.append(time).append(
": "
);

100.

101.
builder.append(title).append(
"\n"
);

102.

103.
}

104.

105.
TextView myTextView = (TextView)findViewById(R.id.myText);

106.

107.
myTextView.setText(builder);

108.

109.
}

110.

111.
//此函数接受一个Cursor作为输入并格式化,以便用户能够理解输出的内容

112.

113.
//查询条目方法

114.

115.
private

Cursor getItems() {

116.

117.
// TODO Auto-generated method stub

118.

119.
SQLiteDatabase db = mySqlite.getReadableDatabase();

120.

121.
Cursor cursor = db.query(TABLE_NAME,FROM,
null
,
null
,
null
,
null
,ORDER_BY);

122.

123.
startManagingCursor(cursor);

124.

125.
return

cursor;

126.

127.
}

128.

129.
//因为查询不用修改数据库,因此利用只读句柄,然后调用查询的query方法来执行SQL语句,FROM是

130.

131.
//想要使用的列构成的数组,ORDER_BY告诉SQLite按照从新到旧的顺序返回查询结果。

132.

133.
//添加条目方法

134.

135.
private

void
addItem(String string) {

136.

137.
// TODO Auto-generated method stub

138.

139.
SQLiteDatabase db = mySqlite.getWritableDatabase();

140.

141.
ContentValues values =
new
ContentValues();

142.

143.
values.put(TIME,System.currentTimeMillis());

144.

145.
values.put(TITLE,string);

146.

147.
db.insert(TABLE_NAME,
null
,values);

148.

149.
}

150.

151.
//因为要修改数据库,所以调用getWritableDatabase()方法来获取数据库的一个写句柄,当然也可以获取

152.

153.
//到读句柄。

154.

155.
}


完成上述文件-》运行,即可看到结果显示出来。这样就完成了第一个Android数据库程序。

当然,这里面需要注意的问题:

1. 新建数据库的问题

新建数据库的时候,会遇到我们用:

db.execSQL("CREATE TABLE "+ TABLE_NAME+"("+_ID+"INTEGER PRIMARY KEY AUTOINCREMENT,"+TIME+"INTEGER,"+TITLE+"TEXT NOT NULL);");

这样一条语句。有时候SQL语句书写错误,比如少一个空格就会引起程序异常退出。这个时候建议在db.execSQL()函数中填写一个字符串变量,然后把自己写的SQL语句赋给字符串变量,在做赋值操作的时候,先打印出来看看,是不是少空格,多+号什么的小错误。

2. 版本号的问题

关于构造函数中的版本号,是有明确的说明的。

public MySQLite(Context context,String name,CursorFactory factory,int version) {

super(context,DATABASE_NAME,factory,DATABASE_VERSION);

这个版本号version当你有新版本的时候,则做更新操作,新版本的版本号将要比老版本高,如果此时修改版本号,不是向高修改,而是向低修改的话,程序就会发生异常了。

3. 数据库文件的问题

对于onCreate()方法,在程序运行的时候,只运行一次,运行的这一次就是去创建数据库。将数据库的文件存储在SD卡中。路径是data/data/包名/databases里。可以在Eclipse里通过DDMS里的File Explorer来查看。当数据库文件存在了以后,则不会再次运行。

完成了上述例子,就算完成了一个数据库的应用。但是如果列表中有数千个或者上百万个事件。程序将运行的非常慢。甚至于耗尽内存。解决办法就是数据绑定。

有了数据绑定,只需要几行代码,就可以将数据连接到视图,从而实现需求。为了演示,我们修改上面的实例。

将主程序HelloSQLite.java继承ListActivity而不是Activity。

将代码修改为如下所示:

view sourceprint?

001.
package com.blueeagle;

002.

003.
/*

004.

005.
*  Android开发之数据保存技术

006.

007.
*  HelloSQLite.java

008.

009.
*  Created on: 2011-8-16

010.

011.
*  Author: blueeagle

012.

013.
*  Email: liujiaxiang@gmail.com

014.

015.
*/

016.

017.
import android.app.ListActivity;

018.

019.
import android.content.ContentValues;

020.

021.
import android.database.Cursor;

022.

023.
import android.database.sqlite.SQLiteDatabase;

024.

025.
import android.os.Bundle;

026.

027.
import android.widget.SimpleCursorAdapter;

028.

029.
import
static
com.blueeagle.Constants.TABLE_NAME;

030.

031.
import
static
com.blueeagle.Constants.TIME;

032.

033.
import
static
com.blueeagle.Constants.TITLE;

034.

035.
import
static
android.provider.BaseColumns._ID;

036.

037.

038.

039.
public

class
HelloSQLiteextends ListActivity {

040.

041.
/** Called when the activity is first created. */

042.

043.
private

MySQLite mySqlite;

044.

045.
private

static
String[] FROM={_ID,TIME,TITLE};

046.

047.
private

static
String ORDER_BY = TIME+
" DESC"
;

048.

049.
//@Override

050.

051.
public

void
onCreate(Bundle savedInstanceState) {

052.

053.
super.onCreate(savedInstanceState);

054.

055.
setContentView(R.layout.main);

056.

057.

058.

059.
mySqlite =
new
MySQLite(
this
);

060.

061.
try
{

062.

063.
addItem(
"Hello,Android!"
);

064.

065.
Cursor cursor = getItems();

066.

067.
showItems(cursor);

068.

069.
}

070.

071.
finally{

072.

073.
mySqlite.close();

074.

075.
}

076.

077.
}

078.

079.
private

static
int
[] TO = {R.id.rowid,R.id.
time
,R.id.title};

080.

081.
private

void
showItems(Cursor cursor) {

082.

083.
SimpleCursorAdapter adapter =
new
SimpleCursorAdapter(
this
,R.layout.item,cursor,FROM,TO);

084.

085.
//这里有必要说明一下SimpleCursorAdapter构造函数的5个参数。1.对应于当前Activity的引用,2.一个资源,它定义一个列表条目的视图,

086.

087.
//3.数据集光标,4.一组列名称,数据来源于这些列。5.视图列表,这是数据的目的地。

088.

089.
setListAdapter(adapter);

090.

091.

092.

093.
}

094.

095.
private

Cursor getItems() {

096.

097.
SQLiteDatabase db = mySqlite.getReadableDatabase();

098.

099.
Cursor cursor = db.query(TABLE_NAME,FROM,null,null,null,null,ORDER_BY);

100.

101.
startManagingCursor(cursor);

102.

103.
return

cursor;

104.

105.
}

106.

107.
private

void
addItem(String string) {

108.

109.
// TODO Auto-generated method stub

110.

111.
SQLiteDatabase db = mySqlite.getWritableDatabase();

112.

113.
ContentValues values =
new
ContentValues();

114.

115.
values.put(TIME,System.currentTimeMillis());

116.

117.
values.put(TITLE,string);

118.

119.
db.insert(TABLE_NAME,null,values);

120.

121.
}

122.

123.
}


将main.xml修改成如下形式:

view sourceprint?

01.
<?xml version=
"1.0"

encoding=
"utf-8"
?>

02.

03.
<LinearLayout xmlns:android=
"http://schemas.android.com/apk/res/android"

04.

05.
android:layout_width=
"fill_parent"

06.

07.
android:layout_height=
"fill_parent"

08.

09.
>

10.

11.
<ListView

12.

13.
android:id=
"@android:id/list"

14.

15.
android:layout_height=
"wrap_content"

16.

17.
android:layout_width=
"wrap_content"
>

18.

19.
</ListView>

20.

21.
<TextView

22.

23.
android:id=
"@android:id/empty"

24.

25.
android:layout_height=
"wrap_content"

26.

27.
android:layout_width=
"wrap_content"

28.

29.
android:text=
"empty!"
>

30.

31.
</TextView>

32.

33.
</LinearLayout>


增加item.xml为如下:

view sourceprint?

01.
<?
xml

version
=
"1.0"

encoding
=
"utf-8"
?>

02.

03.
<
RelativeLayout

xmlns:android
=
"http://schemas.android.com/apk/res/android"

04.

05.
android:orientation
=
"horizontal"

06.

07.
android:layout_width
=
"fill_parent"

08.

09.
android:layout_height
=
"fill_parent"

10.

11.
android:padding
=
"10sp"

12.

13.
>

14.

15.
<
TextView

16.

17.
android:id
=
"@+id/rowid"

18.

19.
android:layout_height
=
"wrap_content"

20.

21.
android:layout_width
=
"wrap_content"
>

22.

23.
</
TextView
>

24.

25.
<
TextView

26.

27.
android:id
=
"@+id/rowidcolon"

28.

29.
android:layout_height
=
"wrap_content"

30.

31.
android:layout_width
=
"wrap_content"

32.

33.
android:text
=
":"

34.

35.
android:layout_toRightOf
=
"@id/rowid"
>

36.

37.
</
TextView
>

38.

39.
<
TextView

40.

41.
android:id
=
"@+id/time"

42.

43.
android:layout_height
=
"wrap_content"

44.

45.
android:layout_width
=
"wrap_content"

46.

47.
android:layout_toRightOf
=
"@id/rowidcolon"
>

48.

49.
</
TextView
>

50.

51.
<
TextView

52.

53.
android:id
=
"@+id/timecolon"

54.

55.
android:layout_height
=
"wrap_content"

56.

57.
android:layout_width
=
"wrap_content"

58.

59.
android:text
=
":"

60.

61.
android:layout_toRightOf
=
"@id/time"
>

62.

63.
</
TextView
>

64.

65.
<
TextView

66.

67.
android:id
=
"@+id/title"

68.

69.
android:layout_height
=
"fill_parent"

70.

71.
android:layout_width
=
"wrap_content"

72.

73.
android:textStyle
=
"italic"

74.

75.
android:ellipsize
=
"end"

76.

77.
android:singleLine
=
"true"

78.

79.
android:layout_toRightOf
=
"@id/timecolon"
>

80.

81.
</
TextView
>

82.

83.
</
RelativeLayout
>


运行结果如图所示。



当然,这只是一个DEMO,用来展示如何使用SQLite,这个DEMO存在一些问题。其他应用程序都不能向事件数据库添加内容,甚至于无法看这些事件。那么针对这一点,引出了Android里的内容提供器,即ContentProvider。

内容提供器

在Android安全模型中,一个应用程序编写的文件无法被其他任何应用程序所读写。每个应用程序都有自己的Linux用户ID和数据目录,以及其受保护的内存空间。Android程序可以通过下面两种方式进行彼此间的通信。

第一种是IPC(Inter-Process Communication,进程间通信):一个进程使用AIDL(Android Interface Definition Language,接口定义语言)和IBinder接口声明一个任意的API。调用该API时,将在进程间安全且有效地队参数进行编组。这项先进技术用于对后台Service线程进行远程过程调用。

第二种就是ContentProvider:进程在系统中将他们本身注册为某些数据类型的提供者。请求该信息时,Android就会通过一个固定的API调用这些进程,以它们认为合适的方式查询或者修改内容。

ContentProvider管理的任何信息部分都通过一个URI来寻址,这个URI类似于以下形式:content://authority/path/id

其中content://是标准要求的前缀。authority是提供者的名称,建议使用完全限定包名称,避免出现名称冲突。Path是提供者内部的一个虚拟目录,用于标识被请求的数据类型。Id是被请求的特定记录的主键,要请求获得具有特定类型的所有记录,可以省略此参数以及后面的斜杠。

Android已经内置提供了几个提供者,包括:

content://browser;

content://contacts;

content://media;

content://settings。

下面我们就来演示如何使用内容提供器。

将HelloSQLite程序改为使用内容提供器的。对于HelloSQLite的提供者,下面都是有效的URI:

content://com.blueeagle.HelloSQLite/3 ——表示取id为3的数据

content://com.blueeagle.HelloSQLite/ ——表示取所有数据

首先需要向Contants.java添加两个常量:

public static finalString AUTHORITY = "com.blueeagle.HelloSQLite";

public static finalUri CONTENT_URI = Uri.parse("content://"+AUTHORITY+"/"+TABLE_NAME);

接下来可以对HelloSQLite类做一些修改:

代码如下:

view sourceprint?

001.
package

com.blueeagle;

002.

003.
/*

004.

005.
*  Android开发之数据保存技术(一)

006.

007.
*  HelloSQLite.java

008.

009.
*  Created on: 2011-8-16

010.

011.
*  Author: blueeagle

012.

013.
*  Email: liujiaxiang@gmail.com

014.

015.
*/

016.

017.

018.

019.
import

android.app.ListActivity;

020.

021.
import

android.content.ContentValues;

022.

023.
import

android.database.Cursor;

024.

025.
import

android.os.Bundle;

026.

027.
import

android.widget.SimpleCursorAdapter;

028.

029.
import

static
com.blueeagle.Constants.TIME;

030.

031.
import

static
com.blueeagle.Constants.TITLE;

032.

033.
import

static
com.blueeagle.Constants.CONTENT_URI;

034.

035.
import

static
android.provider.BaseColumns._ID;

036.

037.

038.

039.
public

class
HelloSQLite
extends

ListActivity {

040.

041.
/** Called when the activity is first created. */

042.

043.
//private <a href="http://www.it165.net/database/dbmy/"
target="_blank" class="keylink">MySQL</a>ite mySqlite;

044.

045.
private

static
String[] FROM = {_ID,TIME,TITLE};

046.

047.
private

static
String ORDER_BY = TIME+
" DESC"
;

048.

049.
//@Override

050.

051.
public

void
onCreate(Bundle savedInstanceState) {

052.

053.
super
.onCreate(savedInstanceState);

054.

055.
setContentView(R.layout.main);

056.

057.

058.

059.
addItem(
"Hello,Android!"
);

060.

061.
Cursor cursor = getItems();

062.

063.
showItems(cursor);

064.

065.
}

066.

067.
private

static
int
[] TO = {R.id.rowid,R.id.time,R.id.title};

068.

069.

070.

071.
//显示查询结果

072.

073.
private

void
showItems(Cursor cursor) {

074.

075.
// TODO Auto-generated method stub

076.

077.
SimpleCursorAdapter adapter =
new
SimpleCursorAdapter(
this
,R.layout.item,cursor,FROM,TO);

078.

079.
setListAdapter(adapter);

080.

081.
}

082.

083.
//使用ContentProvider时,geiItems方法也化简了

084.

085.
private

Cursor getItems() {

086.

087.
return

managedQuery(CONTENT_URI,FROM,
null
,
null
,ORDER_BY);

088.

089.
}

090.

091.
//此处使用Activity.managedQuery()方法,将内容URI,感兴趣的列表和应该使用的排序顺序传递给该方法。

092.

093.
private

void
addItem(String string) {

094.

095.
ContentValues values =
new
ContentValues();

096.

097.
values.put(TIME,System.currentTimeMillis());

098.

099.
values.put(TITLE,string);

100.

101.
getContentResolver().insert(CONTENT_URI,values);

102.

103.
}

104.

105.
//没有了对getWriteableDatabase()的调用,对insertOrThrow的调用替换成了getContentResolver().insert(CONTENT_URI,values)

106.

107.
//我们使用了一个URI而不是用数据库句柄。

108.

109.
}


下面就是实现ContentProvider

ContentProvider是一个类似于Activity的高级对象,需要向系统进行声明。因此,实现ContentProvider的第一步是将其添加到AndroidManifest.xml文件中的<activity>标签之前。

其中android:name是类名,android:authorities是在内容URI中使用的字符串。接下来创建我们自己的ContentProvider类,为继承类。ContentProvider创建后就会被调用:public boolean onCreate()ContentProvider类中有6个继承而来的方法,需要实现:

具体来说需要实现ContentProvider 类中的6个抽象方法。

Cursor query(Uri uri,String[] projection,String selection,String[] selectionArgs,String sortOrder):将查询的数据以Cursor 对象的形式返回。

Uri insert(Uri uri,ContentValues values):向 Content Provider中插入新数据记录,ContentValues 为数据记录的列名和列值映射。int update(Uri uri,ContentValues values,String selection,String[] selectionArgs):更新Content Provider中已存在的数据记录。int delete(Uri uri,String selection,String[] selectionArgs):从Content Provider中删除数据记录。

String getType(Uri uri):返回Content Provider中的数据( MIME )类型。

boolean onCreate():当 Content Provider 启动时被调用。

定义一个 URI 类型的静态常量,命名为CONTENT_URI。 必须为该常量对象定义一个唯一的URI字符串,一般的做法是将 ContentProvider子类的全称类名作为URI字符串。

定义每个字段的列名,如果采用的数据库存储系统为SQLite 数据库,数据表列名可以采用数据库中表的列名。不管数据表中有没有其他的唯一标识一个记录的字段,都应该定义一个"_id"字段 来唯一标识一个记录。模式使用 "INTEGER PRIMARY KEY AUTOINCREMENT" 自动更新 一般将这些列名字符串定义为静态常量,如"_id"字段名定义为一个名为"_ID" 值为 "_id" 的静态字符串对象。

创建好的一个Content Provider必须在AndroidManifest.xml中声明。

<provider android:name=".ItemsProvider"

android:authorities="com.blueeagle" />

其中name属性为ContentProvider 子类的全称类名,authorities 属性唯一标识了一个ContentProvider。还可以通过 setReadPermission() 和 setWritePermission() 来设置其操作权限。当然也可以再上面的 xml中加入 android:readPermission 或者 android: writePermission属性来控制其权限。

注意:因为ContentProvider可能被不同的进程和线程调用,所以这些方法必须是线程安全的。

然后需要使用UriMatcher,用于匹配Uri。

用法如下:

首先把需要匹配Uri路径全部给注册上:

对于Uri:

什么是URI?将其分为A,B,C,D 4个部分:

A:标准前缀,用来说明一个Content Provider控制这些数据,无法改变的;"content://"

B:URI的标识,它定义了是哪个Content Provider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的 类名。这个标识在 元素的 authorities属性中说明:一般是定义该ContentProvider的包.类的名称 ;"content://hx.android.text.myprovider"

C:路径,不知道是不是路径,通俗的讲就是你要操作的数据库中表的名字,或者你也可以自己定义,记得在使用的时候保持一致就ok了;"content://hx.android.text.myprovider/tablename"

D:如果URI中包含表示需要获取的记录的ID;则就返回该id对应的数据,如果没有ID,就表示返回全部; "content://hx.android.text.myprovider/tablename/#" #表示数据id

注册完需要匹配的Uri后,就可以使用sMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,例如匹配content://com.blueeagle路径,返回的匹配码为1。

//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码

UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

//添加需要匹配的uri,如果匹配就会返回匹配码

//如果match()方法匹配content://com.blueeagle路径,返回匹配码为1

sUriMatcher.addURI(“content://com.blueeagle”,“HelloSQLite”,1);

//如果match()方法匹配content://com.blueeagle/ ***路径,返回匹配码为2

//#号为通配符

sUriMatcher.addURI(“content://com.blueeagle”,“HelloSQLite/#”,2);

switch(sUriMatcher.match(Uri.parse("content://com.blueeagle /***"))) {

case 1 break;

case 2 break;

default:
//不匹配 break;

}

自定义的contentprovider如下:

ItemsProvider.java

view sourceprint?

001.
package

com.blueeagle;

002.

003.
/*

004.

005.
*  Android开发之数据保存技术(一)

006.

007.
*  ItemsProvider.java

008.

009.
*  Created on: 2011-8-16

010.

011.
*  Author: blueeagle

012.

013.
*  Email: liujiaxiang@gmail.com

014.

015.
*/

016.

017.

018.

019.
import

android.content.ContentProvider;

020.

021.
import

android.content.ContentUris;

022.

023.
import

android.content.ContentValues;

024.

025.
import

android.content.UriMatcher;

026.

027.
import

android.database.Cursor;

028.

029.
import

android.database.sqlite.SQLiteDatabase;

030.

031.
import

android.net.Uri;

032.

033.
import

android.text.TextUtils;

034.

035.
import

static
com.blueeagle.Constants.CONTENT_URI;

036.

037.
import

static
com.blueeagle.Constants.TABLE_NAME;

038.

039.
import

static
com.blueeagle.Constants.AUTHORITY;

040.

041.
import

static
android.provider.BaseColumns._ID;

042.

043.

044.

045.
public

class
ItemsProvider
extends

ContentProvider {

046.

047.

048.

049.
private

static
final
int
ITEMS =
1
;

050.

051.
private

static
final
int
ITEMS_ID =
2
;

052.

053.
/** The MIME type of a directory of items */

054.

055.
private

static
final
String CONTENT_TYPE

056.

057.
=
"vnd.android.cursor.dir/vnd.com.blueeagle"
;

058.

059.
/** The MIME type of a single item */

060.

061.
private

static
final
String CONTENT_ITEM_TYPE

062.

063.
=
"vnd.android.cursor.item/vnd.com.blueeagle"
;

064.

065.

066.

067.
private

<a href=
"http://www.it165.net/database/dbmy/"

target=
"_blank"
class
=
"keylink"
>MySQL</a>ite myDataBase ;

068.

069.
private

UriMatcher myUriMatcher;

070.

071.

072.

073.
@Override

074.

075.
public

boolean
onCreate() {

076.

077.
myUriMatcher =
new
UriMatcher(UriMatcher.NO_MATCH);

078.

079.
myUriMatcher.addURI(AUTHORITY,
"HelloSQLite"
,ITEMS);

080.

081.
myUriMatcher.addURI(AUTHORITY,
"HelloSQLite/#"
,ITEMS_ID);

082.

083.
myDataBase =
new
MySQLite(getContext());

084.

085.
return

true
;

086.

087.
}

088.

089.
@Override

090.

091.
public

int
delete(Uri uri,String selection,

092.

093.
// TODO Auto-generated method stub

094.

095.
String[] selectionArgs) {

096.

097.
SQLiteDatabase db = myDataBase.getWritableDatabase();

098.

099.
int

count;

100.

101.
switch

(myUriMatcher.match(uri)) {

102.

103.
case

ITEMS:

104.

105.
count = db.delete(TABLE_NAME,selection,selectionArgs);

106.

107.
break
;

108.

109.
case

ITEMS_ID:

110.

111.
long

id = Long.parseLong(uri.getPathSegments().get(
1
));

112.

113.
count = db.delete(TABLE_NAME,appendRowId(selection,id),

114.

115.
selectionArgs);

116.

117.
break
;

118.

119.
default
:

120.

121.
throw

new
IllegalArgumentException(
"Unknown URI "

+ uri);

122.

123.
}

124.

125.
// Notify any watchers of the change

126.

127.
getContext().getContentResolver().notifyChange(uri,
null
);

128.

129.
return

count;

130.

131.
}

132.

133.
@Override

134.

135.
public

String getType(Uri uri) {

136.

137.
// TODO Auto-generated method stub

138.

139.
switch

(myUriMatcher.match(uri)) {

140.

141.
case

ITEMS:

142.

143.
return

CONTENT_TYPE;

144.

145.
case

ITEMS_ID:

146.

147.
return

CONTENT_ITEM_TYPE;

148.

149.
default
:

150.

151.
throw

new
IllegalArgumentException(
"Unknown URI "

+ uri);

152.

153.
}

154.

155.
}

156.

157.
@Override

158.

159.
public

Uri insert(Uri uri,ContentValues values) {

160.

161.
// TODO Auto-generated method stub

162.

163.
SQLiteDatabase db = myDataBase.getWritableDatabase();

164.

165.
// Validate the requested uri

166.

167.
if

(myUriMatcher.match(uri) != ITEMS) {

168.

169.
throw

new
IllegalArgumentException(
"Unknown URI "

+ uri);

170.

171.
}

172.

173.
// Insert into database

174.

175.
long

id = db.insertOrThrow(TABLE_NAME,
null
,values);

176.

177.
// Notify any watchers of the change

178.

179.
Uri newUri = ContentUris.withAppendedId(CONTENT_URI,id);

180.

181.
getContext().getContentResolver().notifyChange(newUri,
null
);

182.

183.
return

newUri;

184.

185.
}

186.

187.
@Override

188.

189.
public

Cursor query(Uri uri,String[] projection,

190.

191.
String selection,String[] selectionArgs,String orderBy) {

192.

193.
// TODO Auto-generated method stub

194.

195.
if

(myUriMatcher.match(uri) == ITEMS_ID) {

196.

197.
long

id = Long.parseLong(uri.getPathSegments().get(
1
));

198.

199.
selection = appendRowId(selection,id);

200.

201.
}

202.

203.
// Get the database and run the query

204.

205.
SQLiteDatabase db = myDataBase.getReadableDatabase();

206.

207.
Cursor cursor = db.query(TABLE_NAME,projection,selection,

208.

209.
selectionArgs,
null
,
null
,orderBy);

210.

211.
// Tell the cursor what uri to watch,so it knows when its

212.

213.
// source data changes

214.

215.
cursor.setNotificationUri(getContext().getContentResolver(),

216.

217.
uri);

218.

219.
return

cursor;

220.

221.
}

222.

223.
private

String appendRowId(String selection,
long

id) {

224.

225.
// TODO Auto-generated method stub

226.

227.
return

_ID +
"="
+ id

228.

229.
+ (!TextUtils.isEmpty(selection)

230.

231.
?
" AND ("
+ selection +

')'

232.

233.
:
""
);

234.

235.
}

236.

237.
@Override

238.

239.
public

int
update(Uri uri,ContentValues values,String selection,

240.

241.
String[] selectionArgs) {

242.

243.
// TODO Auto-generated method stub

244.

245.
SQLiteDatabase db = myDataBase.getWritableDatabase();

246.

247.
int

count;

248.

249.
switch

(myUriMatcher.match(uri)) {

250.

251.
case

ITEMS:

252.

253.
count = db.update(TABLE_NAME,values,selection,

254.

255.
selectionArgs);

256.

257.
break
;

258.

259.
case

ITEMS_ID:

260.

261.
long

id = Long.parseLong(uri.getPathSegments().get(
1
));

262.

263.
count = db.update(TABLE_NAME,values,appendRowId(

264.

265.
selection,id),selectionArgs);

266.

267.
break
;

268.

269.
default
:

270.

271.
throw

new
IllegalArgumentException(
"Unknown URI "

+ uri);

272.

273.
}

274.

275.
// Notify any watchers of the change

276.

277.
getContext().getContentResolver().notifyChange(uri,
null
);

278.

279.
return

count;

280.

281.
}

282.

283.
}


总结一下,创建一个新的内容提供器。

1.通过扩展抽象类ContentProvider可以创建新的内容提供器。重写onCreate方法来打开或者初始化将要使用这个新的提供器来提供底层数据源。

2.还应该提供那些用来返回指向这个提供器的完整的URI的公共静态CONTENT_URI变量。提供器之间的内容URI应该是唯一的,所以最好的做法是使URI路径以包名为基础。

定义一个内容提供器URI一般的形式为:

content://com.pakagename/datapath

例如:content://com.blueeagle/items

或者:content://com.blueeagle./items/3

内容URI可以表示为这两种形式中的任意一种形式。前面的一种URI表示对那种类型中所有的值的请求,后面附加一个/3的表示对一条记录的请求(这里请求的是记录3)。这两种形式来访问提供器都是可行的。

完成这项工作最简单的方式是使用一个UriMatcher。当通过内容解析器来访问内容提供器的时候,可以配置UriMathcer来解析URI以确定它们的形式。就像前文说的那样。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐