ContentProvider笔记 四大组件

Quibbler 2020-12-21 1228

ContentProvider笔记


        ContentProvider作为四大组件很常用,但是相关代码一写完少则半年都不会变动,不会天天四大组件来回折腾。好记性不如烂笔头,简要记录一下ContentProvider的用法。



1、ContentProvider

        ContentProvider用来在多个应用程序之间共享数据,涉及到Binder跨进程传输那部分咱先不提。你要在同一个APP内部也使用ContentProvider也没毛病(提供统一接口访问)


1.1、概念

        ContentProvider众所周知是Android四大组件之一,从12年以来一直都是。为第三方应用程序提供数据,封装数据通过ContentResolver接口将数据其提供给其它应用程序 。


1.2、用途

        ContentProvider的主要用途是为不同的应用之间数据共享,提供统一的接口。只需要一个Uri就可以获取其它应用提供的特定数据,当然如果这个“三方”应用的Uri一直在单方面变更,那么没法合作了!


1.3、原理

        关于ContentProvider的部分原理详见ContentProvider启动流程及原理



2、系统ContentProvider

        Android系统内置了很多标准的ContentProvider,比如短信、联系人、日历等数据就是框架内置的ContentProvider。在源码的/packages/providers目录下维护了十几个系统ContentProvider。举几个常见的系统ContentProvider:


2.1、ContactsProvider

        ContactsProvider2是手机联系人内容提供器,提供联系人的数据,需要相关权限:

    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.WRITE_CONTACTS" />


2.2、CalendarProvider

        CalendarProvider2是系统日历ContentProvider,提供系统日历的创建、查询、删除日程等操作,不过需要申请日历相关的权限:

    <uses-permission android:name="android.permission.READ_CALENDAR" />
    <uses-permission android:name="android.permission.WRITE_CALENDAR" />


2.3、TelephonyProvider

        TelephonyProvider是手机短信数据库内容提供器,通过他可以操作手机中的短信,不过这种危险的权限越来越被Google收紧,普通应用就别瞎搞了。关于短信的操作详见我的另一篇博客短信操作:原来用户隐私这么容易暴露



3、定义ContentProvider

        ContentProvider通常操作数据库,这就得结合SQLiteDatabase实现ContentProvider的增删改查功能,参考Android数据库帮助类SQLiteOpenHelper。标准的ContentProvider实现参照官方示例:Creating a content provider


3.1、创建

        AndroidStudio开发工具很方便,提供了创建Android各种组件的快捷方法,可以直接右键菜单新建ContentProvider组件。

        

3.2、注册

        AndroidManifest.xml中一定要注册ContentProvider,并且通常ContentProvider组件作为提供给外部应用程序的数据访问接口,exported属性一般设置为true。

    <provider
        android:name=".MyContentProvider"
        android:authorities="quibbler.com"
        android:enabled="true"
        android:exported="false" />


3.3、authorities

        authorities就是访问ContentProvider的Uri,关于Uri详见Android Uri总算把Android这块零碎的知识点都链接起来了。


3.4、实现方法

        ContentProvider抽象类有四个抽象方法需要开发者自己去实现,分别是增删改查四个方法以及一个getType()方法。这几个方法一般操作封装的内部数据库,如果不熟练怎么写,看一看GitHub上的代码多练练就会了。

        insert(Uri uri, ContentValues values):示例参考Inserting data

        delete(Uri uri, String selection, String[] selectionArgs):详见Deleting data

        update(Uri uri, ContentValues values, String selection,String[] selectionArgs):详见Updating data

        query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder)Constructing the query

        getType(Uri uri):详见Provider Data Types


3.5、相关工具

        使用ContentProvider还得要会用UriMatcher,用它来处理Uri,好在很早之前就储备过UriMatcher的知识,详见Uri工具:UriMatcher和ContentUris。Android知识点零碎,需要先各个都学习一下,再系统的组织起来,形成自己的知识网格。



4、ContentResolver

        通过ContentResolver发出请求时,系统将检查给定URI的权限,并将请求传递给在该权限下注册的ContentProvider。ContentProvider可以根据需要解释其余的URI。

        ContentResolver主要是通过URI调用Context上下文的getContentResolver()获取ContentProvider提供的增删改查接口,进而进行增删改查等操作。

        


