四大组件之ContentProvider探索

ContentProvider主要负责存储和共享数据,底层是由Binder实现的,所以能够跨进程通信,在不同的应用程序之间进行数据共享,它还可以选择对哪一部分数据进行共享,从而保证程序中的隐私数据不会有泄露风险。

ContentProvider有两种形式:可以使用现有的内容提供者来操作和读取相应程序中的数据,也可以创建自己的内容提供者给这个程序的数据提供外部访问接口。

  • 访问系统ContentProvider
  • 自定义ContentProvider

一.访问系统ContentProvider

实际上ContentProvider是对SQLiteOpenHelper的进一步封装,所以可以通过ContentProvider对应用中的数据进行增删改查操作,其它应用是通过ContentResolver来访问ContentProvider提供的数据,而ContentResolver是通过uri来定位自己要访问的数据。

1.URI

uri(Uniform Resource Identifier)即统一资源标识符,是用于标识某一互联网资源名称的字符串,以联系人Contacts的Uri为例:
在这里插入图片描述
schema:协议申明,形式为content://
authority:权限,ContentProvider的唯一标识
path:数据表的表名
id:数据表中数据的标识,可选字段

2.ContentResolver

ContentResolver类中提供了一系列对数据增删改查操作的方法,类似于数据库操作,insert()插入数据,update()更新数据,delete()删除数据,query()查询数据,下面以访问系统通讯录为例:

(1)添加权限,Android6.0之上要动态获取权限。

<uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>
int contactsPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS);
        if (contactsPermission != PackageManager.PERMISSION_GRANTED) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},REQUEST_CODE_PERMISSIONS);
            }
        }

(2)查询系统联系人,获取联系人名称和所有的电话号码。

        Uri uri = ContactsContract.Contacts.CONTENT_URI;
        Cursor cursor = getContentResolver().query(uri, null, null, null, null);//查询系统联系人
        if (cursor != null) {
            while (cursor.moveToNext()) {
                String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));//联系人名称
                int id  = cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts._ID));//联系人唯一标识id
                Cursor cursor1 = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, new String[]{ContactsContract.CommonDataKinds.Phone.NUMBER},
                        ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + id, null, null);//通过id查询联系人所有号码
                if (cursor1 != null) {
                    while (cursor1.moveToNext()) {
                        String phone = cursor1.getString(cursor1.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                        Log.d(TAG,name+":"+phone);//打印联系人名称和电话号码
                    }
                }
                cursor1.close();
            }
        }
        cursor.close();
    }

二.自定义ContentProvider

ContentProvider的使用可以分为以下几个步骤:

  • 创建自己的数据列表
  • 自定义ContentProvider,实现相关的抽象方法
  • 在配置文件中注册以及定义相关的访问权限
  • 通过ContentResolver根据uri进行增删改查操作
  • ContentObserver监听ContentProvider的变化

1.创建数据列表

public class DBOpenHelper extends SQLiteOpenHelper {

    public final static String DATABASE_NAME = "com.czy.contentproviderdemo.provider";
    public final static String DATABASE_STUDENT_TABLE_NAME = "student";
    private final static int DATABASE_VERSION = 1;
    private Context mContext;
    private final static String CREATE_STUDENT_TABLE = "CREATE TABLE IF NOT EXISTS "
            + DATABASE_STUDENT_TABLE_NAME
            + "(id INTEGER PRIMARY KEY,"
            + "name TEXT VARCHAR(20) NOT NULL,"
            + "gender BIT DEFAULT(1),"
            + "number TEXT VARCHAR(12) NOT NULL,"
            + "score INTEGER CHECK(score >= 0 and score <= 100))";

