深入Understanding Android ContentProvider详解

所属分类: 软件编程 / Android 阅读数: 1936
收藏 0 赞 0 分享
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,而且没有同步。
更多精彩内容其他人还在看

使用ViewPager实现android软件使用向导功能实现步骤

现在的大部分android软件,都是使用说明,就是第一次使用该软件时,会出现向导,可以左右滑动,然后就进入应用的主界面了,下面我们就实现这个功能
收藏 0 赞 0 分享

android在异步任务中关闭Cursor的代码方法

android在异步任务中如何关闭Cursor?在我们开发应用的时候,很多时候会遇到这种问题,下面我们就看看代码如何实现
收藏 0 赞 0 分享

Android自定义桌面功能代码实现

android自定义桌面其实很简单,看一个例子就明白了
收藏 0 赞 0 分享

android将图片转换存到数据库再从数据库读取转换成图片实现代码

有时候我们想把图片存入到数据库中,尽管这不是一种明智的选择,但有时候还是不得以会用到,下面说说将图片转换成byte[]数组存入到数据库中去,并从数据库中取出来转换成图像显示出来
收藏 0 赞 0 分享

TextView显示系统时间(时钟功能带秒针变化

用System.currentTimeMillis()可以获取系统当前的时间,我们可以开启一个线程,然后通过handler发消息,来实时的更新TextView上显示的系统时间,可以做一个时钟的功能
收藏 0 赞 0 分享

Android用ListView显示SDCard文件列表的小例子

本文简单实现了用ListView显示SDCard文件列表,目录的回退等功能暂不讨论,获取文件列表,files即为所选择目录下的所有文件列表
收藏 0 赞 0 分享

Android拦截外拨电话程序示例

这篇文章主要介绍了Android拦截外拨电话的示例,大家参考使用吧
收藏 0 赞 0 分享

通过Html网页调用本地安卓(android)app程序代码

如何使用html网页和本地app进行传递数据呢?经过研究,发现还是有方法的,总结了一下,大致有一下几种方式
收藏 0 赞 0 分享

android Textview文字监控(Textview使用方法)

以手机号充值为例,当用户输入最后一位数时候,进行汇率的变换,本文就实现类似这样的功能
收藏 0 赞 0 分享

Android ListView长按弹出菜单二种实现方式示例

这篇文章主要介绍了Android ListView长按弹出菜单的方法,大家参考实现
收藏 0 赞 0 分享
查看更多