【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】在系统的短信源码中存在通知机制的实现,因此在进行短信事件的通知时只需要注册内容观察者即可;

可以查看一下源码:

 

posted @ 2017-11-02 16:59  OzTaking  阅读(488)  评论(0)    收藏  举报