当前位置:  编程技术>移动开发

深入Understanding Android ContentProvider详解

    来源: 互联网  发布时间:2014-10-16

    本文导语:  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,而且没有同步。

    
 
 

您可能感兴趣的文章:

 
本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • 深入JDBC sqlserver连接写法的详解
  • 深入mysql YEAR() MONTH() DAYOFMONTH()日期函数的详解
  • 深入SQLServer中ISNULL与NULLIF的使用详解
  • 深入C++可见性与生命期的区别详解
  • 深入mysql并发插入优化详解
  • 深入android Unable to resolve target 'android-XX'详解
  • 深入MYSQL字符数字转换的详解
  • 深入SQL Server中定长char(n)与变长varchar(n)的区别详解
  • 深入C#任务管理器中应用程序选项隐藏程序本身的方法详解
  • 深入Windows下的回车是回车换行(rn)还是换行回车(nr)的详解
  • 深入分析NTFS中文件被锁定导致Process.Start失败的详解
  • 深入C# 内存管理以及优化的方法详解
  • 深入c# Func委托的详解
  • 深入分析Java内存区域的使用详解
  • Informatica bulk与normal模式的深入详解
  • 深入JAVA对象深度克隆的详解
  • 深入mysql存储过程中表名使用参数传入的详解
  • 深入SQL截取字符串(substring与patindex)的详解
  • 深入Java不可变类型的详解
  • 深入Android开发FAQ的详解
  • Docker支持更深入的容器日志分析
  • 关于《深入浅出MFC》
  • Linux有没有什么好的高级的书,我要深入,
  • 深入理解linux内核
  • [100分]有没有关于binutils的深入的资料?或者深入底层的资料?
  • 深入理解PHP内核 TIPI
  • 想深入学习Java应该学习哪些东西
  • 哪位有《JSP深入编程》电子版?
  • 想要深入学习LINUX该学什么?
  • 100分求:哪儿有《深入理解linux内核》可供下哉!
  • 如何深入Linux的内核学习?


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3