您的位置:首页 > 编程语言 > PHP开发

使用ContentProvider 实现数据共享

2014-12-01 20:01 597 查看

使用ContentProvider 实现数据共享

一 ContentProvider简介

ContentProvider(数据提供者)是在应用程序间共享数据的一种接口机制。 它提供了更为高级的数据共享方法,应用程序可以指定需要共享的数据,而其他应用程序则可以在不知数据来源、路径的情况下,对共享数据进行查询、添加、删除和更新等操作。许多Android系统的内置数据也通过ContentProvider提供给用户使用,例如通讯录、音视频文件和图像文件等。

在创建ContentProvider时,需要首先使用数据库、文件系统或网络实现底层存储功能,然后在继承ContentProvider的类中实现基本数据操作的接口函数,包括添加、删除、查找和更新等功能。调用者不能够直接调用ContentProvider的接口函数,而是通过使用ContentResolver对象,使用URI间接调用ContentProvider。

使用ContentProvider可以在不同的应用程序之间共享数据。 它为存储和获取数据提供了统一的接口;ContentProvide对数据进行封装,不用关心数据存储的细节。ContentProvider不管底层数据的实际存储方式,对外统一使用表的形式来组织数据 。

二 Uri简介

由于ContentProvider的使用,离不开URI,因此单独找一个小节,介绍一下URI。

Android平台,URI主要分三个部分:scheme, authority and path。其中authority又分为host和port。

格式如下:

scheme://host:port/path

举个实际的例子:

content://com.example.project:200/folder/subfolder/etc

\---------/ \---------------------------/ \---/ \--------------------------/

scheme host port path

\--------------------------------/

authority

其中,content:// 这个部分是android的contentprovider 规定的,就像上网的协议默认是http:// 一样。

com.example.project:200 这个部分就是contentProvider的authority。系统就是由这个部分摘到要操作哪一个contentProvider。

folder/subfolder/etc 资源部分,当需要访问不同的资源时,这个部分是动态改变的。

我们在程序中一般是不直接用URI来标识contentProvider的;我们通常见到的用定义的常量来标识。例如standard contentProvider中的Contacts,我们就用Contacts.People.CONTENT_URI来标识Contacts contentProvider中People这个表。那么要标识某个具体的人怎么办呢? 这就用到了ContentUris.withAppendedId() 和 Uri.withAppendedPath()。例如我们要表示content://contacts/people/20,那么我们就可以用如下语句:

Uri uri = ContentUris.withAppendedId(People.CONTENT_URI, 20); 或者

Uri uri = Uri.withAppendedPath(People.CONTENT_URI, "20");

三 开发ContentProvider

1. ContentProvider与 ContentResolver的关系

从ContentProvider与 ContentResolver, url 三者的关系看;uri 是 ContentProvider与 ContentResolver 进行数据交换的标示。ContentResolver 通过uri 操作数据会委托给相应的ContentProvider 来实现。

2. 开发ContentProvider

开发ContentProider 只需要两步:

(1)开发ContentProider的子类,该子类需要实现增删改查的方法。

(2)在AndroidManifest.java 文件中注册该 ContentProider,指定 android:authorities 属性。

注意:上面实现的ContentProider 的 query() , insert(), update(), delete() 方法,不是给应用本身使用的,而是给其他应用使用的。至于这四个方法如何实现,完全由开发者本身决定。一般是数据库。

3.配置 ContentProider

android 要求必须为 四大组件显示配置(activity,service ,ContentProider,BroadcastReceiver)。

ContentProider 的配置一般如下:

<!-- 注册contentProvider  -->
<provider
android:name=".InfoProvider"
android:authorities="com.example.infos.infoprovider"
android:exported="true" />


通常的属性如下:

name: 指定该 ContentProider 的实现类的类名;

authorities: 指定该ContentProider 对应的uri,

exported:指定该 ContentProider 是否允许其他应用调用。一般设置为true。

4. ContentResolver 的使用介绍

Context 提供了 getContentResolver() 方法,这表示 activity 以及 service 等组件都可以通过getContentResolver() 方法获取ContentResolver。

获取了ContentResolver 之后,就可以调用ContentResolver 的 query() , insert(), update(), delete() 方法了----实际上是调用的 对应的ContentProider 的query() , insert(), update(), delete() 方法。

5. 创建 ContentProider 的注意事项

因为经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher 和ContentUris。

UriMatcher:用于匹配Uri,它的用法如下:

1.首先把你需要匹配Uri路径全部给注册上,如下:

//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码(-1)。

UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

//如果match()方法匹配content://com.ex.sqlite.provider.contactprovider/contact,返回匹配码为1

