Android開發指南-框架主題-內容提供器

內容提供器Content Providersjava

內容提供器用來存放和獲取數據並使這些數據能夠被全部的應用程序訪問。它們是應用程序之間共享數據的惟一方法;不存在全部Android軟件包都能訪問的公共儲存區域。android

Android爲常見數據類型(音頻,視頻,圖像,我的聯繫人信息,等等)裝載了不少內容提供器。你能夠看到在android.provider包裏列舉了一些。你還能查詢這些提供器包含了什麼數據(儘管,對某些提供器,你必須獲取合適的權限來讀取數據)。數據庫

若是你想公開你本身的數據,你有兩個選擇:你能夠建立你本身的內容提供器(一個ContentProvider子類)或者你能夠給已有的提供器添加數據-若是存在一個控制一樣類型數據的內容提供器且你擁有寫的權限。數組

這篇文檔是一篇關於如何使用內容提供器的簡介。先是一個簡短的基礎知識討論,而後探究如何查詢一個內容提供器,如何修改內容提供器控制的數據,以及如何建立你本身的內容提供器。安全

內容提供器的基礎知識Content Provider Basicsapp

內容提供器究竟如何在表層下保存它的數據依賴於它的設計者。可是全部的內容提供器實現了一個公共的接口來查詢這個提供器和返回結果-增長,替換,和刪除數據也是同樣。dom

這是一個客戶端直接使用的接口,通常是經過ContentResolver對象。你能夠經過getContentResolver()從一個活動或其它應用程序組件的實現裏獲取一個ContentResolver:ide

ContentResolver cr = getContentResolver();ui

而後你可使用這個ContentResolver的方法來和你感興趣的任何內容提供器交互。this

當初始化一個查詢時,Android系統識別查詢目標的內容提供器並確保它正在運行。系統實例化全部的ContentProvider對象;你歷來不須要本身作。事實上,你歷來不會直接處理ContentProvider對象。一般,對於每一個類型的ContentProvider只有一個簡單的實例。但它可以和不一樣應用程序和進程中的多個ContentProvider對象通信。進程間的交互經過ContentResolver和ContentProvider類處理。

數據模型The data model

內容提供器以數據庫模型上的一個簡單表格形式暴露它們的數據,這裏每個行是一個記錄,每一列是特別類型和含義的數據。好比,關於我的信息以及他們的電話號碼可能會如下面的方式展現:

_ID
 NUMBER
 NUMBER_KEY
 LABEL
 NAME
 TYPE
 
13
 (425) 555 6677
 425 555 6677
 Kirkland office
 Bully Pulpit
 TYPE_WORK
 
44
 (212) 555-1234
 212 555 1234
 NY apartment
 Alan Vain
 TYPE_HOME
 
45
 (212) 555-6657
 212 555 6657
 Downtown office
 Alan Vain
 TYPE_MOBILE
 
53
 201.555.4433
 201 555 4433
 Love Nest
 Rex Cars
 TYPE_HOME
 

每一個記錄包含一個數字的_ID字段用來惟一標識這個表格裏的記錄。IDs能夠用來匹配相關表格中的記錄-好比,用來在一張表格中查找我的電話號碼並在另一張表格中查找這我的的照片。

一個查詢返回一個Cursor 對象可在表格和列中移動來讀取每一個字段的內容。它有特定的方法來讀取每一個數據類型。因此,爲了讀取一個字段,你必須瞭解這個字段包含了什麼數據類型。(後面會更多的討論查詢結果和遊標Cursor對象)。

惟一資源標識符URIs

每一個內容提供器暴露一個公開的URI(以一個Uri 對象包裝)來惟一的標識它的數據集。一個控制多個數據集(多個表)的內容提供器爲每個數據集暴露一個單獨的URI。全部提供器的URIs以字符串"content://"開始。這個content:形式代表了這個數據正被一個內容提供器控制着。

若是你正準備定義一個內容提供器,爲了簡化客戶端代碼和使未來的升級更清楚,最好也爲它的URI定義一個常量。Android爲這個平臺全部的提供器定義了CONTENT_URI 常量。好比,匹配我的電話號碼的表的URI和包含我的照片的表的URI是:(均由聯繫人Contacts內容提供器控制)

android.provider.Contacts.Phones.CONTENT_URI
android.provider.Contacts.Photos.CONTENT_URI

相似的,最近電話呼叫的表和日程表條目的URI以下:Similarly, the URIs for the table of recent phone calls and the table of calendar entries are:

android.provider.CallLog.Calls.CONTENT_URI
android.provider.Calendar.CONTENT_URI

這個URI常量被使用在和這個內容提供器全部的交互中。每一個ContentResolver 方法採用這個URI做爲它的第一個參數。正是它標識了ContentResolver應該和哪一個內容提供器對話以及這個內容提供器的哪張表格是其目標。

查詢一個內容提供器Querying a Content Provider

你須要三方面的信息來查詢一個內容提供器:

·         用來標識內容提供器的URI

·         你想獲取的數據字段的名字

·         這些字段的數據類型

若是你想查詢某一條記錄,你一樣須要那條記錄的ID。

生成查詢Making the query

你可使用ContentResolver.query()方法或者Activity.managedQuery()方法來查詢一個內容提供器。兩種方法使用相同的參數序列,並且都返回一個Cursor對象。不過,managedQuery()使得活動須要管理這個遊標的生命週期。一個被管理的遊標處理全部的細節,好比當活動暫停時卸載自身,而活動從新啓動時從新查詢它本身。你可讓一個活動開始管理一個還沒有被管理的遊標對象,經過以下調用: Activity.startManagingCursor()。

不管query()仍是managedQuery(),它們的第一個參數都是內容提供器的URI-CONTENT_URI常量用來標識某個特定的ContentProvider和數據集(參見前面的URIs)。

爲了限制只對一個記錄進行查詢,你能夠在URI後面擴展這個記錄的_ID值-也就是,在URI路徑部分的最後加上匹配這個ID的字符串。好比,若是ID是23,那麼URI會是:

content://. . . ./23

有一些輔助方法,特別是ContentUris.withAppendedId() 和Uri.withAppendedPath(),使得爲URI擴展一個ID變得簡單。因此,好比,若是你想在聯繫人數據庫中查找記錄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()方法的其它參數限定了更多的查詢細節。以下:

·         應該返回的數據列的名字。null值返回全部列。不然只有列出名字的列被返回。全部這個平臺的內容提供器爲它們的列定義了常量。好比,android.provider.Contacts.Phones類對前面說明過的通信錄中各個列的名字定義了常量ID, NUMBER, NUMBER_KEY, NAME, 等等。

·         指明返回行的過濾器,以一個SQL WHERE語句格式化。 null值返回全部行。(除非這個URI限定只查詢一個單獨的記錄)。

·         選擇參數

·         返回行的排列順序,以一個SQL ORDER 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");

這個查詢從聯繫人內容提供器中獲取了數據。它獲得名字,首選電話號碼,以及每一個聯繫人的惟一記錄ID。同時它在每一個記錄的_COUNT字段告知返回的記錄數目。

列名的常量被定義在不一樣的接口中-_ID和_COUNT 定義在BaseColumns裏, NAME在PeopleColumns裏,NUMBER在PhoneColumns裏。Contacts.People類已經實現了這些接口,這就是爲何上面的代碼實例只須要使用類名就能夠引用它們的緣由。

查詢的返回結果What a query returns

一個查詢返回零個或更多數據庫記錄的集合。列名,默認順序,以及它們的數據類型是特定於每一個內容提供器的。但全部提供器都有一個_ID列,包含了每一個記錄的惟一ID。另外全部的提供器均可以經過返回_COUNT 列告知記錄數目。它的數值對於全部的行而言都是同樣的。

下面是前述查詢的返回結果的一個例子:

_ID
 _COUNT
 NAME
 NUMBER
 
44
 3
 Alan Vain
 212 555 1234
 
13
 3
 Bully Pulpit
 425 555 6677
 
53
 3
 Rex Cars
 201 555 4433
 

獲取到的數據經過一個遊標Cursor對象暴露出來,經過遊標你能夠在結果集中先後瀏覽。你只能用這個對象來讀取數據。若是想增長,修改和刪除數據,你必須使用一個ContentResolver對象。

讀取查詢所獲數據Reading retrieved data

查詢返回的遊標對象能夠用來訪問結果記錄集。若是你經過指定的一個ID來查詢,這個集合將只有一個值。不然,它能夠包含多個數值。(若是沒有匹配結果,那還多是空的。)你能夠從表格中的特定字段讀取數據,但你必須知道這個字段的數據類型,由於這個遊標對象對於每種數據類型都有一個單獨的讀取方法-好比getString(), getInt(), 和getFloat()。(不過,對於大多數類型,若是你調用讀取字符串的方法,遊標對象將返回給你這個數據的字符串表示。)遊標可讓你按列索引請求列名,或者按列名請求列索引。

下面的代碼片段演示瞭如何從前述查詢結果中讀取名字和電話號碼:

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的字符串可用來獲取這個數據,通常而言,較小的數據(例如,20到50K或更小)最可能被直接存放到表格中,能夠經過調用Cursor.getBlob()來獲取。它返回一個字節數組。

若是這個表格條目是一個content: URI,你不應試圖直接打開和讀取該文件(會由於權限問題而失敗)。相反,你應該調用ContentResolver.openInputStream()來獲得一個InputStream對象,你可使用它來讀取數據。

修改數據Modifying Data

保存在內容提供器中的數據能夠經過下面的方法修改:

·         增長新的記錄

·         爲已有的記錄添加新的數據

·         批量更新已有記錄

·         刪除記錄

全部的數據修改操做都經過使用ContentResolver方法來完成。一些內容提供器對寫數據須要一個比讀數據更強的權限約束。若是你沒有一個內容提供器的寫權限,這個ContentResolver方法會失敗。

增長記錄Adding records

想要給一個內容提供器增長一個新的記錄,第一步是在ContentValues對象裏構建一個鍵-值對映射,這裏每一個鍵和內容提供器的一個列名匹配而值是新記錄中那個列指望的值。而後調用ContentResolver.insert()並傳遞給它提供器的URI和這個ContentValues映射圖。這個方法返回新記錄的URI全名-也就是,內容提供器的URI加上該新記錄的擴展ID。你可使用這個URI來查詢並獲得這個新記錄上的一個遊標,而後進一步修改這個記錄。下面是一個例子:

import android.provider.Contacts.People;

import android.content.ContentResolver;

import android.content.ContentValues;

 

ContentValues values = new ContentValues();

 

// Add Abraham Lincoln to contacts and make him a favorite.

values.put(People.NAME, "Abraham Lincoln");

// 1 = the new contact is added to favorites

// 0 = the new contact is not added to favorites

values.put(People.STARRED, 1);

 

Uri uri = getContentResolver().insert(People.CONTENT_URI, values);

增長新值Adding new values

一旦記錄已經存在,你就能夠添加新的信息或修改已有信息。好比,上例中的下一步就是添加聯繫人信息-如一個電話號碼或一個即時通信IM或電子郵箱地址-到新的條目中。

在聯繫人數據庫中增長一條記錄的最佳途徑是在該記錄URI後擴展表名,而後使用這個修正的URI來添加新的數據值。爲此,每一個聯繫人表暴露一個CONTENT_DIRECTORY常量的表名。下面的代碼繼續以前的例子,爲上面剛剛建立的記錄添加一個電話號碼和電子郵件地址:

Uri phoneUri = null;

Uri emailUri = null;

 

// Add a phone number for Abraham Lincoln.  Begin with the URI for

// the new record just returned by insert(); it ends with the _ID

// of the new record, so we don't have to add the ID ourselves.

// Then append the designation for the phone table to this URI,

// and use the resulting URI to insert the phone number.

phoneUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);

 

values.clear();

values.put(People.Phones.TYPE, People.Phones.TYPE_MOBILE);

values.put(People.Phones.NUMBER, "1233214567");

getContentResolver().insert(phoneUri, values);

 

// Now add an email address in the same way.

emailUri = Uri.withAppendedPath(uri, People.ContactMethods.CONTENT_DIRECTORY);

 

values.clear();

// ContactMethods.KIND is used to distinguish different kinds of

// contact methods, such as email, IM, etc.

values.put(People.ContactMethods.KIND, Contacts.KIND_EMAIL);

values.put(People.ContactMethods.DATA, "test@example.com");

values.put(People.ContactMethods.TYPE, People.ContactMethods.TYPE_HOME);

getContentResolver().insert(emailUri, values);  

你能夠經過調用接收字節流的ContentValues.put()版原本把少許的二進制數據放到一張表格裏去。這對於像小圖標或短小的音頻片段這樣的數據是可行的。可是,若是你有大量二進制數據須要添加,好比一張相片或一首完整的歌曲,則須要把該數據的content: URI放到表裏而後以該文件的URI調用ContentResolver.openOutputStream() 方法。(這致使內容提供器把數據保存在一個文件裏而且記錄文件路徑在這個記錄的一個隱藏字段中。)

考慮到這一點,MediaStore 內容提供器,這個用來分發圖像,音頻和視頻數據的主內容提供器,利用了一個特殊的約定:用來獲取關於這個二進制數據的元信息的query()或managedQuery()方法使用的URI,一樣能夠被openInputStream()方法用來數據自己。相似的,用來把元信息放進一個MediaStore記錄裏的insert()方法使用的URI,一樣能夠被openOutputStream()方法用來在那裏存放二進制數據。下面的代碼片段說明了這個約定:

import android.provider.MediaStore.Images.Media;

import android.content.ContentValues;

import java.io.OutputStream;

 

// Save the name and description of an image in a ContentValues map. 

ContentValues values = new ContentValues(3);

values.put(Media.DISPLAY_NAME, "road_trip_1");

values.put(Media.DESCRIPTION, "Day 1, trip to Los Angeles");

values.put(Media.MIME_TYPE, "image/jpeg");

 

// Add a new record without the bitmap, but with the values just set.

// insert() returns the URI of the new record.

Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values);

 

// Now get a handle to the file for that record, and save the data into it.

// Here, sourceBitmap is a Bitmap object representing the file to save to the database.

try {

    OutputStream outStream = getContentResolver().openOutputStream(uri);

    sourceBitmap.compress(Bitmap.CompressFormat.JPEG, 50, outStream);

    outStream.close();

} catch (Exception e) {

    Log.e(TAG, "exception while writing image", e);

}

批量更新記錄Batch updating records

要批量更新一組記錄(例如,把全部字段中的"NY"改成"New York"),能夠傳以須要改變的列和值參數來調用ContentResolver.update()方法。

刪除一個記錄Deleting a record

要刪除單個記錄,能夠傳以一個特定行的URI參數來調用ContentResolver.delete()方法。

要刪除多行記錄,能夠傳以須要被刪除的記錄類型的URI參數來調用ContentResolver.delete()方法(例如,android.provider.Contacts.People.CONTENT_URI)以及一個SQL WHERE 語句來定義哪些行要被刪除。(當心:若是你想刪除一個通用類型,你得確保包含一個合法的WHERE語句,不然你可能刪除比設想的多得多的記錄!)

建立一個內容提供器Creating a Content Provider

要建立一個內容提供器,你必須:

·         創建一個保存數據的系統。大多數內容提供器使用Android的文件儲存方法或SQLite數據庫來存放它們的數據,可是你能夠用任何你想要的方式來存放數據。Android提供SQLiteOpenHelper類來幫助你建立一個數據庫以及SQLiteDatabase類來管理它。

