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 这三者的简单使用。

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

参考链接:

  • android ContentProvider 的应用实例

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

你可能感兴趣的:(ContentProvider , ContentResolver, ContentObserver 相关)