本文共 7615 字,大约阅读时间需要 25 分钟。
综述 Content providers是一个Android应用程序的主要部分,主要是为应用程序提供内容。它对数据进行封装然后通过单一的ContentResolver接口提供给应用程序。只有需要在多个应用程序间共享数据时,content provider才是必须的。例如:有多个应用程序(例如:打电话程序和发短信程序等等)都需要使用联系人的数据,因此我们必须将这些数据存放在content provider。如果不需要在多个应用程序中共享数据,那么可以直接在应用程序中使用SQLiteDatabase。 当程序通过一个ContentResolver接口请求Content provider时,系统就会检查传递进来的URI的权限,并且将请求传递给拥有这些权限的content provider。content provider可以以任意的方式解析URI。UriMatcher类对于解析URIs是非常有帮助的。 对于content provider主要要实现的方法有如下几个: onCreate():主要是初始化provider; query(Uri, String[], String, String[], String):讲数据返回给它的调用者 insert(Uri, COntentValues):插入新的数据到content provider update(Uri, COntentValues, String, String[]):更新content provider中的数据 delete(Uri, String, String[])删除content provider中的数据 getType(Uri):返回content provider中数据的MIME类型 数据存取方法(例如:insert(Uri, ContentValues)和update(Uri, ContentValues, String, String[]))可能会在同一时间被多个线程调用,并且是线程安全的。其他的方法(例如:onCreate())仅仅被程序的主线程调用,而且必须要避免在其中进行耗时的操作。具体的请看方法的描述。 对于ContentResolver将会自动的传给合适的ContentProvider实例,因此子类不需要担心夸进程调用的细节。 开发者指南 对于如何使用content provider的细节,请阅读Content Providers开发者指南。 下面是Content Providers开发者指南: Content Providers存取数据,并且使这些数据对于所有的程序都是可用的。Android系统中并没有对任何程序都可使用的共同存储区域,因此,这是在多个应用程序间共享数据的唯一方法。 Android系统包含许多的对常见类型数据(音频、视频、图片以及个人联系信息等等)的content providers。你可以在android.provider包中看到一部分的content provider。你可以查询这个content provider中包含的数据(尽管对于其中的一部分数据,你必须要拥有合适的权限)。 如果你想让自己的数据对外部公开,你有两个选择: 1.你可以创建你自己的content provider(一个Content Provider子类) 2.你可以将自己的数据添加到已经存在的content provide,如果存在包含着和你想公开数据的相同类型数据的content provider,并且你拥有权限,你就可以将这些数据写入到这个content provider中。 这篇文档是介绍如何使用content provider。在简洁的讨论了一下基本后,这些讨论覆盖了如何查询一个content provider中的内容,如何修改一个content provider中包含的数据,如何为自己创建一个content provider。 Content Provider 基础知识 content provider如何存储数据是依赖于它的设计的。但是所有的content provider都实现了一个共同的接口用于查询provider并且返回结果--以及:添加,修改和删除数据等。 客户端不会直接使用这个接口,几乎都是通过ContentResolver对象。你可以通过实现在Activity中或其它应用程序组件中的getContentResolver()方法来获得ContentResolver. ContentResolver cr = getContentResolver(); 你可以通过ContentResolver的方法和感兴趣的content providers进行交互。 当一个查询操作初始化后,Android系统识别查询要进行的操作对象,并且确保它处于运行状态。系统实例化了所有的Contentprovider对象:你根本不需要进行这样的操作。实际上,你从来就不会直接和ContentProvider打交道。实际上,对于每一种ContentProvider,系统仅仅有一个实例。但是这一个ContentProvider实例却可以和处于多个程序、多个进程中的ContentResolver对象进行通信。多个进程间的交互是通过ContentResolver类和ContentProvider类进行的。 数据模型 Content providers是像数据库中的表的模型一样暴露自己所包含的数据,每一行都是一条记录,每一列都是一个特定的类型并且有特定的意义。例如:联系人的信息以及他们的电话号码就可能像如下一样存储:这里有一些帮助的方法,特别是ContentUris.withAppendedId()和Uri.withAppendedPath()方法,他们使得追加一个ID到URI上变得更加容易。这两个静态方法都返回追加了ID的Uri对象。因此,如果你想在联系人的数据库中查找到标示为23的这条记录,你可以像下面这样构建一个查询器:
import android.provider.Contacts.People; import android.content.ContentUris; import android.net.Uri; import android.database.Cursor; // Use the ContentUris method to produce the base URI for the contact with _ID == 23. Uri myPerson = ContentUris.withAppendedId(People.CONTENT_URI, 23); // Alternatively, use the Uri method to produce the base URI. // It takes a string rather than an integer. Uri myPerson = Uri.withAppendedPath(People.CONTENT_URI, "23"); // Then query for this specific record: Cursor cur = managedQuery(myPerson, null, null, null, null);对于query()方法和managedQuery()方法的其他参数的详细介绍如下: 1.要返回的列的名称。如果指定为null,那么就会返回所有的列。否则,只有那些别列出的名字的列才会被返回。所有的content provider都有平台定义的列常量。例如:android.provider.Contacts.Phones类定义了phone表中的列的名字,就像之前介绍的_ID,NUMBER,NUMBER_KEY,NAME等等。 2.对于要返回的行的过滤器,如同SQL语句中的WHERE语句(除掉WHERE自身)。若指定为N,则返回所有的行(除非指定了只查询特定的单个记录)。 3.进行选择的参数 4.一个代表返回的所有的行排序的参数,如同SQL语句中的QRDER BY语句(除了ORDER BY自身)。若指定为null,那么就按照记录在表中的记录返回,有可能是无序的。 让我们看看一个查询并获取联系人名字和他们电话号码的例子:
import android.provider.Contacts.People; import android.database.Cursor; // Form an array specifying which columns to return. String[] projection = new String[] { People._ID, People._COUNT, People.NAME, People.NUMBER }; // Get the base URI for the People table in the Contacts content provider. Uri contacts = People.CONTENT_URI; // Make the query. Cursor managedCursor = managedQuery(contacts, projection, // Which columns to return null, // Which rows to return (all rows) null, // Selection arguments (none) // Put the results in ascending order by name People.NAME + " ASC");这个查询操作从Contacts content provider的People表中获得数据。它获得了联系人姓名和他们的主要电话号码以及每一条记录的ID。他也将查询到的总的记录数通过_COUNT域返回。 对于每个列的名字的常量是定义在不同的接口中的——_ID和_COUNT在BaseColumns。NAME是在PeopleColumns,NUMBER是在PhoneColumns。Contacts.People类实现了所有的这些接口,这也就是为什么上面的示例中我们可以仅仅通过类名就引用这些方法。 一个查询操作的返回值 一个查询操作会返回0条或者是更多的数据库记录。列的名称,它们默认的顺序,以及他们对于每个contect provider的数据类型。每一个provider都有一个数值型的_ID列,也就是每条记录的唯一标示ID。每一个provider也可以通过_COUNT列来返回这次查询操作总共返回了多少条记录,对于返回的每条记录,_COUNT域的值都是一样的。 下面是通过查询操作返回的上面例子的结果的示例:
import android.provider.Contacts.People;private void getColumnData(Cursor cur){ if (cur.moveToFirst()) { String name; String phoneNumber; int nameColumn = cur.getColumnIndex(People.NAME); int phoneColumn = cur.getColumnIndex(People.NUMBER); String imagePath; do { // Get the field values name = cur.getString(nameColumn); phoneNumber = cur.getString(phoneColumn); // Do something with the values. ... } while (cur.moveToNext()); }}如果一个查询操作可以返回二进制数据,例如图片或者是声音,那么这些数据可能会直接记录在表中,或者是对于这类型数据可能会特殊指定一个字符串content:URI指定表的入口来允许我们对这类型的数据进行存取。一般地,少量的数据(从20K到50K或者更少的)几乎都是直接记录在表中的并且可以通过Cursor.getBlob()方法读取,他返回一个byte类型的数组。 如果表的入口是一个content:URI,你不应该直接打开或者读取(因为一件事,权限问题可能导致这个操作失败)。此时,你应该使用ContentRecesolver.openInputStream()方法获得一个InputStream对象,再通过这个InputStream对象来读去数据。
转载地址:http://jvqab.baihongyu.com/