前言:
上一篇介绍广播接收者(BroadcastReceover),本篇继续,记叙下Android四大组件最后一个不常用,但是非常有用的ContentProvider。Google文档
本文主要从以下几个方面介绍:
- 特点
- 优缺点
- 数据访问
- 创建内容提供器
- 工作机制
特点
- Android四大组件之一,需要进行注册,一般有name,authorities,export等属性
- 是一种定义数据共享的接口,并是不android数据存储的方式之一
- 跨进程数据访问
- android系统很多系统应用都是使用ContentProvider方式进行储存的(图片,通讯录,视频,音频等)
- 数据更新监听方便
优缺点
优点:
- 为数据访问提供统一的接口,解决了不同储存方式需要不同的API的访问所带来的繁琐
- 跨进程数据的访问,实现了不同App数据访问提供了很大的便利
缺点:
- 不能单独使用,必须需要和其他的储存方式结合使用
数据访问
下面示例如何获取通讯录信息:
Activity:
public class ContentProviderActivity extends AppCompatActivity {
private ContentResolver mContentResolver;//
StringBuilder builder=new StringBuilder("");
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_content_provider);
mContentResolver=getContentResolver();
tv= (TextView) findViewById(R.id.tv_show_contract);
findViewById(R.id.tv_press_contract).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//动态申请权限
if(ContextCompat.checkSelfPermission(ContentProviderActivity.this, android.Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(ContentProviderActivity.this,new String[]{Manifest.permission.READ_CONTACTS},1);
}else{
readContacts();
}
}
});
}
private void readContacts(){
Cursor cursor=null;
try {
cursor=mContentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null);
if(cursor!=null){
while(cursor.moveToNext()){
String displayName=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String number=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
builder.append(displayName);
builder.append(": ");
builder.append(number);
builder.append(" ");
Log.i("wy",displayName+" "+number);
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(cursor!=null){
cursor.close();
}
tv.setText(builder);
}
}
}
以上:
- 借助ContentResolver类访问内容提供器中共享的数据,其提供了一系列的增删改查(CRUD)操作,其中insert方法用于添加数据,update更新数据,delete删除数据,query用于查询数据。和SQLiteDatabase类似,只是参数上稍微有些区别,ContentResolver中接收的参数是不接收表名参数的,而是使用一个Uri参数代码(关于Uri内容欢迎参考一篇就够了系列之Activity全解析中关于Scheme的介绍)。
- 由于联系人信息是危险权限,所以需要动态申请,同时manifest中也需要申明
- 代码中的查询等方法可以看到是个常量,其实也源码的一个封装。
创建内容提供器
public class MyContentProvider extends ContentProvider {
public static final int DIR1=0;
public static final int ITEM1=1;
public static final int DIR2=2;
public static final int ITEM2=3;
public static final String AUTHRITY="com.wenyi.interview.provider";
private static UriMatcher uriMatcher;
private MyDatabaseHelper dbhelper;
static {
uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHRITY,"book",DIR1);
uriMatcher.addURI(AUTHRITY,"book/#",ITEM1);
uriMatcher.addURI(AUTHRITY,"category",DIR2);
uriMatcher.addURI(AUTHRITY,"category/#",ITEM2);
}
/**
* 初始化内容提供器的时候调用,通常会在这里完成对数据库的创建和升级等操作
* @return true表示初始化成功,false表示初始化失败
* 只有当contentResolver尝试访问我们的程序中的数据是,内容提供器才会被初始化
*/
@Override
public boolean onCreate() {
dbhelper=new MyDatabaseHelper(getContext(),"MyStore.db",null,2);
return true;
}
/**
* 查询数据
* @param uri 确定查询哪张表
* @param projection 确定查询哪些列
* @param selection 约束查询哪些行
* @param selectionArgs 约束查询哪些行
* @param sortOrder 用于对结果进行排序,查询的结果存放在Cursor对象中返回
* @return
*/
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
SQLiteDatabase db=dbhelper.getReadableDatabase();
Cursor cursor=null;
switch (uriMatcher.match(uri)){
case DIR1:
cursor=db.query("Book",projection,selection,selectionArgs,null,null,sortOrder);
break;
case ITEM1:
String bookid=uri.getPathSegments().get(1);
cursor=db.query("Book",projection,"id=?",new String[]{bookid},null,null,sortOrder);
break;
case DIR2:
cursor=db.query("Category",projection,selection,selectionArgs,null,null,sortOrder);
break;
case ITEM2:
String categoryid=uri.getPathSegments().get(1);
cursor=db.query("Category",projection,"id=?",new String[]{categoryid},null,null,sortOrder);
break;
default:
break;
}
return cursor;
}
/**
*
* @param uri
* @return 传入的内容URI来返回相应的MIME类型
*/
@Nullable
@Override
public String getType(@NonNull Uri uri) {
switch (uriMatcher.match(uri)){
case DIR1:
return "vnd.android.cursor.dir/vnd.com.wenyi.interview.provider.book";
case ITEM1:
return "vnd.android.cursor.item/vnd.com.wenyi.interview.provider.book";
case DIR2:
return "vnd.android.cursor.dir/vnd.com.wenyi.interview.provider.category";
case ITEM2:
return "vnd.android.cursor.item/vnd.com.wenyi.interview.provider.category";
default:
break;
}
return null;
}
/**
* 添加数据
* @param uri 确定要添加到的表
* @param values 待添加的数据
* @return 添加完成后,返回一个用于表示这条心记录的URI
*/
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
return null;
}
/**
* 删除数据
* @param uri 确定删除哪一张表中的数据
* @param selection 约束删除哪些行
* @param selectionArgs 约束删除哪些行
* @return 被删除的行
*/
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
/**
* 更新数据
* @param uri 更新的表
* @param values 新数据保存在其中
* @param selection 约束更新的行
* @param selectionArgs 约束更新的行
* @return 受影响的行作为返回值
*/
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
}
代码有点长,现在来细细分析:
- 自定义MyContentProvider继承自ContentProvider,重写相应的方法,方法都有解释
- 四个常量分别表示访问Book表中的说有数据,访问Book表中的单条数据,访问Category表中的所有数据和访问Category表中的单条数据
- 在manifest中进行注册,
<provider
android:exported="true"
android:enabled="true"
android:authorities="com.wenyi.interview.provider"
android:name=".FourComponet.contentProvider.MyContentProvider"/> - 代码中,UriMatche类,可以用来配皮Uri类型数据,其中,“*”和“#”分别表示匹配任意长度的任意字符和任意长度的数字。
- 创建MyDatabaseHelper对象,继承自SQLiteOpenHelper,方便数据可操作,因为内容提供着只是接口标准,需要和数据存储方式结合使用
- 只在query中写了相应代码,其他方法也需要写类似代码
- getType方法中,返回值很奇特。实际上,这是MIME字符格式,MIME主要由3部分组成,在Android中,格式为: a: 必须以vnd开头 b: 如果内容Uri以路径结尾,则后面接 android.cursor.dir/。如果内容Uri以id结尾,则后面接android.cursor.item/ c: 在最后面接 vnd.authority.path
- 这样,我们的内容提供器就创建完成了,通过这种机制,必须知道URI,才能顺利的访问该数据库中的内容,比保证了可访问,又保证了安全性。
工作机制
暂不分析
欢迎阅读其他系列文章: