【0050】Android基础-37-内容提供者(ContentProvider)--!!!需要手敲代码
【0】需要注意的特殊点:
【0.1】数据库的创建中,第一列的id必须是下划线开头的;

【0.2】 【注意】在 内容提供者端的查询之后结果集cursor不能够关闭,否则外部应用无法查询到数据;
同样,db也是可以不能关的;
【1】为什么需要内容提供者?从实例开始:
【答】要从应用中的数据库中取出数据,需要修改文件的权限;
如果不支持修改权限则无法使用操作数据库的方法进行取数据;
因此:需要使用内容提供者进行数据的读取和操作;
【1.0】新建类继承数据库的SQLiteOpenHelper类,报错;
【报错的原因】在父类中没有默认的无参的构造方法,需要自己实现;

【修改】

【版本参数的定义】至少从1开始;

【1.1】创建数据库:在onCreate方法中进行创建数据库的表;

【说明】在Android 中所有的数据库的类型都是通过String表示的,再写varchar只是为了更好的以后阅读代码;

【数据库生成的目录】/data/data/包名/目录下



【1.2】将张三李四数据取出来;
【第一种方法】

【第二种方法】我们选择第二种方法
【cursor.getCount】得到所有表的行数;



【1.3】通过命令行查询数据
【注意】查询的时候需要以分号“;”结尾;

【1.4】更改dos工具的编码项的方法
出现乱码更改dos编码的方法:





【2】新实例:程序B在点击查询按钮后去查询程序A的数据
必须拿到SQLiteDatabase类才能操作数据库;
【2.1】获得类读取数据


【2.2】bug产生:权限不够




【解决办法】修改权限


【3】内容提供者原理
关键的角色:内存解析者和内容提供者


【4】通过内容解析者执行CURD
【4.1】定义内容提供者
【4.1.1】定义内容提供者 定义一个类继承contentProvider


【4.1.2】配置清单


【解决办法】增加属性:Authorities,可以根据需求自己随意定义;

【4.1.3】定义一个urimatcher:定义出来的uri可以代表自己认为可以代表的数据,只有访问该定义的uri才能访问暴露的内容提供者的数据;
例如:只有访问www.cnblogs.com才能登陆博客网站,进行浏览等操作;此处提供的也是这样的一个网址;



【4.1.4】写一个静态代码块 添加匹配规则 :匹配规则的主要作用是



【第三个参数code的作用】作为判断后续的CURD操作的正确性;

【4.1.5】取到myOpenHelper对象
可以通过虚拟的getContext()取到上下文;(之前的代码不同请注意)
1 //当内容提供者初始化 会执行此方法 2 @Override 3 public boolean onCreate() { 4 5 //[3]初始化 myopenHelpler 对象 就可以获取到sqlitedatabases对象 我们就可以操作数据库 6 7 myOpenHelper = new MyOpenHelper(getContext()); 8 9 return false; 10 }
【4.1.6】按照我们添加的匹配规则 暴露想暴露的方法;
【说明】方法暴露想暴露哪个需要根据自己的实际使用需求
方法的查询:注意db.query参数的传递,外部的参数传递什么,这里的参数就传递什么;这里不能写死,因为外部会调用此处的方法,在通过这里的方法查询数据;
1 // 这个方法对外暴露的 2 @Override 3 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 4 int code = sURIMatcher.match(uri); 5 if (code == QUERYSUCESS) { 6 // 说明路径匹配成功 7 SQLiteDatabase db = myOpenHelper.getReadableDatabase(); 8 // 调用query方法 9 Cursor cursor = db.query("info", projection, selection, selectionArgs, null, null, sortOrder); 10 11 // 发送一条消息 说明说明数据库被操作了 12 getContext().getContentResolver().notifyChange(uri, null); 13 14 // db.close(); 15 // 小细节 ☆ 这个cursor不能关 16 return cursor; 17 18 } else { 19 // 说明路径不匹配 20 // return null; 21 throw new IllegalArgumentException("哥们 :uri路径不匹配 请检测路径"); 22 23 } 24 25
【4.1.7】如果发现如下log日志 就说明内容提供者写的没有问题

【4.2】调用程序使用内容解析者调用别的程序的数据


【4.2.1】直接获取内容解析者

【4.2.2】提供的地址的注意细节:需要添加协议"content://";
例如:在百度查询的时候是具有协议头的,浏览器一般自动补全:http://www.baidu.com


1 //点击按钮 查询第一个应用里面数据库的信息 2 public void click4(View v){ 3 // 第二种 查询方式 因为第一个应用里面的私有的数据库 已经通过内容提供者暴露出来了 所以通过内容解析者去获取数据 4 Uri uri = Uri.parse("content://com.itheima.provider/query"); 5 //获取内容解析者获取数据 6 Cursor cursor = getContentResolver().query(uri, new String[]{"name","money"}, null, null, null); 7 if (cursor!=null) { 8 9 while(cursor.moveToNext()){ 10 //注意此处的传递的参数0:指的就是上面的数组中new String[]{"name","money"}定义的name数据; 11 //不能在传递1; 12 String name = cursor.getString(0); 13 String money = cursor.getString(1); 14 15 System.out.println("第二个应用:"+name+"---"+money); 16 17 } 18 } 19 20 }
【注意】在查询之后结果集cursor不能够关闭,否则外部应用无法查询到数据;

【查询路径不匹配报的异常】

【5】使用内容提供者进行增删改CUD(查询功能在前面已经介绍过了)
【5.1】内容提供者端的CUD
【5.1.1】插入

