欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

深入Understanding Android ContentProvider详解

程序员文章站 2023-11-20 17:36:10
1. 什么是contentprovider也即内容提供者,是对所有数据访问的一层抽象,为数据访问提供了统一的接口。它有以下优点:a. 对数据的抽象,为所有的组件提供统一的访...
1. 什么是contentprovider
也即内容提供者,是对所有数据访问的一层抽象,为数据访问提供了统一的接口。它有以下优点:
a. 对数据的抽象,为所有的组件提供统一的访问数据的方式,从而让组件不必关心具体数据的呈现形式(文件or数据库)。数据,也可以只关心自身的管理,而不用去管使用者的访问问题。这样就达到了很好的封装。
b. 接口更加方便,更加方便的让组件之间传送数据
contentprovider的访问标识为uri,通过统一的contentresolver进行访问,而contentresolver和uri跟application的上下文context以及组件之间的信息传送工具intent都是无缝接合,这就让组件之间进行数据共享和数据传递更加的方便和快捷。
所以,contentprovider的最大好处在于它可以在不同组件之间方便的共享。所以,如果你的应用里面用到的数据需要在不同的组件之间共享,那么实现一个contentprovider无疑是最佳方案。
2. 实现方式
contentprovider的实现方式非常简单,只需要根据需求实现一些接口即可,比如:query, insert, delete, update, openfile等。但是具体的数据的呈现形式则是根据不同的目的进行*选择,比如对于结构化数据,选择sqlitedatabase可能是比较好的方案,大量的字节流可能文件是首选等等。
需要注意一点的是,虽然android中百分之九十的contentprovider内部都是用sqlitedatabase来存储结构化数据,但这并不意味着contentprovider只能从sqlitedatabase来管理数据。contentprovider定义了一些接口,你只需要按照需要返回正确的数据即可,具体 的实现方式则由你*选择。
比如,contacts的contentprovider能提供以vcard的方式输出,也就是说当读取一个vcard的uri时,这个流是一个vcard形式的文件流,实现起来的思路就是这样:
复制代码 代码如下:

cursor query(uri, ....) {
   if (uri is for vcard) {
       query the contact's infomation
       create a cursor with two columns name and size
       put contact's name into cursor
       sum all contact's field  and get size
       put that size into cursor
       return the cursor
   }
}

这样通过query就能得到这个vcard的相关信息文件名字和大小,再通过openinputstream就可以读取这个vcard文件流,但是实际上contentprovider是没有vcard形式的数据,也没有一个vcard的文件,它只是在openfile的时候,识别出vcard的uri,把contact数据转化成vcard形式写入输出流中:
复制代码 代码如下:

parcelfiledescriptor openfile(uri...) {
    if (uri is for vcard) {
       generate vcard with vcardcomposer
       write to output stream
    }
}

3. 其他替代方案
contentprovider不是必须的,每个应用必然用到数据,但是可以选择用创建一个contentprovider来管理,也可以直接使用文件或数据库,如下面的例子:
复制代码 代码如下:

package com.android.effective;
import android.app.activity;
import android.content.contentvalues;
import android.content.context;
import android.database.cursor;
import android.database.sqlite.sqlitedatabase;
import android.database.sqlite.sqliteopenhelper;
import android.database.sqlite.sqlitedatabase.cursorfactory;
import android.os.bundle;
import android.util.log;
public class sqlitedatabasedemo extends activity {
    private static final string tag = "sqlitedatabasedemo";
    @override
    public void oncreate(bundle savedinstancestate) {
        super.oncreate(savedinstancestate);
        mydatabase db = new mydatabase(this);

        int id = db.setname("michael jordan");
        log.e(tag, "id of " + id + " is " + db.getname(id));
    }

    private class mydatabase {
        private static final string name = "demo.db";
        private static final string table = "demo";
        private final string[] projection = new string[] {"_id", "name" };
        private mydatabasehelper helper;

        public mydatabase(context context) {
            helper = new mydatabasehelper(context, name, null, 1);
        }

        public string getname(int id) {
            final cursor c = helper.getreadabledatabase().query("demo", projection, "_id=" + id,
                    null, null, null, null);
            if (c == null || !c.movetofirst()) {
                return null;
            }
            return c.getstring(1);
        }

        public int setname(string name) {
            contentvalues cv = new contentvalues();
            cv.put("name", name);
            return (int) helper.getwritabledatabase().insert(table, "name", cv);
        }
    }

    private class mydatabasehelper extends sqliteopenhelper {
        public mydatabasehelper(context context, string name,
                cursorfactory factory, int version) {
            super(context, name, factory, version);
        }
        @override
        public void oncreate(sqlitedatabase db) {
            db.execsql("create table demo (_id integer primary key, name text);");
        }
        @override
        public void onupgrade(sqlitedatabase db, int old, int newver) {

        }
    }
}

这个例子中就没有使用contentprovider而是让activity直接操作sqlitedatabase来实现数据的管理,或者不用数据库而直接使用文件进行管理数据。
这种方式实现起来可能更简单,对于需求不大,数据量不大,且只有单一组件使用的情况下,完全可以用这种方式。但是它的缺点也很明显,就是在组件之间传递会十分麻烦,甚至不能够在组件之间共享。为了共享,就要把数据层进行抽象,使其独立于任何一个activity,以满足不同的组件对数据进行读写,但是这样一来跟实现一个contentprovider就没有区别了,还不如实现一个contentprovider来的方便。
所以,规则就是如果某些数据只在一个activity中使用,那么没有必要创建contentprovider,直接使用文件或直接操作database就可以达到目的。但是如果需要跟其他的组件进行共享和传递数据,就必须使用contentprovider。
另外,有了contentprovider也可以方便跟其他应用进行交互,把数据传递给其他应用的组件。
在使用sqliteopenhelper一定要注意线程同步问题,保证每一个sqlitedatabase的方法(如execsql)的线程安全性,否则可能会引起十分罕见的异常。曾遇到一个sqlitestatement报出的npe(nullpointerexception),就是由于有多个线程在操作同一个sqliteopenhelper,而且没有同步。