ContentProvider:内容提供者,它是android的四大组件之一。
android中数据库中的数据不能跨进程访问,但是有时候会有这种需求,例如微信访问联系人列表。这是怎么实现呢?用到的就是ContentProvider。
ContentProvider的创建
1、写一个类A继承ContentProvider
2、然后再清单文件AndroidManifest.xml中进行声明一下,其中anthorities是主机名称,可以随便起名,最好是包名,它是ContentProvider的唯一标识,通过这个属性外部应用就可以访问我们的定义的内容提供者。
<provider
android:authorities="cn.demo.zx_provider_learn.dao"
android:name=".dao.PersonProvider"
android:exported="true"/>
3、继承ContentProvider的类中有四个操作数据库的方法,分别是怎删改查。如果数据库很多,那么怎样操作特定的数据库呢,那就需要通过传递的Uri来判断操作哪个数据库,所以想要了解这些东西之前需要明白URI、URL和URN。
Uri-Uniform Resourse Identifier-统一资源标识符,用来标识资源,例如,班级里有几十个同学,老师需要知道他们谁是谁。识别的方法有两种,URL和URN。
-Url-Uniform Resourse Location-统一资源位置,资源所在的位置来标识资源,例如:班级里第一排第一个位置的是张三。
-Urn-Uniform Resourse Name - 统一资源名称 ,资源的名称来标识资源,例如:班级里换了座位了,第一排第一个位置的不是张三了,但是老师还是能够识别出张三是哪个一个,原因就是识别他的方法不是靠位置,而是靠他的名称。
继承ContentProvider的类中需要用到UriMatcher来匹配传递过来的Uri。例如:
private static final int PERSON = 1;
/**
* UriMatcher是Uri匹配器,如果传递过来的Uri不符合就返回-1
* 如果传递过来的Uri是content://cn.demo.zx_provider_learn.dao/chaperson,那么返回
*/
public static UriMatcher matcher = new UriMatcher(-1);
/**
* 添加Uri,如果传递的Uri在UriMatcher中存在,那么就说明可以有操作的方法,否则就没有
* authority: 主机名,与清单文件AndroidManifest.xml中注册的一致
* path:数据库的路径,可以随便起名,
* code:如果匹配后返回的结果
*/
static {
matcher.addURI("cn.demo.zx_provider_learn.dao","chaperson",PERSON);
}
举例:
PersonProvider:
public class PersonProvider extends ContentProvider {
private static final int PERSON = 1;
/**
* 路径匹配,因为应用中可能有很多数据库,那么在ContentProvider中可能有操作不同数据库的方法
* 那么你怎样调用操作特定数据库的方法呢?
* 这里就需要使用UriMatcher方法
*/
public static UriMatcher matcher = new UriMatcher(-1);
/**
* 添加需要判断的路径
* authority: 主机名,与清单文件AndroidManifest.xml中注册的一致
* path:数据库的路径,可以随便起名,
* code:如果匹配后返回的结果
*/
static {
matcher.addURI("cn.demo.zx_provider_learn.dao","chaperson",PERSON);
}
MySQLiteOpenHelper mHelper = null;
@Override
public boolean onCreate() {
mHelper = new MySQLiteOpenHelper(getContext());
return false;
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
if(matcher.match(uri)==PERSON){
SQLiteDatabase db = mHelper.getReadableDatabase();
return db.query("person",projection,selection,selectionArgs,null,null,sortOrder);
}else{
throw new IllegalArgumentException("这个Uri不处理。。。。。。。。。。。");
}
}
@Nullable
@Override
public String getType(Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
if(matcher.match(uri)==PERSON) {
SQLiteDatabase db = mHelper.getWritableDatabase();
long insert = db.insert("person", null, values);
return Uri.parse("content://cn.demo.zx_provider_learn.dao/chaperson/" + insert);
}else{
throw new IllegalArgumentException("这个Uri不处理。。。。。。。。。。。");
}
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
if(matcher.match(uri)==PERSON) {
SQLiteDatabase db = mHelper.getWritableDatabase();
int delete = db.delete("person", selection, selectionArgs);
return delete;
}else{
throw new IllegalArgumentException("这个Uri不处理。。。。。。。。。。。");
}
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
if(matcher.match(uri)==PERSON) {
SQLiteDatabase db = mHelper.getWritableDatabase();
int update = db.update("person", values, selection, selectionArgs);
return update;
}else{
throw new IllegalArgumentException("这个Uri不处理。。。。。。。。。。。");
}
}
}
AndroidMenifest:
<provider
android:authorities="cn.demo.zx_provider_learn.dao"
android:name=".dao.PersonProvider"
android:exported="true"/>
调用者:
mUri = Uri.parse("content://cn.demo.zx_provider_learn.dao/chaperson");
/**
* 增加
* @param view
*/
public void add(View view){
ContentValues values = new ContentValues();
values.put("name","这是调用者");
values.put("age",28);
getContentResolver().insert(mUri,values);
}
/**
* 删除
* @param view
*/
public void delete(View view){
getContentResolver().delete(mUri,"id=?",new String[]{1+""});
}
/**
* 修改
* @param view
*/
public void update(View view){
ContentValues values = new ContentValues();
values.put("name","小王八蛋");
values.put("age",500);
getContentResolver().update(mUri,values,"name=?",new String[]{"王八蛋"});
}
/**
* 查
* @param view
*/
public void find(View view){
Cursor cursor = getContentResolver().query(mUri, null, null, null, null);
while (cursor.moveToNext()){
Person p = new Person();
p.setId(cursor.getInt(cursor.getColumnIndex("id")));
p.setName(cursor.getString(cursor.getColumnIndex("name")));
p.setAge(cursor.getInt(cursor.getColumnIndex("age")));
Toast.makeText(this, p.toString(), Toast.LENGTH_SHORT).show();
}
}
备份短信
手机中的短信应用的数据结构(com.android.providers.telephony/databases/mmsms.db)
sms中几个重要的参数:
thread_id 会话编号
address 对方电话号码
date 时间
read 读取状态 0未读 1已读
type 类型 1接收 2发送
body 短信内容
步骤:
1、打开系统上层所有应用的源代码中的packages\providers\TelephonyProvider,找到并且打开清单文件AndroidMenifest.xml,找到操作短信的内容提供者
<provider android:name="SmsProvider"
android:authorities="sms"
android:multiprocess="true"
android:readPermission="android.permission.READ_SMS"
android:writePermission="android.permission.WRITE_SMS" />
2、打开SmsProvider.java文件,找到UriMatcher,很明显Uri是content://sms就是操作短信的标识符。
private static final UriMatcher sURLMatcher =
new UriMatcher(UriMatcher.NO_MATCH);
static {
sURLMatcher.addURI("sms", null, SMS_ALL);
sURLMatcher.addURI("sms", "#", SMS_ALL_ID);
sURLMatcher.addURI("sms", "inbox", SMS_INBOX);
sURLMatcher.addURI("sms", "inbox/#", SMS_INBOX_ID);
sURLMatcher.addURI("sms", "sent", SMS_SENT);
sURLMatcher.addURI("sms", "sent/#", SMS_SENT_ID);
sURLMatcher.addURI("sms", "draft", SMS_DRAFT);
3、获取短信:
/**
* 短信备份
* @param view
*/
public void backupSms(View view){
new Thread(){
@Override
public void run() {
super.run();
List<SmsBean>list = new ArrayList<>();
Cursor cursor = getContentResolver().query(Uri.parse("content://sms"), new String[]{"address", "date", "read", "body", "type"}, null, null, null);
while (cursor.moveToNext()){
SmsBean smsBean = new SmsBean();
smsBean.setAddress(cursor.getString(cursor.getColumnIndex("address")));
smsBean.setDate(cursor.getString(cursor.getColumnIndex("date")));
smsBean.setRead(cursor.getString(cursor.getColumnIndex("read")));
smsBean.setBody(cursor.getString(cursor.getColumnIndex("body")));
smsBean.setType(cursor.getString(cursor.getColumnIndex("type")));
Toast.makeText(this, smsBean.toString(), Toast.LENGTH_SHORT).show();
System.out.println("短信::"+smsBean.toString());
list.add(smsBean);
}
}
}.start();
}
利用内容提供者操作联系人信息
应用中数据表结构:com.android.providers.contacts/databases/contacts2.db
数据库中几个重要的表
raw_contacts:
保存联系人编号
contact_id:联系人的id
data:
通讯录信息表,保存联系人的具体信息
raw_contact_id( 外键,指向raw_contacts中的contact_id ),当前信息所属的联系人的id
data1,所存储的信息
mimetype_id(外键,指向mimetypes中的_id),代表当前记录表示的信息类型
mimetypes:
信息类型表,存储了联系人信息的类型
_id 编号
mimetype 该编号表示的联系人信息类型
步骤
1、打开系统上层所有应用的源代码中的packages\providers\ContactsProvider,找到并且打开清单文件AndroidMenifest.xml,找到操作联系人的内容提供者
<provider android:name="ContactsProvider2"
android:authorities="contacts;com.android.contacts"
android:label="@string/provider_label"
android:multiprocess="false"
android:readPermission="android.permission.READ_CONTACTS"
android:writePermission="android.permission.WRITE_CONTACTS">
2、打开ContactsProvider2.java文件,找到UriMatcher,通过UriMatcher找到查询raw_contacts表和data表的路径。
matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
所以需要传递的Uri是:
content://com.android.contacts/raw_contacts
content://com.android.contacts/data
查询联系人
/**
* 查询联系人
* @param view
*/
public void findContacts(View view){
//获取联系人个数
Cursor cursor = getContentResolver().query(uri_raw_contact, null, null, null, null);
while (cursor.moveToNext()){
String contact_id = cursor.getString(cursor.getColumnIndex("contact_id"));
Cursor c = getContentResolver().query(uri_data, null, "raw_contact_id=?", new String[]{contact_id}, null);
ContactInfo info = new ContactInfo();
while (c.moveToNext()){
String mimetypes = c.getString(c.getColumnIndex("mimetype"));
if("vnd.android.cursor.item/name".equals(mimetypes)){
info.setName(c.getString(c.getColumnIndex("data1")));
}else if("vnd.android.cursor.item/phone_v2".equals(mimetypes)){
info.setPhone(c.getString(c.getColumnIndex("data1")));
}else if("vnd.android.cursor.item/email_v2".equals(mimetypes)){
info.setEmail(c.getString(c.getColumnIndex("data1")));
}else if("vnd.android.cursor.item/organization".equals(mimetypes)){
info.setCompany(c.getString(c.getColumnIndex("data1")));
}else if("vnd.android.cursor.item/postal-address_v2".equals(mimetypes)){
info.setAddress(c.getString(c.getColumnIndex("data1")));
}
}
Toast.makeText(this, info.toString(), Toast.LENGTH_SHORT).show();
}
}
添加联系人
/**
* 添加联系人
* @param view
*/
public void addContacts(View view){
String name = et_name.getText().toString();
String phone = et_phone.getText().toString();
String email = et_email.getText().toString();
String address = et_address.getText().toString();
String company = et_company.getText().toString();
//向raw_contact表中添加联系人个数
ContentValues values = new ContentValues();
Uri uri = getContentResolver().insert(uri_raw_contact, values);
long id = ContentUris.parseId(uri);
Cursor cursor = getContentResolver().query(uri_raw_contact, new String[]{"contact_id"}, "_id=?", new String[]{id + ""}, null);
String contact_id = null;
while(cursor.moveToNext()){
contact_id = cursor.getString(cursor.getColumnIndex("contact_id"));
}
//添加联系人名称
values.clear();
values.put("mimetype","vnd.android.cursor.item/name");
values.put("raw_contact_id",contact_id);
values.put("data1",name);
getContentResolver().insert(uri_data,values);
//添加联系人手机号
values.clear();
values.put("mimetype","vnd.android.cursor.item/phone_v2");
values.put("raw_contact_id",contact_id);
values.put("data1",phone);
getContentResolver().insert(uri_data,values);
//添加联系人邮箱
values.clear();
values.put("mimetype","vnd.android.cursor.item/email_v2");
values.put("raw_contact_id",contact_id);
values.put("data1",email);
getContentResolver().insert(uri_data,values);
//添加联系人地址
values.clear();
values.put("mimetype","vnd.android.cursor.item/postal-address_v2");
values.put("raw_contact_id",contact_id);
values.put("data1",address);
getContentResolver().insert(uri_data,values);
//添加联系人公司
values.clear();
values.put("mimetype","vnd.android.cursor.item/organization");
values.put("raw_contact_id",contact_id);
values.put("data1",company);
getContentResolver().insert(uri_data,values);
}
源码下载