Android开发总结笔记 四大组件之ContentPovider(上) 1-2-8

来源:互联网 发布:js获取整个form的值 编辑:程序博客网 时间:2024/05/01 00:34

ContentProvider(内容提供者)负责管理结构化数据集的入口。ContentProvider将数据压缩并定义一个数据安全机制。ContentProvider提供一个标准化接口让进程间进行数据交互。
简单来说,如果我们想让别的程序访问自己程序的数据,就可以用到。比如说,我们要提供一些搜索建议,或者复制粘贴一些复杂的数据、文件到其他的程序。当然,Android系统本身也有提供一些ContentProvider让其他程序访问,比如说通讯录、音频、视频、图片等等。

因为ContentProvider进行的是一些I/O操作,所以要在子线程完成

1、ContentProvider的使用
使用ContentProvider前,先来了解一下下面两个概念

ContentProvider的执行原理(让其他应用可以访问自己应用的数据)
实现ContentProvider类,注册一个URI。然后其他应用只要使用ContentResolver根据URI就可以访问到我们应用所提供的数据,而这种数据不一定是数据库,也可以是文件,xml等。

URI
URI的专业名词叫做Uniform Resource Identifier(统一资源标识符),大概的意思就是标识资源所在的位置。

我们就来分析一下这个URI的构成
content://com.example.provider.myprovider/words/4
content:这是一个协议头,跟http、ftp这些一样。
com.example.provider.myprovider:接着就是provider的全限定类名
words:资源部分,如果后面没有内容,就表示访问全部记录。
4:访问id为4的记录

不过也不一定是上面所说的那样,比如说xml文件,后面的就是对应xml的节点



下面我们直接就用代码来演示一下ContentProvider的基本用法,增删改查


public class MainActivity extends AppCompatActivity {
 
 
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
 
private void init() {
Uri uri = Uri.parse("content://sms/");
ContentResolver contentResolver = getContentResolver();
String[] column={"address", "date", "type", "body"};
Cursor cursor = contentResolver.query(uri,column, null, null, null);
while (cursor.moveToNext()) {
String address = cursor.getString(0);
String date = cursor.getString(1);
String type = cursor.getString(2);
String body = cursor.getString(3);
Log.i("地址", address);
Log.i("日期", date);
Log.i("类型", type);
Log.i("主体", body);
}
cursor.close();
}
 
 
}
上面一段代码用于访问收信箱的一些内容,由于要访问到短信,所以就要用到下面这个权限
<uses-permission android:name="android.permission.READ_SMS"/>
然后就有这样的结果




这里咱们来分析一下ContentResolver这个query方法

public final Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

  • uri  这个不用说了,就是一个路径
  • projection   需要查询的列对应的字符串数组
  • selection    SQL中的选择语句(where)
  • selectionArgs   此处匹配选择语句的带有"?"的地方
在这里要说一下,为了防止SQL注入。一般来说selection里面的数据不会让用户去输入,而是用"?"来进行通配


ContentUris这个类可以快速地给Uri添加上一个id
ContentUris.withAppendedId(uri,1);



咱们来插入一个联系人


方式①

ContentValues values = new ContentValues();
//首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactId
Uri rawContactUri = getContentResolver().insert(ContactsContract.RawContacts.CONTENT_URI, values);
long rawContactId = ContentUris.parseId(rawContactUri);
 
//往data表入姓名数据
values.clear();
values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, "zhangsan");
getContentResolver().insert(
android.provider.ContactsContract.Data.CONTENT_URI, values);
 
//往data表入电话数据
values.clear();
values.put(android.provider.ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, "13800138000");
values.put(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
getContentResolver().insert(
android.provider.ContactsContract.Data.CONTENT_URI, values);
 
//往data表入Email数据
values.clear();
values.put(android.provider.ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
values.put(ContactsContract.CommonDataKinds.Email.DATA, "10000@qq.com");
values.put(ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.TYPE_WORK);
getContentResolver().insert(
android.provider.ContactsContract.Data.CONTENT_URI, values);

方式②

//使用事务添加联系人
Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
Uri dataUri = Uri.parse("content://com.android.contacts/data");
 
ContentResolver resolver = getContentResolver();
ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
ContentProviderOperation op1 = ContentProviderOperation.newInsert(uri)
.withValue("account_name", null)
.build();
operations.add(op1);
 
//依次是姓名,号码,邮编
ContentProviderOperation op2 = ContentProviderOperation.newInsert(dataUri)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, "张三")
.build();
operations.add(op2);
 
ContentProviderOperation op3 = ContentProviderOperation.newInsert(dataUri)
.withValueBackReference(android.provider.ContactsContract.Contacts.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, "13800138000")
.withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE)
.build();
operations.add(op3);
 