    public DBOpenHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        mContext = context;
    }

    public DBOpenHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
        super(context, DATABASE_NAME, factory, DATABASE_VERSION);
        mContext = context;
    }

    public DBOpenHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version, @Nullable DatabaseErrorHandler errorHandler) {
        super(context, DATABASE_NAME, factory, DATABASE_VERSION, errorHandler);
        mContext = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_STUDENT_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

2.自定义ContentProvider

public class StudentContentProvider extends ContentProvider {

    private final static String AUTHORITY = "com.czy.contentproviderdemo.provider";
    private final static int STUDENT_URI_CODE = 0;
    /*
    UriMatcher类是帮助匹配ContentProvider中的uri
    它有两个方法addURI()和match()
    addURI()方法用来把uri和uri_code相关联
    match()方法能够根据传递的uri匹配到相应的uri_code
    * */
    private final static UriMatcher uriMatcher;
    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(AUTHORITY,"student",STUDENT_URI_CODE);
    }
    private Context mContext;
    private SQLiteDatabase mDataBase;


    @Override
    public boolean onCreate() {
        mContext = getContext();
        mDataBase = new DBOpenHelper(mContext).getWritableDatabase();
        return true;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        int uriType = uriMatcher.match(uri);
        Cursor cursor = null;
        switch (uriType) {
            case STUDENT_URI_CODE:
                cursor = mDataBase.query(DBOpenHelper.DATABASE_STUDENT_TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder, null);
                break;
                default:
                    new IllegalArgumentException("unSupport Uri :" +uri);
                    break;
        }
        return cursor;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {//获得数据的MIME类型
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        int uriType = uriMatcher.match(uri);
        long row = 0;
        switch (uriType) {
            case STUDENT_URI_CODE:
                row = mDataBase.insert(DBOpenHelper.DATABASE_STUDENT_TABLE_NAME, null, values);
                break;
            default:
                new IllegalArgumentException("unSupport Uri :" +uri);
                break;
        }
        if (row > 0) {
            mContext.getContentResolver().notifyChange(uri,null);

        }

        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        int uriType = uriMatcher.match(uri);
        int rowDelete = 0;
        switch (uriType) {
            case STUDENT_URI_CODE:
                rowDelete = mDataBase.delete(DBOpenHelper.DATABASE_STUDENT_TABLE_NAME, selection, selectionArgs);
            default:
                new IllegalArgumentException("unSupport Uri :" +uri);
                break;
        }
        if (rowDelete > 0) {
            mContext.getContentResolver().notifyChange(uri,null);

        }

        return rowDelete;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        int uriType = uriMatcher.match(uri);
        int rowUpdate = 0;
        switch (uriType) {
            case STUDENT_URI_CODE:
                rowUpdate = mDataBase.update(DBOpenHelper.DATABASE_STUDENT_TABLE_NAME, values,selection, selectionArgs);
            default:
                new IllegalArgumentException("unSupport Uri :" +uri);
                break;
        }
        if (rowUpdate > 0) {
            mContext.getContentResolver().notifyChange(uri,null);

        }

        return rowUpdate;
    }
}

注意:

(1)onCreate()方法由系统回调运行在主线程中,其它五个方法由外界回调,运行在binder线程池中,因此在onCreate()方法中不能做耗时操作。

(2)增删改查操作存在多线程并发问题,因此在方法的内部要做好线程的同步。

(3)insert方法的返回值是插入数据所在的行号,update和delete方法的返回值代表是此次操作影响到的行数。

3.配置文件注册

<provider
            android:authorities="com.czy.contentproviderdemo.provider"
            android:name="com.czy.contentproviderdemo.StudentContentProvider"
            android:process=":provider"
            android:exported="true"/>

authorities:provider的权限,形式是包名.provider
name:provider全名,形式是包名.类名
process:开启一个新进程
exported:是否可以被其它程序访问

4.ContentResolver的增删改查操作

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity==";
    private final static String AUTHORITY = "com.czy.contentproviderdemo.provider";
    private final static Uri STUDENT_URI = Uri.parse("content://" + AUTHORITY + "/student");

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
	//删除数据
    public void deleteValue(View view) {
        getContentResolver().delete(STUDENT_URI, "name = ?", new String[]{"update"});
    }
	//更新数据
    public void updateValue(View view) {
        ContentValues contentValues = new ContentValues();
        contentValues.put("id", 0);
        contentValues.put("name", "update");
        contentValues.put("gender", 1);
        contentValues.put("number", "201804111048");
        contentValues.put("score", "90");
        getContentResolver().update(STUDENT_URI, contentValues, "id = ?", new String[]{"0"});
    }
	//查询数据
    public void queryValue(View view) {
        Cursor cursor = getContentResolver().query(STUDENT_URI, new String[]{"id", "name", "gender", "number", "score"}, null, null, null);
        if (cursor != null) {
            while (cursor.moveToNext()) {
                Student student = new Student();
                student.id = cursor.getInt(cursor.getColumnIndex("id"));
                student.name = cursor.getString(cursor.getColumnIndex("name"));
                student.gender = cursor.getInt(cursor.getColumnIndex("gender"));
                student.number = cursor.getString(cursor.getColumnIndex("number"));
                student.score = cursor.getInt(cursor.getColumnIndex("score"));
                Log.d(TAG, "student = " + student.toString());
            }
        }
        cursor.close();

    }
	//插入数据
    public void insertValue(View view) {
        ContentValues contentValues = new ContentValues();
        contentValues.put("id", 0);
        contentValues.put("name", "peter");
        contentValues.put("gender", 0);
        contentValues.put("number", "201804081705");
        contentValues.put("score", "100");
        getContentResolver().insert(STUDENT_URI, contentValues);
    }

    //student数据封装类
    public class Student {
        private final static String TAG = "Student";

        public Integer id;
        public String name;
        public Integer gender;
        public String number;
        public Integer score;

        @Override
        public String toString() {
            return "Student{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", gender=" + gender +
                    ", number='" + number + '\'' +
                    ", score=" + score +
                    '}';
        }
    }
}

5.ContentObserver监听ContentProvider变化

      observerHandler = new ObserverHandler();
      contentObserver = new ContentObserver(observerHandler){
            @Override
            public void onChange(boolean selfChange, Uri uri) {
                super.onChange(selfChange, uri);

            }
        };
        getContentResolver().registerContentObserver(STUDENT_URI,true, contentObserver);//注册监听
    private class ObserverHandler extends Handler{
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);

        }
    }
 @Override
    protected void onDestroy() {
        super.onDestroy();
        getContentResolver().unregisterContentObserver(contentObserver);//注销监听
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值