原文:http://developer.android.com/training/sync-adapters/creating-sync-adapter.html
設(shè)備和服務(wù)器之間執(zhí)行數(shù)據(jù)傳輸?shù)拇a會封裝在應(yīng)用的 Sync Adapter 組件中鞠抑。Sync Adapter 框架會基于我們的調(diào)度和觸發(fā)操作戴质,運行 Sync Adapter 組件中的代碼驱闷。要將同步適配組件添加到應(yīng)用當(dāng)中忙迁,我們需要添加下列部件:
Sync Adapter 類
將我們的數(shù)據(jù)傳輸代碼封裝到一個與 Sync Adapter 框架兼容的接口當(dāng)中。
綁定Service
通過一個綁定服務(wù),允許 Sync Adapter 框架運行 Sync Adapter 類中的代碼。
Sync Adapter 的 XML 元數(shù)據(jù)文件
該文件包含了有關(guān) Sync Adapter 的信息路操。框架會根據(jù)該文件確定應(yīng)該如何加載并調(diào)度數(shù)據(jù)傳輸任務(wù)千贯。
應(yīng)用 manifest 清單文件的聲明
需要在應(yīng)用的 manifest 清單文件中聲明綁定服務(wù)屯仗;同時還需要指出 Sync Adapter 的元數(shù)據(jù)。
這節(jié)課將會向我們展示如何定義他們搔谴。
創(chuàng)建一個 Sync Adapter 類
在這部分課程中魁袜,我們將會學(xué)習(xí)如何創(chuàng)建封裝了數(shù)據(jù)傳輸代碼的 Sync Adapter 類。創(chuàng)建該類需要繼承 Sync Adapter 的基類;為該類定義構(gòu)造函數(shù)峰弹;以及實現(xiàn)相關(guān)的方法距境。在這些方法中,我們定義數(shù)據(jù)傳輸任務(wù)垮卓。
繼承 Sync Adapter 基類:AbstractThreadedSyncAdapter
要創(chuàng)建 Sync Adapter 組件,首先繼承AbstractThreadedSyncAdapter师幕,然后編寫它的構(gòu)造函數(shù)粟按。與使用Activity.onCreate()配置 Activity 時一樣,每次我們重新創(chuàng)建 Sync Adapter 組件的時候霹粥,使用構(gòu)造函數(shù)執(zhí)行相關(guān)的配置灭将。例如,如果我們的應(yīng)用使用一個 Content Provider 來存儲數(shù)據(jù)后控,那么使用構(gòu)造函數(shù)來獲取一個ContentResolver實例庙曙。由于從 Android 3.0 開始添加了第二種形式的構(gòu)造函數(shù),來支持parallelSyncs參數(shù)浩淘,所以我們需要創(chuàng)建兩種形式的構(gòu)造函數(shù)來保證兼容性捌朴。
Note:Sync Adapter 框架是設(shè)計成和 Sync Adapter 組件的單例一起工作的。實例化 Sync Adapter 組件的更多細節(jié)张抄,會在后面的章節(jié)中展開砂蔽。
下面的代碼展示了如何實現(xiàn)AbstractThreadedSyncAdapter和它的構(gòu)造函數(shù):
/**
* Handle the transfer of data between a server and an
* app, using the Android sync adapter framework.
*/publicclassSyncAdapterextendsAbstractThreadedSyncAdapter{? ? ...// Global variables// Define a variable to contain a content resolver instanceContentResolver mContentResolver;/**
* Set up the sync adapter
*/publicSyncAdapter(Context context,booleanautoInitialize){super(context, autoInitialize);/*
* If your app uses a content resolver, get an instance of it
* from the incoming Context
*/mContentResolver = context.getContentResolver();? ? }? ? .../**
* Set up the sync adapter. This form of the
* constructor maintains compatibility with Android 3.0
* and later platform versions
*/publicSyncAdapter(? ? ? ? ? ? Context context,booleanautoInitialize,booleanallowParallelSyncs){super(context, autoInitialize, allowParallelSyncs);/*
* If your app uses a content resolver, get an instance of it
* from the incoming Context
*/mContentResolver = context.getContentResolver();? ? ? ? ...? ? }
在 onPerformSync() 中添加數(shù)據(jù)傳輸代碼
Sync Adapter 組件并不會自動地執(zhí)行數(shù)據(jù)傳輸。它對我們的數(shù)據(jù)傳輸代碼進行封裝署惯,使得 Sync Adapter 框架可以在后臺執(zhí)行數(shù)據(jù)傳輸左驾,而不會牽連到我們的應(yīng)用。當(dāng)框架準備同步我們的應(yīng)用數(shù)據(jù)時极谊,它會調(diào)用我們所實現(xiàn)的onPerformSync()方法诡右。
為了便于將數(shù)據(jù)從應(yīng)用程序轉(zhuǎn)移到 Sync Adapter 組件中,Sync Adapter 框架調(diào)用onPerformSync()轻猖,它具有下面的參數(shù):
Account
該Account對象與觸發(fā) Sync Adapter 的事件相關(guān)聯(lián)帆吻。如果服務(wù)端不需要使用賬戶,那么我們不需要使用這個對象內(nèi)的信息蜕依。
Extras
一個 Bundle 對象桅锄,它包含了一些標(biāo)識,這些標(biāo)識由觸發(fā) Sync Adapter 的事件所發(fā)送样眠。
Authority
系統(tǒng)中某個 Content Provider 的 Authority友瘤。我們的應(yīng)用必須要有訪問它的權(quán)限。通常檐束,該 Authority 對應(yīng)于應(yīng)用的 Content Provider辫秧。
Content provider client
ContentProviderClient針對于由Authority參數(shù)所指向的Content Provider。ContentProviderClient是一個 Content Provider 的輕量級共有接口被丧。它的基本功能和ContentResolver一樣盟戏。如果我們正在使用 Content Provider 來存儲應(yīng)用數(shù)據(jù)绪妹,那么我們可以利用它連接 Content Provider。反之柿究,則將其忽略邮旷。
Sync result
一個SyncResult對象,我們可以使用它將信息發(fā)送給 Sync Adapter 框架蝇摸。
下面的代碼片段展示了onPerformSync()函數(shù)的整體結(jié)構(gòu):
/*
* Specify the code you want to run in the sync adapter. The entire
* sync adapter runs in a background thread, so you don't have to set
* up your own background processing.
*/@OverridepublicvoidonPerformSync(
Account account,
Bundle extras,
String authority,
ContentProviderClient provider,
SyncResult syncResult){/*
* Put the data transfer code here.
*/...? ? }
雖然實際的onPerformSync()實現(xiàn)是要根據(jù)應(yīng)用數(shù)據(jù)的同步需求以及服務(wù)器的連接協(xié)議來制定婶肩,但是我們的實現(xiàn)只需要執(zhí)行一些常規(guī)任務(wù):
連接到一個服務(wù)器
盡管我們可以假定在開始傳輸數(shù)據(jù)時,已經(jīng)獲取到了網(wǎng)絡(luò)連接貌夕,但是 Sync Adapter 框架并不會自動地連接到一個服務(wù)器律歼。
下載和上傳數(shù)據(jù)
Sync Adapter 不會自動執(zhí)行數(shù)據(jù)傳輸。如果我們想要從服務(wù)器下載數(shù)據(jù)并將它存儲到 Content Provider 中啡专,我們必須提供請求數(shù)據(jù)险毁,下載數(shù)據(jù)和將數(shù)據(jù)插入到 Provider 中的代碼。類似地们童,如果我們想把數(shù)據(jù)發(fā)送到服務(wù)器畔况,我們需要從一個文件,數(shù)據(jù)庫或者 Provider 中讀取數(shù)據(jù)病附,并且發(fā)送必需的上傳請求问窃。同時我們還需要處理在執(zhí)行數(shù)據(jù)傳輸時所發(fā)生的網(wǎng)絡(luò)錯誤。
處理數(shù)據(jù)沖突或者確定當(dāng)前數(shù)據(jù)的狀態(tài)
Sync Adapter 不會自動地解決服務(wù)器數(shù)據(jù)與設(shè)備數(shù)據(jù)之間的沖突完沪。同時域庇,它也不會自動檢測服務(wù)器上的數(shù)據(jù)是否比設(shè)備上的數(shù)據(jù)要新,反之亦然覆积。因此听皿,我們必須自己提供處理這些狀況的算法。
清理
在數(shù)據(jù)傳輸?shù)奈猜暱淼担浀靡P(guān)閉網(wǎng)絡(luò)連接尉姨,清除臨時文件和緩存。
Note:Sync Adapter 框架會在一個后臺線程中執(zhí)行onPerformSync()方法吗冤,所以我們不需要配置后臺處理任務(wù)又厉。
除了和同步相關(guān)的任務(wù)之外,我們還應(yīng)該嘗試將一些周期性的網(wǎng)絡(luò)相關(guān)的任務(wù)合并起來椎瘟,并將它們添加到onPerformSync()中覆致。將所有網(wǎng)絡(luò)任務(wù)集中到該方法內(nèi)處理,可以減少由啟動和停止網(wǎng)絡(luò)接口所造成的電量損失肺蔚。有關(guān)更多如何在進行網(wǎng)絡(luò)訪問時更高效地使用電池方面的知識煌妈,可以閱讀:Transferring Data Without Draining the Battery,它描述了一些在數(shù)據(jù)傳輸代碼中可以包含的網(wǎng)絡(luò)訪問任務(wù)。
將 Sync Adapter 綁定到框架上
現(xiàn)在璧诵,我們已經(jīng)將數(shù)據(jù)傳輸代碼封裝在 Sync Adapter 組件中汰蜘,但是我們必須讓框架可以訪問我們的代碼。為了做到這一點之宿,我們需要創(chuàng)建一個綁定Service族操,它將一個特殊的 Android Binder 對象從 Sync Adapter 組件傳遞給框架。有了這一 Binder 對象比被,框架就可以調(diào)用onPerformSync()方法并將數(shù)據(jù)傳遞給它坪创。
在服務(wù)的onCreate()方法中將我們的 Sync Adapter 組件實例化為一個單例。通過在onCreate()方法中實例化該組件姐赡,我們可以推遲到服務(wù)啟動后再創(chuàng)建它,這會在框架第一次嘗試執(zhí)行數(shù)據(jù)傳輸時發(fā)生柠掂。我們需要通過一種線程安全的方法來實例化組件项滑,以防止 Sync Adapter 框架在響應(yīng)觸發(fā)和調(diào)度時,形成含有多個 Sync Adapter 執(zhí)行的隊列涯贞。
下面的代碼片段展示了我們應(yīng)該如何實現(xiàn)一個綁定Service的類枪狂,實例化我們的 Sync Adapter 組件,并獲取 Android Binder 對象:
packagecom.example.android.syncadapter;/**
* Define a Service that returns an IBinder for the
* sync adapter class, allowing the sync adapter framework to call
* onPerformSync().
*/publicclassSyncServiceextendsService{// Storage for an instance of the sync adapterprivatestaticSyncAdapter sSyncAdapter =null;// Object to use as a thread-safe lockprivatestaticfinalObject sSyncAdapterLock =newObject();/*
* Instantiate the sync adapter object.
*/@OverridepublicvoidonCreate(){/*
* Create the sync adapter as a singleton.
* Set the sync adapter as syncable
* Disallow parallel syncs
*/synchronized(sSyncAdapterLock) {if(sSyncAdapter ==null) {? ? ? ? ? ? ? ? sSyncAdapter =newSyncAdapter(getApplicationContext(),true);? ? ? ? ? ? }? ? ? ? }? ? }/**
* Return an object that allows the system to invoke
* the sync adapter.
*
*/@OverridepublicIBinderonBind(Intent intent){/*
* Get the object that allows external processes
* to call onPerformSync(). The object is created
* in the base class code when the SyncAdapter
* constructors call super()
*/returnsSyncAdapter.getSyncAdapterBinder();? ? }}
Note:要看更多 Sync Adapter 綁定服務(wù)的例子宋渔,可以閱讀樣例代碼州疾。
添加框架所需的賬戶
Sync Adapter 框架需要每個 Sync Adapter 擁有一個賬戶類型。在創(chuàng)建 Stub 授權(quán)器章節(jié)中皇拣,我們已經(jīng)聲明了賬戶類型的值⊙媳停現(xiàn)在我們需要在 Android 系統(tǒng)中配置該賬戶類型。要配置賬戶類型氧急,通過調(diào)用addAccountExplicitly()添加一個使用其賬戶類型的虛擬賬戶颗胡。
調(diào)用該方法最合適的地方是在應(yīng)用的啟動 Activity 的onCreate()方法中。如下面的代碼樣例所示:
publicclassMainActivityextendsFragmentActivity{? ? ...? ? ...// Constants// The authority for the sync adapter's content providerpublicstaticfinalString AUTHORITY ="com.example.android.datasync.provider"http:// An account type, in the form of a domain namepublicstaticfinalString ACCOUNT_TYPE ="example.com";// The account namepublicstaticfinalString ACCOUNT ="dummyaccount";// Instance fieldsAccount mAccount;? ? ...@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);? ? ? ? ...// Create the dummy accountmAccount = CreateSyncAccount(this);? ? ? ? ...? ? }? ? .../**? ? * Create a new dummy account for the sync adapter? ? *? ? *@paramcontext The application context? ? */publicstaticAccountCreateSyncAccount(Context context){// Create the account type and default accountAccount newAccount =newAccount(? ? ? ? ? ? ? ? ACCOUNT, ACCOUNT_TYPE);// Get an instance of the Android account managerAccountManager accountManager =? ? ? ? ? ? ? ? (AccountManager) context.getSystemService(? ? ? ? ? ? ? ? ? ? ? ? ACCOUNT_SERVICE);/*
* Add the account and account type, no password or user data
* If successful, return the Account object, otherwise report an error.
*/if(accountManager.addAccountExplicitly(newAccount,null,null))) {/*
* If you don't set android:syncable="true" in
* in your element in the manifest,
* then call context.setIsSyncable(account, AUTHORITY, 1)
* here.
*/}else{/*
* The account exists or some other error occurred. Log this, report it,
* or handle it internally.
*/}? ? }? ? ...}
添加 Sync Adapter 的元數(shù)據(jù)文件
要將我們的 Sync Adapter 組件集成到框架中吩坝,我們需要向框架提供描述組件的元數(shù)據(jù)毒姨,以及額外的標(biāo)識信息。元數(shù)據(jù)指定了我們?yōu)?Sync Adapter 所創(chuàng)建的賬戶類型钉寝,聲明了一個和應(yīng)用相關(guān)聯(lián)的 Content Provider Authority弧呐,對和 Sync Adapter 相關(guān)的一部分系統(tǒng)用戶接口進行控制,同時還聲明了其它同步相關(guān)的標(biāo)識嵌纲。在我們項目的/res/xml/目錄下的一個特定文件內(nèi)聲明這一元數(shù)據(jù)俘枫,我們可以為這個文件命名,不過通常來說我們將其命名為syncadapter.xml疹瘦。
在這一文件中包含了一個 XML 標(biāo)簽崩哩,它包含了下列的屬性字段:
android:contentAuthority
Content Provider 的 URI Authority。如果我們在前一節(jié)課程中為應(yīng)用創(chuàng)建了一個 Stub Content Provider,那么請使用在 manifest 清單文件中添加在標(biāo)簽內(nèi)的android:authorities屬性值邓嘹。這一屬性的更多細節(jié)在本章后續(xù)章節(jié)中有更多的介紹酣栈。
如果我們正使用 Sync Adapter 將數(shù)據(jù)從 Content Provider 傳輸?shù)椒?wù)器上,該屬性的值應(yīng)該和數(shù)據(jù)的 Content URI Authority 保持一致汹押。這個值也是我們在 manifest 清單文件中添加在標(biāo)簽內(nèi)android:authorities屬性的值矿筝。
android:accountType
Sync Adapter 框架所需要的賬戶類型。這個值必須和我們所創(chuàng)建的驗證器元數(shù)據(jù)文件內(nèi)所提供的賬戶類型一致(詳細內(nèi)容可以閱讀:創(chuàng)建 Stub 授權(quán)器)棚贾。這也是在上一節(jié)的代碼片段中窖维。常量ACCOUNT_TYPE的值。
配置相關(guān)屬性
android:userVisible
該屬性設(shè)置 Sync Adapter 框架的賬戶類型是否可見妙痹。默認地铸史,和賬戶類型相關(guān)聯(lián)的賬戶圖標(biāo)和標(biāo)簽在系統(tǒng)設(shè)置的賬戶選項中可以看見,所以我們應(yīng)該將 Sync Adapter 設(shè)置為對用戶不可見(除非我們確實擁有一個賬戶類型或者域名或者它們可以輕松地和我們的應(yīng)用相關(guān)聯(lián))怯伊。如果我們將賬戶類型設(shè)置為不可見琳轿,那么我們?nèi)匀豢梢栽试S用戶通過一個 Activity 中的用戶接口來控制 Sync Adapter。
android:supportsUploading
允許我們將數(shù)據(jù)上傳到云耿芹。如果應(yīng)用僅僅下載數(shù)據(jù)崭篡,那么請將該屬性設(shè)置為false。
android:allowParallelSyncs
允許多個 Sync Adapter 組件的實例同時運行吧秕。如果應(yīng)用支持多個用戶賬戶并且我們希望多個用戶并行地傳輸數(shù)據(jù)琉闪,那么可以使用該特性。如果我們從不執(zhí)行多個數(shù)據(jù)傳輸砸彬,那么這個選項是沒用的颠毙。
android:isAlwaysSyncable
指明 Sync Adapter 框架可以在任何我們指定的時間運行 Sync Adapter。如果我們希望通過代碼來控制 Sync Adapter 的運行時機砂碉,請將該屬性設(shè)置為false吟秩。然后調(diào)用requestSync()來運行 Sync Adapter。要學(xué)習(xí)更多關(guān)于運行 Sync Adapter 的知識绽淘,可以閱讀:執(zhí)行 Sync Adapter涵防。
下面的代碼展示了應(yīng)該如何通過 XML 配置一個使用單個虛擬賬戶,并且只執(zhí)行下載的 Sync Adapter:
在 Manifest 清單文件中聲明 Sync Adapter
一旦我們將 Sync Adapter 組件集成到應(yīng)用中沪铭,我們需要聲明相關(guān)的權(quán)限來使用它壮池,并且還需要聲明我們所添加的綁定Service。
由于 Sync Adapter 組件會運行設(shè)備與網(wǎng)絡(luò)之間傳輸數(shù)據(jù)的代碼杀怠,所以我們需要請求使用網(wǎng)絡(luò)的權(quán)限椰憋。同時,我們的應(yīng)用還需要讀寫 Sync Adapter 配置信息的權(quán)限赔退,這樣我們才能通過應(yīng)用中的其它組件去控制 Sync Adapter橙依。另外证舟,我們還需要一個特殊的權(quán)限,來允許應(yīng)用使用我們在創(chuàng)建 Stub 授權(quán)器中所創(chuàng)建的授權(quán)器組件窗骑。
要請求這些權(quán)限女责,將下列內(nèi)容添加到應(yīng)用 manifest 清單文件中,并作為標(biāo)簽的子標(biāo)簽:
允許 Sync Adapter 訪問網(wǎng)絡(luò)创译,使得它可以從設(shè)備下載和上傳數(shù)據(jù)到服務(wù)器抵知。如果之前已經(jīng)請求了該權(quán)限,那么就不需要重復(fù)請求了软族。
android.permission.READ_SYNC_SETTINGS
允許應(yīng)用讀取當(dāng)前的 Sync Adapter 配置刷喜。例如,我們需要該權(quán)限來調(diào)用getIsSyncable()立砸。
android.permission.WRITE_SYNC_SETTINGS
允許我們的應(yīng)用 對Sync Adapter 的配置進行控制掖疮。我們需要這一權(quán)限來通過addPeriodicSync()方法設(shè)置執(zhí)行同步的時間間隔。另外颗祝,調(diào)用requestSync()方法不需要用到該權(quán)限氮墨。更多信息可以閱讀:執(zhí)行 Sync Adapter。
android.permission.AUTHENTICATE_ACCOUNTS
允許我們使用在創(chuàng)建 Stub 授權(quán)器中所創(chuàng)建的驗證器組件吐葵。
下面的代碼片段展示了如何添加這些權(quán)限:
......
最后,要聲明框架用來和 Sync Adapter 進行交互的綁定Service桥氏,添加下列的 XML 代碼到應(yīng)用 manifest 清單文件中温峭,作為標(biāo)簽的子標(biāo)簽:
標(biāo)簽配置了一個過濾器,它會被帶有android.content.SyncAdapter這一 Action 的 Intent 所觸發(fā)字支,該 Intent 一般是由系統(tǒng)為了運行 Sync Adapter 而發(fā)出的凤藏。當(dāng)過濾器被觸發(fā)后,系統(tǒng)會啟動我們所創(chuàng)建的綁定服務(wù)堕伪,在本例中它叫做SyncService揖庄。屬性android:exported="true"允許我們應(yīng)用之外的其它進程(包括系統(tǒng))訪問這一Service。屬性android:process=":sync"告訴系統(tǒng)應(yīng)該在一個全局共享的欠雌,且名字叫做sync的進程內(nèi)運行該Service蹄梢。如果我們的應(yīng)用中有多個 Sync Adapter,那么它們可以共享該進程富俄,這有助于減少開銷禁炒。
標(biāo)簽提供了我們之前為 Sync Adapter 所創(chuàng)建的元數(shù)據(jù) XML 文件的文件名。屬性android:name指出這一元數(shù)據(jù)是針對于 Sync Adapter 框架的霍比。而android:resource標(biāo)簽則指定了元數(shù)據(jù)文件的名稱幕袱。
現(xiàn)在我們已經(jīng)為 Sync Adapter 準備好所有相關(guān)的組件了。下一節(jié)課將講授如何讓 Sync Adapter 框架運行 Sync Adapter悠瞬。要實現(xiàn)這一點们豌,既可以通過響應(yīng)一個事件的方式涯捻,也可以通過執(zhí)行一個周期性任務(wù)的方式。