Android学习笔记之ContentProvider

1.ContentProvider 内容提供者

  • 内容提供者:主要用于在不同的应用程序之间实现数据共享的功能,允许一个程序访问另一个程序中的数据,同时还能保证被访问数据的安全性。与其他存储方式不同的是:可以选择哪部分数据分享,从而保证隐私数据的安全性。

1.1ContentProvider 特点

  1. 四大组件之一,需要在 AndroidMainifest.xml 文件中进行注册。
  2. 接口的统一,是为数据的获取、添加、修改的操作提供统一的接口。
  3. 跨进程供多个应用程序共享数据;
  4. 设备存储数据:通讯录、图片;
  5. 数据更新监听:UI更新;

1.2ContentProvider 的优缺点

  1. 数据访问统一接口(存储方式都不用管)优点。
  2. 跨进程数据的访问,优点。
  3. 无法单独使用,必须与其他的存储方式结合使用,缺点。

1.3ContentProvider 如何提供数据,实现共享?

  1. 新建类去继承ContentProvider,然后覆写 query、insert、update、delete 等6个抽象方法。
  2. 必须在 AndroidManifest 文件中进行注册。
  3. 把自己的数据通过 uri 的形式共享出去。
  4. 借助UriMather.match()方法实现匹配内容URI的功能。
  5. 第三方可以通过 ContentResolver 来访问该 Provider。

1.4ContentProvider工作流程

  1. 应用程序A(提供者)定义了 ContentProvider,定义了一些数据库和文件来存放数据。
  2. 应用程序B(访问者)获得 ContentResolver 对象,根据Uri访问指定的 ContentProvider。
  3. ContentProvider 就会访问底层的存储方式,并返回数据给 ContentProvider。
  4. ContentProvider 将数据返回给 ContentResolver,ContentResolver 再转到应用B。

2.ContentResolver 内容访问者

  • 想要访问内容提供器中的共享数据,就一定要借助 ContentResolver 类,可以通过 getContentResolver() 方法获取该类实例,并且提供了一系列CRUD操作(insert、update、delete、query),不同于其他存储方式的是不接收表名参数,而是用 Uri 参数来代替(由协议声明、authority和path组成)。

2.1ContentResolver如何实现数据的访问

  • ContentResolver的Contex.getContentResolver().query()
    • 提供了CRUD操作;
    • Transport implements;
    • IContentProvider;
  • Uri,ContentResolver中的增删查改都是用Uri来代替表名参数。
    • 概念:统一资源标识符;
    • 组成:协议、域名authority、路径path;
    • content://com.example.app.authority/path;
    • 作用:访问ContentProvider;
    • 不同:与其他组件交互不同的地方(Intent);
//简单演示获取数据都代码
Cursor mCursor = getContentResolver().query(uri,projection,selection,selectionArgs,orderBy);
if (mCursor != null) {
     while (mCursor.moveToNext()) {
        String column1 = mCursor.getString(mCursor.getColumnIndex("column1"));
        int column2 = mCursor.getInt(mCursor.getColumnIndex("column2"));
    }
    mCursor.close();
}

3.ContentObserver 内容观察者

Android 内部提供了一种 ContentObserver 来监听数据库内容的变化。作用是观察(捕捉)特定 Uri 引起的数据库的变化,继而做一些相应的处理。

3.1如何定义ContentObserver

  • 第一步:创建一个 ContentObserver 的子类,实现 onChange() 方法。
    • 监听的 Uri 中的数据发生变化的时候,会调用 onchange() 方法。
    • 构造方法:public void ContentObserver(Handler handler)参数需要一个 Hanlder,因为 ContentObserver 内部使用了一个实现 Runnable 接口的内部类 NotificationRunnable,需要通过 Handler 去做比如 UI 变化。(可以从主线程传入一个 handler。)
  • 第二步:通过 registerContentObserver 注册 ContentObserver。
    • 第一个参数:需要监听的 uri。
    • 第二个参数:为 false 表示精确匹配,即只匹配该 Uri。为 true 表示可以同时匹配其派生的 Uri,如:content://com.qin.cb/student(精确匹配)content://com.qin.cb/student/# (派生)content://com.qin.cb/student/schoolchild(派生)
    • 第三个参数:ContentObserver 的实例。
  • 第三步:用完后记得取消注册 unregisterContentObserver。
  • 第四步:ContentProvider数据源发送改变后,通知ContentObserver。