1 @Override 2 public Uri insert(Uri uri, ContentValues values) { 3 4 int code = sURIMatcher.match(uri); 5 if (code == INSERTSUCESS) { 6 // 说明路径匹配成功 7 SQLiteDatabase db = myOpenHelper.getReadableDatabase(); 8 9 //insert返回的值是已经在数据库中存在的数据条数 10 long insert = db.insert("info", null, values); 11 Uri uri2 = Uri.parse("com.hahaheheheihei/" + insert); //此处的地址可以随意的更改,一般在公司中需要使用有意义的名字; 12 13 if (insert > 0) { 14 // 发送一条消息 说明说明数据库被操作了 15 getContext().getContentResolver().notifyChange(uri, null); 16 } 17 18 db.close();// 关闭数据库 19 return uri2; 20 21 } else { 22 throw new IllegalArgumentException("姐们 :uri路径不匹配 请检测路径"); 23 } 24 25 }
【5.1.2】删除
1 @Override 2 public int delete(Uri uri, String selection, String[] selectionArgs) { 3 4 int code = sURIMatcher.match(uri); 5 if (code == DELETESUCESS) { 6 // 匹配成功 7 SQLiteDatabase db = myOpenHelper.getReadableDatabase(); 8 9 // 代表影响的行数 10 int delete = db.delete("info", selection, selectionArgs); 11 12 if (delete > 0) { 13 // 发送一条消息 说明说明数据库被操作了 14 getContext().getContentResolver().notifyChange(uri, null); 15 16 } 17 18 return delete; 19 20 } 21 22 return 0; 23 }
【5.1.3】更新
1 @Override 2 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 3 int code = sURIMatcher.match(uri); 4 5 if (code == UPDATESUCESS) { 6 // 路径匹配成功 7 SQLiteDatabase db = myOpenHelper.getWritableDatabase(); 8 9 // 代表影响的行数 10 int update = db.update("info", values, selection, selectionArgs); 11 if (update > 0) { 12 // 发送一条消息 说明说明数据库被操作了 13 getContext().getContentResolver().notifyChange(uri, null); 14 15 } 16 17 return update; 18 19 } else { 20 throw new IllegalArgumentException("大爷:uri路径不匹配 请检测路径"); 21 22 } 23 24 }
【5.2】内容解析者
【说明】ContentValues是一个hashMap,需要添加键值对数据;
【5.2.1】插入
1 // 点击按钮 往数据库里面插入一条数据 2 public void click1(View v) { 3 // 因为第一个应用里面的私有的数据库 已经通过内容提供者暴露出来了 所以通过内容解析者去获取数据 4 Uri uri = Uri.parse("content://com.itheima.provider/insert"); 5 ContentValues values = new ContentValues(); // 实际是map 6 // key: 代表列名 value 对应的值 7 values.put("name", "zhaoliu"); 8 values.put("money", 1000); 9 // 插入一条数据 10 Uri uri2 = getContentResolver().insert(uri, values);//此处返回的uri地址就是之前在内容提供者中定义的有意义的地址; 11 12 System.out.println("uri2:" + uri2); 13 14 }


【5.2.2】删除
1 // 点击按钮删除 赵六删掉 2 public void click2(View v) { 3 // [1]获取内容的解析者 4 Uri uri = Uri.parse("content://com.itheima.provider/delete"); 5 // [2]代表影响的函数 6 int delete = getContentResolver().delete(uri, "name=?", new String[] { "zhaoliu" }); 7 Toast.makeText(getApplicationContext(), "删除了" + delete + "行", 1).show(); 8 9 }

【5.2.3】更新
1 // 给赵六多点钱 1000元 2 public void click3(View v) { 3 // [1] 创建uri 4 Uri uri = Uri.parse("content://com.itheima.provider/update"); 5 // [2]获取内容的解析者 6 ContentValues values = new ContentValues(); 7 values.put("money", "10000000"); 8 int update = getContentResolver().update(uri, values, "name=?", new String[] { "zhaoliu" }); 9 Toast.makeText(getApplicationContext(), "更新了" + update + "行", 1).show(); 10 11 }


【BUG报错】
1 11-15 22:07:14.626: E/AndroidRuntime(25704): FATAL EXCEPTION: main 2 11-15 22:07:14.626: E/AndroidRuntime(25704): java.lang.SecurityException: Permission Denial: opening provider it.oztaking.com.a02_databasedemo.AccountProvider from ProcessRecord{a553fc20 25704:it.oztaking.com.a03_/u0a10053} (pid=25704, uid=10053) that is not exported from uid 10052 3 11-15 22:07:14.626: E/AndroidRuntime(25704): at android.os.Parcel.readException(Parcel.java:1431) 4 11-15 22:07:14.626: E/AndroidRuntime(25704): at android.os.Parcel.readException(Parcel.java:1385) 5 11-15 22:07:14.626: E/AndroidRuntime(25704): at android.app.ActivityManagerProxy.getContentProvider(ActivityManagerNative.java:2611) 6 11-15 22:07:14.626: E/AndroidRuntime(25704): at android.app.ActivityThread.acquireProvider(ActivityThread.java:4515) 7 11-15 22:07:14.626: E/AndroidRuntime(25704): at android.app.ContextImpl$ApplicationContentResolver.acquireProvider(ContextImpl.java:2021) 8 11-15 22:07:14.626: E/AndroidRuntime(25704): at android.content.ContentResolver.acquireProvider(ContentResolver.java:1102) 9 11-15 22:07:14.626: E/AndroidRuntime(25704): at android.content.ContentResolver.update(ContentResolver.java:1034) 10 11-15 22:07:14.626: E/AndroidRuntime(25704): at it.oztaking.com.a03_.MainActivity.MyUpdate(MainActivity.java:75) 11 11-15 22:07:14.626: E/AndroidRuntime(25704): at it.oztaking.com.a03_.MainActivity.onClick(MainActivity.java:47) 12 11-15 22:07:14.626: E/AndroidRuntime(25704): at android.view.View.performClick(View.java:4240) 13 11-15 22:07:14.626: E/AndroidRuntime(25704): at android.view.View$PerformClick.run(View.java:17721) 14 11-15 22:07:14.626: E/AndroidRuntime(25704): at android.os.Handler.handleCallback(Handler.java:730) 15 11-15 22:07:14.626: E/AndroidRuntime(25704): at android.os.Handler.dispatchMessage(Handler.java:92) 16 11-15 22:07:14.626: E/AndroidRuntime(25704): at android.os.Looper.loop(Looper.java:137) 17 11-15 22:07:14.626: E/AndroidRuntime(25704): at android.app.ActivityThread.main(ActivityThread.java:5103) 18 11-15 22:07:14.626: E/AndroidRuntime(25704): at java.lang.reflect.Method.invokeNative(Native Method) 19 11-15 22:07:14.626: E/AndroidRuntime(25704): at java.lang.reflect.Method.invoke(Method.java:525) 20 11-15 22:07:14.626: E/AndroidRuntime(25704): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737) 21 11-15 22:07:14.626: E/AndroidRuntime(25704): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) 22 11-15 22:07:14.626: E/AndroidRuntime(25704): at dalvik.system.NativeStart.main(Native Method)
【错误的原因及修改】需要增加
android:exported="true"
1 是权限的问题,在AndroidManifest.xml 加上红色部分就解决 2 3 <provider android:name="BookProvider" android:authorities="com.example.provides.bookprovider" android:exported="true" />
[源码链接]
https://github.com/wsxingjun/02-databasedemo.git
https://github.com/wsxingjun/03--.git
【6】短信备份案例
【功能】将短信的数据写到xml中;
【6.1】表结构的认识


【6.2】短信路径的获取:非常关键


【查看子目录】



【6.3】权限配置


【6.4】读取数据

【6.5】备份数据到xml中

1 package com.itheima.smsbackup; 2 3 import java.io.File; 4 import java.io.FileNotFoundException; 5 import java.io.FileOutputStream; 6 import java.io.IOException; 7 8 import org.xmlpull.v1.XmlSerializer; 9 10 import android.net.Uri; 11 import android.os.Bundle; 12 import android.os.Environment; 13 import android.app.Activity; 14 import android.database.Cursor; 15 import android.util.Xml; 16 import android.view.Menu; 17 import android.view.View; 18 19 public class MainActivity extends Activity { 20 21 @Override 22 protected void onCreate(Bundle savedInstanceState) { 23 super.onCreate(savedInstanceState); 24 setContentView(R.layout.activity_main); 25 } 26 27 // 点击按钮查询短信内容 然后把短信内容进行备份 28 public void click(View v) { 29 30 try { 31 // [1]获取XmlSerializer的实例 32 XmlSerializer serializer = Xml.newSerializer(); 33 // [2]设置序列化器参数 34 File file = new File(Environment.getExternalStorageDirectory().getPath(), "smsbackup.xml"); 35 FileOutputStream fos = new FileOutputStream(file); 36 serializer.setOutput(fos, "utf-8"); 37 // [3]写xml文档开头 38 serializer.startDocument("utf-8", true); 39 40 // [4]写xml的根节点 41 serializer.startTag(null, "smss"); 42 // [5]构造uri 43 Uri uri = Uri.parse("content://sms/"); 44 45 // [6]由于短信的数据库已经通过内容提供者暴露出来 所以我们直接通过内容解析者查询 46 Cursor cursor = getContentResolver().query(uri, new String[] { "address", "date", "body" }, null, null, 47 null); 48 while (cursor.moveToNext()) { 49 String address = cursor.getString(0); 50 String date = cursor.getString(1); 51 String body = cursor.getString(2); 52 53 // [7]写sms节点 54 serializer.startTag(null, "sms"); 55 // [8]写address节点 56 serializer.startTag(null, "address"); 57 serializer.text(address); 58 serializer.endTag(null, "address"); 59 60 // [9]写date节点 61 serializer.startTag(null, "date"); 62 serializer.text(date); 63 serializer.endTag(null, "date"); 64 // [10]写body节点 65 serializer.startTag(null, "body"); 66 serializer.text(body); 67 serializer.endTag(null, "body"); 68 69 serializer.endTag(null, "sms"); 70 71 } 72 serializer.endTag(null, "smss"); 73 serializer.endDocument(); 74 fos.close(); 75 76 } catch (Exception e) { 77 e.printStackTrace(); 78 } 79 } 80 }
【6.6】增加sd卡的读权限




【7】使用内容解析者在短信中插入一条数据
【7.1】要插入的数据的结构

【7.2】配置权限



【7.3】源码
1 package com.itheima.insertsmsdb; 2 3 import android.net.Uri; 4 import android.os.Bundle; 5 import android.app.Activity; 6 import android.content.ContentValues; 7 import android.view.Menu; 8 import android.view.View; 9 10 public class MainActivity extends Activity { 11 12 @Override 13 protected void onCreate(Bundle savedInstanceState) { 14 super.onCreate(savedInstanceState); 15 setContentView(R.layout.activity_main); 16 } 17 18 //点击按钮 往短信数据库里面插入一条记录 19 public void click(View v) { 20 //[1]由于短信的数据库已经通过内容提供者暴露出来了 所以我想操作数据库 直接通过内容的解析者 21 Uri uri = Uri.parse("content://sms/"); 22 //[2]创建ContentValues 23 ContentValues values = new ContentValues(); 24 values.put("address", "18632525");//招商银行 25 values.put("body", "请您马上过来一趟 否则后果自负"); 26 values.put("date", System.currentTimeMillis()); 27 getContentResolver().insert(uri, values); 28 29 30 } 31 32 }
【8】联系人表的操作
【8.1】联系人表的认识




【8.2】读取联系人的步骤
读取联系人案例 QQ 微信 陌陌等 [1]data表 data1列里面存的是所有联系人的信息 raw_contact_id 列是用来区分一共有几条联系人信息 mimetype_id 列是用来区分数据类型 [2]row_contacts表 中contact_id就是data表的 raw_contact_id 查询联系人的步骤 [1]先查询row_contacts表 的contact_id列 我们就知道一共有几条联系人 [2]我根据contact_id去查询data表 查询data1列和mimetype [3]view_data 是由data表和mimetype表的组合
【8.3】读取联系人
【8.3.1】uri地址的获取





【8.3.2】配置权限



【8.3.3】先查询row_contacts表 的contact_id列 我们就知道一共有几条联系人

【8.3.4】根据contact_id去查询data表 查询data1列和mimetype
【注意】此处查询的data数据是一个view_data,因此在定义数据的时候如果写为
new String[]{"data1","mimetype_id"}则会报错,无法查询;





【出错原因】实际查询data时查询的是view_data视图,而不是data表;
在view_data中不存在mimetype_id这列;

打印出来view_data中存在的所有的列名,未找到minetype_id,只是找到minetype;


【实际查询的数组值】直接查询的时候直接使用minetype,而不是使用minetype_id的标记值;
【修改】
//[2]根据contact_id去查询data表 查询data1列和mimetype_id //☆ ☆ ☆ ☆ 当我们在查询data表的时候 其实查询的是view_data的视图 Cursor dataCursor = context.getContentResolver().query(dataUri, new String[]{"data1","mimetype"}, "raw_contact_id=?", new String[]{contact_id}, null); while(dataCursor.moveToNext()){ String data1 = dataCursor.getString(0); String mimetype = dataCursor.getString(1); }

【8.3.4】将取出来的数据分类添加到list集合中,以便于后面的应用进行listView显示(这也是为什么要放到list集合中的理由);
【1】javabean的书写并且添加toString()方法,

1 package com.itheima.querycontacts; 2 3 public class Contact { 4 5 private String id; 6 private String name; 7 private String phone; 8 private String email; 9 10 public String getId() { 11 return id; 12 } 13 14 public void setId(String id) { 15 this.id = id; 16 } 17 18 public String getName() { 19 return name; 20 } 21 22 public void setName(String name) { 23 this.name = name; 24 } 25 26 public String getPhone() { 27 return phone; 28 } 29 30 public void setPhone(String phone) { 31 this.phone = phone; 32 } 33 34 public String getEmail() { 35 return email; 36 } 37 38 public void setEmail(String email) { 39 this.email = email; 40 } 41 42 @Override 43 public String toString() { 44 return "Contact [id=" + id + ", name=" + name + ", phone=" + phone 45 + ", email=" + email + "]"; 46 } 47 48 }
【2】封装工具类:专门用于联系人信息的查询
1 package com.itheima.querycontacts; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 import android.content.Context; 6 import android.database.Cursor; 7 import android.net.Uri; 8 9 //查询联系人的工具类 10 public class QueryContactsUtils { 11 12 public static List<Contact> queryContacts(Context context) { 13 // [0]创建一个集合 14 15 List<Contact> contactLists = new ArrayList<Contact>(); 16 // [1]先查询row_contacts表 的contact_id列 我们就知道一共有几条联系人 17 Uri uri = Uri.parse("content://com.android.contacts/raw_contacts"); 18 Uri dataUri = Uri.parse("content://com.android.contacts/data"); 19 Cursor cursor = context.getContentResolver().query(uri, 20 new String[] { "contact_id" }, null, null, null); 21 while (cursor.moveToNext()) { 22 String contact_id = cursor.getString(0); 23 24 if (contact_id != null) { 25 // 创建javabean对象 26 Contact contact = new Contact(); 27 28 contact.setId(contact_id); 29 30 System.out.println("contact_id:" + contact_id); 31 // [2]根据contact_id去查询data表 查询data1列和mimetype_id 32 33 // ☆ ☆ ☆ ☆ 当我们在查询data表的时候 其实查询的是view_data的视图 34 35 Cursor dataCursor = context.getContentResolver().query(dataUri, 36 new String[] { "data1", "mimetype" }, 37 "raw_contact_id=?", new String[] { contact_id }, null); 38 while (dataCursor.moveToNext()) { 39 String data1 = dataCursor.getString(0); 40 String mimetype = dataCursor.getString(1); 41 // [3]根据mimetype 区分data1列的数据类型 42 if ("vnd.android.cursor.item/name".equals(mimetype)) { 43 contact.setName(data1); 44 } else if ("vnd.android.cursor.item/phone_v2" 45 .equals(mimetype)) { 46 contact.setPhone(data1); 47 } else if ("vnd.android.cursor.item/email_v2" 48 .equals(mimetype)) { 49 contact.setEmail(data1); 50 } 51 52 } 53 54 // 把javabean对象加入到集合中 55 contactLists.add(contact); 56 } 57 58 } 59 60 return contactLists; 61 62 } 63 64 }
【3】工具类的查询
1 package com.itheima.querycontacts; 2 3 import java.util.List; 4 5 import android.os.Bundle; 6 import android.app.Activity; 7 8 public class MainActivity extends Activity { 9 10 @Override 11 protected void onCreate(Bundle savedInstanceState) { 12 super.onCreate(savedInstanceState); 13 setContentView(R.layout.activity_main); 14 15 List<Contact> queryContacts = QueryContactsUtils 16 .queryContacts(getApplicationContext()); 17 for (Contact contact : queryContacts) { 18 System.out.println("contat:" + contact); 19 20 } 21 22 } 23 24 }

【8.3.5】联系人信息删除真实后台数据的运行
【实际】真实的数据是没有删除的,在data表中都是存在的;只是更改了raw_contact表中的标志而已;
这样的作用是因为在Android的原版中存在联系人与邮箱同步的功能,不删除联系人信息在下次操作的时候比较方便;
在大陆的手机中阉割了这个功能;


【9】插入联系人的信息
插入联系人的步骤
[1]先往row_contacts表插入数据 contact_id
[2]在往data表里面插入数据 data1
【9.1】先插入contact_id:首先需要判断已经存在多少条数据,再确定contact_id的值加1;

【9.2】插入联系人的数据的时候需要知道该数据属于的minetype(类型)和属于第几条联系人的数据;


【9.3】增加权限



【9.4】源码
1 package com.itheima.insertcontactdb; 2 3 import android.net.Uri; 4 import android.os.Bundle; 5 import android.app.Activity; 6 import android.content.ContentValues; 7 import android.database.Cursor; 8 import android.view.Menu; 9 import android.view.View; 10 import android.widget.EditText; 11 12 public class MainActivity extends Activity { 13 14 private EditText et_name; 15 private EditText et_phone; 16 private EditText et_email; 17 18 @Override 19 protected void onCreate(Bundle savedInstanceState) { 20 super.onCreate(savedInstanceState); 21 setContentView(R.layout.activity_main); 22 23 //[1]找到我们关心的控件 24 25 et_name = (EditText) findViewById(R.id.et_name); 26 et_phone = (EditText) findViewById(R.id.et_phone); 27 et_email = (EditText) findViewById(R.id.et_email); 28 29 30 } 31 32 //点击按钮 插入一条联系人信息 33 public void click(View v) { 34 Uri uri = Uri.parse("content://com.android.contacts/raw_contacts"); 35 Uri dataUri = Uri.parse("content://com.android.contacts/data"); 36 37 //[2]获取name phone email Textutils 38 String name = et_name.getText().toString().trim(); 39 String phone = et_phone.getText().toString().trim(); 40 String email = et_email.getText().toString().trim(); 41 42 //[2.1]在插入联系人id的时候 先查询一下 row_contact 一共有几条数据 加+1就是联系人的id 43 Cursor cursor = getContentResolver().query(uri, null, null, null, null); 44 int count = cursor.getCount(); 45 int contact_id = count +1; 46 47 //[3] 先往row_contact表 插入联系人的id (contact_id) 48 ContentValues values = new ContentValues(); 49 values.put("contact_id", contact_id); 50 getContentResolver().insert(uri,values); 51 52 //[4]在把name phone email 插入到data表 53 ContentValues nameValues = new ContentValues(); 54 nameValues.put("data1", name); 55 //☆ ☆ ☆ ☆ ☆ 插入的数据要告诉数据库 属于第几条联系人 和 数据类型 56 nameValues.put("raw_contact_id", contact_id); 57 nameValues.put("mimetype", "vnd.android.cursor.item/name"); 58 getContentResolver().insert(dataUri, nameValues); 59 60 //[5]把phone号码 插入到data表 61 ContentValues phoneValues = new ContentValues(); 62 phoneValues.put("data1", phone); 63 phoneValues.put("mimetype", "vnd.android.cursor.item/phone_v2"); 64 phoneValues.put("raw_contact_id", contact_id); 65 getContentResolver().insert(dataUri, phoneValues); 66 67 68 //[5]把phone号码 插入到data表 69 ContentValues emailValues = new ContentValues(); 70 emailValues.put("data1", email); 71 emailValues.put("mimetype", "vnd.android.cursor.item/email_v2"); 72 emailValues.put("raw_contact_id", contact_id); 73 getContentResolver().insert(dataUri, emailValues); 74 75 } 76 77 78 79 }
【10】内容观察者者

【原理】应用2操作应用1的数据内容,应用3实时监视应用1的变化,如果应用1的数据中发生了变化,同时发出应用改变的消息,则在应用3中可以接收到通知;
(1)内容观察不是四大组件 他不需要在清单文件里配置


【10.1】【应用3】注册内容观察者进行监视操作
因为contentObserver是抽象类,需要写自己的类然后继承;
private class MyContentObserver extends ContentObserver
然后复写onChange()方法;
【说明】参数的设置:
getContentResolver().registerContentObserver(uri, true,new MyContentObserver(new Handler()));
如果参数设置为true:则属于模糊查询;
如果是false,则属于精确查询;
1 package com.itheima.registercontentobserver; 2 3 import android.net.Uri; 4 import android.os.Bundle; 5 import android.os.Handler; 6 import android.app.Activity; 7 import android.database.ContentObserver; 8 import android.view.Menu; 9 10 public class MainActivity extends Activity { 11 12 @Override 13 protected void onCreate(Bundle savedInstanceState) { 14 super.onCreate(savedInstanceState); 15 setContentView(R.layout.activity_main); 16 17 // [1]注册内容观察者 18 Uri uri = Uri.parse("content://com.itheima.provider/"); 19 // false 观察的uri 必须是一个确切的uri 如果是true 20 getContentResolver().registerContentObserver(uri, true, 21 new MyContentObserver(new Handler())); 22 23 } 24 25 // 定义内容观察者 26 private class MyContentObserver extends ContentObserver { 27 28 public MyContentObserver(Handler handler) { 29 super(handler); 30 } 31 32 // 当我们观察的uri发生改变的时候调用 33 @Override 34 public void onChange(boolean selfChange) { 35 System.out.println("哈哈 数据库被操作了 "); 36 37 super.onChange(selfChange); 38 } 39 40 } 41 42 }
【10.2】【应用3】被监视的应用程序自己发消息
1 // 这个方法对外暴露的
2 @Override
3 public Cursor query(Uri uri, String[] projection, String selection,
4 String[] selectionArgs, String sortOrder) {
5
6 ......
7
8 // 发送一条消息 说明说明数据库被操作了
9 getContext().getContentResolver().notifyChange(uri, null);
10
11 ......
12 }
13
14 ==============================
15
16 @Override
17 public Uri insert(Uri uri, ContentValues values) {
18
19 ......
20
21 if (insert > 0) {
22 // 发送一条消息 说明说明数据库被操作了
23 getContext().getContentResolver().notifyChange(uri, null);
24 }
25
26 ......
27 }


【11】【实例】使用内容观察者监听短信的状态
【11.1】注册内容观察者
观察的uri地址就是短信的地址:content://sms/
书写自己的class MyContentObserver
private class MyContentObserver extends ContentObserver {
1 package com.itheima.smslistener; 2 3 import android.net.Uri; 4 import android.os.Bundle; 5 import android.os.Handler; 6 import android.app.Activity; 7 import android.database.ContentObserver; 8 import android.view.Menu; 9 10 public class MainActivity extends Activity { 11 12 @Override 13 protected void onCreate(Bundle savedInstanceState) { 14 super.onCreate(savedInstanceState); 15 setContentView(R.layout.activity_main); 16 17 // [1]注册一个内容观察者 18 Uri uri = Uri.parse("content://sms/"); 19 getContentResolver().registerContentObserver(uri, true, 20 new MyContentObserver(new Handler())); 21 22 } 23 24 private class MyContentObserver extends ContentObserver { 25 26 public MyContentObserver(Handler handler) { 27 super(handler); 28 } 29 30 // 当观察的内容发生改变的时候调用 31 @Override 32 public void onChange(boolean selfChange) { 33 System.out.println("哈哈哈 短信的数据库发生了改变"); 34 super.onChange(selfChange); 35 } 36 37 } 38 39 }
然后直接部署应用程序就可以接收到消息了:

【11.2】在系统的短信源码中存在通知机制的实现,因此在进行短信事件的通知时只需要注册内容观察者即可;
可以查看一下源码:


浙公网安备 33010602011771号