uriMatcher.addURI(“com.ex.sqlite.provider.contactprovider”, “contact”, 1);

//如果match()方法匹配 content://com.ex.sqlite.provider.contactprovider/contact/12,返回匹配码为2

uriMatcher.addURI(“com.ex.sqlite.provider.contactprovider”, “contact/#”, 2); //其中#号为通配符

2.注册完需要匹配的Uri后,就可以使用uriMatcher.match(uri)方法对输入的Uri进行匹配,

如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,

ContentUris:用于获取Uri路径后面的ID部分,它有两个比较实用的方法:

(1)withAppendedId(uri, id)用于为路径加上ID部分

(2)parseId(uri)方法用于从路径中获取ID部分

四 示例ContentProider 的使用

我们这里设计了一个记录个人信息的ContentProider,可以通过其他的进程来添加个人信息,包括姓名,年龄。

也可以通过名字来查询当前姓名的年龄。

1. 设计了一个工具类,用来把 ContentProider 的 Uri,以及数据列等信息以常量的形式公布出来,便于访问。其实它的作用就是告诉其他的应用程序怎么访问该ContentProider。

public class Infos {
//define the authority of the contentProvider
public static final String AUTHORITY= "com.example.infos.infoprovider";

//define the inner class ,which contents the data and name of columns
public static final class Info implements BaseColumns{

//define the name of columns that will be operate
public static final String _ID = "id";
public static final String NAME = "name";
public static final String AGE = "age";

public static final Uri INFOS_URI = Uri.parse(
"content://" + AUTHORITY + "/infos");

public static final Uri INFO_URI = Uri.parse(
"content://" + AUTHORITY + "/info");
}

}


2.接下来,设计了一个 ContentProider 的子类,重写它的增删改查方法,代码如下:

public class InfoProvider extends ContentProvider {

private static UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private InfoDatabaseHelper mInfoDatabaseHelper = null;

private static final int NAMES = 1;
private static final int NAME = 2;

static{
//为uriMatcher 注册两个uri
mUriMatcher.addURI(Infos.AUTHORITY, "/infos", 1);
mUriMatcher.addURI(Infos.AUTHORITY, "/info/#", 2);

}

@Override
public int delete(Uri uri, String where, String[] whereArgs) {
SQLiteDatabase db = mInfoDatabaseHelper.getWritableDatabase();
//记录删除的数目
int num = 0;
switch (mUriMatcher.match(uri)) {
case NAMES:
num = db.delete("info", where, whereArgs);
break;
case NAME:
long id = ContentUris.parseId(uri);
String wheretemp = Infos.Info._ID + id;
if (where != null && "".equals(where)) {
where = wheretemp + " and " + where;
}
num = db.delete("info", where, whereArgs);
default:
break;
}
getContext().getContentResolver().notifyChange(uri, null);
return num;
}

@Override
public String getType(Uri arg0) {
// TODO Auto-generated method stub
return null;
}

@Override
public Uri insert(Uri uri, ContentValues contentValues) {
SQLiteDatabase db = mInfoDatabaseHelper.getReadableDatabase();

switch (mUriMatcher.match(uri)) {
case NAMES:
long id = db.insert("info", Info._ID, contentValues);
//如果插入成功,返回uri
if (id > 0) {
//在已有的uri后面添加id
Uri infoUri = ContentUris.withAppendedId(uri, id);
getContext().getContentResolver().notifyChange(infoUri, null);
return infoUri;
}
break;

default:
break;
}
return null;
}

@Override
public boolean onCreate() {

mInfoDatabaseHelper = new InfoDatabaseHelper(this.getContext(), "peopleinfo.db", null, 1);
if (mInfoDatabaseHelper != null) {
return true;
}
return false;
}

@Override
public Cursor query(Uri uri, String[] projection, String where, String[] whereArgs,
String sortorder) {

SQLiteDatabase db = mInfoDatabaseHelper.getReadableDatabase();
switch (mUriMatcher.match(uri)) {
case NAMES:
return db.query("info", projection, where, whereArgs, null, null, sortorder);

case NAME:
//解析出想要查询的ID
long id = ContentUris.parseId(uri);
String wheretemp = Infos.Info._ID + id;
if (where != null && "".equals(where)) {
where = wheretemp + " and " + where;
}
return db.query("info", projection, where, whereArgs, null, null, sortorder);

default:
break;
}
return null;
}

@Override
public int update(Uri uri, ContentValues contentValues, String where, String[] whereArgs) {

SQLiteDatabase db = mInfoDatabaseHelper.getReadableDatabase();

//记录修改的数目
int num =0;
switch (mUriMatcher.match(uri)) {
case NAMES:
num = db.update("info", contentValues, where, whereArgs);
case NAME:
//解析出想要查询的ID
long id = ContentUris.parseId(uri);
String wheretemp = Infos.Info._ID + id;
if (where != null && "".equals(where)) {
where = wheretemp + " and " + where;
}
num = db.update("info", contentValues, where, whereArgs);
break;

default:
break;
}
// notify the data has been changed
getContext().getContentResolver().notifyChange(uri, null);
return num;
}

}


它里面用到的 InfoDatabaseHelper 类如下:

public class InfoDatabaseHelper extends SQLiteOpenHelper {

public InfoDatabaseHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
}

final String CREATE_TABLE_SQL =
"create table info(_id integer primary key autoincrement ,name , age)";

@Override
public void onCreate(SQLiteDatabase db) {
// 创建数据库
db.execSQL(CREATE_TABLE_SQL);
}

@Override
public void onUpgrade(SQLiteDatabase arg0, int oldVersion, int newVersion) {

System.out.println("--------onUpdate Called--------"
+ oldVersion + "--->" + newVersion);
}

}


上面的InfoProvider 类很简单,它除了继承 ContentProvider 之外,还实现了增删改查方法。当其他的应用通过 ContentResolver 来调用InfoProvider 的这四个方法的时候,就可以真正的访问这里面创建的数据库了,从而达到对个人信息的处理。

3.

接下来,注册 InfoProvider,片段如下:

<provider
android:name=".InfoProvider"
android:authorities="com.example.infos.infoprovider"
android:exported="true" />


4. 为了测试这个 InfoProvider 可以使用,我们编写了一个简单的测试demo:

这个测试demo 可以添加个人信息,也可以查询个人信息,而且都是通过前面我们创建的 InfoProvider 来完成的。

代码如下:

public class MainActivity extends Activity {

Button insert = null;
Button search = null;

ContentResolver mContentResolver = null;

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

insert = (Button)findViewById(R.id.insert);
search = (Button)findViewById(R.id.search);
mContentResolver = getContentResolver();

insert.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {

String name = ((EditText)findViewById(R.id.name)).getText().toString();

String age = ((EditText)findViewById(R.id.age)).getText().toString();

ContentValues contentValues = new ContentValues();
contentValues.put(Info.NAME, name);
contentValues.put(Info.AGE, age);
Uri uri = mContentResolver.insert(Infos.Info.INFOS_URI, contentValues);

if (uri != null) {
Toast.makeText(getApplicationContext(), uri.toString(), Toast.LENGTH_LONG).show();
}
}
});

search.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {
String searchString = ((EditText)findViewById(R.id.key)).getText().toString();

String selection = "name = ?";
String[] selectionArgs = {searchString};
Cursor cursor = mContentResolver.query(Infos.Info.INFOS_URI, null, selection, selectionArgs, null);
ArrayList<Map<String, String>>  infoList = convertCursorToList(cursor);
//创建一个Bundle
Bundle bundle = new Bundle();
//bundle.putStringArrayList("data", infoList);
bundle.putSerializable("data", infoList);

//启动显示的activity
Intent intent = new Intent(getApplicationContext(), ResultActivity.class);
intent.putExtras(bundle);
startActivity(intent);
}
});

}

private ArrayList<Map<String, String>> convertCursorToList(Cursor cursor) {
ArrayList<Map<String, String>> result = new ArrayList<Map<String,String>>();

if (cursor == null) {
return result;
}
while (cursor.moveToNext()) {
//set the info to the list
Map<String, String> maptmp = new HashMap<String, String>();
maptmp.put(Info.NAME, cursor.getString(1));
maptmp.put(Info.AGE, cursor.getString(2));

result.add(maptmp);
}
return result;

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

}


如果是查询的话,我们会新创建一个actiivty,用来显示查询到的数据,activity实现如下:

public class ResultActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.popup);
ListView listView = (ListView) findViewById(R.id.show);
Intent intent = getIntent();

Bundle data = intent.getExtras();

@SuppressWarnings("unchecked")
List<Map<String, String>> list = (List<Map<String, String>>)
data.getSerializable("data");

SimpleAdapter adapter = new SimpleAdapter(ResultActivity.this,
list, R.layout.line
, new String[] { Infos.Info.NAME, Infos.Info.AGE }
, new int[] { R.id.name, R.id.age });

listView.setAdapter(adapter);
}

}


这个简单的demo能够实现 个人信息(姓名,年龄的添加和查询),说明了ContenterProvider 的一般使用方法和注意事项。

五总结

简单的理解, ContenterProvider 就是android里面实现不同进程间数据共享的一种方法,为app开发者提供了一种开发的行为规范和标准。

严格按照这个规则开发,就能够设计出简单明了的应用程序。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: