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

Android Content Provider Tutorial--安卓內容提供者系列2--內容提供者用法

編輯:關於Android編程

Using a Content Provider(如何使用Content Provider)

Objectives(學習目標)

After this section, you will be able to:

Write client code that can read and modify the data managed by a content providerFind and usecontract classesdocumenting the constants exposed by system content providersUse batch access to perform more efficient interaction with a content provider

通過這一部分的學習,你將能夠:

編寫可以讀取和修改由content provider管理的數據的客戶端代碼找到並使用那些記錄了content providers暴露出來的常量的合約類使用分批訪問的方式使得與content provider的交互更有效率

Content Provider Overview(Content Provider概述)

Acontent provideris an application component that shares data with other applications.

Various system content providers manage the user’s contacts, call log, calendar, and other collections of information.User-installed applications can expose their own custom data collections.

content provider是一種與其他應用程序共享數據的應用組件。

各種系統的content providers管理著用戶的聯系人信息、通話記錄、日歷、以及其他信息集。用戶自己裝的app可以把自定義的數據集合暴露出去。

Typically, a content provider presents data as one or more tables, similar to tables in a database.

Each row represents one record, such as a single calendar event.Each column represents a particular attribute of the records, such as an event start time.

通常情況下,一個content provider提供的數據就像數據庫中的一個或多個表一樣。

每一行代表一個記錄,例如一個日程表事件。每一列代表該條記錄的一個特定的屬性,例如事件的開始時間。

Occasionally, a content provider might expose file data.

For example, the system contacts content provider can share a contact’s photo.

一個content provider偶爾也會暴露文件數據。

例如,系統聯系人的content provider可以共享一個聯系人的照片。

Accessing a Content Provider(訪問Content Provider)

A client application accesses the data from a content provider with aContentResolverobject.

The ContentResolver object provides query(), insert(), update(), and delete() methods for accessing data from a content provider.The ContentResolver object invokes identically-named methods on an instance of a concrete subclass ofContentProvider, which typically resides in a separate application process.The ContentProvider acts as an abstraction layer between its data store and the external presentation of data.The ContentResolver object and the ContentProvider object automatically handle the details of inter-process communication.

一個客戶端app想訪問Content provider 提供的數據,需要用到Content Resolver對象。

ContentResolver對象提供了增刪改查的方法來操作content provider中的數據。ContentResolver對象調用一個通常存在於另一個單獨app進程中的一個ContentProvider的具體子類對象的同名方法。ContentProvider充當了介於數據存儲和數據外部表現之間的一個抽象層。ContentResolver對象和ContentProvider對象自動處理進程間通信的細節。

In most cases, the ContentProvider does not reside in the same application process as the client’s ContentResolver. However, if you implement a content provider for your application, other components in your application can access it through a ContentResolver in exactly the same way as they would a content provider in a different application.

在大多數情況下,(提供數據的app的)ContentProvider和(操作數據的)客戶端app的ContentResolver不在同一個進程中。但是,如果您為您的app實現了一個ContentProvider,你的應用內的其他組件也可以通過ContentResolver來訪問它,這跟訪問其他應用內的ContentProvider的方式完全一樣。

For example, to get a list of the words and their locales from the User Dictionary Provider, you call ContentResolver.query():

舉個栗子,為了從User Dictionary Provider中獲取所有words的列表和他們的locals,你需要調用ContentResolver.query():

// Queries the user dictionary and returns results Cursor cursor = getContentResolver().query( UserDictionary.Words.CONTENT_URI, // The content URI of the words table projection, // The columns to return for each row selectionClause // Selection criteria selectionArgs, // Selection criteria sortOrder); // The sort order for the returned rows

Content URIs(內容uri)

Acontent URIis a URI that identifies data in a provider. It consists of:

Thescheme, which is always content:// for a content URI

Theauthority, which is a unique string identifying the specific content provider

Thepath, which identifies a particular record or collection of records managed by the provider

In the preceding example, the full URI for the "words" table is:

內容uri是用來標識content provider中數據的uri。它包括:

scheme,格式總是這樣的: content://

authority,這是一個標識特定的ContentProvider的唯一的字符串

path,它標識了一個由ContentProvider管理的特定的記錄或記錄的集合在前面的例子中,“words”表對應的完整URI是:

content://user_dictionary/words

content://?—?the scheme identifying this as a content URIuser_dictionary?—?the authority of the system user dictionary providerwords?—?the path corresponding to the “words” table

The ContentResolver uses the authority to identify the content provider to contact.

An application implementing a ContentProvider specifies the provider’s authority in the application manifest.

ContentResolver使用授權去識別想要聯系的ContentProvider。

一個實現了ContentProvider的應用程序要在它的清單文件中指定該ContentProvider的權限。

The ContentProvider uses the path to choose the table to access.

A provider usually has a path for each table it exposes.

Many providers allow you to access a single row in a table by appending an ID value to the end of the URI.

For example, to retrieve a row whose _ID is 4 from user dictionary, you can use this content URI:

ContentProvider使用路徑去選擇要訪問的表。

ContentProvider對於它暴露的每一張表都有一個訪問路徑。

許多ContentProvider都允許你通過在uri後面附加一個ID值的方式去訪問表中的某一行。

舉個栗子,在user dictionary中你想檢索出_ID = 4的那一行,你可以使用下面這個內容URI:

Uri singleUri = ContentUri.withAppendedId(UserDictionary.Words.CONTENT_URI,4);

TheUriandUri.Builderclasses contain convenience methods for constructing well-formed Uri objects from strings. TheContentUrisclass contains convenience methods for appending id values to a URI. The previous snippet uses withAppendedId() to append an id to the UserDictionary content URI.

Uri類和Uri.Builder類中封裝了一些比較方便的方法用於把字符串構造成格式良好的Uri對象。ContentUris類中封裝了一些可以將id值附加到URI中的便捷方法。前面的代碼片段就是使用了它的withAppendedId()方法來添加一個id到UserDictionary這個內容URI中。

Contract Classes(合約類)

Acontract classdefines constants that help applications work with the content URIs, column names, and other features of a content provider.

Contract classes are not included automatically with a provider

The provider’s developer has to define them and then make them available to other developers.

合約類定義了一些常量,這些常量使應用程序與內容uri,列名還有content provider的一些其他特性能更好的交互。

合約類不是自動包含在content provider中的。

content provider的開發人員必須定義他們,然後讓其他開發人員也能訪問到。

Many of the providers included with the Android platform have corresponding contract classes in the packageandroid.provider.

For example, the User Dictionary Provider has a contract classUserDictionarycontaining content URI and column name constants.

The content URI for the "words" table is defined in the constant UserDictionary.Words.CONTENT_URI. TheUserDictionary.Wordsclass also contains column name constants.

Android平台中的許多ContentProvider都有相應的合約類,這些類就在android.provider包裡。

例如,User Dictionary Provider有一個合同類叫UserDictionary,它包含內容URI常量和列名常量。

“words”表的內容URI就定義在UserDictionary.Words.CONTENT_URI這個常量中。UserDictionary.Words類也包含了列名常量。

Requesting Access Permission(請求訪問權限)

Many content providers require clients to hold a custom access permission to access data from the provider.

Your client application’s manifest must include aelement with the permission name defined by the provider.

The provider may define separate permissions for "read access" (queries) and "write access" (inserts, updates, and deletes).

For example, the User Dictionary Provider defines the permission android.permission.READ_USER_DICTIONARY for applications that want to retrieve data, and a separate android.permission.WRITE_USER_DICTIONARY permission for inserting, updating, or deleting data.

許多content provider要求客戶端持有一個自定義訪問權限來訪問它的數據。

你的客戶端app的清單文件中必須包含一個< uses-permission >元素,其中包含了由Content provider定義的權限名。

content provider可能定義單獨的“讀取”權限(查詢)和“寫入”(插入、更新和刪除)。

例如,User Dictionary Provider給那些想檢索數據的應用程序定義了android.permission.READ_USER_DICTIONARY權限,對於想插入、更新或刪除數據的,又單獨定義了一個android.permission。WRITE_USER_DICTIONARY權限。

Constructing a Query(構建查詢語句)

The ContentResolver.query() method requires several arguments:

ContentResolver.query()方法需要以下參數:

uri

The URI, using the content:// scheme, for the content to retrieve.

URI,格式如content:// scheme,是為了標識要檢索的內容。

projection

A list of which columns to return. Passing null returns all columns, which can be inefficient.

該返回的數據列的集合。如果傳遞空值則返回所有列,當然這樣的話效率會比較低下。

selection

A filter declaring which rows to return, formatted as an SQL WHERE clause (excluding the WHERE itself). Passing null returns all rows for the given URI.

這部分相當於一個過濾器,它聲明了哪些行該被返回,格式類似於一個SQL語言中的WHERE從句(不包括WHERE這個單詞本身)。如果傳null的話,則返回滿足URI條件的所有行。

selectionArgs

You may include ?s in selection, which are replaced by the values fromselectionArgs, in the order that they appear in the selection.

你可能會在selection參數中包含一些篩選條件,這些條件會被selectionArgs參數中的值所替代,selectionArgs參數順序和seletion中的參數順序一一對應。

sortOrder

How to order the rows, formatted as an SQL ORDER BY clause (excluding the ORDER BY itself). Passing null uses the default sort order, which may be unordered.

這個參數表示如何給返回的行排序,它的格式類似於一個SQL語言中的ORDERBY從句(不含ORDER BY這個關鍵詞本身)。如果傳null的話就使用默認的排序順序,這可能是無序的。

The ContentResolver.query() client method always returns a Cursor containing the columns specified by the query’s projection for the rows that match the query’s selection criteria.

ContentResolver.query()這個客戶端方法總是會返回一個游標,該游標包含了projection參數指定的列,這些列和滿足查詢條件的行相匹配。

If an internal error occurs, the results of the query depend on the particular provider. It may choose to return null, or it may throw an Exception.

如果發生內部錯誤,查詢的結果將取決於特定的Content Provider。它可能會返回null,也可能會拋出異常。

Some Cursor implementations automatically update the object when the provider’s data changes, or trigger methods in an observer object when the Cursor changes, or both.

一些游標會在ContentProvider的數據發生變化時自動更新,或者在游標變化時會觸發觀察者對象中的方法,又或者兩種情況兼而有之。

A Query Example(一個查詢示例)

// A "projection" defines the columns that will be returned for each row String[] projection = { UserDictionary.Words._ID, // Contract class constant for the _ID column name UserDictionary.Words.WORD, // Contract class constant for the word column name UserDictionary.Words.LOCALE // Contract class constant for the locale column name }; // Defines a string to contain the selection clause String selectionClause = null; // An array to contain selection arguments String[] selectionArgs = null; // Gets a word from the UI String searchString = mSearchWord.getText().toString(); // Remember to insert code here to check for invalid or malicious input. // If the word is the empty string, get everything. Otherwise... if (!TextUtils.isEmpty(searchString)) { // Construct a selection clause that matches the word that the user entered. selectionClause = UserDictionary.Words.WORD + " = ?"; // Use the user's input string as the (only) selection argument. selectionArgs = new String[]{ searchString }; } // An ORDER BY clause, or null to get results in the default sort order String sortOrder = null; // Does a query against the table and returns a Cursor object mCursor = getContentResolver().query( UserDictionary.Words.CONTENT_URI, // The content URI of the words table projection, // The columns to return for each row selectionClause // Either null, or the word the user entered selectionArgs, // Either empty, or the string the user entered sortOrder); // The sort order for the returned rows // Some providers return null if an error occurs, others throw an exception if (null == mCursor) { // Insert code here to handle the error. } else if (mCursor.getCount() < 1) { // If the Cursor is empty, the provider found no matches } else { // Insert code here to do something with the results }

This query is analogous to the SQL statement:

上面那個查詢方法類似於下面這個SQL查詢語句:

SELECT _ID, word, locale FROM words WHERE word = ORDER BY word ASC;

Directly concatenating external untrusted data into raw SQL statements can lead toSQL injectionattacks.For example, in the following selection clause:

直接連接外部不可信的數據,寫入到原始SQL語句會有發生SQL 注入的風險。例如下面的selection從句:

// Constructs a selection clause by concatenating the user's input to the column name String selectionClause = "var = " + userInput;

the user could enter "nothing; DROP TABLE *;" for mUserInput, which would result in the selection clause var = nothing; DROP TABLE *;. Since the selection clause is treated as an SQL statement, this might cause the provider to erase all of the tables in the underlying SQLite database (unless the provider is set up to catch SQL injection attempts).

用戶可以輸入"nothing; DROP TABLE *;",賦值到mUserInput變量中,這將導致selection從句中的var = nothing; DROP TABLE *;。由於這個selection從句被視為一個SQL語句,這樣拼接起來很可能會導致ContentProvider去清除底層SQLite數據庫中所有的表(除非ContentProvider設置成能捕獲到SQL注入的嘗試)。

When incorporating untrusted data?—?such as user input?—?into a query, you should always use a selection clause that with ? as a replaceable parameter and a separate array of selection arguments. The selection argument is incorporated into the query as a single argument rather than being directly concatenated into the selection string.

當將不受信任的數據——比如用戶的輸入,拼接到一個查詢語句中時,你應該始終使用一個帶有問號作為占位符和單獨的參數數組的selection從句。寧願將selection參數作為單個參數拼接進查詢語句中,也不要直接連接到SQL查詢字符串中。

Inserting Data(插入數據)

To insert data into a provider, call the ContentResolver.insert() method.

This method inserts a new row into the provider and returns a content URI for that row.

This example shows how to insert a new word into the User Dictionary Provider:

你想將數據插入到ContentProvider中,那就調用ContentResolver.insert()方法。

該方法將一個新行插入到ContentProvider中,並返回該行的內容URI。

下面這個例子示范了如何將一個單詞插入到User Dictionary Provider中:

// Defines a new Uri object that receives the result of the insertion Uri newUri; // Defines an object to contain the new values to insert ContentValues newValues = new ContentValues(); // Sets the values of each column and inserts the word. newValues.put(UserDictionary.Words.APP_ID, "example.user"); newValues.put(UserDictionary.Words.LOCALE, "en_US"); newValues.put(UserDictionary.Words.WORD, "insert"); newValues.put(UserDictionary.Words.FREQUENCY, "100"); newUri = getContentResolver().insert( UserDictionary.Word.CONTENT_URI, // the user dictionary content URI newValues // the values to insert );

The content URI returned in newUri identifies the newly-added row, with the following format:

存在newUri變量中的返回的內容URI標明了新添加的行,格式像下面這樣:

content://user_dictionary/words/

To get the value of _ID from the returned Uri, call ContentUris.parseId().

想從返回的Uri中得到_ID字段的值,那就要調用ContentUris.parseId()方法。

Updating Data(修改數據)

To update one or more rows, use a ContentValues object with the updated value and selection criteria.

Invoke ContentResolver.update() to perform the update.

You need to add values to the ContentValues object for only the columns you’re updating.

If you want to clear the contents of a column, set the value to null.

想更新一行或多行數據,需要將ContentValues對象與更新後的值還有selection條件一起使用。

調用ContentResolver.update()來執行更新。

對於你要更新的那些列,你需要添加值到ContentValues對象中。

如果你想清空的一個列的內容,請將值設為null。

For example, the following snippet changes all the rows whose locale has the language "en" to a have a locale of null:

舉個栗子,下面的代碼片段把所有locale的值是“en”的行都改成了locale為null:

// Defines an object to contain the updated values ContentValues updateValues = new ContentValues(); // Defines selection criteria for the rows you want to update String selectionClause = UserDictionary.Words.LOCALE + "LIKE ?"; String[] selectionArgs = {"en_%"}; // Defines a variable to contain the number of updated rows int rowsUpdated = 0; // Sets the updated value and updates the selected words. updateValues.putNull(UserDictionary.Words.LOCALE); rowsUpdated = getContentResolver().update( UserDictionary.Words.CONTENT_URI, // the user dictionary content URI updateValues // the columns to update selectionClause // the column to select on selectionArgs // the value to compare to );

Deleting Data(刪除數據)

Deleting rows is similar to retrieving row data.

Invoke ContentResolver.delete() to perform the update.

Specify selection criteria for the rows you want to delete.

The method returns the number of rows deleted.

For example, the following snippet deletes rows whose appid matches "user".

刪除行數據類似於檢索行數據。

調用ContentResolver.delete()來執行刪除操作。為你想刪除行指明的篩選條件。

該方法返回的是被刪除的行的數目。

舉個栗子,下面的代碼片段演示了刪除所有appid是“user”的行。

// Defines selection criteria for the rows you want to delete String selectionClause = UserDictionary.Words.APP_ID + " LIKE ?"; String[] selectionArgs = {"user"}; // Defines a variable to contain the number of rows deleted int rowsDeleted = 0; // Deletes the words that match the selection criteria rowsDeleted = getContentResolver().delete( UserDictionary.Words.CONTENT_URI, // the user dictionary content URI selectionClause // the column to select on selectionArgs // the value to compare to );

Batch Access(批量訪問)

Batch accessallows you to perform multiple operations with a content provider in a single ContentResolver call.

The request is much more efficient, as it requires only one IPC call.

Depending on the specific content provider implementation, a content provider might implement a batch access as a single atomic transaction.

批量訪問的意思是你可以在調用一個ContentResolver的方法中對一個ContentProvider執行多個操作。

這樣請求起來更有效率,因為它只需要一個IPC調用。

根據content provider的具體實現,content provider可能會執行一些批量訪問,就像一個單個的原子事務一樣。

To access a content provider in batch mode:

Create an ArrayList ofContentProviderOperationobjects.

Invoke ContentResolver.applyBatch() to send the operations to the specified content provider, supplying the provider’sauthoritystring and the ContentProviderOperation array as arguments.

The return value is an array ofContentProviderResultobjects, each one representing the result of the corresponding ContentProviderOperation request.

以批處理模式訪問content provider:

創建一個裝ContentProviderOperation對象的ArrayList。

調用ContentResolver.applyBatch()方法來給指定的ContentProvider發送操作指令,要傳遞的參數是ContentProvider的授權字符串和ContentProviderOperation集合。

返回值是一個裝有ContentProviderResult對象的數組,數組中的每個元素都代表著對應的ContentProviderOperation請求的結果。

Batch Access, the ContentProviderOperation and ContentProviderResult Classes(這兩個類的介紹)

The ContentProviderOperation class has a set of static methods that returnbuildersfor each type of operation.

To create a ContentProviderOperation object:

Obtain an appropriate builder.

Use the builder methods to configure the parameters of the operation.

Invoke the build() method to create the final ContentProviderOperation object.

ContentProviderOperation類有一組靜態方法,返回值是每種操作對應的builder.

創建一個ContentProviderOperation對象:

獲得一個適當的builder。

使用builder裡的方法來配置操作的參數。

調用build()方法來創建最終的ContentProviderOperation對象。

The ContentProviderResult object has two public fields, one of which is set depending on the corresponding operation:

ContentProviderResult對象有下面兩個公共字段,其中一個的設置取決於相應的操作:

Integer count

The count of rows affects by a delete or update operation

刪除或更新操作涉及到的行的數量

Uri uri

The Uri of a newly inserted row

新插入行的Uri

Batch Access, Example(批量訪問示例)

This shows an example of batch inserts into the User Dictionary Provider:

這是一個批量插入User Dictionary Provider的示例:

// Declare the operations ArrayList ArrayList batchOps = new ArrayList(); // Declare an array of new terms String[] words = {"foo", "bar", "wibble"}; // Declare a new array will contain the Uris of the new records Uri newUris[words.length]; // Create a set of insert ContentProviderOperations for (int index; words.length; index++) { batchOps.add(ContentProviderOperation.newInsert(UserDictionary.Word.CONTENT_URI) .withValue(UserDictionary.Words.APP_ID, "example.user") .withValue(UserDictionary.Words.LOCALE, "en_US") .withValue(UserDictionary.Words.WORD, words[index]) .withValue(UserDictionary.Words.FREQUENCY, "100") .build()); } // Invoke the batch insertion ContentProviderResult[] opResults = getContentResolver().applyBatch(UserDictionary.AUTHORITY, batchOps); // Extract the Uris of the new records for (int index; opResults.length; index++) { newUris[index] = opResults[index].uri; }

Topic Summary(主題總結)

You should now be able to:

Write client code that can read and modify the data managed by a content provider

Find and usecontract classesdocumenting the constants exposed by system content providers

Use batch access to perform more efficient interaction with a content provider

現在你應該可以做到以下這些:

編寫可以讀取和修改由content provider管理的數據的客戶端代碼找到並使用記錄了系統暴露出的常量的合約類

使用批量訪問的方式更有效地與ContentProvider交互

下一篇鏈接:Android Content Provider Tutorial--安卓內容提供者系列3--操作安卓聯系人

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