//第一步: 创建一个 ContentObserver 的子类,实现 onChange() 方法。
public class MyContentObserver extends ContentObserver{
    private Handler handler;
    public MyContentObserver(Handler handler) {
        super(handler);
        this.handler=handler;
    }
    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);
        //  在onChange方法里面我们就可以执行变化的操作了
    }
}
//第二步:将这个监听者与我们所要监听的对象绑定
//通过 registerContentObserver 注册 ContentObserver。
MyContentObserver mObserver = new MyContentObserver(); 
getContentResolver().registerContentObserver(Uri uri, boolean notifyForDescendants, ContentObserver observer);
//第三步:取消注册 unregisterContentObserver
getContentResolver().unregisterContentObserver(mObserver)
//ContentProvider数据源发送改变后,通知ContentObserver。
getContext().getContentResolver().notifyChange(uri, null);

3.2也可以这样简单写


public class Main2Activity extends AppCompatActivity {
    Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //在主线程更新UI  adapter.notifyChangeSet();
        }
    };
    ContentObserver observer = new ContentObserver(mHandler) {
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            //Uri数据改变,非Ui线程不能直接更新
            //发送消息
        }
    };
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //注册内容观察者
        this.getContentResolver().registerContentObserver(uri, notifyForDescendants, observer);
    }
}

3.3为什么自定义的 ContentProvider 数据源发生改变后,却没有监听到任何反应?

每个ContentProvider数据源发生改变后,如果想通知其监听对象, 例如ContentObserver时,必须在其对应方法 update / insert / delete 时,显式地调用 this.getContentReslover().notifychange(uri,null) 方法,回调监听处理逻辑。否则,我们的 ContentObserver 是不会监听到数据发生改变的。

4.说说ContentProvider、ContentResolver、ContentObserver 之间的关系

  • ContentProvider内容提供者,用于对外提供数据,提供数据的增删改查操作,数据源可以是数据库、文件、XML、网络等,ContentProvider为这些数据的访问提供了统一的接口,可以用来做进程间数据共享。
  • ContentResolver内容解析者,用于获取内容提供者提供的数据。ContentResolver可以不同URI操作不同的ContentProvider中的数据,外部进程可以通过ContentResolver与ContentProvider进行交互。
  • ContentObserver内容监听器,可以监听数据的改变状态,当ContentProvider数据源发生改变时,如果想通知其监听对象, 例如ContentObserver时,就必须在其调用对应方法 update / insert / delete 时,显式地调用 this.getContentReslover().notifychange(uri,null) 方法,回调监听处理逻辑。

5.为什么要用 ContentProvider?它和 sql 的实现上有什么差别?

  • ContentProvider 屏蔽了数据存储的细节,内部实现对用户完全透明,用户只需要关心操作数据的
    uri 就可以了,ContentProvider 可以实现不同 app 之间共享。

  • Sql 也有增删改查的方法,但是 sql 只能操作本应用下的数据库。而 ContentProvider 还可
    以去增删改查本地文件. xml 文件的读取等。

6.多个进程同时调用一个ContentProvider的query获取数据,ContentPrvoider是如何反应的呢?

尽管ContentResolver与ContentProvider类隐藏了实现细节,ContentProvider除了onCreate()是在UI线程运行,其余所提供的query(),insert(),delete(),update()都是在ContentProvider进程的线程池中被调用执行的,而不是进程的主线程中。因为那些方法可能同时被多个线程所调用,所以他们都应该是线程安全的。这个线程池是由Binder创建和维护的,其实使用的就是每个应用进程中的Binder线程池。

7.你觉得Android设计ContentProvider的目的是什么呢?

  1. 隐藏数据的实现方式,对外提供统一的数据访问接口;
  2. 更好的数据访问权限管理。ContentProvider可以对开发的数据进行权限设置,不同的URI可以对应不同的权限,只有符合权限要求的组件才能访问到ContentProvider的具体操作。
  3. ContentProvider封装了跨进程共享的逻辑,我们只需要Uri即可访问数据。由系统来管理ContentProvider的创建、生命周期及访问的线程分配,简化我们在应用间共享数据(进程间通信)的方式。我们只管通过ContentResolver访问ContentProvider所提示的数据接口,而不需要担心它所在进程是启动还是未启动。

8.运行在主线程的ContentProvider为什么不会影响主线程的UI操作?

  • ContentProvider的onCreate()是运行在UI线程的,而query(),insert(),delete(),update()是运行在线程池中的工作线程的,所以调用这几个方法并不会阻塞ContentProvider所在进程的主线程,但可能会阻塞调用者所在的进程的UI线程!
  • 所以,调用ContentProvider的操作仍然要放在子线程中去做。虽然直接的CRUD的操作是在工作线程的,但系统会让你的调用线程等待这个异步的操作完成,你才可以继续线程之前的工作。

你可能感兴趣的:(Android学习笔记之ContentProvider)