内容提供者的作用
1.应用程序创建的数据库默认都是私有的,别的应用程序不可以访问里面的数据.
2.如果需要把自己应用程序私有的数据库暴露给别的用户增删改查,就需要使用内容提供者.
3.作用: 一个应用程序访问另外一个应用程序在硬盘上存储的数据
注:ContentProvider可以理解为:应用程序数据库的后门.
编写ContentProvider的流程
创建一个自定义内容提供者的Android项目:
1.写一个类继承ContentProvider, 重写的所有方法(主要是增删改查)默认都是空实现.
2.在清单文件下声明provider. 必须指定主机名!!!
<provider
android:name="内容提供者的包名"
android:authorities="自定义主机名"><provider/>
注:主机名也就是对外提供的uri,可以任意自定义,一般的规则是应用程序包名.
3.在内容提供者代码内部定义UriMatcher -用于判断uri是否匹配
//使用静态变量和代码块,便于自动加载.
static UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
mUriMatcher.addURI("在清单文件里面定义的authorities", "自定义匹配字符串", 成功返回的标识);
}
注:1.自定义匹配字符串规则: 一般使用被操作的数据库中的表名.
2.成功返回标识:SUCCESS, ERROR
4.在内容提供者执行增删改查方法时,判断uri是否合法
int code=matcher.match(uri);
if(code==SUCCESS){
//数据库增删改查的逻辑
}else{
throw new IllegalArgumentException("口令不正确,")
}
5.实例化数据
MyDBHelper helper = new MyDBHelper(this);
helper.getWritableDatabase();
再创建另一个Android项目访问内容提供者:
在MainActivity里写对数据库增删改查的逻辑方法
1.创建内容提供者解析器
ContentResolver resolver=上下文.getContentResolver();
2.定义要访问的Uri路径
Uri uri = Uri.parse("content://自定义主机名/自定义匹配字符串")
//注意:content://是标准写法
3.通过内容解析器执行操作:增,删,改,查
如添加:resolver.insert(uri, values);
小结:
1.写一个类继承ContentProvider,实现增删改查的方法
2.在清单文件中配置内容提供者,指定android:authorities="com.yashiro.database"
3.在内容提供者代码的内部,声明uriMatcher
4.通过uriMatcher检查uri的路径是否正确
5.在另外一个应用程序里面,通过contentResolver 增删改查
案例
<案例一: 用内容提供者操作系统短信>
思路: 直接访问系统短信应用的内容提供者,实现对短信应用数据库的增加和删除操作.
步骤:
1.通过查找系统源码,可以确定短信息内容提供者的Uri 应该为:content://sms
2.查看Android 模拟器下的/data/data/com.android.providers.telephony/databases/目录,查看其mmssms.db文件,确定sms表存储的数据格式:
*其中,address 存储的是联系人号码,date 是发送日期,type 对应短信的类型(发送是1/接收是2),body是短信的主体内容。
3.创建一个Android项目,在MainActivity里写删除和添加短信的业务逻辑
1).配置清单文件,添加短信的读写权限
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.WRITE_SMS"/>
2).实现添加和删除业务逻辑
/**
* 利用内容提供者,添加短信
*
* @param view
*/
public void add(View view) {
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://sms");
ContentValues values = new ContentValues();
values.put("address", "110");
values.put("type", 1);
values.put("date", System.currentTimeMillis());
values.put("body", "小子诶....你小心了,我们盯上你le~~");
resolver.insert(uri, values);
Toast.makeText(this, "添加成功", 0).show();
}
/**
* 利用内容提供者,删除短信
*
* @param view
*/
public void delete(View view) {
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://sms");
resolver.delete(uri, "address=?", new String[] { "110" });
Toast.makeText(this, "删除成功", 0).show();
}
<案例二: 用内容提供者操作系统联系人>
开发现状:
现在很多App 都可以对系统联系人进行操作,比如微信就可以直接将号码添加到系统联系人中,比如QQ 可以关联/备份/恢复系统联系人。
完成这些操作,需要能够访问并修改系统联系人的数据库.
思路: 直接访问系统联系人应用的内容提供者,实现对联系人数据的增加和删除操作.
步骤:
1.打开Android 源码,查看packages\providers\路径下的文件,其中ContactsProvider 就是联系人的内容提供者.找到清单文件,发现内容提供者的类名为ContactsProvider2.java.
2.根据原码确定Uri信息如下:
操作raw_contacts 表的Uri:
content://com.android.contacts/raw_contacts
操作data 表的Uri:
content://com.android.contacts/data
3.注意事项:
*由于contacts2.db 数据库使用了视图,所以操作数据库表时,看到的表字段名称和真实操作的有所不同。
*比如:data 表在查询的时候没有mimetype_id 字段,取代的是mimetype 字段。
*使用指南:
1.查询raw_contact表 获取联系人的contact_id.
2.根据contact_id查询data表,获取联系人的数据
3.根据mimetype确定数据的类型
4.查询手机联系人
步骤:
1).创建一个项目,配置清单文件,添加读取联系人的权限
android:readPermission="android.permission.READ_CONTACTS"
2).创建一个联系人bean,有信息name,phone,Email,QQ.
3).创建一个联系人工具类,获取所有的联系人信息,放到一个List集合中返回.
public class ContactInfoUtils {
/**
* 获取所有的联系人信息
*
* @param context
* 上下文
* @return
*/
public static List<ContactInfo> getAllContactInfos(Context context) {
List<ContactInfo> infos = new ArrayList<ContactInfo>();
ContentResolver resolver = context.getContentResolver();
// 查询raw_contact表
Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
Uri datauri = Uri.parse("content://com.android.contacts/data");
Cursor cursor = resolver.query(uri, new String[] { "contact_id" },
null, null, null);
//根据游标判断是否有数据
while (cursor.moveToNext()) {
String id = cursor.getString(0);
System.out.println("Id:" + id);
if (id != null) {
ContactInfo info = new ContactInfo();
// 查询data表
Cursor datacursor = resolver.query(datauri, new String[] {
"data1", "mimetype" }, "raw_contact_id=?",
new String[] { id }, null);
//根据游标判断是否有数据
while (datacursor.moveToNext()) {
String data1 = datacursor.getString(0);
String mimetype = datacursor.getString(1);
if ("vnd.android.cursor.item/name".equals(mimetype)) {
info.setName(data1);
} else if ("vnd.android.cursor.item/im".equals(mimetype)) {
info.setQq(data1);
} else if ("vnd.android.cursor.item/email_v2"
.equals(mimetype)) {
info.setEmail(data1);
} else if ("vnd.android.cursor.item/phone_v2"
.equals(mimetype)) {
info.setPhone(data1);
}
}
datacursor.close();
infos.add(info);
}
}
cursor.close();
return infos;
}
}
4).在MainActiviry里获取联系人信息的集合,并显示到界面上
infos = ContactInfoUtils.getAllContactInfos(this);
ListView lv = (ListView) findViewById(R.id.lv);
//设置适配器
lv.setAdapter(new BaseAdapter() {
@Override
public int getCount() {
return infos.size();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView tv = new TextView(MainActivity.this);
tv.setText(infos.get(position).toString());
return tv;
}
...
5.添加手机联系人
步骤:
1).新建一个Android项目,配置清单文件
<!-- 读取联系人 写入联系人 -->
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
2).设置布局文件,获取用户录入的姓名,电话,邮箱信息
<EditText
android:id="@+id/et_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="姓名" />
<EditText
android:id="@+id/et_phone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="电话" />
<EditText
android:id="@+id/et_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="邮箱" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="save"
android:text="保存数据" />'
3).在MainActivity里写实现添加的业务逻辑
public class MainActivity extends Activity {
private EditText et_name;
private EditText et_phone;
private EditText et_email;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//关心控件
et_name = (EditText) findViewById(R.id.et_name);
et_phone = (EditText) findViewById(R.id.et_phone);
et_email = (EditText) findViewById(R.id.et_email);
}
public void save(View view) {
//获取输入信息
String name = et_name.getText().toString().trim();
String phone = et_phone.getText().toString().trim();
String email = et_email.getText().toString().trim();
//定义要访问的Uri路径
Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
Uri datauri = Uri.parse("content://com.android.contacts/data");
//获取游标
Cursor cursor = getContentResolver().query(uri, new String[] { "_id" },
null, null, "_id desc");
//游标初始化到开始位置
cursor.moveToFirst();
int _id = cursor.getInt(0);
int newId = _id + 1;
//文本数据
ContentValues values = new ContentValues();
values.put("contact_id", newId);
//根据uri添加数据
getContentResolver().insert(uri, values);
// 往data表里面添加数据.
// 1.添加name
ContentValues nameValue = new ContentValues();
nameValue.put("raw_contact_id", newId);
nameValue.put("data1", name);
nameValue.put("mimetype", "vnd.android.cursor.item/name");
getContentResolver().insert(datauri, nameValue);
// 2.添加email
ContentValues emailValue = new ContentValues();
emailValue.put("raw_contact_id", newId);
emailValue.put("data1", email);
emailValue.put("mimetype", "vnd.android.cursor.item/email_v2");
getContentResolver().insert(datauri, emailValue);
// 3.添加phone
ContentValues phoneValue = new ContentValues();
phoneValue.put("raw_contact_id", newId);
phoneValue.put("data1", phone);
phoneValue.put("mimetype", "vnd.android.cursor.item/phone_v2");
getContentResolver().insert(datauri, phoneValue);
Toast.makeText(this, "添加数据成功", 0).show();
}
}
内容观察者 ContentObserver
•观察数据库内容是否发生改变,如果改变,通知观察者
使用步骤:
1.在内容提供者ContentProvider的增删改查方法中,增加通知的方法:
getContext().getContentResolver().notifyChange(uri, null);
2.在观察者类注册观察者,监控内容提供者的方法
//定义观察的uri
Uri uri = Uri.parse("content://被观察的主机名/数据库表名");
//注册观察者
getContentResolver().registerContentObserver(uri, true, new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
System.out.println("我是观察者,我发现银行的数据库变化了.");
super.onChange(selfChange);
}
});
小结:
内容观察者一般配合内容提供者一起使用.让内容提供者即向另外的一个程序暴露了私有的数据库,又将数据库的变化情况告诉给了观察者.