ContentProvider , ContentResolver, ContentObserver 相关

关于 ContentResolverContentObserver

ContentObserver

ContentObserver 内容观察者,观察 Uri 引起 ContentProvider 中的数据变化,并且去通知,在 onChange()方法中,回调监听。

它的简单使用:

  1. 创建一个类,继承与 ContentObserver, 实现它的 onChange() 方法:
private class MediaContentObserver extends ContentObserver {

    private Uri contentUri;

   /**
    * Creates a content observer.
    *
    * @param handler The handler to run {@link #onChange} on, or null if none.
   */
   public MediaContentObserver(Handler handler, Uri contentUri) {
       super(handler);
        this.contentUri = contentUri;
   }

   @Override
   public void onChange(boolean selfChange) {
      super.onChange(selfChange);
      
      handlerMediaContentChange(contentUri);
   }
}
  1. 然后创建一个实例,并注册:
    ContentObserver externalObserver = new MediaContentObserver(handler,    MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
Application.getContext().getContentResolver().registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                false, externalObserver);

注意: 需要一个 handler, 因为 ContentObserver 内部使用了一个实现 Runnable 接口 的内部类 NotificationRunnable, 需要通过 handler 去做比如 UI 变化。

    /**
    * uri 是需要监听的 uri;
    * 第二个参数: false 表示要精确匹配, 即只匹配该URI, 为 true  表示可以同时匹   配其派生 的 URI (例如:content://com.qin.cb/student 精确匹配;content:// com.qin.cb.student/# 派生, 为true 时,才会匹配到)
    */
    registerContentObserver(Uri uri, boolean notifyForDescendants,  ContentObserver observer)

  1. 注册后,在需要销毁的地方记得及时销毁 :

    getContentResolver().unregisterContentObserver(externalObserver);
    

它与 ContentProvider 的使用

  1. 注册 ContentObserver

    getContentResolver().registerContentObserver (uri);
    
  2. 当该 URIContentProvider 数据发生变化时, 通知外界。

    public class TestContentProvider extends ContentProvider {
        
        ...
        public Uri insert(Uri uri, ContentValues values) {
            db.insert("user", "userId", values);
            
            //通知访问者
            getContext().getContentResolver.notifyChange(uri, null);
        }
    }
    
  3. unregister

    同上。


URI

上述 URI (Uniform Resource Identifier)统一资源标识符 在 ContentProvider 中有固定的格式:

content://Authority/Path/Id

前面 的 content 是不可改变的,固定的部分;

Authority: 表示授权信息,用于区别不同的 ContentProvider

Path: 表名, 用以区分 ContentProvider 中不同的数据表;

Id: Id 号, 用以区别表中的不同数据


ContentProvider

是进程间进行数据交互 的工具, 可实现跨进程通信.

看到一个实现了 ContentProvider 的类主要代码:

public class TestContentProvider extends ContentProvider {

    ...
    
    public static Uri createBaseUri(...) {
        ...
        return ...;
    } 
    
    @Nullable
    @Override
    public Bundle call(String method, String arg, Bundle extras) {
        ...     
    }
    
    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        return null;
    }

    @Nullable
    @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;
    }
    
}

主要操作 是为了数据的操作提供了接口回调的方法。

ContentProvider 并不会直接与外面的进程进行数据交互,而是通过 ContentResolver.


ContentResolver

它可以帮助我们去查询所有有关SD卡目录下的一些文件信息,例如 媒体文件, 通话记录,照片等。

ContentResolver 提供了与 ContentProvider 相同名字的方法,用于数据的增,删查,改。

public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
            @Nullable String[] projection, @Nullable String selection,
            @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        
    return query(uri, projection, selection, selectionArgs, sortOrder, null);
        
}

//其他几个方法类似

它的参数含义:

  • Uri uri : 用于检索的 URI;

  • String[] projection: 查询后用来返回 的列的列表。 当值为 null 时,会返回所有的列,效率会比较低;

  • @Nullable String selection: 用来声明要返回那些行的过滤器,其格式为 SQL WHERE 子句。 当为 null 时,会返回所有行。

  • @Nullable String[] selectionArgs: 与第三个参数 selection 可配合使用。可以在 selection 中包含 "?", 它将按照在 selection 中显示的顺序替换 为 selectionArgs 中的值(字符串), 例如:

    CallLog.Calls.DATE + ">=? and " + CallLog.Calls.TYPE + "=" +
                    CallLog.Calls.MISSED_TYPE, new String[]{ "" + getTimeInMillisSomeDaysAgo(30) }
    

    意思是,查询 CallLog.Call.DATE >= 30天,并且 TYPE = MISSED_TYPE 的通话记录.

  • @Nullable String sortOrder: 行的排列依据,按照时间降序排列等, 当为 null 时 将使用默认排序顺序。

