深入Understanding Android ContentProvider详解

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

时间: 2024-09-27 16:58:43

深入Understanding Android ContentProvider详解的相关文章

Android 混淆详解

Android 混淆详解 转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/69388246 本文出自[赵彦军的博客] 混淆的基本概念 什么是混淆? 代码混淆亦称花指令,是将计算机程序的代码,转换成一种功能上等价,但是难于阅读和理解的形式的行为. 混淆的目的 1.混淆的目的是为了加大反编译的成本,但是并不能彻底防止反编译. 2.压缩apk 资源文件 开启混淆 一般我们做项目的时候,都是分为 release 和 debug 版本,re

Android Menu详解及示例代码_Android

Android Menu 详细介绍: 1.选项菜单 OptionsMenu 2.上下文菜单 ContextMenu 3.子菜单 SubMenu 组成Android用户界面的除了View以外,还有菜单和对话框,这一讲我们就共同学习一下菜单的使用. 菜单是用户界面中最常见的元素,使用也非常频繁,在Android中,菜单被分为如下三种,选项菜单(OptionsMenu).上下文菜单(ContextMenu)和子菜单(SubMenu),下面分别举例说明. 一.选项菜单 OptionsMenu Andro

Android签名详解(debug和release)

Android签名详解(debug和release) 1. 为什么要签名 1) 发送者的身份认证 由于开发商可能通过使用相同的Package Name来混淆替换已经安装的程序,以此保证签名不同的包不被替换 2) 保证信息传输的完整性 签名对于包中的每个文件进行处理,以此确保包中内容不被替换 3) 防止交易中的抵赖发生,Market对软件的要求 2. 签名的说明 1) 所有的应用程序都必须有数字证书,Android系统不会安装一个没有数字证书的应用程序 2) Android程序包使用的数字证书可以

使用OpenGL开发Android应用详解系列三

注:近三篇转载中的视锥体部分结合着来看,再参照老罗的3d变换,基本可以初步理解和完成相关视锥体调整. 使用OpenGL开发Android应用详解系列三 [原创]转载请注明出处 我一家网 http://www.5yijia.com 前面两节主要介绍了一下OpenGL的基本概念,以及在Android开发中引入OpenGL时,Android项目的基本构成情况.这一节开始,我们通过具体的实例,来进行简单3D图形的描画. 注:代码基础还是采用上一节: 使用OpenGL开发Android应用详解系列二中使用

Android CardView详解及使用方法和实例_Android

Android  CardView详解 Android5.0中向我们介绍了一个全新的控件–CardView,从本质上看,可以将CardView看做是FrameLayout在自身之上添加了圆角和阴影效果.请注意:CardView被包装为一种布局,并且经常在ListView和RecyclerView的Item布局中,作为一种容器使用. 发现个好看的东东 CardView,他在support v7包中~~ 顾名思义就是卡片view,可以设置阴影,圆角,等等.. 样子是这样的: 或者你还可以放到list

如何正确使用Android线程详解_Android

前言 对于移动开发者来说,"将耗时的任务放到子线程去执行,以保证UI线程的流畅性"是线程编程的第一金科玉律,但这条铁则往往也是UI线程不怎么流畅的主因.我们在督促自己更多的使用线程的同时,还需要时刻提醒自己怎么避免线程失控. 多线程编程之所以复杂原因之一在于其并行的特性,人脑的工作方式更符合单线程串行的特点.一个接着一个的处理任务是大脑最舒服的状态,频繁的在任务之间切换会产生"头痛"这类系统异常.人脑的多任务和计算机的多任务性能差异太大导致我们在设计并行的业务逻辑之

Android CoordinatorLayout详解及实例代码_Android

Android CoordinatorLayout详解 一.CoordinatorLayout有什么作用 CoordinatorLayout作为"super-powered FrameLayout"基本实现两个功能: 1.作为顶层布局 2.调度协调子布局 CoordinatorLayout使用新的思路通过协调调度子布局的形式实现触摸影响布局的形式产生动画效果.CoordinatorLayout通过设置子View的 Behaviors来调度子View.系统(Support V7)提供了A

Android RecyclerView详解之实现 ListView GridView瀑布流效果_Android

 什么是RecyclerView RecyclerView 是Google推出的最新的 替代ListView.GridView的组件,RecyclerView是用来显示大量数据的容器,并通过有限数量的子View,来提高滚动时的性能. 与ListView不同,RecyclerView 不再负责布局,而是专注于布局复用.布局主要通过 LayoutManager来管理,目前提供了3种常用的布局管理: LinearLayoutManager 线性布局管理器 (ListView效果) GridLayout

Android GPS详解及示例代码_Android

LBS(Location Based Services)直译的话就是基于地理位置的服务,这里面至少有两层意思,第一要能轻易的获取当前的地理位置,譬如经纬度海拔等,另一个就是在当前位置的基础上提供增值服务,譬如找附近的加油站.餐馆.酒店等.这里面的第一步:获取用户当前位置,我们就可以用Android的GPS定位服务来得到.Android提供了基于网络的定位服务和基于卫星的定位服务两种.在设置->位置和安全设置里面的前三项就是,最后一个增强型GPS是为了辅助快速找卫星的.  那么我们现在就写一个简单