Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Content Provider Tutorial--安卓內容提供者系列4--如何創建內容提供者

Android Content Provider Tutorial--安卓內容提供者系列4--如何創建內容提供者

編輯:關於Android編程

Creating a Content Provider(如何創建一個ContentProvider)

Content Provider Overview(ContentProvider概述)

A content provider manages access to a central repository of data.

You implement a provider as a subclassContentProvider, which is the interface between your provider and other applications.

You can define other related classes, such as acontract classto define public constants such as field names to help other applications access the data from your content provider.

一個Content Provider管理著對一個中央數據倉庫的訪問。

你要創建一個ContentProvider只要寫個子類實現ContentProvider接口就可以了,這是你的ContentProvider和其他應用程序之間的接口。

你可以定義其他相關類,比如合約類來定義諸如字段名稱這樣的公共常量,從而使其他應用程序能訪問到你的ContentProvider裡的數據。

In addition to the content provider, you can implement activities in your application that allow the user to query and modify the data managed by your provider.

除了content provider,你還可以在你的應用程序中創建activity,使用戶能夠查詢和修改由你的ContentProvider管理的數據。

You can expose these activities to other applications by registering implicit intent filters in your application manifest to launch appropriate activities.

通過在你的應用程序清單文件中注冊隱式意圖過濾器,你可以把這些activity暴露給其他應用程序去啟動。

Some examples of implicit intent actions you might want to support are:

這是一些你可能需要支持的隱式意圖的action:

Intent.ACTION_VIEW

Launch an activity to view a single record(啟動一個activity去查看單個記錄)

Intent.ACTION_EDIT

Launch an activity to edit a single record(啟動一個activity去編輯單個記錄)

Intent.ACTION_PICK

Launch an activity to select a record from the collection stored in the provider

(啟動一個activity去從ContentProvider中的集合中挑出一個記錄)

In addition to these actions, your intent filters should typically include a MIME type to identify the type of data managed by your provider. The data MIME types are discussed later in this section.

除了這些action,你的意圖過濾器通常還應該包括一個MIME類型去識別由你的ContentProvider管理的數據類型。數據的MIME類型將在本節稍後討論。

Why Implement a Content Provider?(為什麼要實現一個ContentProvider?)

You should implement a content provider if you want to:

Offer complex data or files to other applications

Allow users to copy complex data from your app into other apps

Provide custom search suggestions using the search framework

Expose data collections for use in application widgets

如果你想達到下面的目的,你就應該實現一個ContentProvider:

為其他應用程序提供復雜的數據或文件

允許用戶將復雜的數據從你的應用程序復制到其他應用程序

使用搜索框架提供自定義搜索建議

在應用程序的小部件中公開數據集合供使用

You don’t need a content provider to manage access to an SQLite database if the use is entirely within your own application. However, even for use within a single application, you might consider implementing a content provider to:

Encapsulate access to a data source

Use the CursorLoader class to take advantage of the loader framework added in Honeycomb, which automatically uses a worker thread to query a content provider and return a Cursor

如果是完全在自己的應用程序中管理對SQLite數據庫的訪問,那你並不需要content provider。但是,即使是在單個應用程序中,出於以下目的考慮,你應該試試去實現一個ContentProvider:

封裝對一個數據源的訪問

使用CursorLoader類去充分利用Android3.0中添加的加載器框架,該框架會自動使用一個工作線程去查詢ContentProvider並返回一個游標。

Steps to Implementing a Content Provider(實現ContentProvider的步驟)

Follow these steps to build your provider:(跟著下面的步驟走)

Design the raw storage for your data. A content provider can offer data in two ways:

"Structured" data

Data that normally goes into a database, array, or similar structure.

Store the data in a form that’s compatible with tables of rows and columns.

A row represents an entity, such as a person or an item in inventory.

A column represents some data for the entity, such a person’s name or an item’s price.

A common way to store this type of data is in an SQLite database, but you can use any type of persistent storage.

為你的數據設計原始存儲。content provider可以有兩種方式提供數據:

“結構化”數據——通常是存在數據庫,數組,或類似結構的數據。

以一種類似表那樣的行和列的形式將數據存儲起來。

一行代表一個實體,正如一個人或庫存中的一個項目。

一列代表該實體的一些數據,比如一個人的名字或一個項目的價格。

存儲這類數據一個常見方式是存在一個SQLite數據庫中,當然你也可以使用任何其他類型的持久化存儲方式。

File data

Data that normally goes into files, such as photos, audio, or videos.

