編輯:關於Android編程
內容提供程序管理對中央數據存儲庫的訪問。您將 提供程序作為 Android 應用中的一個或多個類(連同清單文件 中的元素)實現。其中一個類會實現子類ContentProvider,即您的提供程序與 其他應用之間的界面。盡管內容提供程序旨在向其他應用提供 數據,但您的應用中必定有這樣一些 Activity,它們允許用戶 查詢和修改由提供程序管理的數據。
本主題的其余部分列出了開發內容提供程序的基本步驟和 需要使用的 API。
著手開發前的准備工作
請在著手開發提供程序之前執行以下操作:
決定您是否需要內容提供程序。如果您想提供下列一項或多項功能,則需要開發內容 提供程序:
您想為其他應用提供復雜的數據或文件
您想允許用戶將復雜的數據從您的應用復制到其他應用中
您想使用搜索框架提供自定義搜索建議
如果完全是在 您自己的應用中使用,則“根本不”需要提供程序即可使用 SQLite 數據庫。
如果您尚未完成此項操作,請閱讀內容提供程序基礎知識主題,了解有關提供程序的詳情。
接下來,請按照以下步驟開發您的提供程序:
為您的數據設計原始存儲。內容提供程序以兩種方式提供數據:
定義ContentProvider類及其 所需方法的具體實現。此類是您的數據與 Android 系統其余部分之間的界面。如需了解有關此類的詳細信息,請參閱實現ContentProvider類部分。
定義提供程序的權限字符串、其內容 URI 以及列名稱。如果您想讓 提供程序的應用處理 Intent,則還要定義 Intent 操作、Extra 數據 以及標志。此外,還要定義想要訪問您的數據的應用必須具備的權限。 您應該考慮在一個單獨的協定類中將所有這些值定義為常量;以後您可以將此類公開給其他開發者。
添加其他可選部分,如示例數據或可以在提供程序與雲數據之間同步數據的AbstractThreadedSyncAdapter實現。
文件數據
通常存儲在文件中的數據,如 照片、音頻或視頻。將文件存儲在您的應用的私有 空間內。您的提供程序可以應其他應用發出的文件請求 提供文件句柄。
“結構化”數據
通常存儲在數據庫、數組或類似結構中的數據。 以兼容行列表的形式存儲數據。行 表示實體,如人員或庫存項目。列表示 實體的某項數據,如人員的姓名或商品的價格。此類數據通常 存儲在 SQLite 數據庫中,但您可以使用任何類型的 持久存儲。
設計數據存儲
內容提供程序是用於訪問以結構化格式保存的數據的界面。在您創建該界面之前,必須決定如何存儲數據。 您可以按自己的喜好以任何形式存儲數據,然後根據需要設計讀寫數據的界面。
以下是 Android 中提供的一些數據存儲技術:
Android 系統包括一個 SQLite 數據庫 API,Android 自己的提供程序使用它來存儲面向表的數據。SQLiteOpenHelper類可幫助您創建數據庫,SQLiteDatabase類是用於訪問數據庫的基類。
請記住,您不必使用數據庫來實現存儲庫。提供程序在外部表現為一組表,與關系數據庫類似,但這並不是對提供程序內部實現的要求;
對於存儲文件數據,Android 提供了各種面向文件的 API。如果您要設計提供媒體相關數據(如音樂或視頻)的提供程序,則可開發一個合並了表數據和文件的提供程序;
要想使用基於網絡的數據,請使用java.net和android.net中的類。 您也可以將基於網絡的數據與本地數據存儲(如數據庫)同步,然後以表或文件的形式提供數據。
數據設計考慮事項
以下是一些設計提供程序數據結構的技巧:
表數據應始終具有一個“主鍵”列,提供程序將其作為與每行對應的唯一數字值加以維護。 您可以使用此值將該行鏈接到其他表中的相關行(將其用作“外鍵”)。 盡管您可以為此列使用任何名稱,但使用BaseColumns._ID是最佳選擇,因為將提供程序查詢的結果鏈接到ListView的條件是,檢索到的其中一個列的名稱必須是_ID;
如果您想提供位圖圖像或其他非常龐大的文件導向型數據,請將數據存儲在一個文件中,然後間接提供這些數據,而不是直接將其存儲在表中。 如果您執行了此操作,則需要告知提供程序的用戶,他們需要使用ContentResolver文件方法來訪問數據;
使用二進制大型對象 (BLOB) 數據類型存儲大小或結構會發生變化的數據。 例如,您可以使用 BLOB 列來存儲協議緩沖區或JSON結構。
您也可以使用 BLOB 來實現獨立於架構的表。在這類表中,您需要以 BLOB 形式定義一個主鍵列、一個 MIME 類型列以及一個或多個通用列。 這些 BLOB 列中數據的含義通過 MIME 類型列中的值指示。 這樣一來,您就可以在同一表中存儲不同類型的行。 舉例來說,聯系人提供程序的“數據”表ContactsContract.Data便是一個獨立於架構的表。
設計內容 URI
內容 URI是用於在提供程序中標識數據的 URI。內容 URI 包括整個提供程序的符號名稱(其權限)和一個指向表或文件的名稱(路徑)。 可選 ID 部分指向 表中的單個行。ContentProvider的每一個數據訪問方法都將內容 URI 作為參數;您可以利用這一點確定要訪問的表、行或文件。
設計權限
提供程序通常具有單一權限,該權限充當其 Android 內部名稱。為避免與其他提供程序發生沖突,您應該使用 Internet 域所有權(反向)作為提供程序權限的基礎。 由於此建議也適用於 Android 軟件包名稱,因此您可以將提供程序權限定義為包含該提供程序的軟件包名稱的擴展名。 例如,如果您的 Android 軟件包名稱為com.example.,則應為提供程序授予權限com.example..provider。
設計路徑結構
開發者通常通過追加指向單個表的路徑來根據權限創建內容 URI。 例如,如果您有兩個表:table1和table2,則可以通過合並上一示例中的權限來生成 內容 URIcom.example..provider/table1和com.example..provider/table2。路徑並不限定於單個段,也無需為每一級路徑都創建一個表。
處理內容 URI ID
按照慣例,提供程序通過接受末尾具有行所對應 ID 值的內容 URI 來提供對表中單個行的訪問。同樣按照慣例,提供程序會將該 ID 值與表的_ID列進行匹配,並對匹配的行執行請求的訪問。
這一慣例為訪問提供程序的應用的常見設計模式提供了便利。應用會對提供程序執行查詢,並使用CursorAdapter以ListView顯示生成的Cursor。 定義CursorAdapter的條件是,Cursor中的其中一個列必須是_ID
用戶隨後從 UI 上顯示的行中選取其中一行,以查看或修改數據。 應用會從支持ListView的Cursor中獲取對應行,獲取該行的_ID值,將其追加到內容 URI,然後向提供程序發送訪問請求。 然後,提供程序便可對用戶選取的特定行執行查詢或修改。
內容 URI 模式
為幫助您選擇對傳入的內容 URI 執行的操作,提供程序 API 加入了實用類UriMatcher,它會將內容 URI“模式”映射到整型值。 您可以在一個switch語句中使用這些整型值,為匹配特定模式的一個或多個內容 URI 選擇所需操作。
內容 URI 模式使用通配符匹配內容 URI:
*:匹配由任意長度的任何有效字符組成的字符串
#:匹配由任意長度的數字字符組成的字符串
以設計和編碼內容 URI 處理為例,假設一個具有權限com.example.app.provider的提供程序能識別以下指向表的內容 URI:
content://com.example.app.provider/table1:一個名為table1的表
content://com.example.app.provider/table2/dataset1:一個名為dataset1的表
content://com.example.app.provider/table2/dataset2:一個名為dataset2的表
content://com.example.app.provider/table3:一個名為table3的表
提供程序也能識別追加了行 ID 的內容 URI,例如,content://com.example.app.provider/table3/1對應由table3中1標識的行的內容 URI。
可以使用以下內容 URI 模式:
以下代碼段說明了UriMatcher中方法的工作方式。 此代碼采用不同方式處理整個表的 URI 與單個行的 URI,它為表使用的內容 URI 模式是content:///,為單個行使用的內容 URI 模式則是content:////。
方法addURI()會將權限和路徑映射到一個整型值。 方法match()會返回 URI 的整型值。switch語句會在查詢整個表與查詢單個記錄之間進行選擇:
public class ExampleProvider extends ContentProvider {
...
// Creates a UriMatcher object.
private static final UriMatcher sUriMatcher;
...
/*
* The calls to addURI() go here, for all of the content URI patterns that the provider
* should recognize. For this snippet, only the calls for table 3 are shown.
*/
...
/*
* Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used
* in the path
*/
sUriMatcher.addURI("com.example.app.provider", "table3", 1);
/*
* Sets the code for a single row to 2. In this case, the "#" wildcard is
* used. "content://com.example.app.provider/table3/3" matches, but
* "content://com.example.app.provider/table3 doesn't.
*/
sUriMatcher.addURI("com.example.app.provider", "table3/#", 2);
...
// Implements ContentProvider.query()
public Cursor query(
Uri uri,
String[] projection,
String selection,
String[] selectionArgs,
String sortOrder) {
...
/*
* Choose the table to query and a sort order based on the code returned for the incoming
* URI. Here, too, only the statements for table 3 are shown.
*/
switch (sUriMatcher.match(uri)) {
// If the incoming URI was for all of table3
case 1:
if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC";
break;
// If the incoming URI was for a single row
case 2:
/*
* Because this URI was for a single row, the _ID value part is
* present. Get the last path segment from the URI; this is the _ID value.
* Then, append the value to the WHERE clause for the query
*/
selection = selection + "_ID = " uri.getLastPathSegment();
break;
default:
...
// If the URI is not recognized, you should do some error handling here.
}
// call the code to actually do the query
}
另一個類ContentUris會提供一些工具方法,用於處理內容 URI 的id部分。Uri類和Uri.Builder類包括一些工具方法,用於解析現有Uri對象和構建新對象。
實現 ContentProvider 類
ContentProvider實例通過處理來自其他應用的請求來管理對結構化數據集的訪問。 所有形式的訪問最終都會調用ContentResolver,後者接著調用ContentProvider的具體方法來獲取訪問權限。
必需方法
抽象類ContentProvider定義了六個抽象方法,您必須將這些方法作為自己具體子類的一部分加以實現。 所有這些方法(onCreate()除外)都由一個嘗試訪問您的內容提供程序的客戶端應用調用:
content://com.example.app.provider/*
匹配提供程序中的任何內容 URI。
content://com.example.app.provider/table2/*:
匹配表dataset1和表dataset2的內容 URI,但不匹配table1或table3的內容 URI。
content://com.example.app.provider/table3/#:匹配table3中單個行的內容 URI,如content://com.example.app.provider/table3/6對應由6標識的行的內容 URI。
query()
從您的提供程序檢索數據。使用參數選擇要查詢的表、要返回的行和列以及結果的排序順序。 將數據作為Cursor對象返回。
insert()
在您的提供程序中插入一個新行。使用參數選擇目標表並獲取要使用的列值。 返回新插入行的內容 URI。
update()
更新您提供程序中的現有行。使用參數選擇要更新的表和行,並獲取更新後的列值。 返回已更新的行數。
delete()
從您的提供程序中刪除行。使用參數選擇要刪除的表和行。 返回已刪除的行數。
getType()
返回內容 URI 對應的 MIME 類型。
onCreate()
初始化您的提供程序。Android 系統會在創建您的提供程序後立即調用此方法。 請注意,ContentResolver對象嘗試訪問您的提供程序時,系統才會創建它。
請注意,這些方法的簽名與同名的ContentResolver方法相同。
您在實現這些方法時應考慮以下事項:
所有這些方法(onCreate()除外)都可由多個線程同時調用,因此它們必須是線程安全方法。
避免在onCreate()中執行長時間操作。將初始化任務推遲到實際需要時進行。實現onCreate()方法部分對此做了更詳盡的描述;
盡管您必須實現這些方法,但您的代碼只需返回要求的數據類型,無需執行任何其他操作。 例如,您可能想防止其他應用向某些表插入數據。 要實現此目的,您可以忽略insert()調用並返回 0。
實現 query() 方法
ContentProvider.query()方法必須返回Cursor對象。如果失敗,則會引發Exception。 如果您使用 SQLite 數據庫作為數據存儲,則只需返回由SQLiteDatabase類的其中一個query()方法返回的Cursor。 如果查詢不匹配任何行,您應該返回一個Cursor實例(其getCount()方法返回 0)。 只有當查詢過程中出現內部錯誤時,您才應該返回null。
如果您不使用 SQLite 數據庫作為數據存儲,請使用Cursor的其中一個具體子類。 例如,在MatrixCursor類實現的游標中,每一行都是一個Object數組。 對於此類,請使用addRow()來添加新行。
請記住,Android 系統必須能夠跨進程邊界傳播Exception。 Android 可以為以下異常執行此操作,這些異常可能有助於處理查詢錯誤:
IllegalArgumentException(您可以選擇在提供程序收到無效的內容 URI 時引發此異常)
NullPointerException
實現 insert() 方法
insert()方法會使用ContentValues參數中的值向相應表中添加新行。 如果ContentValues參數中未包含列名稱,您可能想在您的提供程序代碼或數據庫架構中提供其默認值。
此方法應該返回新行的內容 URI。要想構建此方法,請使用withAppendedId()向表的內容 URI 追加新行的_ID(或其他主鍵)值。
實現 delete() 方法
delete()方法不需要從您的數據存儲中實際刪除行。 如果您將同步適配器與提供程序一起使用,應該考慮為已刪除的行添加“刪除”標志,而不是將行整個移除。 同步適配器可以檢查是否存在已刪除的行,並將它們從服務器中移除,然後再將它們從提供程序中刪除。
實現 update() 方法
update()方法采用insert()所使用的相同ContentValues參數,以及delete()和ContentProvider.query()所使用的相同selection和selectionArgs參數。 這樣一來,您就可以在這些方法之間重復使用代碼。
實現 onCreate() 方法
Android 系統會在啟動提供程序時調用onCreate()。您只應在此方法中執行運行快速的初始化任務,並將數據庫創建和數據加載推遲到提供程序實際收到數據請求時進行。 如果您在onCreate()中執行長時間的任務,則會減慢提供程序的啟動速度, 進而減慢提供程序對其他應用的響應速度。
例如,如果您使用 SQLite 數據庫,可以在ContentProvider.onCreate()中創建一個新的SQLiteOpenHelper對象,然後在首次打開數據庫時創建 SQL 表。 為簡化這一過程,在您首次調用getWritableDatabase()時,它會自動調用SQLiteOpenHelper.onCreate()方法。
以下兩個代碼段展示了ContentProvider.onCreate()與SQLiteOpenHelper.onCreate()之間的交互。第一個代碼段是ContentProvider.onCreate()的實現:
public class ExampleProvider extends ContentProvider
/*
* Defines a handle to the database helper object. The MainDatabaseHelper class is defined
* in a following snippet.
*/
private MainDatabaseHelper mOpenHelper;
// Defines the database name
private static final String DBNAME = "mydb";
// Holds the database object
private SQLiteDatabase db;
public boolean onCreate() {
/*
* Creates a new helper object. This method always returns quickly.
* Notice that the database itself isn't created or opened
* until SQLiteOpenHelper.getWritableDatabase is called
*/
mOpenHelper = new MainDatabaseHelper(
getContext(), // the application context
DBNAME, // the name of the database)
null, // uses the default SQLite cursor
1 // the version number
);
return true;
}
...
// Implements the provider's insert method
public Cursor insert(Uri uri, ContentValues values) {
// Insert code here to determine which table to open, handle error-checking, and so forth
...
/*
* Gets a writeable database. This will trigger its creation if it doesn't already exist.
*
*/
db = mOpenHelper.getWritableDatabase();
}
}
下一個代碼段是SQLiteOpenHelper.onCreate()的實現,其中包括一個幫助程序類:
...
// A string that defines the SQL statement for creating a table
private static final String SQL_CREATE_MAIN = "CREATE TABLE " +
"main " + // Table's name
"(" + // The columns in the table
" _ID INTEGER PRIMARY KEY, " +
" WORD TEXT"
" FREQUENCY INTEGER " +
" LOCALE TEXT )";
...
/**
* Helper class that actually creates and manages the provider's underlying data repository.
*/
protected static final class MainDatabaseHelper extends SQLiteOpenHelper {
/*
* Instantiates an open helper for the provider's SQLite data repository
* Do not do database creation and upgrade here.
*/
MainDatabaseHelper(Context context) {
super(context, DBNAME, null, 1);
}
/*
* Creates the data repository. This is called when the provider attempts to open the
* repository and SQLite reports that it doesn't exist.
*/
public void onCreate(SQLiteDatabase db) {
// Creates the main table
db.execSQL(SQL_CREATE_MAIN);
}
}
實現 ContentProvider MIME 類型
ContentProvider類具有兩個返回 MIME 類型的方法:
getType()
您必須為任何提供程序實現的必需方法之一。
getStreamTypes()
系統在您的提供程序提供文件時要求實現的方法。
表的 MIME 類型
getType()方法會返回一個 MIME 格式的String,後者描述內容 URI 參數返回的數據類型。Uri參數可以是模式,而不是特定 URI;在這種情況下,您應該返回與匹配該模式的內容 URI 關聯的數據類型。
對於文本、HTML 或 JPEG 等常見數據類型,getType()應該為該數據返回標准 MIME 類型。
對於指向一個或多個表數據行的內容 URI,getType()應該以 Android 供應商特有 MIME 格式返回 MIME 類型:
類型部分:vnd
子類型部分:
如果 URI 模式用於單個行:android.cursor.item/
如果 URI 模式用於多個行:android.cursor.dir/
提供程序特有部分:vnd..
您提供和。值應具有全局唯一性,值應在對應的 URI 模式中具有唯一性。適合選擇貴公司的名稱或您的應用 Android 軟件包名稱的某個部分作為。 適合選擇 URI 關聯表的標識字符串作為。
例如,如果提供程序的權限是com.example.app.provider,並且它公開了一個名為table1的表,則table1中多個行的 MIME 類型是:
vnd.android.cursor.dir/vnd.com.example.provider.table1
對於table1的單個行,MIME 類型是:
vnd.android.cursor.item/vnd.com.example.provider.table1
文件的 MIME 類型
如果您的提供程序提供文件,請實現getStreamTypes()。 該方法會為您的提供程序可以為給定內容 URI 返回的文件返回一個 MIME 類型String數組。您應該通過 MIME 類型過濾器參數過濾您提供的 MIME 類型,以便只返回客戶端想處理的那些 MIME 類型。
例如,假設提供程序以.jpg、.png和.gif格式文件形式提供照片圖像。 如果應用調用ContentResolver.getStreamTypes()時使用了過濾器字符串image/*(任何是“圖像”的內容),則ContentProvider.getStreamTypes()方法應返回數組:
{ "image/jpeg", "image/png", "image/gif"}
如果應用只對.jpg文件感興趣,則可以在調用ContentResolver.getStreamTypes()時使用過濾器字符串*\/jpeg,ContentProvider.getStreamTypes()應返回:
{"image/jpeg"}
如果您的提供程序未提供過濾器字符串中請求的任何 MIME 類型,則getStreamTypes()應返回null。
實現協定類
協定類是一種public final類,其中包含對 URI、列名稱、MIME 類型以及其他與提供程序有關的元數據的常量定義。 該類可確保即使 URI、列名稱等數據的實際值發生變化,也可以正確訪問提供程序,從而在提供程序與其他應用之間建立合同。
協定類對開發者也有幫助,因為其常量通常采用助記名稱,因此可以降低開發者為列名稱或 URI 使用錯誤值的可能性。 由於它是一種類,因此可以包含 Javadoc 文檔。 集成開發環境(如 Eclipse)可以根據協定類自動完成常量名稱,並為常量顯示 Javadoc。
開發者無法從您的應用訪問協定類的類文件,但他們可以通過您提供的.jar文件將其靜態編譯到其應用內。
舉例來說,ContactsContract類及其嵌套類便屬於協定類。
實現內容提供程序權限
默認情況下,存儲在設備內部存儲上的數據文件是您的應用和提供程序的私有數據文件;
您創建的SQLiteDatabase數據庫是您的應用和提供程序的私有數據庫;
默認情況下,您保存到外部存儲的數據文件是公用並可全局讀取的數據文件。 您無法使用內容提供程序來限制對外部存儲內文件的訪問,因為其他應用可以使用其他 API 調用來對它們執行讀取和寫入操作;
用於在您的設備的內部存儲上打開或創建文件或 SQLite 數據庫的方法調用可能會為所有其他應用同時授予讀取和寫入訪問權限。 如果您將內部文件或數據庫用作提供程序的存儲庫,並向其授予“可全局讀取”或“可全局寫入”訪問權限,則您在清單文件中為提供程序設置的權限不會保護您的數據。 內部存儲中文件和數據庫的默認訪問權限是“私有”,對於提供程序的存儲庫,您不應更改此權限。
如果您想使用內容提供程序權限來控制對數據的訪問,則應將數據存儲在內部文件、SQLite 數據庫或“雲”中(例如,遠程服務器上),而且您應該保持文件和數據庫為您的應用所私有。
實現權限
即使底層數據為私有數據,所有應用仍可從您的提供程序讀取數據或向其寫入數據,因為在默認情況下,您的提供程序未設置權限。 要想改變這種情況,請使用屬性或
您可以通過清單文件中的一個或多個
以下列表描述了提供程序權限的作用域,從適用於整個提供程序的權限開始,然後逐漸細化。 更細化的權限優先於作用域較大的權限:
統一讀寫提供程序級別權限
一個同時控制對整個提供程序讀取和寫入訪問的權限,通過
單獨的讀取和寫入提供程序級別權限
針對整個提供程序的讀取權限和寫入權限。您可以通過
路徑級別權限
針對提供程序中內容 URI 的讀取、寫入或讀取/寫入權限。您可以通過
臨時權限
一種權限級別,即使應用不具備通常需要的權限,該級別也能授予對應用的臨時訪問權限。 臨時訪問功能可減少應用需要在其清單文件中請求的權限數量。 當您啟用臨時權限時,只有持續訪問您的所有數據的應用才需要“永久性”提供程序訪問權限。
假設您需要實現電子郵件提供程序和應用的權限,如果您想允許外部圖像查看器應用顯示您的提供程序中的照片附件, 為了在不請求權限的情況下為圖像查看器提供必要的訪問權限,可以為照片的內容 URI 設置臨時權限。 對您的電子郵件應用進行相應設計,使應用能夠在用戶想要顯示照片時向圖像查看器發送一個 Intent,其中包含照片的內容 URI 以及權限標志。 圖像查看器可隨後查詢您的電子郵件提供程序以檢索照片,即使查看器不具備對您提供程序的正常讀取權限,也不受影響。
要想啟用臨時權限,請設置
該屬性的值決定可訪問的提供程序范圍。 如果該屬性設置為true,則系統會向整個提供程序授予臨時權限,該權限將替代您的提供程序級別或路徑級別權限所需的任何其他權限。
如果此標志設置為false,則您必須向
要向應用授予臨時訪問權限, Intent 必須包含FLAG_GRANT_READ_URI_PERMISSION和/或FLAG_GRANT_WRITE_URI_PERMISSION標志。它們通過setFlags()方法進行設置。
如果android:grantUriPermissions屬性不存在,則假設其為false。
元素
與Activity和Service組件類似,必須使用
權限 (android:authorities)
用於在系統內標識整個提供程序的符號名稱。
提供程序類名 (android:name)
實現ContentProvider的類。
權限
指定其他應用訪問提供程序的數據所必須具備權限的屬性:
android:grantUriPermssions:臨時權限標志
android:permission:統一提供程序范圍讀取/寫入權限
android:readPermission:提供程序范圍讀取權限
android:writePermission:提供程序范圍寫入權限
啟動和控制屬性
這些屬性決定 Android 系統如何以及何時啟動提供程序、提供程序的進程特性以及其他運行時設置:
android:enabled:允許系統啟動提供程序的標志。
android:exported:允許其他應用使用此提供程序的標志。
android:initOrder:此提供程序相對於同一進程中其他提供程序的啟動順序。
android:multiProcess:允許系統在與調用客戶端相同的進程中啟動提供程序的標志。
android:process:應在其中運行提供程序的進程的名稱。
android:syncable:指示提供程序的數據將與服務器上的數據同步的標志。
信息屬性
提供程序的可選圖標和標簽:
android:icon:包含提供程序圖標的 Drawable 資源。 該圖標出現在設置>應用>全部中應用列表內的提供程序標簽旁;
android:label:描述提供程序和/或其數據的信息標簽。 該標簽出現在設置>應用>全部中的應用列表內。
Intent 和數據訪問
應用可以通過Intent間接訪問內容提供程序。 應用不會調用ContentResolver或ContentProvider的任何方法,而會發送一個啟動 Activity 的 Intent,該 Activity 通常是提供程序自身應用的一部分。 目標 Activity 負責檢索和顯示其 UI 中的數據。視 Intent 中的操作而定,目標 Activity 可能還會提示用戶對提供程序的數據進行修改。 Intent 可能還包含目標 Activity 在 UI 中顯示的“extra”數據;用戶隨後可以選擇更改此數據,然後使用它來修改提供程序中的數據。
您可能想使用 Intent 訪問權限來幫助確保數據完整性。您的提供程序可能依賴於根據嚴格定義的業務邏輯插入、更新和刪除數據。 如果是這種情況,則允許其他應用直接修改您的數據可能會導致無效的數據。 如果您想讓開發者使用 Intent 訪問權限,請務必為其提供詳盡的參考資料。 向他們解釋為什麼使用自身應用 UI 的 Intent 訪問比嘗試通過代碼修改數據更好。
處理想要修改您的提供程序數據的傳入 Intent 與處理其他 Intent 沒有區別。
1、結構體的創建及導入,結構體指針等。以JniNativeInterface, DexHeader為例。解析Dex的函數如下: F5後如下:&nbs
前些天在展訊6825C 上調試gc2115攝像頭,發現後攝顯示效果非常的差,出現很嚴重的整個預覽界面豎條紋現象,但是對光線變化還是有反應的,初步判斷是sens
前言:在上篇中,分析了MediaPlayer的從創建到setDataSource過程,盡管看了代碼,但是沒有從MediaPlayer生態上認識各類庫之間依賴調用關系,在本
直接附代碼:#import "MyView.h"#import // 行距const CGFloat kGlobalLineLeading = 5.0