Android四大组件之ContentProvider(内容提供者)

“退笔如山未足珍,读书万卷始通神。”在人生当中有很多经历,我们用自己的经历来书写自己的人生,在面试的时候要将自己知道的尽力的去说完。

言归正传,我今天将Android四大组件之一ContentProvider进行初步的讲解。

一、ContentProvider定义

      首先我们必须要明白的是ContentProvider(内容提供者)是android中的四大组件之一,但是在一般的开发中,可能使用比较少。ContentProvider为不同的软件之间数据共享,提供统一的接口。而且ContentProvider是以类似数据库中表的方式将数据暴露,也就是说ContentProvider就像一个“数据库”。那么外界获取其提供的数据,也就应该与从数据库中获取数据的操作基本一样,只不过是采用URI(统一资源定位符)来表示外界需要访问的“数据库”。至于如何从URI中识别出外界需要的是哪个“数据库”这就是Android底层需要做的事情了,也就是说,如果我们想让其他的应用使用我们自己程序内的数据,就可以使用ContentProvider定义一个对外开放的接口,从而使得其他的应用可以使用我们自己应用中的文件、数据库内存储的信息。当然,自己开发的应用需要给其他应用共享信息的需求可能比较少见,但是在Android系统中,很多数据如:联系人信息、短信信息、图片库、音频库等,这些信息在开发中还是经常用到的,这些信息谷歌工程师已经帮我们封装好了,我们可以使用谷歌给我的Uri去直接访问这些数据。所以对于ContentProvider我们还是需要认真的学习的,在遇到获取联系人信息,图片库,音视频库等需求的时候,才能更好的实现功能。

二、作用

      应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用sharedpreferences共享数据,需要使用sharedpreferences API读写数据。而使用ContentProvider共享数据的好处是统一了数据访问方式,这也是为什么会有内容提供者的原因。

三、URI(统一资源定位符)

通用资源标志符(Universal Resource Identifier, 简称"URI")。
Uri代表要操作的数据,Android上可用的每种资源 - 图像、视频片段等都可以用Uri来表示。
URI一般由三部分组成:

  • 访问资源的命名机制。
  • 存放资源的主机名。
  • 资源自身的名称,由路径表示。

Android的Uri由以下三部分组成: "content://"、数据的路径、标示ID(可选)
举些例子,如: 
所有联系人的Uri: content://contacts/people
某个联系人的Uri: content://contacts/people/5
所有图片Uri: content://media/external
某个图片的Uri:content://media/external/images/media/4

主要方法有:

getScheme() :获取Uri中的scheme字符串部分,在这里是http
getSchemeSpecificPart():获取Uri中的scheme-specific-part:部分,这里

是:http://www.baidu.com:8080/yourpath/fileName.html?
getFragment():获取Uri中的fragment部分,即dmk
getAuthority():获取Uri中Authority部分,即www.baidu.com:8080
getPath():获取Uri中path部分,即/yourpath/fileName.html
getQuery():获取Uri中的query部分,即id=15&name=du
getHost():获取Authority中的Host字符串,即www.baidu.com
getPost():获取Authority中的Port字符串,即8080

四、ContentProvider详解

  1. ContentProvider能干什么?
    •  ContentProvider将应用中的数据对其它应用进行共享, 提供增删改查的方法
    •  ContentProvider统一了数据的访问方式,不必针对不同数据类型采取不同的访问策略 
    • ContentProvider将数据封装,只暴露出我们希望提供给其它程序的数据  
    • ContentProvider中可以注册观察者, 监听数据的变化  
  2. ContentProvider如何创建?
    • 定义类继承ContentProvider, 实现抽象方法 
    • 在清单文件中注册:在清单文件的节点下进行配置标签,标签中需要指定name和authorities属性
      • name:完整的类名。可以省略包名(manifest节点的package值),注意:省略后的类名是以"."开头的。
      • authorities:是访问Provider时的路径,要唯一 
    • 在手机上注册  
              将应用安装到手机上即可, 不用运行程序  
    • 其它应用怎么访问  
              外部应用使用ContentResolver类对ContentProvider中的数据进行访问(CRUD操作)  获取解析器ContentResolver  
                      ContentResolver resolver = Context.getContentResolver();  
      通过resolver.insert(), delete(), update(), query()方法访问Uri关联的ContentProvider  
    • Uri的处理        
              URI代表要操作的数据,由scheme、authorites、path三部分组成  
                  eg:  
                      content://com.jxn.provider/person  
                      scheme   |   authorites   | path  
      • schema:表明要访问ContentProvider。固定为:"content://"  
      • Authority(主机名或授权):定义了是哪个ContentProvider提供这些数据。  
    • path:路径,可根据业务逻辑自定义。eg: person、person/insert、person/insert/10等等  
    • ID:通常定义URI时使用"#"号占位符代替, 使用时替换成对应的数字  
                  "content://com.jxn.provider/person/#" #表示数据id(#代表任意数字)  
                  "content://com.jxn.provider/person/*" *来匹配任意文本  

  3. ConventProvider代码示例:
Android系统提供了两个用于操作Uri的工具类:UriMatcher 和 ContentUris   
  
            1,UriMatcher类用于匹配Uri,用法如下:  
              
            第一步:把你需要匹配的Uri路径全部给注册上,如下:  
              
                //常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码  
                UriMatcher  matcher = new UriMatcher(UriMatcher.NO_MATCH);  
                //如果match()方法匹配content://com.jxn.provider.personprovider/person路径,返回匹配码为1  
                matcher.addURI("com.jxn.provider.personprovider", "person", 1);//添加需要匹配uri,如果匹配就会返回匹配码  
                //如果match()方法匹配content://com.jxn.provider.personprovider/person/230路径,返回匹配码为2  
                matcher.addURI("com.jxn.provider.personprovider", "person/#", 2);//#号为通配符  
              
            第二步:使用matcher.match(uri)方法对输入的Uri进行匹配,如果匹配成功就返回匹配码  
                switch (matcher.match(Uri.parse("content://com.jxn.provider.personprovider/person/10"))) {   
                    case 1  
                        // 相应的业务操作  
                        break;  
                    case 2  
                        // 相应的业务操作  
                        break;  
                    default:  
                        // 相应的业务操作  
                        break;  
                }  
          
        2,ContentUris类用于为路径加上ID和获取路径的ID  
  
                给Uri加上id:   ContentUris.withAppendedId(uri, id)   
                获取id:       ContentUris.parseId(uri)  
  
  
    6,监听内容提供者数据变化  
      
        1,如果ContentProvider的访问者需要知道ContentProvider中的数据发生了变化,可以在ContentProvider 发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者,例如:  
            public class PersonContentProvider extends ContentProvider {  
                public Uri insert(Uri uri, ContentValues values) {  
                    db.insert("person", "personid", values);  
                    // 注:如果没有调用notifyChange()方法,即使其它应用注册了ContentObserver,也不会知道ContentProvider中的数据的变化  
                    getContext().getContentResolver().notifyChange(uri, null);  
                }  
            }  
              
        2,如果ContentProvider的访问者需要得到数据变化通知,必须使用ContentObserver对数据(用uri描述)进行监听,当监听到数据变化通知时,系统就会调用ContentObserver的onChange()方法:  
            getContentResolver().registerContentObserver(Uri.parse("content://com.jxn.providers.personprovider/person"),true, new PersonObserver(new Handler()));  
            public class PersonObserver extends ContentObserver{  
                public PersonObserver(Handler handler) {  
                    super(handler);  
                }  
                public void onChange(boolean selfChange) {  
                    //此处可以进行相应的业务处理  
                }  
            }  
              
    7,补充  
        getType()方法:主要用于匹配数据类型,返回当前Uri所代表数据的MIME类型。  
            如果返回数据是单条数据:vnd.android.cursor.item   
            如果返回数据是多条数据:vnd.android.cursor.dir  
  
  