Store the files in your application’s private space.

In response to a request for a file from another application, your provider can offer a handle to the file.

文件數據——指通常寫入到文件中的數據,如照片、音頻或視頻等

將文件存儲在應用程序的私有空間。

為了響應其他應用程序向你發起的文件請求,你的ContentProvider需要提供一個該文件的句柄。

Define the provider’s external interface.

This includes its authority string, its content URIs, and column names.

Also define the permissions that you will require for applications that want to access your data.

Consider defining all of these values as constants in a separatecontract classthat you can expose to other developers.

定義ContentProvider的外部接口。

這包括其授權字符串,及其內容uri,還有列名。

為了能讓其他應用程序可以訪問你的數據,你也需要定義相應的權限。

另一個要考慮的是:你要在一個單獨的合約類裡定義所有這些值並暴露出來,這樣可以使其他開發人員也能使用。

Define a concrete implementation of the ContentProvider class and its required methods.

This class is the interface between your data and the rest of the Android system.

定義一個ContentProvider的具體實現類及其所需的方法。

這個類是你的數據和Android系統的其他部分之間的接口。

Designing Data Storage(設計數據存儲方式)

You can store the data in any form you like, and then design the interface to read and write the data as necessary. Options include:

For table-oriented data, Android includes an SQLite database API that Android’s own providers use to store table-oriented data. TheSQLiteDatabaseclass is the base class for accessing databases, theSQLiteOpenHelperclass helps you create databases and handle upgrade scenarios.

您可以以任何你喜歡的方式來存儲數據,然後在必要的時候設計讀取和寫入數據的接口。有下面這些選項可選:

對於面向表的數據,Android包含一個SQLite數據庫API,Android自己的ContentProvider就是使用它來存儲面向表的數據的。SQLiteDatabase類是訪問數據庫的基類,SQLiteOpenHelper類可以幫你創建數據庫和處理數據庫升級場景。

Remember that you don’t have to use a database to implement your repository. A provider appears externally as a set of tables, similar to a relational database, but this is not a requirement for the provider’s internal implementation.

記住,你不需要使用數據庫來實現倉庫。一個ContentProvider外部表現為一組表,類似於關系數據庫,但對於ContentProvider的內部實現來說並不需要這樣的要求。

For storing file data, Android has a variety of file-oriented APIs. For example, if you’re designing a provider that offers media-related data such as music or videos, you can have a provider that combines table data and files.

Android有很多面向文件操作的api,可用於存儲文件數據。例如,如果你正在設計一個ContentProvider用來提供像音樂視頻等媒體數據,你可以搞一個將表數據和文件結合起來的ContentProvider。

For working with network-based data, use classes injava.netandandroid.net. You can also synchronize network-based data to a local data store such as a database, and then offer the data as tables or files.

對於處理基於網絡的數據,可以使用java.net和android.net包中的類。還可以將基於網絡的數據同步到本地存儲,比如數據庫中,然後提供以表或者文件的形式來提供數據。

Implementing SQLiteOpenHelper(實現SQLiteOpenHelper)

An example of a SQLiteOpenHelper implementation(舉個栗子)

