android除了使用AIDL进程间通信,ContentProvider也可以实现数据间的共享,正好做了个SQL的demo,顺便把这个也总结下。使用起来很简单,一个应用将数据暴露出来,另外别的应用就可以根据定下的规则来访问这个数据库了,就像系统media数据库一样。定制好Uri,便可以查询到共享的数据库表了。
建立了两个Module,一个提供数据,一个访问数据,如下
app可实现数据的共享,首先要实现ContentProvider类如下代码,最好和数据库的SQLiteOpenHelper在同一个包里,这样访问变量也方便。
package database.qin.xue.com.databasedemo;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.util.Log;
import java.util.List;
/**
* Created by xue.qin on 2017/6/11.
*/
public class DemoContentProvider extends ContentProvider {
private static final String TAG = "DemoContentProvider";
public static final String AUTHORITY = "database.qin.xue.com.databasedemo";
private DemoDatabaseHelper mHelper;
private static final int DEMO_BASE = 1;
private static final int DEMO_BASE_ID = 2;
private static final int DEMO_BASE_CLOMN = 3;
private static final UriMatcher sURLMatcher = new UriMatcher(UriMatcher.NO_MATCH);
/* --常量 UriMatcher.NO_MATCH 表示不匹配任何路径的返回码
--# 号为通配符:匹配数字
--* 号为任意字符:匹配任意字符
*/
static {
sURLMatcher.addURI(AUTHORITY, "demobase", DEMO_BASE);
sURLMatcher.addURI(AUTHORITY, "demobase/#", DEMO_BASE_ID);
sURLMatcher.addURI(AUTHORITY, "demobase/*/#", DEMO_BASE_CLOMN);
}
@Override
public boolean onCreate() {
Log.i(TAG, "onCreate()");
mHelper = new DemoDatabaseHelper(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Log.i(TAG, "uri.getPath() = " + uri.getPath());
List<String> list = uri.getPathSegments();
for (String str : list) {
Log.i(TAG, "str = " + str);
}
int match = sURLMatcher.match(uri);
Log.i(TAG, "query() match = " + match);
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
switch (match) {
case DEMO_BASE:
qb.setTables(DemoDatabaseHelper.TABLE_NAME);
break;
case DEMO_BASE_ID:
qb.setTables(DemoDatabaseHelper.TABLE_NAME);
qb.appendWhere(DemoContract.DemoColumns2._ID + "=");
qb.appendWhere(uri.getLastPathSegment());
break;
case DEMO_BASE_CLOMN:
if(uri.getPathSegments().get(1).equals(DemoContract.DemoColumns2.GENDER)){
qb.setTables(DemoDatabaseHelper.TABLE_NAME);
qb.appendWhere(DemoContract.DemoColumns2.GENDER + "=");
qb.appendWhere(uri.getLastPathSegment());
}else if(uri.getPathSegments().get(1).equals(DemoContract.DemoColumns2.AGE)){
qb.setTables(DemoDatabaseHelper.TABLE_NAME);
qb.appendWhere(DemoContract.DemoColumns2.AGE + "=");
qb.appendWhere(uri.getLastPathSegment());
}
break;
default:
throw new IllegalArgumentException("Unknown URL " + uri);
}
SQLiteDatabase db = mHelper.getReadableDatabase();
Cursor ret = qb.query(db, projection, selection, selectionArgs,
null, null, sortOrder);
return ret;
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
}
并且在AndroidManifest.xml中注册这个Provider,如下:
<provider
android:name=".DemoContentProvider"
android:authorities="database.qin.xue.com.databasedemo"
android:enabled="true"
android:exported="true"></provider>
这里的authorities就是Uri里对应的Authorities通常是提供数据的应用的包名,也可自定义字符串。
需要写一个UriMathcher来匹配Uri,这样别的应用就可以使用Uri来访问当前应用的数据了。
private static final UriMatcher sURLMatcher = new UriMatcher(UriMatcher.NO_MATCH);
/* --常量 UriMatcher.NO_MATCH 表示不匹配任何路径的返回码
--# 号为通配符:匹配数字
--* 号为任意字符:匹配任意字符
*/
static {
sURLMatcher.addURI(AUTHORITY, "demobase", DEMO_BASE);
sURLMatcher.addURI(AUTHORITY, "demobase/#", DEMO_BASE_ID);
sURLMatcher.addURI(AUTHORITY, "demobase/*/#", DEMO_BASE_CLOMN);
}
# 可以匹配任意字符
*只能匹配数字。
这里我并没有将所有的增删改都重写,只是重写了query这个函数,其他的也是类似的
对可以使用int match = sURLMatcher.match(uri); 获取匹配后的Code。根据不同的code来区分uri,然后调用本地Helper来定义查询,返回Cusor。
例如 我在customer访问端中调用。
Uri.parse("content://" + "database.qin.xue.com.databasedemo" + "/demobase")
这样一个Uri,
content:// 只要是ContentProvider 提供的都是这个Sheme
"database.qin.xue.com.databasedemo" 是authority
/demobase 是要匹配的部分。
然后可以使用ContentReslover来查询这个Uri
Cursor cursor = getContentResolver().query(uri, PROJECTION, null, null, null);
在app的提供端,首先要匹配这个uri,
int match = sURLMatcher.match(uri);
获得匹配返回码,根据不同的返回值,做出不同的对策,例如我在这里,没有做任何过滤只是将整张表查询出来,返回Cursor。
Cursor ret = qb.query(db, projection, selection, selectionArgs,
null, null, sortOrder);
返回的Cursor被房问端接收到后,就实现了数据的共享,便可以访问了。
例如我要查询所有年龄为9的条目。
写了这样的Uri
Uri.parse("content://" + "database.qin.xue.com.databasedemo" + "/demobase/age/9")
在ContentProvider中,对这个Uri进行了二次的解析,根据自定的规则查询返回的就是查询后的结果。在这个例子中全部数据如图
查询后的Log显示为
06-11 20:26:28.125 20000-20000/com.xue.qin.demo.customer I/MainActivity: cursor.getCount() = 2
06-11 20:26:28.125 20000-20000/com.xue.qin.demo.customer I/MainActivity: id = 6 name = xiaogang age = 9 gender = 0
06-11 20:26:28.126 20000-20000/com.xue.qin.demo.customer I/MainActivity: id = 7 name = lihua age = 9 gender = 0
可见在访问端也能查新访问到提供端共享的数据。
总结,使用ContentProvider来提供数据,是对查询数据的一次封装,将Uri解析成能够本地查询的语句来进行查询和返回查询结果。
Demo地址 点击打开链接