几万年都不见得用一次的ContentProvider,这几天说要用到,除了记得他是四大组件,还真是没啥印象了。。
用到provider,无非是想要跨进程进行DB操作,AIDL,或者两个app,我就是用了AIDL,再说吧。。
先来看下怎么使用ContentProvider进行数据操作。
Uri uri = Uri.parse("content://com.example.test.aidl.provider/student");
ContentValues values = new ContentValues();
values.put("name", "sss");
values.put("age", 555);
values.put("stuno", 222);
getContentResolver().insert(uri, values);
调用getContentResolver()得到ContentResolver对象,根据传入的uri,可以知道调用方期望获取的是哪个provider,哪张表,哪条数据。
数据库
既然是DB操作,肯定要有DB了,先搞个DB
public class DBOpenHelper extends SQLiteOpenHelper {
public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
String sql = "create table if not exists student(id integer primary key autoincrement,name varchar(20),age integer,stuno varchar(20))";
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
创建DB的时候,新建了一张student表,包含姓名,年龄,学号还有自增ID
ContentProvider
首先创建一个DBProvider继承ContentProvider,默认实现有六个方法,分别为onCreate,query,insert,delete,update,还有getType。
public class DBProvider extends 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;
}
}
当ContentResolver尝试访问Provider时,Provider才会初始化,所以在onCreate里创建数据库
public boolean onCreate() {
//provider 初始化时创建一个名字为stu.db 版本为1的数据库,这时数据库中有student表
dbOpenHelper = new DBOpenHelper(getContext(),"stu.db",null,1);
return true;
}
再说下URI
一个标准的Uri如下格式
content://com.example.test.aidl.provider/student 访问student表
content://com.example.test.aidl.provider/student/1 访问student表下id为1的数据
包括
- 声明协议: content://
- authority :com.example.test.aidl.provider(自定义,一般为包名+.provider)
- path : student (表名)
- id:1(可不加,不加时访问整张表)
uri支持通配符
- ** * **匹配任意字符
- ** # ** 匹配任意数字
例如
content://com.example.test.aidl.provider/*可以匹配该provider下的任意表
content://com.example.test.aidl.provider/student/#可以匹配student表下的任意一行数据
关于uri的匹配
用到UriMatcher这个类
在静态代码块中,初始化匹配规则
private static final int STUDENT_DIR = 0;
private static final int STUDENT_ITEM =1;
private static String AUTHORITY = "com.example.test.aidl.provider";
private static UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "student", STUDENT_DIR);
uriMatcher.addURI(AUTHORITY, "student/#", STUDENT_ITEM);
}
addURI方法添加我们的匹配规则,第一个参数是自定义的AUTHORITY,第二个是Path,可以是表名,也可以是表名下的id结尾,这里#就是上面说的通配符,匹配任意数字,第三个参数是成功匹配uri时返回的自定义整型码
而uri的作用,都体现在除了onCreate之外的五个实现方法中,下面是整个ContentProvider的代码
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
/**
* Created by walker on 2016/12/7.
*/
public class DBProvider extends ContentProvider {
private DBOpenHelper dbOpenHelper;
private static final int STUDENT_DIR = 0;
private static final int STUDENT_ITEM =1;
private static String AUTHORITY = "com.example.test.aidl.provider";
private static UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "student", STUDENT_DIR);
uriMatcher.addURI(AUTHORITY, "student/#", STUDENT_ITEM);
}
@Override
public boolean onCreate() {
dbOpenHelper = new DBOpenHelper(getContext(),"stu.db",null,1);
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
Cursor cursor = null;
switch(uriMatcher.match(uri)){
case STUDENT_DIR:
cursor = db.query("student", projection, selection, selectionArgs, null, null, sortOrder);
break;
case STUDENT_ITEM:
String id = uri.getPathSegments().get(1); //切割URI AUTHORITY之后的字符,0是路径,1是id
cursor = db.query("student", projection, "id = ?", new String[]{id}, null, null, sortOrder);
break;
}
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
//==============================================================
//返回各个Uri对象对应的MIME类型,MIME由三个部分组成,以vnd开头,
//如果URI以路径结尾,接android.cursor.dir/ 如果以id结尾,接android.cursor.item/
//最后接vnd.权限.路径
//对于content://com.example.test.aidl.provider/student
//对应的MIME为 vnd.android.cursor.dir/vnd.com.example.test.aidl.provider.student
//==============================================================
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case STUDENT_DIR:
return "vnd.android.cursor.dir/vnd.com.example.test.aidl.provider.student";
case STUDENT_ITEM:
return "vnd.android.cursor.item/vnd.com.example.test.aidl.provider.student";
default:
break;
}
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
Uri uriReturn = null;
switch (uriMatcher.match(uri)) {
case STUDENT_DIR:
case STUDENT_ITEM:
long newID = db.insert("student", null, values);
uriReturn = Uri.parse("content://"+AUTHORITY+"/student/"+newID);
break;
default:
break;
}
return uriReturn;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
int deleteRow = 0;
switch (uriMatcher.match(uri)) {
case STUDENT_DIR:
deleteRow = db.delete("student", selection, selectionArgs);
break;
case STUDENT_ITEM:
String id = uri.getPathSegments().get(1);
deleteRow = db.delete("student", "id = ?" , new String[]{id});
break;
}
return deleteRow;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
int updateRows = 0;
switch (uriMatcher.match(uri)) {
case STUDENT_DIR:
updateRows = db.update("student",values, selection, selectionArgs);
break;
case STUDENT_ITEM:
String id = uri.getPathSegments().get(1);
updateRows = db.update("student",values, "id = ?" , new String[]{id});
break;
}
return updateRows;
}
}
可以看到,switch里面对传进来的uri进行了匹配,根据结果进行不同操作,也就是一些db操作
这里说下getPathSegments获取的是path,也就是uri中authority之后的部分,这部分有路径,也可能有id,0是路径,1是id(如果有)
关于getType,返回的MIME不知道有啥用,貌似即使provider不exported出去,也是能够让其他进程访问到。
最后别忘了注册provider和exported它
<provider
android:exported="true"
android:authorities="com.example.test.aidl.provider"
android:name=".DBProvider" />