public class DbHelper extends SQLiteOpenHelper { public static final String DB_NAME = "timeline.db"; public static final int DB_VERSION = 1; public static final String TABLE = "status"; public DbHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } @Override public void onCreate(SQLiteDatabase db) { String sql = String.format("create table %s ( %s INT PRIMARY KEY," + "%s INT, %s TEXT, %s TEXT);", TABLE, StatusContract.Columns._ID, StatusContract.Columns.CREATED_AT, StatusContract.Columns.USER, StatusContract.Columns.MESSAGE); Log.d("DbHelper", "sql: "+sql); db.execSQL(sql); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // Temporary for development purposes db.execSQL("drop table if exists "+ TABLE); onCreate(db); } }

Data Design Considerations(數據設計注意事項)

Table data should always have aprimary keycolumn that the provider maintains as a unique numeric value for each row.

You can use this value to link the row to related rows in other tables (using it as aforeign key).

Although you can use any name for this column, usingBaseColumns._IDis the best choice, because linking the results of a provider query to a ListView requires one of the retrieved columns to have the name _ID.

表數據應該總是要有一個主鍵列,ContentProvider用來作為每一行的一個獨特的數字值。

您可以使用這個值去將本行關聯到其他表中的使用該值作為一個外鍵的行。

盡管您可以給這一列定任何名稱,但最好還是用BaseColumns._ID吧,因為把ContentProvider查詢的結果顯示到ListView上,需要一個檢索為列名為_ID的列。

If you want to provide bitmap images or other large pieces of file-oriented data, store the data in a file and then provide it indirectly rather than storing it directly in a table.

If you do this, you need to tell users of your provider that they need to use a ContentResolver file method to access the data.

如果你想提供位圖圖像或其他大塊文件數據,與其將數據直接存儲在一個表裡,不如將數據先存在一個文件中然後間接地提供出去。

如果你這樣做的話,你需要告訴用戶你的ContentProvider的使用者,他們需要使用ContentResolver的文件操作方法去訪問數據。

Use theBinary Large OBject(BLOB) data type to store data that varies in size or has a varying structure.

For example, you can use a BLOB column to store a small icon or a JSON structure.

You can also use a BLOB to implement a schema-independent table.

In this type of table, you define a primary key column, a MIME type column, and one or more generic columns as BLOB data.

The meaning of the data in the BLOB columns is indicated by the value in the MIME type column.

This allows you to store different row types in the same table.

The contacts provider’s "data" table,ContactsContract.Data, is an example of a schema-independent table.

使用二進制大對象(BLOB)數據類型去存儲不同大小或不同結構的數據。

例如,你可以使用一個BLOB列來存儲一個小圖標或一個JSON型數據。

您還可以使用一個BLOB去實現一個不依賴模式的表。

在這種類型的表中,你定義了一個主鍵列,一個MIME類型列,和一個或多個通用列用來表示BLOB數據。

BLOB列中的數據的含義是由MIME類型列中的值表示的。

這樣的話,你就可以在同一個表中存儲不同的行類型了。

聯系人ContentProvider的“數據”表——ContactsContract.Data,就是一個不依賴模式的表的例子。

Designing Content URIs(設計內容URI)

Acontent URIis a URI that identifies data in a provider, consisting of:

內容URI是一種標識ContentProvider中的數據的URI,它包括:

Anauthority

The symbolic name of the entire provider

整個ContentProvider的符號名稱

A path

A name pointing to a table or file

指向一個表或文件的路徑名稱

An ID (optional)

The last path component, pointing to an individual row in a table

路徑中最後一個組成部分,指向一個表中的單獨的一行

Every data access method of ContentProvider receives a content URI as an argument.

This allows you to determine the table, row, or file to access.

ContentProvider的每個數據訪問方法都接收一個內容URI作為參數。

這樣就可以確定你要訪問的那個表、行或文件。

Content URIs: Selecting an Authority(內容URI:選擇一個授權)

A provider usually has a singleauthority, which is a unique string that serves as its Android-internal name.

To avoid conflicts with other providers, you should use Internet domain ownership (in reverse) as the basis of your provider authority.

Because this recommendation is also true for Android package names, you can define your provider authority as an extension of the name of the package containing the provider.

For example, if your Android package name is com.example., you should give your provider the authority com.example..provider.

一個ContentProvider通常有一個單獨的授權,這是一個獨一無二的字符串作為其在安卓系統內的名稱。

為了避免與其他ContentProvider的沖突,您應該使用因特網域名所有權(反過來拼寫)作為你的ContenProvider授權的基礎。

因為這個建議也適用於Android的包名,你可以定義你的ContentProvider的授權為一個包名基礎上包含provider的擴展名稱。

例如,如果您的安卓包名是com.example.,你應該給你的ContentProvider的授權命名為com.example..provider。

Content URIs: Designing the Path Structure(內容 URI:設計路徑結構)

Typically you should create content URIs from the authority by appending paths that point to individual tables.

For example, if you have two tables table1 and table2, you combine the authority from the previous example to yield the content URIs com.example..provider/table1 and com.example..provider/table2.

Paths aren’t limited to a single segment, and you don’t need to have a table for each level of the path.

通常情況下你應該從授權中通過附加指向單個表的路徑來創建內容uri。

例如,如果你有兩個表:table1和table2,你把上面例子中的授權和表名綁定起來就產生了內容URI:com.example..provider/table1和com.example..provider/table2。

路徑並不局限於一個單一的部分,你不需要為每個級別的路徑到要有一個表。

Content URIs: Handling IDs(內容 URI:處理ID)

By convention, providers offer access to a single row in a table by accepting a content URI with an ID value for the row at the end of the URI.

Also by convention, providers match the ID value to the table’s _ID column, and perform the requested access against the row that matches.

按照慣例,ContentProvider通過接受一個結尾是行ID值的內容URI的方式,提供訪問表中的一行的方法

同樣按照慣例,ContentProvider根據內容URI中的ID值來匹配表的_ID列,然後對匹配的行執行請求的訪問。

This convention facilitates a common design pattern for apps accessing a provider.

The app does a query against the provider and displays the resulting Cursor in a ListView using a CursorAdapter.

The definition of CursorAdapterrequiresone of the columns in the Cursor to be _ID

本公約使應用程序訪問ContentProvider的通用設計模式更方便。

應用程序對ContentProvider做查詢操作,然後通過使用CursorAdapter在ListView控件中顯示查詢結果。

CursorAdapter的定義需要游標中的一個叫_ID的列

The user then picks one of the displayed rows from the UI in order to look at or modify the data.

The app gets the corresponding row from the Cursor backing the ListView, gets the _ID value for this row, appends it to the content URI, and sends the access request to the provider.

The provider can then do the query or modification against the exact row the user picked.

然後用戶從UI顯示的行中選擇一行以查看或修改數據。

應用程序從游標中得到了相應的行,得到了這一行的_ID值,並將該_ID值添加到內容URI結尾,然後向ContentProvider發送訪問請求。

ContentProvider就可以對用戶選擇的那一行做查詢或修改操作了。

Content URI Pattern Matching with UriMatcher(用UriMatcher來匹配內容URI模式)

TheUriMatchercan simplify the common task of validating and parsing URIs.

After instantiating a UriMatcher object, you can register URI patterns to map to integer values.

Then you can test URIs against the registered patterns, and switch on the corresponding integer value returned.

UriMatcher可以簡化驗證和解析uri的常見任務流程。

實例化一個UriMatcher對象後,你可以注冊URI模式映射到整數值上。

然後你就可以根據注冊的模式來測試URI,並在相應的整數返回值上做切換選擇。

When you instantiate a UriMatcher, you provide an integer value to return if a given URI matches none of the registered patterns.

Typically, you should use UriMatcher.NO_MATCH for this values.

在實例化一個UriMatcher時,如果給定URI不能匹配任何已注冊的模式,那你就需要提供一個整數返回值。

通常來說,您應該使用UriMatcher.NO_MATCH這個值。

Use the method UriMatcher.addURI(String authority, String path, int code) to map URI patterns to return values.

In addition to static paths, the UriMatcher supports two wildcard characters in patterns:

使用UriMatcher.addURI(String authority, String path, int code)方法將URI模式映射到返回值中。

除了靜態路徑之外,UriMatcher還支持兩個通配符模式:

The ContactsContract class and its nested classes are examples of contract classes.

ContactsContract類及其嵌套類都是合約類的例子。

Example Contract Class(合約類示例)

public final class StatusContract { private StatusContract() {} /** The authority for the contacts provider */ public static final String AUTHORITY = "com.marakana.android.yamba.provider"; /** A content:// style uri to the authority for this table */ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/status"); /** The MIME type of {@link #CONTENT_URI} providing a directory of status messages. */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.marakana.status"; /** The MIME type of a {@link #CONTENT_URI} a single status message. */ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.marakana.status"; /** * Column definitions for status information. */ public final static class Columns implements BaseColumns { private Columns() {} /** * The name of the user who posted the status message *

Type: TEXT

*/ public static final String USER = "user"; /** * The status message content *

Type: TEXT

*/ public static final String MESSAGE = "message"; /** * The date the message was posted, in milliseconds since the epoch *

Type: INTEGER (long)

*/ public static final String CREATED_AT = "createdAt"; /** * The default sort order for this table */ public static final String DEFAULT_SORT_ORDER = CREATED_AT + " DESC"; } }

