数据库在Android当中是私有的,不能将数据库设为WORLD_READABLE,每个数据库都只能创建它的包访问。这意味着只有创建这个数据库的应用程序才可访问它。也就是说不能跨越进程和包的边界,直接访问别的应用程序的数据库。那么如何在应用程序间交换数据呢? 如果需要在进程间传递数据,可以使用ContentProvider来实现。
大部分使用ContentProvider操作的数据都来自于数据库,但是也可以来自于文件、SharedPreferences、XML或网络等其他存储方式。
第一步:创建一个类,继承ContentProvider,实现以下几个方法
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return null;
}
@Nullable
@Override
public String getType(Uri uri) {
return null;
}
@Nullable
@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;
}
那么我们就建立一个BaseContentProvider来实现这些方法:
我们要知道,contentProvider,是根据uri的地址来进行查询的。
所以如果要对不同的表进行操作,也就需要不同的uri地址。
那么我们再建立一个类,用来获取uri的地址
protected abstract String getTable();
protected abstract String getType();
protected abstract Uri getContentUri();
/**
* 根据类创建ContentValues
* @param t
* @return
*/
public ContentValues createContentValues(T t){
ContentValues values = new ContentValues();
Class<?> cls = t.getClass();
for (Field field : cls.getDeclaredFields()) {
String typeName = field.getType().getName();
String fieldName = field.getName();
if (fieldName.equals(BaseColumns._ID))
continue;
field.setAccessible(true);
try {
if (typeName.equals(STRING_NAME)) {
values.put(fieldName, (String) field.get(t));
} else if (typeName.equals(INT_NAME)) {
values.put(fieldName, field.getInt(t));
} else if (typeName.equals(FLOAT_NAME)) {
values.put(fieldName, field.getFloat(t));
} else if (typeName.equals(DOUBLE_NAME)) {
values.put(fieldName, field.getDouble(t));
} else if (typeName.equals(BOOLEAN_NAME)) {
values.put(fieldName, field.getBoolean(t) ? 1 : 0);
}
} catch (Exception e) {
if (e != null && StringUtil.isNotEmpty(e.getMessage())) {
Log.e(TAG, e.getMessage());
}
}
}
return values;
}
public T create(Class<T> cls, Cursor cursor) throws InstantiationException, IllegalAccessException {
T t = cls.newInstance();
for (Field field : cls.getDeclaredFields()) {
String typeName = field.getType().getName();
String fieldName = field.getName();
field.setAccessible(true);
int columnIndex = cursor.getColumnIndex(fieldName);
if (columnIndex != -1) {
if (typeName.equals(STRING_NAME)) {
field.set(t, cursor.getString(columnIndex));
} else if (typeName.equals(INT_NAME)) {
field.setInt(t, cursor.getInt(columnIndex));
} else if (typeName.equals(FLOAT_NAME)) {
field.setFloat(t, cursor.getFloat(columnIndex));
} else if (typeName.equals(DOUBLE_NAME)) {
field.setDouble(t, cursor.getDouble(columnIndex));
} else if (typeName.equals(BOOLEAN_NAME)) {
field.setBoolean(t, cursor.getInt(columnIndex) == 1 ? true : false);
}
}
}
return t;
}
然后我们可以创建一个具体的类来实现几个抽象方法
public class UserColumn extends ColumnsType<User> implements BaseColumns {
public static final String TABLE_USER = "user";
private static final Uri CONTENT_URI = Uri.parse(MyContentProvider.BASE_URI + TABLE_USER);
private static final String CONTENT_TYPE = MyContentProvider.BASE_TYPE + TABLE_USER;
@Override
protected String getTable() {
return TABLE_USER;
}
@Override
protected String getType() {
return CONTENT_TYPE;
}
@Override
protected Uri getContentUri() {
return CONTENT_URI;
}
}
接下来我们开始实现delete等方法了
比如:delete
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
ColumesType<User> ct = new ColumesType();
String table = ct.getTable();
int count = db.delete(table, selection, selectionArgs);
//观察者模式,通知发生变化
getContext().getContentResolver().notifyChange(uri, null, true);
return count ;
}
通过上面这个方法,那我们直接就可以删除这个user表的数据,那我们可以再抽取一下:
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int typeCode = uriMatcher.match(uri);
ColumnsType<?> columnsType = createColumnsType(typeCode);
String table = columnsType.getTable();
int count = db.delete(table, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null, true);
return count;
}
通过匹配uri,确定我们的表名等数据
public class MyContentProvider extends BaseContentProvider {
public static String BASE_URI;
public static final String BASE_TYPE = "vnd.android.cursor.dir/";
private static final int TABLE_USER = 1;
private static String AUTHORITY;
static {
BASE_URI = "content://com.csdn.provider/";
AUTHORITY = "com.csdn.provider";
}
public MyContentProvider() {
super(new UriMatcher(UriMatcher.NO_MATCH));
}
@Override
public boolean onCreate() {
Context context = getContext();
PackageInfo pi = null;
try {
pi = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
DataBaseHelper helper = DataBaseHelper.getInstance(context,pi == null ? 1 : pi.versionCode);
db = helper.getWritableDatabase();
return super.onCreate();
}
//通过匹配uri,得到columnsType,可以获取表的信息
@Override
protected ColumnsType<?> createColumnsType(int typeCode) {
ColumnsType<?> columnsType;
switch (typeCode) {
case TABLE_USER:
columnsType = new UserColumn();
break;
default:
throw new IllegalArgumentException("Unknown typecode " + typeCode);
}
return columnsType;
}
//添加匹配
@Override
protected void addColumnsType(UriMatcher uriMatcher) {
uriMatcher.addURI(AUTHORITY, UserColumn.TABLE_USER,TABLE_USER);
}
}
最后,不要忘了注册哈
<!-- 注册ContentProvider
需要添加3条属性
android:name 用于指定ContentProvider的子类的具体路径
android:authorities 用于指定后期ContentResolver在读取的时候,Uri路径中间的作者名
android:exported="true" 向外开放数据
ContentProvider用来暴露数据,ContentResolver当然就用来获取数据啦
同理,我们只要根据uri匹配,获取到我们这个类的uri,就可以进行操作了,再以delete为例:
public void delete(String key, String value) {
context.getContentResolver().delete(columns.getContentUri(), key + "=?", new String[]{value});
}