zoukankan      html  css  js  c++  java
  • 07、Android--ContentProvider

    ContentProvider

    ContentProvider分为系统的和自定义的,系统的也就是例如联系人,图片等数据。内容提供者将一些特定的应用程序数据供给其它应用程序使用。数据可以存储于文件系统、

    SQLite数据库或其它方式。它继承于ContentProvider基类,为其它应用程序取用和存储它管理的数据实现了一套标准方法。

    应用程序并不直接调用这些方法,而是使用一个 ContentResolver对象,调用它的方法作为替代。ContentResolver可以与任意内容提供者进行会话,与其合作来对所有相关

    交互通讯进行管理。

    ContentProvider基础

    Android提供一些主要数据类型的ContentProvider,比如音频、视频、图片和私人通讯录等,可在android.provider包下找到一些Android提供的ContentProvider。通过获

    得这些ContentProvider可以查询它们包含的数据,当然前提是已获得适当的读取权限。

    方法 描述
    public boolean onCreate() 在创建ContentProvider调用
    public Cursor query(Uri, String[], String, String[], String) 用于查询指定Uri的ContentProvider,返回一个Cursor
    public Uri insert(Uri, ContentValues) 用于添加数据到指定Uri的ContentProvider中
    public int update(Uri, ContentValues, String, String[]) 用于更新指定Uri的ContentProvider中的数据
    public int delete(Uri, String, String[]) 用于从指定Uri的ContentProvider中删除数据
    public String getType(Uri) 用于返回指定的Uri中的数据的MIME类型

    1、如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头。

    要得到所有person记录的Uri为content://contacts/person,那么返回的MIME类型字符串为"vnd.android.cursor.dir/person"。

    2、如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头。

    要得到id为10的person记录的Uri为content://contacts/person/10,那么返回的MIME类型字符串应为"vnd.android.cursor.item/person"。

    ContentResolver

    当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver类来完成,要获取ContentResolver对象,可以使用

    Context提供的getContentResolver()方法。

    ContentResolver contentResolver = getContentResolver();  
    

    ContentResolver提供的方法和ContentProvider提供的方法对应的有以下几个方法:

    方法 描述
    public Uri insert(Uri, ContentValues) 用于添加数据到指定Uri的ContentProvider中。
    public int delete(Uri, String, String[]) 用于从指定Uri的ContentProvider中删除数据。
    public int update(Uri , ContentValues, String, String[]) 用于更新指定Uri的ContentProvider中的数据。
    public Cursor query(Uri , String[] , String , String[] , String ) 用于查询指定Uri的ContentProvider。

    URI

    外部程序只需知道内容提供者的Uri路径信息,通过ContentResolver即可调用内容提供者。

    字段 描述
    schema 用来说明一个ContentProvider控制这些数据。"content://"
    主机名或授权Authority 它定义了是哪个ContentProvider提供这些数据。
    path 路径,URI下的某一个Item。
    ID 通常定义Uri时使用”#”号占位符代替, 使用时替换成对应的数字。
    content://com.itheima.provider/person/# #表示数据id(#代表任意数字)
    content://com.itheima.provider/person/* *来匹配任意文本

    ContentProvider实例

    操作短信

    ContentProvider操作短信示例如下:

    public class SmsUtils {
        /** 声明一个接口 , 包含一些回调函数 */
        public interface BackupSmsCallBack {
            /**
             * 短信备份之前调用的方法
             * @param max    短信的总条目个数
             */
            public void beforeSmsBackup(int max);
            /**
             * 当短信备份过程中调用的方法
             * 
             * @param process 当前备份的进度
             */
            public void onSmsBackup(int process);
        }
        /**
         * 备份用户的短信
         * @param context 上下文
         * @param callback 短信备份的接口
         * @param filename 备份后的文件名称
         * @return 是否备份成功
         */
        public static boolean backupSms(Context context,
                BackupSmsCallBack callback, String filename) {
            try {
                ContentResolver resolver = context.getContentResolver();
                Uri uri = Uri.parse("content://sms/");
                File file = new File(Environment.getExternalStorageDirectory(),
                        filename);
                FileOutputStream fos = new FileOutputStream(file);
                XmlSerializer serializer = Xml.newSerializer();
                serializer.setOutput(fos, "utf-8");
                serializer.startDocument("utf-8", true);
                serializer.startTag(null, "info");
                Cursor cursor = resolver.query(uri, new String[] { "address",
                        "date", "body", "type" }, null, null, null);
               int max = cursor.getCount();
                serializer.attribute(null, "total", String.valueOf(max));
                callback.beforeSmsBackup(max);
                int process = 0;
                while (cursor.moveToNext()) {
                    serializer.startTag(null, "sms");
                    serializer.startTag(null, "address");
                    String address = cursor.getString(0);
                    serializer.text(address);
                    serializer.endTag(null, "address");
                    serializer.startTag(null, "date");
                    String date = cursor.getString(1);
                    serializer.text(date);
                    serializer.endTag(null, "date");
                    serializer.startTag(null, "body");
                    String body = cursor.getString(2);
                    serializer.text(body);
                    serializer.endTag(null, "body");
                    serializer.startTag(null, "type");
                    String type = cursor.getString(3);
                    serializer.text(type);
                    serializer.endTag(null, "type");
                    serializer.endTag(null, "sms");
                    Thread.sleep(2000);
                    process++;
                    // pb.setProgress(process);
                    // pd.setProgress(process);
                    callback.onSmsBackup(process);
                }
                cursor.close();
                serializer.endTag(null, "info");
                serializer.endDocument();
                fos.close();
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    }  
    

    操作联系人

    准备工作

    a)通过DDMS,查看Android模拟器下的com.android.providers.contacts包下的数据库,查看其contact2.db数据库的内容。

    b)查看数据库,其中raw_contacts表存放的是联系人条数信息,data表中存放的是raw_contacts中的每一条id对应的具体信息,不同类型的信息由mimetype_id来标识。
    raw_contacts表:

    data表:

    mimetypes表:

    c)打开Android源码,查看packagesproviders路径下的文件,其中ContactsProvider就是联系人的内容提供者。

    打开清单文件,寻找联系人的内容提供者对应的是哪个java文件。

    打开ContactsProvider2.java文件,查看此内容提供者的uri路径信息

    操作raw_contacts表的Uri:

    content://com.adroid.contacts/raw_contacts

    操作data表的Uri:

    content://com.adroid.contacts/data

    操作数据库表时注意contacts2.db数据库使用了视图,所以操作数据库表时表结构有所改变,注意操作时要操纵的列的列名已经改变。

    比如:data表在查询的时候没有mimetype_id,取代的是mimetype

    操作实例

    public static List<ContactInfo> getContactInfos(Context context) {
        // 内容提供者的解析器
        ContentResolver resolver = context.getContentResolver();
        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);
        List<ContactInfo> contactInfos = new ArrayList<ContactInfoUtils.ContactInfo>();
        while (cursor.moveToNext()) {
            String id = cursor.getString(0);
            System.out.println("id=" + id);
            if (id != null) {
                ContactInfo info = new ContactInfoUtils().new ContactInfo();
                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.name = data1;// 姓名
                    } else if ("vnd.android.cursor.item/phone_v2".equals(mimetype)) {
                        info.phone = data1;// 电话
                    } else if ("vnd.android.cursor.item/email_v2".equals(mimetype)) {
                        info.email = data1;// 邮箱
                    }
                }
                contactInfos.add(info);
            }
        }
        return contactInfos;
    }
    public class ContactInfo {
        public String name;
        public String email;
        public String phone;
    }  
    

    自定义ContentProvider

    a)新建一个类继承ContentProvider来创建内容提供器,并实现六个抽象方法。

    public class MyProvider extends ContentProvider {
        // 初始化内容提供器调用。通常完成数据库的创建和升级操作。
        @Override
        public boolean onCreate() {
            return false;
        }
        // 从内容提供器中查询数据,使用uri参数确定查询哪张表。
        @Override
        public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {
            return null;
        }
        // 向内容提供器中添加一条数据使用uri参数确定添加哪张表。
        @Override
        public Uri insert(Uri uri, ContentValues values) {
            return null;
        }
        // 从内容提供器中删除数据,使用uri来确定删除哪张表。
        @Override
        public int delete(Uri uri, String selection, String[] selectionArgs) {
            return 0;
        }
        // 更新内容提供器中已有的数据,使用uri参数来确定更新哪张表。
        @Override
        public int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {
            return 0;
        }
        // 根据传入的内容URI来返回相应的MIME类型。
        @Override
        public String getType(Uri uri) {
            return null;
        }
    }  
    

    b) uri的格式上面已经有叙述,接下来我们通过UriMatcher类实现匹配内容URI的功能

    public class MyProvider extends ContentProvider {  
        // 表示访问table1和table2中的所有数据或单条数据
        public static final int TABLE1_DIR = 0;
        public static final int TABLE1_ITEM = 1;
        public static final int TABLE2_DIR = 2;
        public static final int TABLE2_ITEM = 3;
        
        private static UriMatcher mUriMatcher;
        
        static{
            mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
            mUriMatcher.addURI("cn.legend.provider", "table1", TABLE1_DIR);
            mUriMatcher.addURI("cn.legend.provider", "table1/#", TABLE1_ITEM);
            mUriMatcher.addURI("cn.legend.provider", "table2", TABLE2_DIR);
            mUriMatcher.addURI("cn.legend.provider", "table2/#", TABLE2_ITEM);
            
        }
        @Override
        public boolean onCreate() {
            return false;
        }
        
        @Override
        public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {
            switch (mUriMatcher.match(uri)) {
            case TABLE1_DIR:
                // 查询table1表中的所有数据
                break;
            case TABLE1_ITEM:
                // 查询table1表中的单条数据
                break;
            case TABLE2_DIR:
                // 查询table2表中的所有数据
                break;
            case TABLE2_ITEM:
                // 查询table2表中的单条数据
                break;
            }
            return null;
        }
        
        @Override
        public Uri insert(Uri uri, ContentValues values) {
            return null;
        }
        
        @Override
        public int delete(Uri uri, String selection, String[] selectionArgs) {
            return 0;
        }
        
        @Override
        public int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {
            return 0;
        }
        
        @Override
        public String getType(Uri uri) {
            switch (mUriMatcher.match(uri)) {
            case TABLE1_DIR:
                return "vnd.android.cursor.dir/vnd.cn.legend.provider.table1";
            case TABLE1_ITEM:           
                return "vnd.android.cursor.dir/vnd.cn.legend.provider.table1";
            case TABLE2_DIR:          
                return "vnd.android.cursor.dir/vnd.cn.legend.provider.table2";
            case TABLE2_ITEM:           
                return "vnd.android.cursor.dir/vnd.cn.legend.provider.table2";
            }
            return null;
        }
    }  
    

    c)上述只是以查询为例,增删改查都大同小异,只有getType()方法比较特殊,它是获取uri对象所对应的MIME类型,MIME字符串由三部分组成:

    1. 必须以vnd开头。
    2. 如果URI以路径结尾,则后接 android.cursor.dir/,如果URI以id结尾则后接 android.cursor.item/。
    3. 最后接上 vnd.<authority>.<path>。

    对于content://cn.legend.provide/table1这个URI所对应MIME:

    vnd.android.cursor.dir/vnd.cn.legend.provider.table1

    对于content://cn.legend.provider/table1/1这个URI所对应MIME:

    vnd.android.cursor.item/vnd.cn.legend.provider.table1

    ContentObserver

    ContentObserver的使用类似与设计模式中的观察者模式,ContentObserver是观察者,被观察的ContentProvider是被观察者。

    当被观察者ContentProvider的数据发生了增删改的变化,就会及时的通知给ContentProvider,ContentObsserver做出相应的处理。

    public class MainActivity extends Activity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
             
            // 注册观察者Observser
            this.getContentResolver().registerContentObserver(Uri.parse("content://sms"),true,new SMSObserver(new Handler()));
        }
     
        private final class SMSObserver extends ContentObserver {
            public SMSObserver(Handler handler) {
                super(handler);
            }
            @Override
            public void onChange(boolean selfChange) {
                Cursor cursor = MainActivity.this.getContentResolver().query(
                        Uri.parse("content://sms/inbox"), null, null, null, null);
                while (cursor.moveToNext()) {
                    StringBuilder sb = new StringBuilder();
                    sb.append("address=").append(
                            cursor.getString(cursor.getColumnIndex("address")));
                    sb.append(";subject=").append(
                            cursor.getString(cursor.getColumnIndex("subject")));
                    sb.append(";body=").append(
                            cursor.getString(cursor.getColumnIndex("body")));
                    sb.append(";time=").append(
                            cursor.getLong(cursor.getColumnIndex("date")));
                    System.out.println("--------has Receivered SMS::" + sb.toString());
                }
            }
        }
    }  
    

    这些知识contentObserver的基本使用,更细节的使用方式待后续补充。

  • 相关阅读:
    ☀【布局】
    _#【CSS3】
    _#minheight
    【其它】引入css
    【css3】url
    鼠标闲置一段时间后自动隐藏
    图解SQLServer2005获取WebService数据
    Oracle字符串字段内的字符排序
    一个c#读取扫雷内存的demo
    sqlserver使用bcp分解字符串
  • 原文地址:https://www.cnblogs.com/pengjingya/p/5505378.html
Copyright © 2011-2022 走看看