Android四大组件之ContentProvider

                ContentProvider 内容提供者
##内容提供者的作用
应用程序创建的数据库都是私有的,别的应用程序是不可以进行访问的,如果有需求需要
把自己应用程序的数据库暴露给用户使用,此时我们就需要使用到内容提供者.

一:内容提供者的创建
    1,写一个类继承ContentProvider,实现他的CURD方法(空实现)
    2,在清单文件中配置内容提供者,指定主机名:android:authorities="com.itheima.db"
    3,在ContentProviter里面声明一个UriMatcher(放在静态代码块中)
            static UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
            static {
            //检查uri的规则,如果uri匹配失败 返回-1
                                    主机名            表明      定义个常量
                mUriMatcher.addURI("com.itheima.db", "account", SUCCESS);
            }
    4,通过UriMatcher 检查rui的路径是否正确,uri路径="content://再加配置信息"
    5,在另一个应用程序里面,通过contentResolver(内容提供者的解析器)  在CURD方法里面操作
    
    内容提供者学习的目的,(开发用的少,因为一般都把数据私有化.而内容提供者是暴露自己的数据)
    1,知道内容提供者原理
    2,能看得懂源码
    
二:学习内容提供者的目的
    1. 了解内容提供者原理
    2. 能看懂系统源码
    3. 获取系统应用内容提供者所提供的数据,例如联系人、短信应用

    ##如何去分析系统应用的内容提供者
    1. 查看数据库,分析数据库的表和字段
    2. 操作内容提供者需要uri
    3. 找到系统应用的源代码,首先去清单文件中查找主机名authorities

            <provider android:name="SmsProvider"
                  android:authorities="sms"
                  android:multiprocess="true"
                  android:readPermission="android.permission.READ_SMS"
                  android:writePermission="android.permission.WRITE_SMS" />
    4. 去对应的Provider的源代码中查找匹配规则,确定表名

            static {
                sURLMatcher.addURI("sms", null, SMS_ALL); //所有短信
                sURLMatcher.addURI("sms", "#", SMS_ALL_ID); //所有短信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); //草稿箱
                sURLMatcher.addURI("sms", "draft/#", SMS_DRAFT_ID);
            }
    5. 根据主机名和表名确定uri,使用ContentResolver的增删改查方法操作对应的数据库
三:##通知栏提醒Notification
显示在另外一个进程的界面里面的

1. 在低版本中的写法(api小于16),创建Notification时直接new Notification()
                                                //等到系统服务(通知服务)
        NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        
        //1.初始化Notification                            //三个参数没在拉下来显示出来的
        Notification notification = new Notification(R.drawable.ic_launcher, "有新的消息到来了", System.currentTimeMillis());

        //2.创建通知栏的点击事件
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_CALL);
        intent.setData(Uri.parse("tel://110"));
        //PendingIntent延时的意图,可以打开Activity、Service和发送广播
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, 0);
        //3.设置通知的点击事件   //啦下来的时候参数显示
        notification.setLatestEventInfo(this, "我是标题", "我是文本", contentIntent);

        //4.显示通知
        nm.notify(0, notification);  
2. 在高版本中的写法(api大于等于16),创建Notification时使用Notification.Builder

        NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    
        //1.初始化Notification                        //构建器
        Notification notification = new Notification.Builder(this)
            //链式调用
            .setContentTitle("我是标题")
            .setContentText("我是文本")
            .setSmallIcon(R.drawable.ic_launcher)
            .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher))
            .setContentIntent(PendingIntent intent) //设置点击事件
            .build();  //产生notification

        //2.显示通知
        nm.notify(0, notification);

3. Notification中使用自定义View  

    api小于16
        //低版本直接new出来  高版本不是
        Notification notification = new Notification(R.drawable.ic_launcher, "有新的消息到来了", System.currentTimeMillis());
        //设置自定义布局                            包名            布局
        RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.xxx);
        notification.contentView = remoteViews;
    
    api大于16
    
        Builder builder = Notification.Builder(this);
        //设置自定义布局
        builder.setContent(RemoteViews views);

