目录:
一contentProvider的介绍
二自定义contentProvider的过程
三访问自定义contentProvidre的内容
四例子(获取系统短信和插入系统短信):
五例子(获取系统联系人和插入系统联系人)
六自定义内容观察者
---------------------------------------------------------------------------------------------------------------------------------
一contentProvider的介绍
<1>contentProvider的介绍
contentProvider即内容提供者,是android四大组件之一,主要的功能是把本应用的私有数据共享给其他应用访问(主要是数据库信息),也就是让其他应用通过内容提供者访问本应用中的私有数据。
<2>文件权限
android文件的权限表示(10个字符): -rwxrwxxxx
第一个字符表示文件的类型:-为文件,d为文件夹
第一组rwx:文件拥有者的(owner)权限。(第一个字母代表权限为可读;第二个字母代表权限为可写;第三个字母代表权限为可执行,其中的-代表权限为没有)
第二组rwx:同组用户(gruper)的权限(两个应用一般来说都是other用户,除非经过设置才能让两个应用成为同组用户)(微信和qq不是同组用户,是其他用户)
第三组rwx;其他用户(other)的权限
(3)在一个应用下一般来说只有一个数据库,在这个数据库下有很多的表,用来存储信息。当然也可以创建多个数据库。
<4>contentProvider不是context的子类。
二自定义contentProvider
<1>自定义contentProvider的步骤:
1>自定义一个类继承contentProvider:实现其其中的六个方法oncreate() getType(URI uri),insert(),updata(),delete(),query()
1.oncreate()--这个方法在app启动的时候就会调用,不要执行耗时操作,如果使用 SqliteOpenHelper的话,在这个方法中创建该对象,
但是不要调用getWriteable()或者getReadable()获取SqliteDatabase对象,获取SqliteDatabase对象的动作
应该在具体操作数据的方法(insert(),updata(),delete(),query())中调用
@return boolean 如果为true,即provider成功加载;否则为false。
2.getType(Uri uri)--返回的是给定的uri上的数据的mimeType类型,如果是数据库文件的话,
uri的类型代表的是表的一条数据的时候:例如:table/3的话:vnd.android.cursor.item/name
uri的类型代表的是多条数据(比说一个表或者多个表,或者一个表中的多条数据)的时候:vnd.android.cursor.dir/name
至于name的命名可以自己随便命名,但是一般是authority+path,比如说:com.example.provider.table1
3.insert(Uri uri,ContentValue values)--第一个参数为authroity+path;
第二个参数:插入数据的对象
return:uri,要插入数据的uri。
4.delete(Uri uri,String selection,String[] selectArgs):第一个参数:authority+path;
第二个参数:条件语句。
第三个参数:条件语句中的values
return: int 需要自己设置,一般来说为删除的行数,
5.update(Uri uri,ContentValues values,String selection,String[] selctionArgs)
第一个参数:authority+path
第二个参数:要替换的对象values
第三个参数:条件语句
第四个参数:条件语句中的values
return int 需要自己设置,一般来说为替换的总行数
6.query(Uri uri,String[] projection,String selection,String[] selectionArgs,String order):
第一个参数:authority+path
第二个参数:要查询的列命的数组
第三个参数:条件语句
第四个参数:条件语句的values
return:Cursor cursor对象
2>在清单文件中注册这个类。使用的是标签<provider>,注意其:
<provider
android:name=".contentProviders.MyContentProvider"
//主机号:也就是其他第三方应用访问的根url;
android:authorities="com.heman.haha"
//是否可以导出,一般为true。
android:exported="true" />
3>一些方法
UriMather
*用于判断一条uri跟指定的多条uri中的哪条匹配。
*匹配原则:
//指定多条uri
um.addURI("com.itheima.person", "person", PERSON_CODE);
um.addURI("com.itheima.person", "company", COMPANY_CODE);
//#号可以代表任意数字(我们可以在路径之后携带数据,#就是用来携带数字的,可以作为查询条件使用,即将这个
数字作为特定的id,然后进行删除id所在的行,或者插入id所在的行等等)
//*号代表任意文本(我们可以在路径之后携带数据,*就是用来携带数字的,同样一般也是用来作为条件使用的。)
um.addURI("com.itheima.person", "person/#", QUERY_ONE_PERSON_CODE);
此时匹配的还是person表。
*通过uri匹配器可以实现操作不同的表:
@Override
public Uri insert(Uri uri, ContentValues values) {
if(um.match(uri) == PERSON_CODE){
db.insert("person", null, values);
}
else if(um.match(uri) == COMPANY_CODE){
db.insert("company", null, values);
}
else{
throw new IllegalArgumentException();
}
return uri;
}
* 如果路径中带有数字,把数字提取出来的api
int id = (int) ContentUris.parseId(uri);
4>例子:
//这是继承contentProvider的类
public class MyContentProvider extends ContentProvider {
private SQLiteDatabase readableDatabase;
private UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private MySqLiteDatabaseOneOpenhelper sqLiteDatabaseOneOpenhelper;
{
/**
* 第一个参数就是自定义contentprovider在清单文件中authorities的配置
* 第二个参数path,是每张表的一个唯一的代表,其他应用在访问的时候,传入的url:即Uri.parse("content://authority/path")
* 第三个代表码,即uriMather.match(uri)的返回值
* */
uriMatcher.addURI("com.heman.haha", "person", 1);
uriMatcher.addURI("com.heman.haha", "teacher", 2);
uriMatcher.addURI("com.heman.haha", "teacher/#", 3);//#替代所有的数字;*替代所有其他符号
uriMatcher.addURI("com.heman.haha","*",4); //此时url:Uri.parse("content://com.heman.haha/*)代表的就是person和teacher两张表了
}
@Override
public boolean onCreate() {
Log.d("qindong","contentProvider的oncreate()方法被调用了");
//getContext()内容提供者运行在哪个上下文,就用那个上下文。
sqLiteDatabaseOneOpenhelper = new MySqLiteDatabaseOneOpenhelper(getContext(),3);
return true;
}
@Override
public String getType(@NonNull Uri uri) {
String type="no type";
switch (uriMatcher.match(uri)){
case 1:
type="vnd.android.cursor.item/com.heman.haha.person";
break;
case 4:
type="vnd.android.cursor.dir/com.heman.haha.table";
break;
}
return type;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
getReadableDatabase();
Cursor cursor=null;
if(uriMatcher.match(uri)==1 ){
cursor = readableDatabase.query("person", projection, selection, selectionArgs, null, null, sortOrder);
}else if(uriMatcher.match(uri)==2){
cursor = readableDatabase.query("teacher", projection, selection, selectionArgs, null, null, sortOrder);
}else if(uriMatcher.match(uri)==3){
//获取携带的数据即uri之后的id
int id= (int) ContentUris.parseId(uri);
cursor=readableDatabase.query("person",new String[]{"name","phone","money"},"_id=?",new String[]{id+""},null,null,null);
}
return cursor;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
getReadableDatabase();
if(uriMatcher.match(uri)==1){
readableDatabase.insert("person", null, values);
sendSelfNotifyChange(uri);
}else if(uriMatcher.match(uri)==2){
readableDatabase.insert("teacher", null, values);
sendSelfNotifyChange(uri);
}
return uri;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
getReadableDatabase();
int rows=-1;
if(uriMatcher.match(uri)==1){
rows = readableDatabase.delete("person", selection, selectionArgs);
sendSelfNotifyChange(uri);
}else if(uriMatcher.match(uri)==2){
rows = readableDatabase.delete("teacher", selection, selectionArgs);
sendSelfNotifyChange(uri);
}
return rows;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
getReadableDatabase();
int rows=-1;
if(uriMatcher.match(uri)==1){
rows= readableDatabase.update("person", values, selection, selectionArgs);
sendSelfNotifyChange(uri);
}else if(uriMatcher.match(uri)==2){
rows = readableDatabase.update("teacher", values, selection, selectionArgs);
sendSelfNotifyChange(uri);
}
return rows;
}
//这是当数据库发生改变的时候,发出的通知
public void sendSelfNotifyChange(Uri uri){
getContext().getContentResolver().notifyChange(uri,null);
}
public void getReadableDatabase(){
if(null==readableDatabase){
readableDatabase = sqLiteDatabaseOneOpenhelper.getReadableDatabase();
}
}
}
//这是该类在清单文件中的配置
<provider
android:name=".contentProviders.MyContentProvider"
android:authorities="com.heman.haha"
android:exported="true" />
三访问自定义contentProvidre的内容
访问自定义contentProvider的内容,此时一般是在别的应用中了,此时我们需要使用contentResolver类了,具体的使用如下:
//1.首先获取到contentResolver对象
contentResolver = getContentResolver();
//.调用insert方法
contentResolver.insert(Uri uri,ContentVlaues valus);
//调用delete方法
contentResolver.delete(Uri uri,String selection,String[] selectionArgs)
//调用query方法
contentResolver.query(Uri uri,String[] columnNames,String selection,String[] selectinArgs,String order);
//调用update方法
contentResolver.update(Uri uri,ContentValues,String selection,String[] selectionArgs);
2>注意:
注意在上述方法传入的uri,有一个标签的,即content://,这个比如:
private String mainip="content://com.heman.haha/teacher";
Uri.parse(mainipone)
3>例子:
private String mainip="content://com.heman.haha/teacher";
private String mainipone="content://com.heman.haha/teacher/4";
private String rootPath="content://com.heman.haha";
private void queryData() {
//1.获取contentResolver
ContentResolver contentResolver = getContentResolver();
Cursor cursor = contentResolver.query(Uri.parse(mainip), new String[]{"name", "phone", "money"},
"money>?", new String[]{"1000000"}, null);
while (cursor.moveToNext()){
String name = cursor.getString(cursor.getColumnIndex("name"));
String phone=cursor.getString(cursor.getColumnIndex("phone"));
int money = cursor.getInt(cursor.getColumnIndex("money"));
Log.d("qindong",name+"++++++++++++"+phone+"++++++++++++++++++++++"+money);
}
}
private void updateData() {
//1.获取contentResolver
ContentResolver contentResolver = getContentResolver();
ContentValues contentValue=new ContentValues();
contentValue.put("name","lisihahahhah");
contentResolver.update(Uri.parse(mainip),contentValue,"money>?",new String[]{"10000003"});
}
private void deleteData() {
//1.获取contentResolver
ContentResolver contentResolver = getContentResolver();
contentResolver.delete(Uri.parse(mainip),"money>?",new String[]{"10000005"});
}
private void insertData() {
//1.获取contentResolver
ContentResolver contentResolver = getContentResolver();
//2.调用insert方法
ContentValues contentValue=new ContentValues();
for(int index=0;index<10;index++){
//首先清空contentValue内容数据
contentValue.clear();
contentValue.put("name","张三"+index);
contentValue.put("phone","1666666666"+index);
contentValue.put("money","1000000"+index);
contentResolver.insert(Uri.parse(mainip),contentValue);
}
}
四例子(获取系统短信和插入系统短信):
1>简介:
* 只需要关注sms表
* 只需要关注4个字段
* body:短信内容
* address:短信的发件人或收件人号码(跟你聊天那哥们的号码)
* date:短信时间
* type:1为收到,2为发送
###读取系统短信,首先查询源码获得短信数据库内容提供者的主机名和路径(即sms),然后访问内容提供者(掌握)
##注意系统的短信应用就是一个app应用,此时在我们的app去访问系统的短信,就是两个应用之间的事情,而且系统短信应用提供了contentProvider。
2>获取系统短信的代码
public void onquerySms(View view) {
ContentResolver contentResolver = getContentResolver();
Cursor cursor = contentResolver.query(Uri.parse("content://sms"), new String[]{"body", "address", "type", "date"}, null, null, null);
while (cursor.moveToNext()) {
String body = cursor.getString(cursor.getColumnIndex("body"));
String address = cursor.getString(cursor.getColumnIndex("address"));
String type = cursor.getString(cursor.getColumnIndex("type"));
String date = cursor.getString(cursor.getColumnIndex("date"));
Log.d("qindong", "这是短信:" + body + "=============" + address + "============" + type + "===========" + date + "==================");
}
cursor.close();
}
注意:要添加权限:
<uses-permission android:name="android.permission.READ_SMS"/>
3>插入系统短信的代码
android 4.4以上,系统不允许非短信默认app去插入短信到系统短信库中,所以以下代码在android 4.4以上的结果就是代码执行,但是没有任何效果;如果想要这段代码起作用,必须要将该app设置为默认短信app
public void insertSms(View view) {
ContentResolver contentResolver = getContentResolver();
ContentValues contentValue = new ContentValues();
contentValue.put("body", "我现在有事,一会给你打电话");
contentValue.put("address", "15036195507");
contentValue.put("type", "2");
contentValue.put("date", System.currentTimeMillis());
contentResolver.insert(Uri.parse("content://sms"), contentValue);
}
注意:要添加权限:
<uses-permission android:name="android.permission.WRITE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
五例子:(获取系统联系人和插入系统联系人)
1>简介:
* raw_contacts表:
* contact_id:联系人id
* data表:保存联系人的所有信息,一个信息占一行
* data1:联系人的每一条具体信息的内容
比如说:联系人名字:
列明有很多比如说这三列data1,mimetype_id,raw_contact_id
每列的信息分别是 秦东,2,1
这就是所谓的一个信息占一行。
* raw_contact_id:联系人id,描述该行信息属于哪个联系人
* mimetype_id:数据的mimetype类型,标识数据属于什么类型。
*mimetype,数据的mimety类型。
* mimetypes表:通过mimetype_id到该表查看具体类型
###系统联系人同样是一个应用,我们的app是另一个应用,此时我们的app就是在访问别的应用的私有数据,并且系统联系人应用提供了contentProvider。
1>获取系统联系人:
/**
* @funtion 读取系统的联系人资料
*/
public void querycontact(View view) {
ContentResolver contentResolver = getContentResolver();
//1.查询raw_contacts表中的contact_id值,,这个就是每个联系人的id值 其主机号为:com.android.contacts
Cursor cursor = contentResolver.query(Uri.parse("content://com.android.contacts/raw_contacts"), new String[]{"contact_id"}, null, null, null);
while (cursor.moveToNext()) {
String contact_id = cursor.getString(cursor.getColumnIndex("contact_id"));
//当用户将联系人删除的时候,此时系统会将这个联系人的contact_id设置为null,所以此时应该将这种状况排除。
if (null == contact_id) {
continue;
}
Log.d("qindong", "hahah:" + contact_id);
//2.查询data表中,根据contact_id,查询data1(名字和号码,email),mimetype(名字和号码,还有email)
Cursor cursorData = contentResolver.query(Uri.parse("content://com.android.contacts/data"), new String[]{"data1", "mimetype"},
"raw_contact_id = ?", new String[]{contact_id}, null);
Contact contact = new Contact();
while (cursorData.moveToNext()) {
String mimetype = cursorData.getString(cursorData.getColumnIndex("mimetype"));
String data1 = cursorData.getString(cursorData.getColumnIndex("data1"));
//通过mimetype类型判断是什么那个值
if ("vnd.android.cursor.item/name".equals(mimetype)) {
contact.setName(data1);
} else if ("vnd.android.cursor.item/email_v2".equals(mimetype)) {
contact.setEmail(data1);
} else if ("vnd.android.cursor.item/phone_v2".equals(mimetype)) {
contact.setPhone(data1);
}
}
}
}
注意:添加权限
<uses-permission android:name="android.permission.READ_CONTACTS"/>
1>插入系统联系人:
/**
* @funtion 往系统的联系人中插入数据
*/
public void insertcontact(View view) {
int contactId = 0;
//首先查询raw_contact_id表中的contact_id的最大值
ContentResolver contentResolver = getContentResolver();
//这次使用_id而不是用contact_id值,是因为当用户将一个联系人删除的时候,此时contact_id的为null,所以我们如果获取这个字段,那么有可能就会获取到null,但是使用
//_id字段则不会,_id字段是该表的主键,从1开始
Cursor query = contentResolver.query(Uri.parse(rawcontactsPath), new String[]{"_id"}, null, null, null);
//这是获取该游标中最后一个值,注意这里用的是if字段,不是while字段,query.moveToLast()为ture的时候,表示此时获取到的最后一个,如果时while,就会无限循环。
if (query.moveToLast()) {
contactId = query.getInt(0);
contactId++;
Log.d("qindong", contactId + "");
}
ContentValues contentValue = new ContentValues();
contentValue.put("contact_id", contactId);
contentResolver.insert(Uri.parse(rawcontactsPath), contentValue);
ContentValues contentValues = new ContentValues();
contentValues.put("data1", "张三");
contentValues.put("mimetype", "vnd.android.cursor.item/name");
contentValues.put("raw_contact_id", contactId);
contentResolver.insert(Uri.parse(dataPath), contentValues);
contentValues.clear();
contentValues.put("data1", "15035281908");
contentValues.put("mimetype", "vnd.android.cursor.item/phone_v2");
contentValues.put("raw_contact_id", contactId);
contentResolver.insert(Uri.parse(dataPath), contentValues);
}
注意:添加权限
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
六、自定义内容观察者:
当内容提供者操作的数据发生改变的时候,此时内容提供者会发出一个通知,而内容观察者就会收到这个通知,然后根据自己的业务需求做一些操作。但是想要内容提供者操作的数据发生改变,内容提供者发出通知,这个是要对内容提供者进行修改的。
1>内容提供者发出通知:
ContentResolver contentResolver = getContext().getContentResolver();
//第一个参数是uri,第二个参数是contentObserve对象(可指定对象,若为null,即不指定)
contentResolver.notifyChange(uri,null);
2>内容观察者接受通知:
ContentResolver contentResolver = getContentResolver();
//第二个参数 boolean 如果为true,那么以这个第一个参数中的uri为开头的所有uri,发生改变的时候,此时你都可以收到通知。
// 如果为false,仅仅是第一个参数中的uri,即使是以第一个uri开头的uri,你也收不到,只有和你的uri一模一样时, 你才能收到通知。 我们一般设置为true.
contentResolver.registerContentObserver(Uri.parse("content://com.heman.haha/teacher"),true,new ContentObserver(new Handler()) {
/**
* 这两个方法任何一个都行,如果有onChange(boolean selfChange, Uri uri)则方法public void onChange(boolean selfChange)
* 不会执行,如果只有public void onChange(boolean selfChange),则该方法执行。
* */
@Override
public void onChange(boolean selfChange) {
}
//第二个参数uri,即表示那个uri发生了变化,因为我们registerContentObserver()第二个参数设置为true,所以说具体的uri有可能是和我们的传入的第一个参数是不同的
//这个地方的uri是一个具体的uri。
@Override
public void onChange(boolean selfChange, Uri uri) {
}
});
3>完整的例子:
//这是应用A的contentProvider
public class MyContentProvider extends ContentProvider {
private SQLiteDatabase readableDatabase;
private UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private MySqLiteDatabaseOneOpenhelper sqLiteDatabaseOneOpenhelper;
{
/**
* 第一个参数就是自定义contentprovider在清单文件中authorities的配置
* 第二个参数path,是每张表的一个唯一的代表,其他应用在访问的时候,传入的url:即Uri.parse("content://authority/path")
* 第三个代表码,即uriMather.match(uri)的返回值
* */
uriMatcher.addURI("com.heman.haha", "person", 1);
uriMatcher.addURI("com.heman.haha", "teacher", 2);
uriMatcher.addURI("com.heman.haha", "teacher/#", 3);//#替代所有的数字;*替代所有其他符号
uriMatcher.addURI("com.heman.haha","*",4); //此时url:Uri.parse("content://com.heman.haha/*)代表的就是person和teacher两张表了。
}
@Override
public boolean onCreate() {
Log.d("qindong","contentProvider的oncreate()方法被调用了");
//getContext()内容提供者运行在哪个上下文,就用那个上下文。
sqLiteDatabaseOneOpenhelper = new MySqLiteDatabaseOneOpenhelper(getContext(),3);
return true;
}
@Override
public String getType(@NonNull Uri uri) {
String type="no type";
switch (uriMatcher.match(uri)){
case 1:
type="vnd.android.cursor.item/com.heman.haha.person";
break;
case 4:
type="vnd.android.cursor.dir/com.heman.haha.table";
break;
}
return type;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
getReadableDatabase();
Cursor cursor=null;
if(uriMatcher.match(uri)==1 ){
cursor = readableDatabase.query("person", projection, selection, selectionArgs, null, null, sortOrder);
}else if(uriMatcher.match(uri)==2){
cursor = readableDatabase.query("teacher", projection, selection, selectionArgs, null, null, sortOrder);
}else if(uriMatcher.match(uri)==3){
//获取携带的数据即uri之后的id
int id= (int) ContentUris.parseId(uri);
cursor=readableDatabase.query("person",new String[]{"name","phone","money"},"_id=?",new String[]{id+""},null,null,null);
}
return cursor;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
getReadableDatabase();
if(uriMatcher.match(uri)==1){
readableDatabase.insert("person", null, values);
sendSelfNotifyChange(uri);
}else if(uriMatcher.match(uri)==2){
readableDatabase.insert("teacher", null, values);
sendSelfNotifyChange(uri);
}
return uri;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
getReadableDatabase();
int rows=-1;
if(uriMatcher.match(uri)==1){
rows = readableDatabase.delete("person", selection, selectionArgs);
sendSelfNotifyChange(uri);
}else if(uriMatcher.match(uri)==2){
rows = readableDatabase.delete("teacher", selection, selectionArgs);
sendSelfNotifyChange(uri);
}
return rows;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
getReadableDatabase();
int rows=-1;
if(uriMatcher.match(uri)==1){
rows= readableDatabase.update("person", values, selection, selectionArgs);
sendSelfNotifyChange(uri);
}else if(uriMatcher.match(uri)==2){
rows = readableDatabase.update("teacher", values, selection, selectionArgs);
sendSelfNotifyChange(uri);
}
return rows;
}
//这是当数据库发生改变的时候,发出的通知
public void sendSelfNotifyChange(Uri uri){
getContext().getContentResolver().notifyChange(uri,null);
}
public void getReadableDatabase(){
if(null==readableDatabase){
readableDatabase = sqLiteDatabaseOneOpenhelper.getReadableDatabase();
}
}
}
//这是应用B中的注册的内容观察者
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initEvent();
getContentProviderNotify();
}
private void getContentProviderNotify() {
ContentResolver contentResolver = getContentResolver();
contentResolver.registerContentObserver(Uri.parse("content://com.heman.haha/teacher"),true,new ContentObserver(new Handler()) {
/**
* 这两个方法任何一个都行,如果有onChange(boolean selfChange, Uri uri)则方法public void onChange(boolean selfChange)
* 不会执行,如果只有public void onChange(boolean selfChange),则该方法执行。
* */
@Override
public void onChange(boolean selfChange) {
}
//返回的uri就是我们在使用contentResolver操作数据库时,传入的uri,可用于判断是哪个表发生了变化。
@Override
public void onChange(boolean selfChange, Uri uri) {
}
});
}