Android中内容提供与处理

711 阅读6分钟

四大组件之一的内容提供者,我们虽然都是知道的,但是还是有很多小伙伴不是特别会用这个东西。我在这里写下这个博客,方便大家用到的时候进行查阅。

内容提供器ContentProvider

1.组成

内容提供器是跟数据存取有关的组件,完整的内容组件由内容提供器ContentProvider,内容解析器ContentResolver,内容观察器ContentObserver这三部分组成。

2.作用

ContentProvider为APP存取内部数据提供统一的外部接口,让不同的应用之间得以共享数据。

3.ContentProvider接口提供的接口方法

ContentProvider只是一个服务端的数据存取接口,开发者需要在其基础上实现一个具体类,并重写一下相关数据库管理方法。

  • onCreate:创建数据库并获得数据库连接。
  • query:查询数据。
  • insert:插入数据。
  • update:更新数据。
  • delete:删除数据。
  • getType:获取数据类型。

4.使用ContentProvider前提

要想使用ContentProvider,首先得实现SQLite的数据表帮助类,然后由ContentProvider封装对外接口。

5.代码示例

首先我们定义一个实体类

public class UserInfo {

    private long rowid;
    private int xuhao;
    private String name;
    private int age;
    private long height;
    private float weight;
    private boolean married;
    private String update_time;
    private String phone;
    private String password;

    public long getRowid() {
        return rowid;
    }

    public void setRowid(long rowid) {
        this.rowid = rowid;
    }

    public int getXuhao() {
        return xuhao;
    }