4. RemoteViews使用方式

        RemoteViews views = new RemoteViews(getPackageName(), R.layout.xxx);
        views.setTextViewText(R.id.xxx, "hello"); //设置TextView的文本
        views.setImageViewResource(R.id.xxx, R.drawable.xxx); //设置ImageView的图片
        views.setOnClickPendingIntent(R.id.xx, pendingIntent); //设置按钮的点击事件

四:##如何打开短信界面
尝试打开系统某个界面的思路

1. 从logcat中查看是哪个Activity
2. 在上层源码中搜索该工程
3. 查看清单文件中是否有隐式意图可以激活,或者直接使用显示意图激活

        //打开短信界面的隐式意图
        Intent intent = new Intent();
        intent.setAction("android.intent.action.MAIN");
        intent.addCategory("android.intent.category.DEFAULT");
        intent.setType("vnd.android.cursor.dir/mms");
        startActivity(intent);

##联系人数据库
1. 路径:data/data/com.android.providers.contacts/databases/contacts2.db
2. 主要操作的3张表:
    1. raw_contact:联系人的id表
        * contact_id 保存联系人的id
    2. data:联系人的数据表
        * raw_contact_id 表示属于哪个联系人
        * data1 具体的数据
        * mimetype_id 数据的类型,使用该id去mimetypes表中查询数据类型
    3. mimetypes:联系人的数据类型表
3. 查询联系人数据库的数据的步骤
    1. 查询raw_contact表,获取所有联系人id
    2. 根据联系人id,查询data表,该联系人的所有数据
    3. 根据mimetype确定数据类型

##如何读取联系人数据
    ContentResolver resolver = getContentResolver();
    //1.查询raw_contact表,获取所有联系人id
    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);

        //2.根据联系人id,查询data表,该联系人的所有数据
        Cursor datacursor = resolver.query(datauri, new String[]{"data1","mimetype"}, "raw_contact_id=?", new String[]{id}, null);
        while(datacursor.moveToNext()){

            //3.根据mimetype确定数据类型
            String data1 = datacursor.getString(0);
            System.out.println("data1:"+data1);
            String mimetype = datacursor.getString(1);
            System.out.println("mimetype:"+mimetype);
        }
        datacursor.close();
        System.out.println("------------");
    }
    cursor.close();

* 备注:加上READ_CONTACTS权限,取mimetype时有个小细节,直接在data表里面就可以查询mimetype数据类型了,实际是查询数据库中的视图。

* 数据库视图:视图是虚表,是从一个或几个基本表(或视图)中导出的表。  
    1. 简单性:看到的就是需要的。视图不仅可以简化用户对数据的理解,也可以简化他们的操作。那些被经常使用的查询可以被定义为视图。  
    2. 安全性:通过视图用户只能查询和修改他们所能见到的数据。数据库中的其它数据则既看不见也取不到。

##系统联系人应用删除一个联系人的处理逻辑
* 联系人应用删除一个联系人时,只是在raw_contact表中,把对应联系人的id置为null了
* Google这么设计的目的是为了进行联系人与服务器同步,还有减少计算量
    * 比如,本地有4个联系人,服务器有5个联系人,而且服务器也具有添加联系人的功能,那同步时的逻辑是要往本地加一个联系人呢?还是要在服务器减一个联系人呢?
    * 另外对比少哪一个数据是一个非常麻烦的算法,要从A里取一个去遍历B里是否有这样一个数据

##添加一个联系人到数据库的步骤
1. 在raw_contact表中添加一个联系人的id
2. 在data表里添加联系人的数据,姓名、电话、邮箱等

##内容观察者


    //注册内容观察者
    Uri uri = Uri.parse("content://com.itheima.db/account");
                        //注册内容观察者(相应刚刚对应的)        true,路径前面保持一致表示数据改变了
    getContentResolver().registerContentObserver(uri, true, new ContentObserver(new Handler()) {
        //内容发生改变就会调用
        @Override
        public void onChange(boolean selfChange) {
            System.out.println("我是观察者,我发现银行的数据库变化了.");
            super.onChange(selfChange);
        }
        
    });

* 内存观察者一般用于观察系统数据库的变化,例如联系人、短信等。系统的数据库发生变化是不需要操作数据库的
人手动调用ContentResolver.notifyChange()的,所以无论是谁改变了数据库我们都会受到通知。

你可能感兴趣的:(Android四大组件之ContentProvider)