·         擴展ContentProvider類來提供數據訪問接口。

·         在清單manifest文件中爲你的應用程序聲明這個內容提供器(AndroidManifest.xml)。

下面的章節對後來兩項任務有一些標註。

擴展ContentProvider類Extending the ContentProvider class

你能夠定義一個ContentProvider子類來暴露你的數據給其它使用符合ContentResolver和遊標Cursor對象約定的應用程序。理論上,這意味須要實現6個ContentProvider類的抽象方法:

query()
insert()
update()
delete()
getType()
onCreate()

query()方法必須返回一個遊標Cursor對象能夠用來遍歷請求數據,遊標自己是一個接口,但Android提供了一些現成的Cursor對象給你使用。例如,SQLiteCursor能夠用來遍歷SQLite數據庫。你能夠經過調用任意的SQLiteDatabase類的query()方法獲得它。還有一些其它的遊標實現-好比MatrixCursor-用來訪問沒有存放在數據庫中的數據。

由於這些內容提供器的方法能夠從不一樣的進程和線程的各個ContentResolver對象中調用,因此它們必須以線程安全的方式來實現。

周到起見,當數據被修改時,你可能還須要調用ContentResolver.notifyChange()方法來通知偵聽者。

除了定義子類之外,你應該還須要採起其它一些步驟來簡化客戶端的工做和讓這個類更容易被訪問:

·         定義一個public static final Uri 命名爲CONTENT_URI。這是你的內容提供器處理的整個content: URI的字符串。你必須爲它定義一個惟一的字符串。最佳方案是使用這個內容提供器的全稱(fully qualified)類名(小寫)。所以,例如,一個TransportationProvider類能夠定義以下:

public static final Uri CONTENT_URI = Uri.parse("content://com.example.codelab.transporationprovider");

若是這個內容提供器有子表,那麼爲每一個子表也都定義CONTENT_URI常量。這些URIs應該所有擁有相同的權限(既然這用來識別內容提供器),只能經過它們的路徑加以區分。例如:

content://com.example.codelab.transporationprovider/train
content://com.example.codelab.transporationprovider/air/domestic
content://com.example.codelab.transporationprovider/air/international

請查閱本文最後部分的Content URI Summary以對content: URIs有一個整體的瞭解。

·         定義內容提供器返回給客戶端的列名。若是你正在使用一個底層數據庫,這些列名一般和SQL數據庫列名一致。一樣還須要定義公共的靜態字符串常量用來指定查詢語句以及其它指令中的列。

確保包含一個名爲"_id"(常量_ID)的整數列來返回記錄的IDs。你應該有這個字段而無論有沒有其它字段(好比URL),這個字段在全部的記錄中是惟一的。若是你在使用SQLite數據庫,這個_ID 字段應該是下面的類型:

INTEGER PRIMARY KEY AUTOINCREMENT

其中AUTOINCREMENT描述符是可選的。可是沒有它,SQLite的ID數值字段會在列中已存在的最大數值的基礎上增長到下一個數字。若是你刪除了最後的行,那麼下一個新加的行會和這個刪除的行有相同的ID。AUTOINCREMENT能夠避免這種狀況,它讓SQLite老是增長到下一個最大的值而無論有沒有刪除。

·         在文檔中謹慎的描述每一個列的數據類型。客戶端須要這些信息來讀取數據。

·         若是你正在處理一個新的數據類型,你必須定義一個新的MIME類型在你的ContentProvider.getType()實現裏返回。這個類型部分依賴於提交給getType()的content: URI參數是否對這個請求限制了特定的記錄。有一個MIME類型是給單個記錄用的,另一個給多記錄用。 使用Uri 方法來幫助判斷哪一個是正在被請求的。下面是每一個類型的通常格式:

²  對於單個記錄:    vnd.android.cursor.item/vnd.yourcompanyname.contenttype

好比,一個火車記錄122的請求,URI以下

content://com.example.transportationprovider/trains/122

可能會返回這個MIME類型:

vnd.android.cursor.item/vnd.example.rail

²  對於多個記錄:    vnd.android.cursor.dir/vnd.yourcompanyname.contenttype

好比, 一個全部火車記錄的請求,URI以下

content://com.example.transportationprovider/trains

可能會返回這個MIME類型:

vnd.android.cursor.dir/vnd.example.rail

·         若是你想暴露過於龐大而沒法放在表格裏的字節數據-好比一個大的位圖文件-這個給客戶端暴露數據的字段事實上應該包含一個content: URI字符串。這個字段給了客戶端數據訪問接口。這個記錄應該有另外的一個字段,名爲"_data",列出了這個文件在設備上的準確路徑。這個字段不能被客戶端讀取,而要經過ContentResolver。客戶端將在這個包含URI的用戶側字段上調用ContentResolver.openInputStream() 方法。ContentResolver會請求那個記錄的"_data"字段,並且由於它有比客戶端更高的許可權,它應該可以直接訪問那個文件並返回給客戶端一個包裝的文件讀取接口。

自定義內容提供器的實現的一個例子,參見SDK附帶的Notepad例程中的NodePadProvider 類。

聲明內容提供器Declaring the content provider

爲了讓Android系統知道你開發的內容提供器,能夠用在應用程序的AndroidManifest.xml文件中以 元素聲明它。未經聲明的內容提供器對Android系統不可見。

名字屬性是ContentProvider子類的全稱名(fully qualified name)。權限屬性是標識提供器的content: URI的權限認證部分。例如若是ContentProvider子類是AutoInfoProvider,那麼 元素可能以下:

< p>

          authorities="com.example.autos.autoinfoprovider"

          . . . />

請注意到這個權限屬性忽略了content: URI的路徑部分。例如,若是AutoInfoProvider爲各類不一樣的汽車或製造商控制着各個子表,Note that the authorities attribute omits the path part of a content: URI. For example, if AutoInfoProvider controlled subtables for different types of autos or different manufacturers,

content://com.example.autos.autoinfoprovider/honda
content://com.example.autos.autoinfoprovider/gm/compact
content://com.example.autos.autoinfoprovider/gm/suv

這些路徑將不會在manifest裏聲明。權限是用來識別提供器的,而不是路徑;你的提供器能以任何你選擇的方式來解釋URI中的路徑部分。

其它 屬性能夠設置數據讀寫許可,提供能夠顯示給用戶的圖標和文本,啓用或禁用這個提供器,等等。若是數據不須要在多個內容提供器的運行版本中同步則能夠把multiprocess屬性設置成"true"。這使得在每一個客戶進程中都有一個提供器實例被建立,而無需執行IPC調用。

Content URI 總結

這裏回顧一下content URI的重要內容:

 


A.      標準前綴代表這個數據被一個內容提供器所控制。它不會被修改。

B.      URI的權限部分;它標識這個內容提供器。對於第三方應用程序,這應該是一個全稱類名(小寫)以確保惟一性。權限在 元素的權限屬性中進行聲明:

< p>

          authorities="com.example.transportationprovider"

          . . .  >

C.      用來判斷請求數據類型的路徑。這能夠是0或多個段長。若是內容提供器只暴露了一種數據類型(好比,只有火車),這個分段能夠沒有。若是提供器暴露若干類型,包括子類型,那它能夠是多個分段長-例如,提供"land/bus", "land/train", "sea/ship", 和"sea/submarine"這4個可能的值。

D.      被請求的特定記錄的ID,若是有的話。這是被請求記錄的_ID數值。若是這個請求不侷限於單個記錄, 這個分段和尾部的斜線會被忽略:

content://com.example.transportationprovider/trains

 

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/iefreer/archive/2009/09/07/4528800.aspx

相關文章
相關標籤/搜索