Implementing permissions(實現權限)

By default, all applications can read from or write to your provider?—?even if the underlying data is private?—?because by default your provider does not have permissions set.

To change this, set permissions for your provider in your manifest file, using attributes or child elements of theelement.

You can set permissions that apply to the entire provider, or to certain tables, or even to certain records, or all three.默認情況下,所有應用程序都可以讀取或者寫入到你的ContentProvider——即使底層數據是私有的——因為默認情況下你的ContentProvider沒有權限。

為了改變這一狀況,你要在清單文件中為你的ContentProvider設置權限,使用元素的屬性或者其子元素。

你可以設置權限為適用於整個ContentProvider,或某些表,甚至某些記錄,或者以上三者。

You define permissions for your provider with one or moreelements in your manifest file. * To make the permission unique to your provider, use Java-style scoping for the android:name attribute. * For example, name the read permission com.example.app.provider.permission.READ_PROVIDER.

你在你的清單文件中以一個或多個元素來為ContentProvider定義權限。*為了使你的ContentProvider保持唯一,使用java風格來為android:name 屬性值命名。例如,將讀取權限命名為: com.example.app.provider.permission.READ_PROVIDER。

You can specify several types of provider permissions, with more fine-grained permissions taking precedence over ones with larger scope:

您可以指定幾種類型的ContentProvider權限,更細化的權限將優先於那些較大范圍的權限:

Single read-write provider-level permission(單一的讀寫ContentProvider級權限)

One permission that controls both read and write access to the entire provider, specified with the android:permission attribute of theelement.

一個控制讀寫整個ContentProvider的權限,由元素的android:permission 屬性值來標識。

Separate read and write provider-level permission(區分ContentProvider級的讀和寫的權限)

A read permission and a write permission for the entire provider.

You specify them with the android:readPermission and android:writePermission attributes of theelement.

They take precedence over the permission required by android:permission.

整個提供者的讀權限和寫權限。

你通過在元素的屬性值android:readPermission和android:writePermission來指定他們。

他們優先於android:permission中定義的權限。

Path-level permission(路徑級權限)

Read, write, or read/write permission for a content URI in your provider.

You specify each URI you want to control with achild element of theelement.

For each content URI you specify, you can specify a read/write permission, a read permission, or a write permission, or all three.

The read and write permissions take precedence over the read/write permission.

Also, path-level permission takes precedence over provider-level permissions.

ContentProvider中內容URI的讀、寫或讀/寫權限。

你通過設置的子元素< path-permission >來指定每個你想要控制的URI。

對於每個你指定的內容URI,你可以指定一個讀/寫權限,讀權限,寫權限,或者以上三者。

讀和寫的權限要優先於讀/寫權限。

同時,路徑級權限要優先於ContentProvider級權限。

Implementing the ContentProvider Class(實現ContentProvider類)

The abstract classContentProviderdefines six abstract methods that you must implement as part of your own concrete subclass. All of these methods except onCreate() are called by a client application that is attempting to access your content provider:

抽象類ContentProvider定義了六個抽象方法,你必須實現他們作為具體子類的一部分。所有這些方法(除了onCreate()方法外)都會被客戶端應用程序調用,以訪問你的ContentProvider:

onCreate()

Initialize your provider. The Android system calls this method immediately after it creates your provider. Note that the system doesn’t create your provider until a client tries to access it through a ContentResolver.

該方法用來初始化你的ContentProvider。安卓系統在創建完你的ContentProvider之後會馬上調用該方法。注意,只有在客戶端程序視圖通過ContentResolver來訪問的時候,系統才會創建您ContentProvider。

query()

Retrieve data from your provider. Use the arguments to select the table to query, the rows and columns to return, and the sort order of the result. Return the data as a Cursor object.

檢索你的ContentProvider中的數據。使用參數來選擇你要查詢的表,和要返回的行和列,以及對結果的排序順序。然後以一個游標對象作為數據返回。

insert()

Insert a new row into your provider. Use the arguments to select the destination table and to get the column values to use. Return a content URI for the newly-inserted row.

插入一個新行到你的ContentProvider中。使用參數去選擇目標表和和要使用的列。返回一個新插入的行的內容URI。

update()

Update existing rows in your provider. Use the arguments to select the table and rows to update and to get the updated column values. Return the number of rows updated.

更新你的ContentProvider中的現有行。使用參數去選擇表和要更新的行,還有獲取更新的列值。返回更新的行的數量。

delete()

Delete rows from your provider. Use the arguments to select the table and the rows to delete. Return the number of rows deleted.

從你的ContentProvider中刪除行。使用參數去選擇表和要刪除的行。返回刪除的行的數量。

getType()

Return the MIME type corresponding to a content URI.

返回一個內容URI相應的MIME類型。

Content Provider Implementation Notes(ContentProvider實現中需要注意的)

All of these methods except onCreate() can be called by multiple threads at once, so they must be thread-safe.

When a client in a different application invokes a provider method through a ContentResolver instance, the system automatically selects a thread from an interprocess thread pool to execute the method in the provider.

If a client component in the same process as the content provider invokes one of these methods through a ContentResolver instance, the system invokes the provider method in thesame threadas the client component method call.

所有這些方法(除了onCreate())都可以由多個線程調用,所以他們必須是線程安全的。