它的使用更加的常见,例如:


//获取到媒体库中的照片文件,该文件满足,时间降序的第一个文件,且只包含两列数据
cursor = getContext().getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                    new String[] {
                    MediaStore.Images.ImageColumns.DATA,
                    MediaStore.Images.ImageColumns.DATE_TAKEN
                    }, null, null,
                    MediaStore.Images.ImageColumns.DATE_ADDED + " desc limit 1");


// 去获取到系统所有的通话记录,并且只包含每个 callLog 的几列数据:
//CallLog.Calls.NUMBER, CallLog.Calls.DATE, CallLog.Calls.CACHED_NAME, CallLog.Calls.TYPE

cursor = getContext().getContentResolver().query(CallLog.Calls.CONTENT_URI, new String[]{
                    CallLog.Calls.NUMBER,
                    CallLog.Calls.DATE,
                    CallLog.Calls.CACHED_NAME,
                    CallLog.Calls.TYPE }, null, null, CallLog.Calls.DEFAULT_SORT_ORDER);

getContext().getContentResolver().delete(CallLog.Calls.CONTENT_URI, whereCause.toString(), null);

CallLog.Calls.CONTENT_URI 对应的是手机系统的 通话记录Uri 部分

删除符合条件的 whereCause 的通话记录。


有关 ContentProvidercall() 方法

这是一个 未公开的函数.

当从 ContentProvider 中获取疏浚时,一般都是通过调用它的 query()来获得,而这个函数将数据放在 cursor 中来返回给调用者(上面的用法即是这样的用法)。

而上述过程,ContentProvider 传给第三方应用程序的数据,是通过匿名共享内存来传输的。当传输的数据量大时, 使用匿名共享内存来传输数据是有很大好处的,可以减少数据的拷贝,提高传输效率.

但是 当传输的数据比较少时,使用匿名共享内存来作为媒介就有点大材小用了,系统创建匿名共享内存也是有开销的。所以,ContentProvider 提供了 call() 函数,让开发者来获取一些自定义数据,这些数据一般都比较小,例如传输一个整数。 这样就可以达到利用好很小的代价达到跨进程传输数据的目的.


个人对 ContentProvider 的看法

所以, 当有部分很小的数据需要在两个进程间进行传递时,也利用 ContentProvider 进行操作。

假设进程 A, 进程B ,都需要访问一个数据 TestData, 那么我们可以利用 ContentProvider 中利用 call()函数, 利用 Bundle 去传值。

例如:

// 在一个 ContentProvider 中

public static int getTestData(){
    Bundle bundle = ....call(uri, METHOD_GET_TEST_DATA, null, null);
    return null == bundle ? 0 : bundle.getInt(EXTRA_KEY_TEST_DATA, 0); 
}

public static void setTestData(int test) {
    Bundle bundle = new Bundle();
    bundle.putInt(EXTRA_KEY_TEST_DATA, test);
    ....call(uri, METHOD_SET_TEST_DATA, null , bundle);
}

//复写 的  call() 方法
@Nullable
@Override
public Bundle call(String method, String arg, Bundle extras) {
    Bundle bundle = new Bundle();
    switch(method) {
        case METHOD_GET_TEST_DATA:
            bundle.putInt(EXTRA_KEY_TEST_DATA, PreferenceHelper.getInt(PREF_KEY_TEST_DATA, 0)(此处为 value));
            break;
            
        case METHOD_SET_TEST_DATA:
            PreferenceHelper.putInt(PREF_KEY_TEST_DATA, extras.getInt(EXTRA_KEY_TEST_DATA));
            break;
            
        case ...
        
        default:
            break;
    }
    
    ...
    
    return bundle;
}

上面介绍了一些 对 ContentObserver, ContentProvider, ContentResolver 这三者的简单使用。

如有错误的地方,请指正,谢谢。

参考链接:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容