在前面的文章中,已经知道了文件读写、SharedPreferences、SQLite数据库三种方式来存储数据,这章是存储数据的另一种方式:ContentProvider.
一、知识储备
1.1 Uri介绍
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://com.ljq.provider.personprovider/person")
二、创建和测试ContentProvider
ContentProvider可以对外提过接口,使外部应用可以对数据进行增删改查,内容提供者类必须要继承与ContentProvider。下面是一个关于ContentProvider的例子。
UserProvider.java
package com.lk.sqlite; import com.lk.util.SQLHelper; 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 UserProvider extends ContentProvider { private SQLHelper sqlHelper; //当不匹配时返回UriMatcher.NO_MATCH private final static UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); private final static int MATCH = 1; private final static int MATCH_ID = 2; static { // 当匹配"com.lk.sqlite.providers.userProvider/user"时返回MATCH URI_MATCHER.addURI("com.lk.sqlite.providers.userProvider", "user", MATCH); // 当匹配"com.lk.sqlite.providers.userProvider/user/数字"时返回MATCH_ID // "#"代表一个数字, "*"代表任何文本 URI_MATCHER.addURI("com.lk.sqlite.providers.userProvider", "user/#", MATCH_ID); } @Override public boolean onCreate() { sqlHelper = new SQLHelper(getContext(), 1); return true; } /** * 提供查询接口 */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteDatabase sqLiteDatabase = sqlHelper.getWritableDatabase(); Cursor cursor = null; switch (URI_MATCHER.match(uri)) { case MATCH_ID: long id = ContentUris.parseId(uri); String whereClause = "id = " + id; if(selection != null && !"".equals(selection)) { whereClause += " and " + selection; } cursor = sqLiteDatabase.query("user", projection, whereClause, selectionArgs, null, null, sortOrder); break; case MATCH: cursor = sqLiteDatabase.query("user", projection, selection, selectionArgs, null, null, sortOrder); break; default: throw new IllegalArgumentException("URI不合法"); } return cursor; } /** * Uri代表单行操作的时候返回"vnd.android.cursor.item",多行操作时返回"vnd.android.cursor.dir/" */ @Override public String getType(Uri uri) { switch (URI_MATCHER.match(uri)) { case MATCH: return "vnd.android.cursor.dir/"; case MATCH_ID: return "vnd.android.cursor.item"; default: throw new IllegalArgumentException("URI不合法"); } } @Override public Uri insert(Uri uri, ContentValues values) { switch (URI_MATCHER.match(uri)) { case MATCH: SQLiteDatabase sqLiteDatabase = sqlHelper.getWritableDatabase(); long rowId = sqLiteDatabase.insert("user", null, values); sqLiteDatabase.close(); return ContentUris.withAppendedId(uri, rowId); default: throw new IllegalArgumentException("URI不合法"); } } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { SQLiteDatabase sqLiteDatabase = sqlHelper.getWritableDatabase(); int effectRow = 0; switch (URI_MATCHER.match(uri)) { case MATCH_ID: long id = ContentUris.parseId(uri); String whereClause = "id = " + id; if(selection != null && !"".equals(selection)) { whereClause += " and " + selection; } effectRow = sqLiteDatabase.delete("user", whereClause, selectionArgs); sqLiteDatabase.close(); break; case MATCH: effectRow = sqLiteDatabase.delete("user", selection, selectionArgs); sqLiteDatabase.close(); break; default: throw new IllegalArgumentException("URI不合法"); } return effectRow; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { SQLiteDatabase sqLiteDatabase = sqlHelper.getWritableDatabase(); int effectRow = 0; switch (URI_MATCHER.match(uri)) { case MATCH_ID: long id = ContentUris.parseId(uri); String whereClause = "id = " + id; if(selection != null && !"".equals(selection)) { whereClause += " and " + selection; } effectRow = sqLiteDatabase.update("user", values, whereClause, selectionArgs); sqLiteDatabase.close(); break; case MATCH: effectRow = sqLiteDatabase.update("user", values, selection, selectionArgs); sqLiteDatabase.close(); break; default: throw new IllegalArgumentException("URI不合法"); } return effectRow; } }
在配置文件中添加ContentProvider组件
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lk.sqlite"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="10" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.lk.sqlite.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- android:authorities是标示ContentProvider的唯一标示,必须唯一 -->
<provider android:name="com.lk.sqlite.UserProvider"
android:authorities="com.lk.sqlite.providers.userProvider" />
<uses-library android:name="android.test.runner"/>
</application>
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.lk.sqlite" />
</manifest>
将这个应用运行到Android模拟机中,在创建另一个Android 项目,测试是否可以利用ContentProvider提供的接口增删改查ContentProvider所在应用。
AndroidTest项目: ContentResolverTest.java
package com.lk.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 ContentResolverTest extends AndroidTestCase { private final static String TAG = "db"; public void testInsert() { ContentResolver contentResolver = getContext().getContentResolver(); Uri url = Uri.parse("content://com.lk.sqlite.providers.userProvider/user"); ContentValues values = new ContentValues(); values.put("name", "王五"); values.put("email", "wangwu@sina.com"); values.put("account", 5000); contentResolver.insert(url , values ); } public void testDelete() { ContentResolver contentResolver = getContext().getContentResolver(); Uri url = Uri.parse("content://com.lk.sqlite.providers.userProvider/user/10"); contentResolver.delete(url, null, null); } public void testDeleteByQuery() { ContentResolver contentResolver = getContext().getContentResolver(); Uri url = Uri.parse("content://com.lk.sqlite.providers.userProvider/user"); contentResolver.delete(url, "name = ?", new String[]{"李四"}); } public void testUpdate() { ContentResolver contentResolver = getContext().getContentResolver(); Uri url = Uri.parse("content://com.lk.sqlite.providers.userProvider/user/16"); ContentValues values = new ContentValues(); values.put("name", "张飞"); values.put("email", "zhangfei@sina.com"); values.put("account", 2000); contentResolver.update(url, values, null, null); } public void testUpdateByQuery() { ContentResolver contentResolver = getContext().getContentResolver(); Uri url = Uri.parse("content://com.lk.sqlite.providers.userProvider/user"); ContentValues values = new ContentValues(); values.put("name", "关羽"); values.put("email", "guanyu@sina.com"); values.put("account", 2500); contentResolver.update(url, values, "id >= ? and id <= ?", new String[]{"12", "14"}); } public void testQuery() { ContentResolver contentResolver = getContext().getContentResolver(); Uri url = Uri.parse("content://com.lk.sqlite.providers.userProvider/user/15"); Cursor cursor = contentResolver.query(url, null, null, null, null); while(cursor.moveToNext()) { Log.i(TAG, "id = " + cursor.getInt(cursor.getColumnIndex("id")) + ", name = " + cursor.getString(cursor.getColumnIndex("name"))); } cursor.close(); } }
在每个方法测试完后,就可以查看一下SQLite数据库是否改变。
三、监听ContentProvider数据变化
但ContentProvider中的数据发送变化时,其他应用可以监听它,前提是监听它的应用的activity必须要是开着的。
首先要在ContentProvider中写代码通知监听者数据变化
@Override public Uri insert(Uri uri, ContentValues values) { switch (URI_MATCHER.match(uri)) { case MATCH: SQLiteDatabase sqLiteDatabase = sqlHelper.getWritableDatabase(); long rowId = sqLiteDatabase.insert("user", null, values); sqLiteDatabase.close(); //通知注册在此URI上的访问者,数据发生了变化 getContext().getContentResolver().notifyChange(uri, null); return ContentUris.withAppendedId(uri, rowId); default: throw new IllegalArgumentException("URI不合法"); } }
在另一个应用的activity中想内容提供者中添加数据
public void save(View view) { ContentResolver contentResolver = this.getContentResolver(); Uri url = Uri.parse("content://com.lk.sqlite.providers.userProvider/user"); // 注册监听"content://com.lk.sqlite.providers.userProvider/user"代表的数据 contentResolver.registerContentObserver(url, true, new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { Uri uri = Uri.parse("content://com.lk.sqlite.providers.userProvider/user"); // 当"content://com.lk.sqlite.providers.userProvider/user"代表的数据发送变化时,系统就会调用这个方法 Cursor cursor = getContentResolver().query(uri , null, null, null, " id desc limit 0, 1"); if(cursor.moveToFirst()) { Log.i("db", "id = " + cursor.getInt(cursor.getColumnIndex("id")) + ", name = " + cursor.getString(cursor.getColumnIndex("name"))); } super.onChange(selfChange); } }); ContentValues values = new ContentValues(); values.put("name", "马超"); values.put("email", "zhaoyun@sina.com"); values.put("account", 10000); contentResolver.insert(url , values ); }
当触发save方法后,就会调用onChange方法。
浙公网安备 33010602011771号