當不同的應用程序的客戶端通過一個ContentResolver實例來調用你的ContentProvider的方法時,系統會自動從一個進程間線程池中選擇一個線程去執行ContentProvider的方法。

如果一個客戶端組件在和ContentProvider同一進程中通過ContentResolver實例來調用這些方法中的一個,系統會在和客戶端組件方法調用的同一線程中調用你的ContentProvider的方法。

Avoid doing lengthy operations in onCreate().

The system automatically invokes onCreate() in the application process’s main thread when in needs to initialize the provider.

Therefore, defer initialization tasks until they are actually needed to avoid blocking your application’s main thread.

避免在onCreate()方法中做冗長的操作。

當需要初始化ContentProvider的時候,系統會在應用程序進程的主線程中自動調用onCreate()。

因此,盡量去延遲初始化任務除非實際上必須這麼做,這樣可以避免程序的主線程被阻塞。

Although you must implement these methods, your code does not have to do anything except return the expected data type.

For example, you may want to prevent other applications from inserting data into some tables. To do this, you can ignore the call to insert() and return null.

盡管你必須實現這些方法,你的代碼除了要返回想要的的數據類型之外什麼都不用做。

例如,您可能想要阻止其他應用程序將數據插入一些表。要做到這一點,您可以忽略對insert()的調用和返回null。

Implementing the onCreate() method(實現OnCreate()方法)

The Android system calls onCreate() when it starts up the provider.

You should perform only fast-running initialization tasks in this method, and defer database creation and data loading until the provider actually receives a request for the data.

If you do lengthy tasks in onCreate(), you block your application’s main thread, potentially causing an Application Not Responding (ANR) condition if your application contains other components.

Android系統在啟動ContentProvider時會調用onCreate()方法。

在這個方法中你應該只執行快速初始化的任務,而把數據庫的創建和數據的加載延遲到ContentProvider實際上接收到請求的數據的時候。

如果你在onCreate()方法中執行太多的任務的話,將會阻塞應用程序的主線程,可能會導致“應用程序沒有響應”(ANR)的情況,如果您的應用程序還包含其他組件的話。

For example, if you are using an SQLite database:

You can create a new SQLiteOpenHelper object in ContentProvider.onCreate().

You can place the calls to getWritableDatabase() or getReadableDatabase() in the implementation of the provider’s data access methods

As a result, any actual database creation or upgrade is deferred until the first data access, and that creation or upgrade takes place in the interprocess communication thread selected to process the call.

比如您正在使用SQLite數據庫:

你可以ContentProvider.onCreate()方法中創建一個新的SQLiteOpenHelper對象。

您可以將getWritableDatabase()或getReadableDatabase()的調用放在ContentProvider的數據訪問方法實現的地方。

因此,任何實際的數據庫創建或升級都被延遲到第一個數據訪問,並且這個數據庫的創建或更新操作發生在專門用來處理這種調用的進程間通信線程中。

Assuming that you are using a SQLiteOpenHelper subclass, the following could be sufficient to initialize your provider:

假設你正在使用SQLiteOpenHelper的子類,那麼對於初始化你的ContentProvider來說,下面這些就足夠了:

public boolean onCreate() { Context context = getContext(); dbHelper = new TimelineHelper(context); return (dbHelper == null) ? false : true; }

Implementing the query() Method(實現查詢方法)

The ContentProvider.query() method must return a Cursor object, or if it fails, throw an Exception.

If you are using an SQLite database as your data storage, you can simply return the Cursor returned by one of the query() methods of the SQLiteDatabase class.

If the query does not match any rows, return a Cursor instance whose getCount() method returns 0.

Return null only if an internal error occurred during the query process.

ContentProvider.query()方法必須返回一個游標對象,如果查詢失敗的話會拋出異常。

如果你正在使用SQLite數據庫來存儲數據,您可以簡單地返回一個由SQLiteDatabase類的query()方法返回的游標對象。

如果查詢不到任何匹配的行,那就返回一個游標實例,該實例的getCount()方法返回0。

只有在查詢過程中發生內部錯誤才返回null。

If you aren’t using an SQLite database as your data storage, use one of the concrete subclasses of Cursor.

For example, theMatrixCursorclass implements a cursor in which each row is an array of Object. With this class, use addRow() to add a new row.

如果你沒有在用SQLite數據庫存儲數據的話,使用游標的一個具體子類。

例如,MatrixCursor類實現了一個游標,它的每一行都是一個數組對象。這個類可以調用addRow()方法來添加一個新行。