ContentProviderOperation op4 = ContentProviderOperation.newInsert(dataUri)
.withValueBackReference(android.provider.ContactsContract.Contacts.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Email.DATA, "10000@qq.com")
.withValue(ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.TYPE_WORK)
.build();
operations.add(op4);
//将上述内容添加到手机联系人中~
resolver.applyBatch("com.android.contacts", operations);
Toast.makeText(getApplicationContext(), "添加成功", Toast.LENGTH_SHORT).show();




public abstract int update (Uri uri, ContentValues values, String selection, String[] selectionArgs)


public abstract int delete (Uri uri, String selection, String[] selectionArgs)


改和删就不详细介绍了,参数跟上面的是一样的




关于联系人的一些URI:

管理联系人的Uri:

ContactsContract.Contacts.CONTENT_URI 

管理联系人的电话的Uri:

ContactsContract.CommonDataKinds.Phone.CONTENT_URI 

管理联系人的Email的Uri:

ContactsContract.CommonDataKinds.Email.CONTENT_URI 

(注:Contacts有两个表,分别是rawContact和Data,

rawContact记录了用户的id和name,

其中id栏名称为:ContactsContract.Contacts._ID, name名称栏为ContactContract.Contracts.DISPLAY_NAME,

电话信息表的外键id为ContactsContract.CommonDataKinds.Phone.CONTACT_ID,

电话号码栏名称为:ContactsContract.CommonDataKinds.Phone.NUMBER.

data表中Email地址栏名称为:
ContactsContract.CommonDataKinds.Email.DATA
其外键栏为:ContactsContract.CommonDataKinds.Email.CONTACT_ID)

关于多媒体的一些URI:

存储在sd卡上的音频文件:

MediaStore.Audio.Media.EXTERNAL_CONTENT_URI 

存储在手机内部存储器上的音频文件:

MediaStore.Audio.Media.INTERNAL_CONTENT_URI 
SD卡上的图片文件内容:

MediaStore.Audio.Images.EXTERNAL_CONTENT_URI

手机内部存储器上的图片:

MediaStore.Audio.Images.INTERNAL_CONTENT_URI

SD卡上的视频:

MediaStore.Audio.Video.EXTERNAL_CONTENT_URI 

手机内部存储器上的视频:

MediaStore.Audio.Video.INTERNAL_CONTENT_URI 

(注:图片的显示名栏:Media.DISPLAY_NAME,

     图片的详细描述栏为:Media.DESCRIPTION

     图片的保存位置:Media.DATA

短信URI:

Content://sms

发送箱中的短信URI:

Content://sms/outbox

收信箱中的短信URI:

Content://sms/sent

草稿中的短信URI:

Content://sms/draft

 

2、自定义ContentProvider
在自定义一个ContentProvider之前,首先要确定你有以下一个或多个的需求
  • 提供一些复杂的数据或者文件给其他的应用
  • 允许用户从你的应用上拷贝一些复杂的数据到其他应用
  • 你想要使用搜索框架提供自定义搜索建议

构造URI,有两个通配符
  • '#' 匹配任意长度的任意字符串
  • '*'  匹配任意长度的数值型字符串

下面就来直接上代码演示一下,用的是SQLite来进行存储数据

首先先创建一个SQLiteOpenHelper用来建表
public class DBHelper extends SQLiteOpenHelper {
 
private final String SQL="CREATE TABLE test(_id INTEGER PRIMARY KEY AUTOINCREMENT,name);";
 
public DBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
 
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL);
 
}
 
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
 
}
}

然后新建一个类继承ContentProvider,然后实现它的一些相关方法
public class CustomProvider extends ContentProvider {
private static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private DBHelper dbHelper;
 
static {
uriMatcher.addURI("com.example.sumup.provider.customprovider", "test", 1);
}
 
@Override
public boolean onCreate() {
dbHelper = new DBHelper(getContext(), "test.db", null, 1);
return true;
}
 
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return null;
}
 
@Override
public String getType(Uri uri) {
return null;
}
 
@Override
public Uri insert(Uri uri, ContentValues values) {
switch (uriMatcher.match(uri)) {
case 1:
SQLiteDatabase db = dbHelper.getReadableDatabase();
long rowId = db.insert("test", null, values);
if (rowId > 0) {
Uri nameUri = ContentUris.withAppendedId(uri, rowId);
getContext().getContentResolver().notifyChange(nameUri, null);
Toast.makeText(getContext(),"数据插入成功",Toast.LENGTH_SHORT).show();
return nameUri;
}
break;
}
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;
}
}
这里就只演示一下insert





ContentObserver,用于监听ContentProvider数据变化
contentResolver.registerContentObserver(uri,true,new CustomObserver(new Handler()));

public class CustomObserver extends ContentObserver{
 
 
public CustomObserver(Handler handler) {
super(handler);
}
 
@Override
public void onChange(boolean selfChange, Uri uri) {
//在这里就可以做一些相关的操作
}
}

在开头也提到过,ContentProvider应该在子线程操作,本文只是为了方便演示。
另外,在官方文档中有提到,ContentProvider的数据检索操作可以使用CursorLoader



0 0