    public void setXuhao(int xuhao) {
        this.xuhao = xuhao;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public long getHeight() {
        return height;
    }

    public void setHeight(long height) {
        this.height = height;
    }

    public float getWeight() {
        return weight;
    }

    public void setWeight(float weight) {
        this.weight = weight;
    }

    public boolean isMarried() {
        return married;
    }

    public void setMarried(boolean married) {
        this.married = married;
    }

    public String getUpdate_time() {
        return update_time;
    }

    public void setUpdate_time(String update_time) {
        this.update_time = update_time;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

其次我们要定义数据库的帮助类

public class UserDBHelper extends SQLiteOpenHelper {

    private static final String TAG = "UserDBHelper";
    private static final String DB_NAME = "user.db";
    private static final int DB_VERSION = 1;
    private static UserDBHelper mHelper = null;
    private SQLiteDatabase mDB = null;
    public static final String TABLE_NAME = "user_info";

    private UserDBHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    private UserDBHelper(Context context, int version) {
        super(context, DB_NAME, null, version);
    }

    /**
     * 单例获取Helper对象
     *
     * @param context
     * @param version
     * @return
     */
    public static UserDBHelper getInstance(Context context, int version) {
        if (version > 0 && mHelper == null) {
            mHelper = new UserDBHelper(context, version);
        } else if (mHelper == null) {
            mHelper = new UserDBHelper(context);
        }
        return mHelper;
    }

    /**
     * 通过读链接获取SQLiteDatabase
     *
     * @return
     */
    public SQLiteDatabase openReadLink() {
        if (mDB == null || mDB.isOpen() != true) {
            mDB = mHelper.getReadableDatabase();
        }
        return mDB;
    }

    /**
     * 通过写链接获取SQLiteDatabase
     *
     * @return
     */
    public SQLiteDatabase openWriteLink() {
        if (mDB == null || mDB.isOpen() != true) {
            mDB = mHelper.getWritableDatabase();
        }
        return mDB;
    }

    /**
     * 关闭数据库
     */
    public void closeLink() {
        if (mDB != null && mDB.isOpen() == true) {
            mDB.close();
            mDB = null;
        }
    }

    /**
     * 获取数据库名称
     *
     * @return
     */
    public String getDbName() {
        if (mHelper != null) {
            return mHelper.getDatabaseName();
        } else {
            return DB_NAME;
        }
    }

    /**
     * 只在第一次打开数据库时执行,再此可进行表结构创建的操作
     *
     * @param db
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        String drop_sql = "DROP TABLE IF EXISTS " + TABLE_NAME + ";";
        db.execSQL(drop_sql);
        String create_sql = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " ("
                + "_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
                + "name VARCHAR NOT NULL," + "age INTEGER NOT NULL,"
                + "height LONG NOT NULL," + "weight FLOAT NOT NULL,"
                + "married INTEGER NOT NULL," + "update_time VARCHAR NOT NULL,"
                + "phone VARCHAR," + "password VARCHAR" + ");";
        db.execSQL(create_sql);
    }

    /**
     * 在数据库版本升高时执行,可以根据新旧版本号进行表结构变更处理
     *
     * @param db
     * @param oldVersion
     * @param newVersion
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //其实这里是根据oldVersion的不同进行字段的添加
        if (oldVersion == 1) {
            // TODO: 2018/11/20 这里一会儿进行测试,先创建的表没有phone和password字段,然后添加
            //Android 的 ALTER 命令不支持一次添加多列,只能分多次添加
            String alter_sql = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + "phone VARCHAR;";
            db.execSQL(alter_sql);
            alter_sql = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + "password VARCHAR;";
            db.execSQL(alter_sql);
        }
    }

    /**
     * 根据条件删除数据
     *
     * @param condition
     * @return
     */
    public int delete(String condition) {
        int count = mDB.delete(TABLE_NAME, condition, null);
        //null的位置,如果condition中用了?作为数值占位,比如"id>?",null要给new String[]{"2"}
        return count;
    }

    /**
     * 删除全部数据
     *
     * @return
     */
    public int deleteAll() {
        int count = mDB.delete(TABLE_NAME, "1=1", null);
        return count;
    }

    /**
     * 增加单条数据,其实就是包装了一下调用增加多条数据
     *
     * @param info
     * @return
     */
    public long insert(UserInfo info) {
        ArrayList<UserInfo> infoArray = new ArrayList<>();
        infoArray.add(info);
        return insert(infoArray);
    }

    /**
     * 根据条件进行查询
     *
     * @param condition
     * @return
     */
    public ArrayList<UserInfo> query(String condition) {
        String sql = String.format("select rowid,_id,name,age,height,weight,married,update_time,phone,password from %s where %s;", TABLE_NAME, condition);
        ArrayList<UserInfo> infoArray = new ArrayList<>();
        Cursor cursor = mDB.rawQuery(sql, null);
        if (cursor.moveToNext()) {
            for (; ; cursor.moveToNext()) {
                UserInfo info = new UserInfo();
                info.setRowid(cursor.getLong(0));
                info.setXuhao(cursor.getInt(1));
                info.setName(cursor.getString(2));
                info.setAge(cursor.getInt(3));
                info.setHeight(cursor.getLong(4));
                info.setWeight(cursor.getFloat(5));
                //SQLite没有布尔型,用0表示false,用1表示true
                info.setMarried(cursor.getInt(6) == 0 ? false : true);
                info.setUpdate_time(cursor.getString(7));
                info.setPhone(cursor.getString(8));
                info.setPassword(cursor.getString(9));
                infoArray.add(info);
                if (cursor.isLast() == true) {
                    break;
                }
            }
        }
        cursor.close();
        return infoArray;
    }

    /**
     * 更新多条数据
     *
     * @param info
     * @param condition
     * @return
     */
    public int update(UserInfo info, String condition) {
        ContentValues cv = new ContentValues();
        cv.put("name", info.getName());
        cv.put("age", info.getAge());
        cv.put("height", info.getHeight());
        cv.put("weight", info.getWeight());
        cv.put("married", info.isMarried());
        cv.put("update_time", info.getUpdate_time());
        cv.put("phone",info.getPhone());
        cv.put("password",info.getPassword());
        int count = mDB.update(TABLE_NAME, cv, condition, null);
        return count;
    }

    /**
     * 更新指定的数据条目
     *
     * @param info
     * @return
     */
    public int update(UserInfo info) {
        return update(info, "rowid=" + info.getRowid());
    }

    /**
     * 增加多条数据
     *
     * @param infoArray
     * @return
     */
    public long insert(ArrayList<UserInfo> infoArray) {
        long result = -1;
        //循环全部数据
        for (int i = 0; i < infoArray.size(); i++) {
            UserInfo info = infoArray.get(i);
            ArrayList<UserInfo> tempArray = new ArrayList<>();
            //如果存在同名记录,就更新记录。注意条件语句的等号后面要用单引号括起来
            if (info.getName() != null && info.getName().length() > 0) {
                String condition = String.format("name='%s'", info.getName());
                tempArray = query(condition);
                if (tempArray.size() > 0) {
                    update(info, condition);
                    result = tempArray.get(0).getRowid();
                    continue;
                }
            }
            //如果不存在唯一性重复的记录,就插入新记录
            ContentValues cv = new ContentValues();
            cv.put("name", info.getName());
            cv.put("age", info.getAge());
            cv.put("height", info.getHeight());
            cv.put("weight", info.getWeight());
            cv.put("married", info.isMarried());
            cv.put("update_time", info.getUpdate_time());
            cv.put("phone",info.getPhone());
            cv.put("password",info.getPassword());
            result = mDB.insert(TABLE_NAME, "", cv);
            //添加成功后返回行号,失败则返回-1
            if (result == -1) {
                return result;
            }
        }
        return result;
    }
}

然后是Provider代码

public class UserInfoProvider extends ContentProvider {

    private UserDBHelper userDB;//声明一个用户数据库的帮助对象
    public static final int USER_INFO = 1;//Uri匹配时的代号
    public static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    static {
        //第一个参数要和清单文件到时候配置的一样。
        //第二个是一个路径
        //第三个是一个匹配代号
        uriMatcher.addURI("com.hao.xxx.provider.UserInfoProvider","/user",USER_INFO);
    }

    @Override
    public boolean onCreate() {
        userDB = UserDBHelper.getInstance(getContext(),1);
        return false;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        Cursor cursor = null;
        if (uriMatcher.match(uri) == USER_INFO){
            //获取SQLite数据库的读连接
            SQLiteDatabase db = userDB.getReadableDatabase();
            //执行SQLite的查询操作
            cursor = db.query(UserDBHelper.TABLE_NAME,projection,selection,selectionArgs,null,null,sortOrder);
            //设置内容解析器监听
            cursor.setNotificationUri(getContext().getContentResolver(),uri);
        }
        return cursor;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        Uri newUri = uri;
        if (uriMatcher.match(uri) == USER_INFO){
            //获取SQLite数据库的写连接
            SQLiteDatabase db = userDB.getWritableDatabase();
            //向指定的表插入数据,返回记录的型号
            long rowId = db.insert(UserDBHelper.TABLE_NAME,null,values);
            if (rowId > 0){//判断插入是否执行成功
                //如果添加成功,利用新记录的行号生成新的地址
                newUri = ContentUris.withAppendedId(Uri.parse("content://com.hao.xxx.provider.UserInfoProvider/user"),rowId);
                //通知监听器,数据已经改变
                getContext().getContentResolver().notifyChange(newUri,null);
            }
            db.close();
        }
        return uri;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        int count = 0;
        if (uriMatcher.match(uri) == USER_INFO){
            //获取SQLite数据库的写连接
            SQLiteDatabase db = userDB.getWritableDatabase();
            //执行SQlite的删除操作,返回删除记录的数目
            count = db.delete(UserDBHelper.TABLE_NAME,selection,selectionArgs);
            db.close();
        }
        return count;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }
}

接着我们是对Provider的配置

在清单文件application节点下

<provider
    android:authorities="com.hao.xxx.provider.UserInfoProvider"
    android:name=".UserInfoProvider"
    android:enabled="true"
    android:exported="true"/>

我们可以清楚的看到,其实Provider中就是将已经按照数据库规则组装好的数据添加到数据库中,或对数据库进行相应的操作。其实就是对外开放的数据库操作接口。

接下来我们聊聊内容解析器

内容解析器ContentResolver

1.ContentResolver对象获取方法

要获取ContentResolver对象,在Activity代码中调用getContentResolver方法即可。

2.ContentResolver提供方法

ContentResolver提供的方法与ContentProvider是一一对应的,比如query,insert,update,delete,getType等方法,连方法的参数类型都一模一样。

3.query方法各参数说明

  • uri:Uri类型,可以理解为本次操作的数据表路径。
  • projection:字符串数组类型,指定将要查询的字段名称列表。
  • selection:字符串类型,指定查询条件。
  • selectionArgs:字符串数组类型,指定查询条件中的参数取值列表。
  • sortOrder:字符串类型,指定排序条件。

4.代码示例

插入数据

private void addUser(ContentResolver resolver,UserInfo userInfo){
        ContentValues name = new ContentValues();
        name.put("name",userInfo.getName());
        name.put("age",userInfo.getName());
        name.put("height",userInfo.getName());
        name.put("weight",userInfo.getName());
        name.put("married",userInfo.getName());
        name.put("update_time",userInfo.getName());
        //通过内容解析器往指定Uri中添加用户信息
        resolver.insert(Uri.parse("content://com.hao.xxx.provider.UserInfoProvider/user"),name);
    }

读取数据

private ArrayList<UserInfo> readAllUser(ContentResolver resolver){
        ArrayList<UserInfo> userArray = new ArrayList<>();
        //通过内容解析器从指定Uri中获取用户记录的游标
        Cursor cursor = resolver.query(Uri.parse("content://com.hao.xxx.provider.UserInfoProvider/user"),null,null,null,null);
        //循环取出游标指向的每条用户记录
        while (cursor.moveToNext()){
            UserInfo user = new UserInfo();
            user.setName(cursor.getString(1));
            user.setAge(cursor.getInt(2));
            user.setHeight(cursor.getLong(3));
            user.setWeight(cursor.getFloat(4));
            user.setMarried(cursor.getInt(5)==0?false:true);
            user.setUpdate_time(cursor.getString(6));
            userArray.add(user);
        }
        cursor.close();
        return userArray;
}

调用方法时

 @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.bt_write:
                //插入数据
                UserInfo userInfo = new UserInfo();
                userInfo.setXuhao(10);
                userInfo.setName("小红");
                userInfo.setUpdate_time("2018-11-24");
                userInfo.setMarried(false);
                userInfo.setAge(99);
                userInfo.setWeight(100);
                userInfo.setHeight(180);
                addUser(getContentResolver(),userInfo);
                break;
            case R.id.bt_read:
                //读取数据
                Toast.makeText(this,readAllUser(getContentResolver()).size()+"",Toast.LENGTH_SHORT).show();
                break;
        }
    }

这样我们就可以将自己的数据库中某一张表开放出去,让其他手机中的应用读取里面的数据了。