Remember that the Android system must be able to communicate the Exception across process boundaries. Android can do this for the following exceptions that may be useful in handling query errors:

+ IllegalArgumentException:: You might choose to throw this if your provider receives an invalid content URI UnsupportedOperationException:: You might choose to throw this is you decide not to support a method, such as delete() NullPointerException:: This type of runtime exception will be reflected to the client if not caught and handled in the provider

記住,Android系統一定能夠跨進程溝通異常。對於下面這些異常,Android系統是可以做些有用操作的,在處理查詢錯誤的時候:

IllegalArgumentException::如果你的ContentProvider接收了一個無效的內容URI參數,你可以拋這個異常;

UnsupportedOperationException:如果你決定不支持某個方法比如delete(),那可以拋這個異常;

NullPointerException:這種運行時異常將會反映給客戶端,如果在CotentProvider中沒有捕獲並處理的話。

The SQLiteQueryBuilder Helper Class

Implementing the query() method can be particularly complex.

The method supports several arguments, almost all of which are optional.

You often need to parse the Uri argument to include path components such as the ID in the query.

You might have default query parameters to use if the client doesn’t specify them.

You might have additional query parameters "hard-coded" into your provider implementation that you need to merge with arguments provided by the client.

You might need to perform joins or other manipulation of the underlying data to present a synthesized view to the client.

實現查詢()方法可以特別復雜。

該方法支持多種參數,幾乎所有的參數是可選的。

你經常需要把Uri參數解析成包含路徑成分比如查詢中的ID。

你可能會有默認的查詢參數使用,如果客戶端不指定它們的話。

你可能有額外的查詢參數以“硬編碼”的形式存在於你的ContentProvider的實現中,那樣你會需要將客戶端提供的參數合並。

您可能需要對底層數據進行連接或其他操作來合成視圖呈現給客戶端。

To simplify the creation of a complex query, Android provides theSQLiteQueryBuilderclass. It supports features such as:

Assembling query parameters incrementally

Merging multiple query parameters, such as several portion of a WHERE clause

Performing joins

Performingprojection mapping, to map column names that the caller passes into query to database column names

The example implementation of a query() shown next provides an example of using SQLiteQueryBuilder.

為了簡化一個復雜查詢的創建,Android系統提供了SQLiteQueryBuilder類。它支持的功能有:

逐步組裝查詢參數

合並多個查詢參數,比如一個WHERE子句的幾部分

執行連接

執行projection映射,將調用者傳來的列名映射到數據庫中的列名

查詢的示例實現()使用SQLiteQueryBuilder下提供了一個示例所示。