5、ContentObserver观察者

        ContentObserver内容观察者通过指定URI 监听ContentProvider数据是否改变,前提是定义的ContentProvider在更新数据时回调ContentResolver的notifyChange(Uri ,ContentObserver)方法。对它的底层原理很感兴趣,有机会看看源码:ContentObserver内容观察者源码解析


5.1、自定义观察者

        继承ContentObserver抽象观察者类,重写onChange()方法,该方法在当我们观察的uri发生改变的时候会调用。

    public static class MyObserver extends ContentObserver{
        public MyObserver(Handler handler) {
            super(handler);
        }
        
        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
        }
    }


5.2、注册观察者

        通过ContentResolver的registerContentObserver()方法注册ContentObserver观察者。

    ContentObserver contentObserver = new MyObserver(new Handler());
    getContentResolver().registerContentObserver(Uri.parse(""), false, contentObserver);


5.3、反注册观察者

        小心内存泄漏,Android中有很多成对的注册和反注册操作。注册后,需要反注册unregisterContentObserver()

    getContentResolver().unregisterContentObserver(contentObserver);



6、使用注意

        作为ContentProvider的使用方,也有一些需要注意的地方。而不是万事无忧,全部甩锅交给ContentProvider提供方。


6.1、content

        通过Uri访问对应Authority的ContentProvider时,必须使用"content://"作为scheme拼接构造Uri。

    Uri uri = Uri.parse("content://" + AUTHORITY);

        否则会抛出异常,从源码可以看到,根据Uri获取对应的ContentProvider:

    public final IContentProvider acquireProvider(Uri uri) {
        if (!"content".equals(uri.getScheme())) {
            return null;
        }
        final String auth = uri.getAuthority();
        if (auth != null) {
            return acquireProvider(mContext, auth);
        }
        return null;
    }

        使用的地方判断空,Uri没有能对应处理的ContentProvider就会抛出异常:

    IContentProvider provider = acquireProvider(url);
    if (provider == null) {
        throw new IllegalArgumentException("Unknown URL " + url);
    }


6.2、构造查询

        查询一般会限制条件,比如查询quer()方法:

    Cursor query(Uri uri,String[] projection,String selection,String[] selectionArgs,String sortOrder)

        如果不限定一些参数条件,会查询所有的数据,浪费资源内存。所以一般最好限定所需要的字段projection,查询的条件selection,以及参数。若执行查询的是SQL语句,所以前面的提供的字段必须存在,否则ContentProvider内部肯定会抛出异常或者内部捕获异常返回空数据(负责任的做法!)。


6.3、权限声明

        一般除了提供一个Uri,除此ContentProvider安全的做法还会声明需要的权限,只有声明该权限的授信应用才允许访问。比如系统日历的CalendarProvider2内容提供器程序就在自己的AndroidManifest.xml中声明了必须的权限,也就是我们熟知的读写日历所需的那两个权限:

    <provider android:name="CalendarProvider2" android:authorities="com.android.calendar"
            android:label="@string/provider_label"
            android:multiprocess="false"
            android:exported="true"
            android:readPermission="android.permission.READ_CALENDAR"
            android:writePermission="android.permission.WRITE_CALENDAR" />


6.4、异常捕获

        注意访问其它应用的ContentProvider并不会导致其它应用异常奔溃,因为ContentProvider的操作都是在Binder线程池中进行。反而如果操作不当,会把自己的应用搞崩溃。因为异常会“跨进程”抛回到调用者所在的程序,这个可以从源码中看出。之前遇到过很多次异常都是因为权限不够、字段变更导致查询抛出异常。

        所以在访问其它ContentProvider的时候,不清楚对方给你抛了什么,最好try-catch访问的代码块。



相关资料:

        Android Developers > Guides > Content providers

        Android Developers > Reference > ContentProvider

        Creating a content provider

        Android:关于ContentProvider的知识都在这里了

        Calendar provider overview

        Contacts Provider

        ContentProvider详解

        ContentProvider 使用详解

        

不忘初心的阿甘
最新回复 (0)
    • 安卓笔记本
      2
        登录 注册 QQ
返回
仅供学习交流,切勿用于商业用途。如有错误欢迎指出:fluent0418@gmail.com