当前位置: 移动技术网 > IT编程>移动开发>Android > 深入Understanding Android ContentProvider详解

深入Understanding Android ContentProvider详解

2019年07月24日  | 移动技术网IT编程  | 我要评论

鬼船重生,斯洛克,郭德纲文武双全

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,而且没有同步。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网