ContentProvider简介

本文详细介绍了Android应用中ContentProvider与ContentResolver的使用方式,包括它们的功能、接口、URI的使用以及权限问题。通过实例展示了如何创建、操作ContentProvider,以及如何通过ContentResolver访问和操作ContentProvider提供的数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.什么是ContentProvider
数据库在Android当中是私有的,当然这些数据包括文件数据和数据库数据以及一些其他类型的数据。
不能将数据库设为
WORLD_READABLE,每个数据库都只能创建它的包访问,
这意味着只有由创建数据库的进程可访问它。如果需要在进程间传递数据,
则可以使用
AIDL/Binder或创建一个ContentProvider,但是不能跨越进程/包边界直接来使用数据库。
一个
ContentProvider类实现了一组标准的方法接口,从而能够让其他的应用保存或读取此ContentProvider的各种数据类型。
也就是说,一个程序可以通过实现一个
ContentProvider的抽象接口将自己的数据暴露出去。
外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,或者是用数据库存储还是用文件存储,还是通过网上获得,这些一切都不重要,
重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道,可以读取程序的数据,也可以删除程序的数据,
当然,中间也会涉及一些权限的问题。下边列举一些较常见的接口,这些接口如下所示

·query(Uriuri,String[]projection,Stringselection,String[]selectionArgs,StringsortOrder):通过Uri进行查询,返回一个Cursor。
·insert(Uriurl,ContentValuesvalues):将一组数据插入到Uri指定的地方。
·update(Uriuri,ContentValuesvalues,Stringwhere,String[]selectionArgs):更新Uri指定位置的数据。
·delete(Uriurl,Stringwhere,String[]selectionArgs):删除指定Uri并且符合一定条件的数据。
2.什么是ContentResolver
外界的程序通过ContentResolver接口可以访问ContentProvider提供的数据,在Activity当中通过getContentResolver()可以得到当前应用的ContentResolver实例。
ContentResolver提供的接口和ContentProvider中需要实现的接口对应,主要有以下几个。

·query(Uriuri,String[]projection,Stringselection,String[]selectionArgs,StringsortOrder):通过Uri进行查询,返回一个Cursor。
·
insert(Uriurl,ContentValuesvalues):将一组数据插入到Uri指定的地方。
·update(Uriuri,ContentValuesvalues,Stringwhere,String[]selectionArgs):更新Uri指定位置的数据。
·delete(Uriurl,Stringwhere,String[]selectionArgs):删除指定Uri并且符合一定条件的数据。
3.ContentProvider和ContentResolver中用到的Uri
在ContentProvider和ContentResolver当中用到了Uri的形式通常有两种,一种是指定全部数据,另一种是指定某个ID的数据。
我们看下面的例子。

·content://contacts/people/这个Uri指定的就是全部的联系人数据。
·content://contacts/people/1这个Uri指定的是ID为1的联系人的数据。
在上边两个类中用到的Uri一般由3部分组成。
·第一部分是方案:"content://"这部分永远不变
·第二部分是授权:"contacts"
·第二部分是路径:"people/","people/1"(如果没有指定ID,那么表示返回全部)。
由于URI通常比较长,而且有时候容易出错,且难以理解。所以,在Android当中定义了一些辅助类,并且定义了一些常量来代替这些长字符串的使用,例如下边的代码:
·Contacts.People.CONTENT_URI(联系人的URI)。
在我们的实例MyProvider中是如下定义的:
publicstaticfinalStringAUTHORITY="com.teleca.PeopleProvider";
publicstaticfinalStringPATH_SINGLE="people/#";
publicstaticfinalStringPATH_MULTIPLE="people";
publicstaticfinalUricontent_URI=Uri.parse("content://"+AUTHORITY+"/"+PATH_MULTIPLE);

实例1
文件MyProvider.java
packagecom.teleca.provider;
importjava.util.HashMap;
importandroid.content.ContentProvider;
importandroid.content.ContentUris;
importandroid.content.ContentValues;
importandroid.content.Context;
importandroid.content.UriMatcher;
importandroid.database.Cursor;
importandroid.database.SQLException;
importandroid.database.sqlite.SQLiteDatabase;
importandroid.database.sqlite.SQLiteOpenHelper;
importandroid.database.sqlite.SQLiteQueryBuilder;
importandroid.net.Uri;
importandroid.text.TextUtils;
importandroid.util.Log;
publicclassMyProviderextendsContentProvider{
publicstaticfinalStringMIME_DIR_PREFIX="vnd.android.cursor.dir";
publicstaticfinalStringMIME_ITEM_PREFIX="vnd.android.cursor.item";
publicstaticfinalStringMIME_ITEM="vnd.msi.people";
publicstaticfinalStringMIME_TYPE_SINGLE=MIME_ITEM_PREFIX+"/"+MIME_ITEM;
publicstaticfinalStringMIME_TYPE_MULTIPLE=MIME_DIR_PREFIX+"/"+MIME_ITEM;
publicstaticfinalStringAUTHORITY="com.teleca.PeopleProvider";
publicstaticfinalStringPATH_SINGLE="people/#";
publicstaticfinalStringPATH_MULTIPLE="people";
publicstaticfinalUricontent_URI=Uri.parse("content://"+AUTHORITY+"/"+PATH_MULTIPLE);
publicstaticfinalStringDEFAULT_SORT_ORDER="nameDESC";
publicstaticfinalString_ID="_id";
publicstaticfinalStringNAME="name";
publicstaticfinalStringPHONE="phone";
publicstaticfinalStringAGE="age";
publicstaticfinalintPEOPLE=1;
publicstaticfinalintPEOPLES=2;
privatestaticUriMatcherURI_MATCHER;
privatestaticHashMap<String,String>PROJECTION_MAP;
publicstaticStringDB_NAME="peopledb";
publicstaticStringDB_TABLE_NAME="people";
SQLiteDatabasedb;
DBOpenHelperdbOpenHelper;
static
{
URI_MATCHER=newUriMatcher(UriMatcher.NO_MATCH);
URI_MATCHER.addURI(AUTHORITY,PATH_MULTIPLE,PEOPLES);
URI_MATCHER.addURI(AUTHORITY,PATH_SINGLE,PEOPLE);
PROJECTION_MAP=newHashMap<String,String>();
PROJECTION_MAP.put(_ID,"_id");
PROJECTION_MAP.put(NAME,"name");
PROJECTION_MAP.put(PHONE,"phone");
PROJECTION_MAP.put(AGE,"age");
}
@Override
publicintdelete(Uriuri,Stringselection,String[]selectionArgs){
//TODOAuto-generatedmethodstub
intcount=0;
switch(URI_MATCHER.match(uri))
{
casePEOPLES:
count=db.delete(DB_TABLE_NAME,selection,selectionArgs);
break;
casePEOPLE:
Stringsegment=uri.getPathSegments().get(1);
Stringwhere="";
if(!TextUtils.isEmpty(selection))
{
where="AND("+selection+")";
}
count=db.delete(DB_TABLE_NAME,"_id="+segment+where,selectionArgs);
break;
default:
thrownewIllegalArgumentException("UnkonwURI"+uri);
}
getContext().getContentResolver().notifyChange(uri,null);//@2
returncount;
}
@Override
publicStringgetType(Uriuri){
//TODOAuto-generatedmethodstub
switch(URI_MATCHER.match(uri))
{
casePEOPLES:
returnMIME_TYPE_MULTIPLE;
casePEOPLE:
returnMIME_TYPE_SINGLE;
default:
thrownewIllegalArgumentException("UnkownURI"+uri);
}
}
@Override
publicUriinsert(Uriuri,ContentValuesvalues){
//TODOAuto-generatedmethodstub
longrowId=0L;
if(URI_MATCHER.match(uri)!=PEOPLES)
{
thrownewIllegalArgumentException("UnkownURI"+uri);
}
rowId=db.insert(DB_TABLE_NAME,null,values);
if(rowId>0)
{
Uriresult=ContentUris.withAppendedId(content_URI,rowId);
getContext().getContentResolver().notifyChange(result,null);//@2
returnresult;
}
else
thrownewSQLException("Failedtoinsertrowinto"+uri);
}
@Override
publicbooleanonCreate(){
//TODOAuto-generatedmethodstub
dbOpenHelper=newDBOpenHelper(this.getContext(),DB_NAME,1);
db=dbOpenHelper.getWritableDatabase();
returnfalse;
}
@Override
publicCursorquery(Uriuri,String[]projection,Stringselection,
String[]selectionArgs,StringsortOrder){
//TODOAuto-generatedmethodstub
SQLiteQueryBuilderqueryBuilder=newSQLiteQueryBuilder();
queryBuilder.setTables(DBInfo.DB_TABLE_NAME);
queryBuilder.setProjectionMap(PROJECTION_MAP);
switch(URI_MATCHER.match(uri))
{
casePEOPLES:
break;
casePEOPLE:
queryBuilder.appendWhere("_id="+uri.getPathSegments().get(1));
break;
default:
thrownewIllegalArgumentException("UnkonwURI"+uri);
}
StringorderBy=null;
if(TextUtils.isEmpty(sortOrder))
{
orderBy=DEFAULT_SORT_ORDER;
}
else
orderBy=sortOrder;
Cursorc=queryBuilder.query(db,projection,selection,selectionArgs,null,null,orderBy);
c.setNotificationUri(getContext().getContentResolver(),uri);//@1
returnc;
}
@Override
publicintupdate(Uriuri,ContentValuesvalues,Stringselection,
String[]selectionArgs){
//TODOAuto-generatedmethodstub
intcount=0;
switch(URI_MATCHER.match(uri))
{
casePEOPLES:
count=db.update(DB_TABLE_NAME,values,selection,selectionArgs);
break;
casePEOPLE:
Stringsegment=uri.getPathSegments().get(1);
Stringwhere="";
if(!TextUtils.isEmpty(selection))
{
where="AND("+selection+")";
}
count=db.update(DB_TABLE_NAME,values,"_id="+segment+where,selectionArgs);
break;
default:
thrownewIllegalArgumentException("UnkonwURI"+uri);
}
getContext().getContentResolver().notifyChange(uri,null);//@2
returncount;
}
}
classDBOpenHelperextendsSQLiteOpenHelper
{
privatestaticfinalStringDB_CREATE="CREATETABLE"
+DBInfo.DB_TABLE_NAME
+"(_idINTEGERPRIMARYKEY,nameTEXTUNIQUENOTNULL,"
+"phoneTEXT,ageINTEGER);";
finalstaticStringtag="hubin";
publicDBOpenHelper(Contextcontext,StringdbName,intversion)
{
super(context,dbName,null,version);
}
publicvoidonCreate(SQLiteDatabasedb)
{
try{
db.execSQL(DB_CREATE);
}
catch(SQLExceptione)
{
Log.e(tag,"",e);
}
}
publicvoidonOpen(SQLiteDatabasedb)
{
super.onOpen(db);
}
publicvoidonUpgrade(SQLiteDatabasedb,intoldVersion,intnewVersion)
{
db.execSQL("DROPTABLEIFEXISTS"+DBInfo.DB_TABLE_NAME);
this.onCreate(db);
}
}
classDBInfo
{
publicstaticStringDB_NAME="peopledb";
publicstaticStringDB_TABLE_NAME="people";
}
注意1:c.setNotificationUri(getContext().getContentResolver(),uri);
这里是把CursorC添加到ContentResolver的监督对象组中去。
一旦有与uri相关的变化,ContentResolver就回通知CursorC.
可能Cursor有个私有的内部类ContentObserver的实现。ContentResolver是通过该类来通知Cursor的。
publicabstractvoidsetNotificationUri(ContentResolvercr,Uriuri)
RegistertowatchacontentURIforchanges.ThiscanbetheURIofaspecificdatarow(forexample,"content://my_provider_type/23"),
oraagenericURIforacontenttype.

Parameters
crThecontentresolverfromthecaller'scontext.Thelistenerattachedtothisresolverwillbenotified.
uriThecontentURItowatch.
注意2:getContext().getContentResolver().notifyChange(uri,null)
通知数据发生了变化。
publicvoidnotifyChange(Uriuri,ContentObserverobserver)
Notifyregisteredobserversthatarowwasupdated.Toregister,callregisterContentObserver().Bydefault,CursorAdapterobjectswillgetthisnotification.
Parameters
observerTheobserverthatoriginatedthechange,maybenull
这里为null的意思可能就是调用在ContentResolver中注册的ContentObserver,反之则是调用参数指定的
文件People.java
packagecom.teleca.provider;
publicclassPeople{
publiclongid;
publicStringname;
publicStringphone;
publicintage;
}

文件
Hello.java
packagecom.teleca.provider;
importjava.util.ArrayList;
importjava.util.List;
importandroid.app.Activity;
importandroid.content.ContentUris;
importandroid.content.ContentValues;
importandroid.database.Cursor;
importandroid.database.SQLException;
importandroid.net.Uri;
importandroid.os.Bundle;
importandroid.os.Handler;
importandroid.util.Log;
importandroid.view.View;
importandroid.view.View.OnClickListener;
importandroid.widget.Button;
publicclassHelloextendsActivity{
/**Calledwhentheactivityisfirstcreated.*/
finalstaticStringtag="hubin";
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Buttonbutton=(Button)findViewById(R.id.Button01);
OnClickListenerlistener=newOnClickListener(){
@Override
publicvoidonClick(Viewv){
cmd=CMD_ADD;
doAction();

}
};
button.setOnClickListener(listener);
Buttonbutton2=(Button)findViewById(R.id.Button02);
OnClickListenerlistener2=newOnClickListener(){
@Override
publicvoidonClick(Viewv){
cmd=CMD_UPDATE;
doAction();

}
};
button2.setOnClickListener(listener2);
Buttonbutton3=(Button)findViewById(R.id.Button03);
OnClickListenerlistener3=newOnClickListener(){
@Override
publicvoidonClick(Viewv){
cmd=CMD_QUERY;
doAction();

}
};
button3.setOnClickListener(listener3);
Buttonbutton4=(Button)findViewById(R.id.Button04);
OnClickListenerlistener4=newOnClickListener(){
@Override
publicvoidonClick(Viewv){
cmd=CMD_QUERY_ALL;
doAction();

}
};
button4.setOnClickListener(listener4);
Buttonbutton5=(Button)findViewById(R.id.Button05);
OnClickListenerlistener5=newOnClickListener(){
@Override
publicvoidonClick(Viewv){
cmd=CMD_DELETE;
doAction();

}
};
button5.setOnClickListener(listener5);
Buttonbutton6=(Button)findViewById(R.id.Button06);
OnClickListenerlistener6=newOnClickListener(){
@Override
publicvoidonClick(Viewv){
cmd=CMD_DELETE_ALL;
doAction();

}
};
button6.setOnClickListener(listener6);
mHandler=newHandler();
}
intcnt=0;
privateHandlermHandler;
intcmd=0;
finalintCMD_ADD=1;
finalintCMD_UPDATE=2;
finalintCMD_QUERY=3;
finalintCMD_QUERY_ALL=4;
finalintCMD_DELETE=5;
finalintCMD_DELETE_ALL=6;
Peoplepeople=newPeople();
finalstaticStringprojection[]=newString[]
{"_id","name","phone","age"};
classDatabaseThreadimplementsRunnable{
publicvoidrun(){
if(cmd==CMD_ADD){
people.name="robin"+System.currentTimeMillis()%100;
people.phone=""+System.currentTimeMillis();
people.age=1;
ContentValuesvalues=newContentValues();
values.put("name",people.name);
values.put("phone",people.phone);
values.put("age",people.age);
Uriuri=getContentResolver().insert(MyProvider.content_URI,values);
people.id=ContentUris.parseId(uri);
Log.i("hubin",uri.toString());
}elseif(cmd==CMD_UPDATE){
ContentValuesvalues=newContentValues();
people.phone=""+System.currentTimeMillis();
values.put("phone",people.phone);
Uriuri=ContentUris.withAppendedId(MyProvider.content_URI,people.id);
getContentResolver().update(uri,values,null,null);
}elseif(cmd==CMD_QUERY){
Uriuri=ContentUris.withAppendedId(MyProvider.content_URI,people.id);
Cursorc=
getContentResolver().query(uri,projection,null,null,null);
Peoplep=get(c);
printPeople(p);
}elseif(cmd==CMD_QUERY_ALL){
Uriuri=MyProvider.content_URI;
Cursorc=
getContentResolver().query(uri,projection,null,null,null);
List<People>list=getAll(c);
inttotal=list.size();
for(inti=0;i<total;i++)
{
printPeople(list.get(i));
}
}
elseif(cmd==CMD_DELETE)
{
Uriuri=ContentUris.withAppendedId(MyProvider.content_URI,people.id);
getContentResolver().delete(uri,null,null);
}
elseif(cmd==CMD_DELETE_ALL)
{
Uriuri=MyProvider.content_URI;
getContentResolver().delete(uri,null,null);
}
cnt++;
}
}
voidprintPeople(Peoplep)
{
Log.i(tag,"id:"+p.id);
Log.i(tag,"name:"+p.name);
Log.i(tag,"phone:"+p.phone);
Log.i(tag,"age:"+p.age);
}
DatabaseThreaddataDealer=newDatabaseThread();
voiddoAction(){
mHandler.post(dataDealer);
}
publicPeopleget(Cursorc)
{
Peoplepeople=newPeople();
try{
Log.i(tag,"count:"+c.getCount());
if(c.getCount()>0)
{
c.moveToFirst();
people=newPeople();
people.id=c.getLong(0);
people.name=c.getString(1);
people.phone=c.getString(2);
people.age=c.getInt(3);
}
}catch(SQLExceptione)
{
Log.i(tag,"",e);
}
finally
{
if(c!=null&&!c.isClosed())
{
c.close();
}
}
returnpeople;
}
publicList<People>getAll(Cursorc)
{
ArrayList<People>ret=newArrayList<People>();
try
{
intcount=c.getCount();
c.moveToFirst();
Peoplepeople;
for(inti=0;i<count;i++)
{
people=newPeople();
people.id=c.getLong(0);
people.name=c.getString(1);
people.phone=c.getString(2);
people.age=c.getInt(3);
ret.add(people);
c.moveToNext();
}

}catch(SQLExceptione)
{
Log.i(tag,"",e);
}
finally
{
if(c!=null&&!c.isClosed())
{
c.close();
}
}
returnret;
}
}
注意:Cursorc不用时要关掉。
文件AndroidManifest.xml
<?xmlversion="1.0"encoding="utf-8"?>
<manifestxmlns:android="http://schemas.android.com/apk/res/android<wbr style="line-height:25px">"<br style="line-height:25px"> package="com.teleca.provider"<br style="line-height:25px"> android:versionCode="1"<br style="line-height:25px"> android:versionName="1.0"&gt;<br style="line-height:25px"> &lt;applicationandroid:icon="@drawable/icon"android:label="@string/app_name"&gt;<br style="line-height:25px"> &lt;activityandroid:name=".Hello"<br style="line-height:25px"> android:label="@string/app_name"&gt;<br style="line-height:25px"> &lt;intent-filter&gt;<br style="line-height:25px"> &lt;actionandroid:name="android.intent.action.MAIN"/&gt;<br style="line-height:25px"> &lt;categoryandroid:name="android.intent.category.LAUNCHER"/&gt;<br style="line-height:25px"> &lt;/intent-filter&gt;<br style="line-height:25px"> &lt;/activity&gt;<br style="line-height:25px"></wbr>
<providerandroid:syncable="true"android:name="MyProvider"android:authorities="com.teleca.PeopleProvider"></provider>
</application>
<uses-sdkandroid:minSdkVersion="7"/>
</manifest>
list_row.xml文件
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" android:id="@+id/list_row" />
main.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" >
<ListView android:id="@id/android:list" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#00FF00" android:layout_weight="1" android:drawSelectorOnTop="false"/>
<Button android:text="@+string/Add" android:id="@+id/Button01" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
<Button android:text="@+string/DeleteAll" android:id="@+id/Button02" android:layout_width="wrap_content"></Button>
</LinearLayout>
需要的权限:
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>
真的需要这些权限?为什么需要呢?也许是我以前写错了吧
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值