ContentProvider重温

几万年都不见得用一次的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支持通配符

关于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" />
就这样写完了,赶紧去找个多进程或者新建个app访问下这个数据库咯

推荐阅读更多精彩内容