Example of Implementing a query() Method(查詢方法的實現示例)

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sort) { SQLiteDatabase db = dbHelper.getWritableDatabase(); // A convenience class to help build the query SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables(T_TIMELINE); switch (uriMatcher.match(uri)) { case STATUS_DIR: break; case STATUS_ITEM: // If this is a request for an individual status, limit the result set to that ID qb.appendWhere(StatusContract.Columns._ID + "=" + uri.getLastPathSegment()); break; default: throw new IllegalArgumentException("Unsupported URI: " + uri); } // Use our default sort order if none was specified String orderBy = TextUtils.isEmpty(sort) ? StatusContract.Columns.DEFAULT_SORT_ORDER : sort; // Query the underlying database Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); // Notify the context's ContentResolver if the cursor result set changes c.setNotificationUri(getContext().getContentResolver(), uri); // Return the cursor to the result set return c; }

Implementing the insert() Method(實現插入方法)

The insert() method adds a new row to the appropriate table, using the values in the ContentValues argument.

If a particular column name is not provide in the ContentValues argument, you might decide to provide a default value for it either in your provider code or in your database schema.

insert()方法可以添加一個新行到適當的表中,它使用ContentValues參數中的值。

如果一個特定的列名沒有在ContentValues參數中提供,你可能需要為它提供一個默認值,不管是在你的ContentProvider代碼中還是在你的數據庫模式中。

The insert() method should return the content URI for the new row.

To construct this, append the new row’s _ID (or other primary key) value to the table’s content URI, using withAppendedId().

You can return null if the insertion fails, or if you decide not to support this operation.

insert()方法應該返回新插入行的內容URI。

為了構造這個,可以使用withAppendedId()方法來將新行的_ID值(或其他主鍵)添加到內容URI結尾處。

如果插入失敗或者你想不支持此類操作的話,你都可以返回null。

Example of Implementing an insert() Method(插入方法的實現示例)

public Uri insert(Uri uri, ContentValues initialValues) { Uri result = null; // Validate the requested Uri if (uriMatcher.match(uri) != STATUS_DIR) { throw new IllegalArgumentException("Unsupported URI: " + uri); } SQLiteDatabase db = dbHelper.getWritableDatabase(); long rowID = db.insert(T_TIMELINE, null, initialValues); if (rowID > 0) { // Return a URI to the newly created row on success result = ContentUris.withAppendedId(StatusContract.CONTENT_URI, rowID); // Notify the Context's ContentResolver of the change getContext().getContentResolver().notifyChange(result, null); } return result; }

Implementing the delete() method(實現刪除方法)

The delete() method should delete rows from your provider.

Use the selection arguments to select the table and the rows to delete.

Return the number of rows deleted.

You might decide always to return 0 if you choose not to support this operation, or throw an UnsupportedOperationException.

使用delete()方法可以從你的ContentProvider中刪除行。

使用selection參數去選擇表和要刪除的行。

返回被刪除的行的數量。

如果你不想支持該操作的話,你可以總是返回0,或者直接拋出UnsupportedOperationException異常。

public int delete(Uri uri, String where, String[] whereArgs) { SQLiteDatabase db = dbHelper.getWritableDatabase(); int count; switch (uriMatcher.match(uri)) { case STATUS_DIR: count = db.delete(T_TIMELINE, where, whereArgs); break; case STATUS_ITEM: String segment = uri.getLastPathSegment(); String whereClause = StatusContract.Columns._ID + "=" + segment + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""); count = db.delete(T_TIMELINE, whereClause, whereArgs); break; default: throw new IllegalArgumentException("Unsupported URI: " + uri); } if (count > 0) { // Notify the Context's ContentResolver of the change getContext().getContentResolver().notifyChange(uri, null); } return count; }

Implementing the update() method(實現更新方法)

The update() method updates existing rows in your provider.

It accepts the same ContentValues argument used by insert(), and the same selection arguments used by delete() and query().

This might allow you to re-use code between these methods.

Return the number of rows deleted.

You might decide always to return 0 if you choose not to support this operation, or throw an UnsupportedOperationException.

使用update()方法可以更新你的ContentProvider中現有的行。

它接受和insert()方法使用的相同的ContentValues參數,以及和delete() 、query()方法使用的相同的selection參數。

這樣你就可以在這些方法之間重用一些代碼。

返回被修改的行的數量。

如果你不想支持該操作的話,你可以總是返回0,或者直接拋出UnsupportedOperationException異常。

public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { SQLiteDatabase db = dbHelper.getWritableDatabase(); int count; switch (uriMatcher.match(uri)) { case STATUS_DIR: count = db.update(T_TIMELINE, values, where, whereArgs); break; case STATUS_ITEM: String segment = uri.getLastPathSegment(); String whereClause = StatusContract.Columns._ID + "=" + segment + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""); count = db.update(T_TIMELINE, values, whereClause, whereArgs); break; default: throw new IllegalArgumentException("Unsupported URI: " + uri); } if (count > 0) { // Notify the Context's ContentResolver of the change getContext().getContentResolver().notifyChange(uri, null); } return count; }

Registering the Provider in the Application Manifest(在清單文件中注冊ContentProvider)

Your content provider must be registered in your application’s manifest by including aelement within theelement.

你的CotentProvider必須在清單文件中注冊,在節點下用表示

android:name

Required. The class name implementing your provider

這個屬性是必須的,你的ContentProvider的實現類

android:authorities

Required. The URI authorities identifying your provider. Typically, you provide only one, but you can provide a semicolon-separated list of authorities.

這個屬性是必須的,URI授權可以識別你的ContentProvider。通常情況下你只需提供一個,但是你也可以提供一系列授權,中間以分號分隔即可。

android:permission

Optional. The name of a permission that clients must have to read or write the content provider’s data. If provided, android:readPermission or android:writePermission take precedence over this attribute.

可選。這裡要寫客戶端讀寫ContentProvider中的數據所需權限的名稱。如果提供了這個權限的話,android:readPermission或android:writePermission屬性定義的權限要優先於這個屬性定義的權限。

android:readPermission

Optional. The name of a permission that clients must have to read the content provider’s data.

可選。這裡要寫客戶讀取ContentProvider中的數據所需權限的名稱。

android:writePermission

Optional. The name of a permission that clients must have to write the content provider’s data.

可選。這裡要寫客戶端寫入數據到ContentProvider中所需權限的名稱。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved