当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。以前我们学习过文件的操作模式,通过指定文件的操作模式为Context.MODE_WORLD_READABLE 或Context.MODE_WORLD_WRITEABLE同样可以对外共享数据,但数据的访问方式会因数据存储的方式而不同,如:采用xml文件对外共享数据,需要进行xml解析来读写数据;采用sharedpreferences共享数据,需要使用sharedpreferences API读写数据。而使用ContentProvider共享数据的好处是统一了数据访问方式。
当应用需要通过ContentProvider对外共享数据时,第一步需要继承ContentProvider并重写下面方法:
public class PersonContentProvider extends ContentProvider{ public boolean onCreate() public Uri insert(Uri uri, ContentValues values) public int delete(Uri uri, String selection, String[] selectionArgs) public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) public String getType(Uri uri)}
第二步需要在AndroidManifest.xml使用<provider>对该ContentProvider进行配置,为了能让其他应用找到该ContentProvider, ContentProvider采用
了authorities(主机名/域名)对它进行唯一标识,你可以把ContentProvider看作是一个网站(想想,网站也是提供数据者),authorities就是他的域名:
<manifest .... > <application android:icon="@drawable/icon" android:label="@string/app_name"> <provider android:name=".PersonContentProvider" android:authorities="cn.itcast.providers.personprovider"/> </application> </manifest>
注意:一旦应用继承了ContentProvider类,后面我们就会把这个应用称为ContentProvider(内容提供者)。
Uri代表了要操作的数据,Uri主要包含了两部分信息:1》需要操作的ContentProvider ,2》对ContentProvider中的什么数据进行操作,一个Uri由以下几部分组成:
ContentProvider(内容提供者)的scheme已经由Android所规定,scheme为:content://
主机名(或叫Authority)用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。
路径(path)可以用来表示我们要操作的数据,路径的构建应根据业务而定,如下:
要操作person表中id为10的记录,可以构建这样的路径:/person/10
要操作person表中id为10的记录的name字段,person/10/name
要操作person表中的所有记录,可以构建这样的路径:/person
要操作xxx表中的记录,可以构建这样的路径:/xxx
当然要操作的数据不一定来自数据库,也可以是文件、xml或网络等其他存储方式,如下:
要操作xml文件中person节点下的name节点,可以构建这样的路径:/person/name
如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下:
Uri uri = Uri.parse ("content://cn.itcast.provider.personprovider/person")因为Uri代表了要操作的数据,所以我们经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher和ContentUris。
掌握它们的使用,会便于我们的开发工作。
UriMatcher类用于匹配Uri,它的用法如下:
首先第一步把你需要匹配Uri路径全部给注册上,如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码 UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH); //如果match()方法匹配content://cn.itcast.provider.personprovider/person路径,返回匹配码为1 sMatcher.addURI(“cn.itcast.provider.personprovider”, “person”, 1);//添加需要匹配uri,如果匹配就会返回匹配码 //如果match()方法匹配content://cn.itcast.provider.personprovider/person/230路径,返回匹配码为2 sMatcher.addURI(“cn.itcast.provider.personprovider”, “person/#”, 2);//#号为通配符 switch (sMatcher.match(Uri.parse("content://cn.itcast.provider.personprovider/person/10"))) { case 1 break; case 2 break; default://不匹配 break; }
注册完需要匹配的Uri后,就可以使用sMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,假
设匹配content://cn.itcast.provider.personprovider/person路径,返回的匹配码为1
ContentUris类用于获取Uri路径后面的ID部分,它有两个比较实用的方法:
withAppendedId(uri, id)用于为路径加上ID部分:
Uri uri =Uri.parse("content://cn.itcast.provider.personprovider/person")
Uri resultUri =ContentUris.withAppendedId(uri, 10);
//生成后的Uri为:content://cn.itcast.provider.personprovider/person/10
parseId(uri)方法用于从路径中获取ID部分:
Uri uri =Uri.parse("content://cn.itcast.provider.personprovider/person/10")
long personid =ContentUris.parseId(uri);//获取的结果为:10
ContentProvider类主要方法的作用:
public boolean onCreate()
该方法在ContentProvider创建后就会被调用, Android开机后, ContentProvider在其它应用第一次访问它时才会被创建。
public Uriinsert(Uri uri, ContentValues values)
该方法用于供外部应用往ContentProvider添加数据。
public int delete(Uri uri, String selection,String[] selectionArgs)
该方法用于供外部应用从ContentProvider删除数据。
public int update(Uri uri, ContentValues values, Stringselection, String[] selectionArgs)
该方法用于供外部应用更新ContentProvider中的数据。
public Cursorquery(Uri uri, String[]projection, String selection, String[] selectionArgs, String sortOrder)
该方法用于供外部应用从ContentProvider中获取数据。
public String getType(Uri uri)
该方法用于返回当前Url所代表数据的MIME类型。如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,例如:要得到所有person记录的Uri为content://cn.itcast.provider.personprovider/person,那么返回的MIME类型字符串应该为:“vnd.android.cursor.dir/person”。如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,例如:得到id为10的person记录,Uri为content://cn.itcast.provider.personprovider/person/10,那么返回的MIME类型字符串应该为:“vnd.android.cursor.item/person”。
使用ContentResolver操作ContentProvider中的数据当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver 类来完成,要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。 ContentResolver 类提供了与ContentProvider类相同签名的四个方法:
public Uriinsert(Uri uri, ContentValues values)
该方法用于往ContentProvider添加数据。
public int delete(Uri uri, String selection,String[] selectionArgs)
该方法用于从ContentProvider删除数据。
public int update(Uri uri, ContentValues values, Stringselection, String[] selectionArgs)
该方法用于更新ContentProvider中的数据。
public Cursorquery(Uri uri, String[]projection, String selection, String[] selectionArgs, String sortOrder)
该方法用于从ContentProvider中获取数据。
这些方法的第一个参数为Uri,代表要操作的ContentProvider和对其中的什么数据进行操作,假设给定的是:Uri.parse(“content://cn.itcast.providers.personprovider/person/10”),那么将会对主机名为cn.itcast.providers.personprovider的ContentProvider进行操作,操作的数据为person表中id为10的记录。
1、工程结构:
2、内容监听者的清单配置
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cn.huangjie.db" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:label="@string/app_name" android:name=".DbActivity" > <intent-filter > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- 加上这行相当于引入包的意思 --> <uses-library android:name="android.test.runner" /> <!-- android:authorities内容提供者的唯一标识,取名最好遵守一定的规则 --> <provider android:name=".PersonProvider" android:authorities="cn.huangjie.provides.personprovider"> </provider> </application> <instrumentation android:name="android.test.InstrumentationTestRunner" android:targetPackage="cn.huangjie.db" android:label="Tests for My App" /> <!-- 上面targetPackage指定的包要和应用的package相同 --> </manifest>
package cn.huangjie.db; import cn.huangjie.service.DBOpenHelper; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; /** * 内容提供者作为系统中一个类型的组件,一定要放在应用所在包或子包底下 */ public class PersonProvider extends ContentProvider{ private DBOpenHelper dbOpenHelper; /** * UriMatcher需要传入一个数字,传入的数字代表如果跟输入进来的uri不匹配 * 的话那么返回的匹配码,这个输入参数就是不匹配的时候返回出来的不匹配码 */ private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH); //匹配码 private static final int PERSONS = 1; private static final int PERSON = 2; static{ MATCHER.addURI("cn.huangjie.provides.personprovider", "person", PERSONS); //在android中,#代表数字,*代表任意字符 MATCHER.addURI("cn.huangjie.provides.personprovider", "person/#", PERSON); } /** * 该方法由系统调用的,当这个内容提供者的实例被创建出来之后这个方法被调用 */ @Override public boolean onCreate() { dbOpenHelper = new DBOpenHelper(this.getContext()); return true; } /** * 返回你目前要操作数据的内容类型 * 例如:txt,html,mp3 */ @Override public String getType(Uri uri) { switch(MATCHER.match(uri)){ case PERSONS: return "vnd.android.cursor.dir/"; case PERSON: return "vnd.android.cursor.item/"; default: throw new IllegalArgumentException("this is Unknown Uri:" + uri); } } /** * 允许外部的应用对内容提供者进行插入 */ @Override public Uri insert(Uri uri, ContentValues values) { SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); switch(MATCHER.match(uri)){ case PERSONS: //返回行号,数据库的RecNo字段不是行号,当行号跟 //如果主键是整型并且是自增长的话那么这个行号是等于主键值 long rowid = db.insert("person", "name", values); // content://cn.huangjie.provides.personprovider/person/10 //以下两种方式进行拼接的uri都可以 // Uri insertUri = Uri.parse("content://cn.huangjie.provides.personprovider/person/" + rowid); Uri insertUri = ContentUris.withAppendedId(uri, rowid); //发出数据变化通知,第二个参数为变化监听者,这里没有监听者 //这样检测此数据的应用会得到相应的通知 this.getContext().getContentResolver().notifyChange(uri, null); return insertUri; default: throw new IllegalArgumentException("this is Unknown Uri:" + uri); } } /** * 允许外部的应用对内容提供者进行删除 * 返回这个操作所影响的记录数 */ @Override public int delete(Uri uri, String selection, String[] selectionArgs) { SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); int num = 0; switch(MATCHER.match(uri)){ case PERSONS: num = db.delete("person", selection, selectionArgs); break; case PERSON: //解析出/person/10这种形式的id值 long rowid = ContentUris.parseId(uri); String where = " personid = " + rowid; if(selection != null && selection.length() > 0){ where += selection; } num = db.delete("person", where, selectionArgs); break; default: throw new IllegalArgumentException("this is Unknown Uri:" + uri); } return num; } /** * 可以供外部的应用查询内容提供者的数据 */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); Cursor cursor = null; switch(MATCHER.match(uri)){ case PERSONS: cursor = db.query("person", projection, selection, selectionArgs, null, null, sortOrder); break; case PERSON: //解析出/person/10这种形式的id值 long rowid = ContentUris.parseId(uri); String where = " personid = " + rowid; if(selection != null && selection.length() > 0){ where += selection; } cursor = db.query("person", projection, where, selectionArgs, null, null, sortOrder); break; default: throw new IllegalArgumentException("this is Unknown Uri:" + uri); } return cursor; } /** * 允许外部的应用对内容提供者进行更新 */ @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); int num = 0; switch(MATCHER.match(uri)){ case PERSONS: num = db.update("person", values, selection, selectionArgs); break; case PERSON: //解析出/person/10这种形式的id值 long rowid = ContentUris.parseId(uri); String where = " personid = " + rowid; if(selection != null && selection.length() > 0){ where += selection; } num = db.update("person", values, where, selectionArgs); break; default: throw new IllegalArgumentException("this is Unknown Uri:" + uri); } return num; } }
package cn.huangjie.test; import android.content.ContentResolver; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import android.test.AndroidTestCase; import android.util.Log; public class ContentProvideTest extends AndroidTestCase{ public void testInsert(){ Uri uri = Uri.parse("content://cn.huangjie.provides.personprovider/person"); //访问内容提供者 ContentResolver resolver = this.getContext().getContentResolver(); ContentValues values = new ContentValues(); values.put("name", "jiehuang"); values.put("age", 545); resolver.insert(uri, values); } public void testDelete(){ Uri uri = Uri.parse("content://cn.huangjie.provides.personprovider/person/30"); //访问内容提供者 ContentResolver resolver = this.getContext().getContentResolver(); int num = resolver.delete(uri, null, null); Log.i("ContentProvide", "删除影响条数:"+num); } public void testUpdate(){ Uri uri = Uri.parse("content://cn.huangjie.provides.personprovider/person/31"); //访问内容提供者 ContentResolver resolver = this.getContext().getContentResolver(); ContentValues values = new ContentValues(); values.put("name", "jieh"); values.put("age", 54); int num = resolver.update(uri, values, null, null); Log.i("ContentProvide", "更新影响条数:"+num); } public void testQuery(){ Uri uri = Uri.parse("content://cn.huangjie.provides.personprovider/person"); //访问内容提供者 ContentResolver resolver = this.getContext().getContentResolver(); Cursor cursor = resolver.query(uri, null, null, null, "personid desc"); while(cursor.moveToNext()){ int id = cursor.getInt(cursor.getColumnIndex("personid")); String name = cursor.getString(cursor.getColumnIndex("name")); Log.i("ContentProvide", id+"#"+name); } } }
2、首先我们需要在内容提供者实现类中增加一个监听的事件
3、在A应用中增加一个按钮,点击按钮往内容提供者中增加一条数据
界面配置文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/button" android:onClick="insert" /><!-- 调用Activity类里面的insert方法 --> </LinearLayout>数值配置文件:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="hello">Hello World, AappActivity!</string> <string name="app_name">A应用</string> <string name="button">往内容提供者添加数据</string> </resources>
package cn.huangjie.aapp; import android.app.Activity; import android.content.ContentResolver; import android.content.ContentValues; import android.net.Uri; import android.os.Bundle; import android.view.View; public class AappActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } public void insert(View v){ Uri uri = Uri.parse("content://cn.huangjie.provides.personprovider/person"); //访问内容提供者 ContentResolver resolver = this.getContentResolver(); ContentValues values = new ContentValues(); values.put("name", "A_App"); values.put("age", 444); resolver.insert(uri, values); } }
package cn.huangjie.other; import android.app.Activity; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.util.Log; public class OtherActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Uri uri = Uri.parse("content://cn.huangjie.provides.personprovider/person"); //第二个参数true表示依赖 this.getContentResolver().registerContentObserver(uri, true, new PersonContentObserver(new Handler())); } private class PersonContentObserver extends ContentObserver{ public PersonContentObserver(Handler handler) { super(handler); } /** * 这个方法只探测到改变了,但是不知道是修改还是增加还是删除等 * 如果是增加的话则从数据库中读取一条最新的记录便是增加的 */ @Override public void onChange(boolean selfChange) { //sql语句:select * from person order by personid desc limit 1 Uri uri = Uri.parse("content://cn.huangjie.provides.personprovider/person"); Cursor cursor = getContentResolver().query(uri, null, null, null, "personid desc limit 1"); if(cursor.moveToFirst()){//如果有一条记录 String name = cursor.getString(cursor.getColumnIndex("name")); Log.i("OtherActivity", "检测到的数据:"+name); } } } }
工程下载:http://download.csdn.net/detail/wxwzy738/6326781