案例:B应用通过A应用提供的ContentProvider访问A应用中的数据  
  
    // 提供ContentProvider的A应用中定义的SQLiteProvider  
    public class SQLiteProvider extends ContentProvider {  
  
        // Uri匹配器, 用来匹配传入的Uri  
        private static final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);  
          
        private static final int PERSON = 1;  
          
        // DBOpenHelper extends SQLiteOpenHelper  
        private DBOpenHelper helper;  
  
        static {  
            // 设置一个Uri, 如果匹配到person, 返回1  
            matcher.addURI("com.jxn.sqlite.provider", "person", PERSON);  
        }  
          
  
        // 其它应用第一次访问时(此时会创建ContentProvider)执行  
        // 第一次启动时执行, 然后会长期驻留在后台, 除非被杀死, 否则不会再执行  
        @Override  
        public boolean onCreate() {  
            helper = new DBOpenHelper(getContext());  
            return true;  
        }  
  
        // 外部应用使用此方法查询数据  
        @Override  
        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {  
            SQLiteDatabase db = helper.getReadableDatabase();  
              
            // 用匹配器匹配传入的uri  
            switch (matcher.match(uri)) {  
                case PERSON:  
                    return db.query("person", projection, selection, selectionArgs, null, null, sortOrder);     // 执行查询  
                default:  
                    throw new RuntimeException("Uri不能识别: " + uri);  
            }  
        }  
  
        // 外部应用使用此方法添加数据  
        @Override  
        public Uri insert(Uri uri, ContentValues values) {  
            SQLiteDatabase db = helper.getWritableDatabase();  
            switch (matcher.match(uri)) {  
                case PERSON:  
                    long id = db.insert("person", "id", values);    // 插入记录, 得到id  
                    return ContentUris.withAppendedId(uri, id);     // 把id跟在uri后面返回  
                default:  
                    throw new RuntimeException("Uri不能识别: " + uri);  
            }  
        }  
  
        @Override  
        public int delete(Uri uri, String selection, String[] selectionArgs) {  
            SQLiteDatabase db = helper.getWritableDatabase();  
            switch (matcher.match(uri)) {  
                case PERSON:  
                    return db.delete("person", selection, selectionArgs);  
                default:  
                    throw new RuntimeException("Uri不能识别: " + uri);  
            }  
        }  
  
        @Override  
        public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {  
            SQLiteDatabase db = helper.getWritableDatabase();  
            switch (matcher.match(uri)) {  
                case PERSON:  
                    return db.update("person", values, selection, selectionArgs);  
                default:  
                    throw new RuntimeException("Uri不能识别: " + uri);  
            }  
        }  
  
        @Override  
        public String getType(Uri uri) {  
            switch (matcher.match(uri)) {  
                case PERSON:  
                    return "vnd.android.cursor.dir/person";     // mimetype  
                default:  
                    throw new RuntimeException("Uri不能识别: " + uri);  
            }  
        }  
    }  
  
  
    // B应用中要访问A应用的数据的测试类  
    public class ProviderTest extends AndroidTestCase {  
          
        public void test() {  
          
            // 获取解析器对象  
            ContentResolver resolver = getContext().getContentResolver();  
              
            // 访问内容提供者  
            Uri uri = Uri.parse("content://com.jxn.sqlite.provider");  
            ContentValues values = new ContentValues();  
  
        }  
          
        public void testQuery() {  
          
            // 获取解析器对象  
            ContentResolver resolver = getContext().getContentResolver();  
            Uri uri = Uri.parse("content://com.jxn.sqlite.provider/person");  
              
            // 访问内容提供者  
            Cursor c = resolver.query(uri, new String[]{ "id", "name", "balance" }, "balance>?", new String[]{ "9000" }, "balance DESC");  
            while (c.moveToNext()) {  
                Person p = new Person(c.getInt(0), c.getString(1), c.getInt(2));  
                System.out.println(p);  
            }  
        }  
          
        public void testInsert() {  
            ContentResolver resolver = getContext().getContentResolver();  
            Uri uri = Uri.parse("content://com.jxn.sqlite.provider/person");  
              
            ContentValues values = new ContentValues();  
            values.put("name", "provider");  
            values.put("balance", 12345);  
              
            // 插入数据, 并且得到这条数据的uri  
            uri = resolver.insert(uri, values);  
              
            System.out.println(uri);  
        }  
          
        public void testUpdate() {  
            ContentResolver resolver = getContext().getContentResolver();  
            Uri uri = Uri.parse("content://com.jxn.sqlite.provider/person");  
              
            ContentValues values = new ContentValues();  
            values.put("name", "update");  
            values.put("balance", 54321);  
            int count = resolver.update(uri, values, null, null);  
            System.out.println(count);  
        }  
          
        public void testDelete() {  
            ContentResolver resolver = getContext().getContentResolver();  
            Uri uri = Uri.parse("content://com.jxn.sqlite.provider/person");  
              
            int count = resolver.delete(uri, null, null);  
            System.out.println(count);  
        }  
          
        public void testGetType() {  
            ContentResolver resolver = getContext().getContentResolver();  
            // 获取uri的类型  
            String type = resolver.getType(Uri.parse("content://com.jxn.sqlite.provider/person"));  
            System.out.println(type);  
        }  
    }  


你可能感兴趣的:(Android基础)