注:本文翻譯自Google官方的Android Developers Training文檔,譯者技術一般,由于喜愛安卓而產生了翻譯的念頭,純屬個人興趣愛好。
原文鏈接: http://developer.android.com/training/sync-adapters/creating-sync-adapter.html
在你應用中的同步適配器組件會封裝在設備和服務器之間傳輸數據的任務代碼。基于你提供的調度和觸發器,同步適配器框架會在同步適配器組件中運行你的代碼。要將同步適配組件添加到你的應用,你需要添加下列部件:
同步適配器類
這是一個將你的數據傳輸代碼封裝到一個接口中,該接口與同步適配器框架兼容。
捆綁 Service
一個組件,它可以允許同步適配器框架在你的同步適配器類中運行代碼。
同步適配器的XML元數據文件
一個文件,包含了你的同步適配器信息。框架會讀取該文件并確定應該如何加載并調度你的數據傳輸任務。
在應用清單文件的聲明
在XML文件中聲明的捆綁服務,并指出同步適配器的元數據。
這節課將會向你展示如何定義這些元素。
一). 創建一個同步適配器類
在這部分課程中,你將會學習如何創建同步適配器類,該類封裝了數據傳輸的代碼。創建該類并包含繼承的同步適配器的基類,為該類定義構造函數,并實現你定義的數據傳輸任務的方法。
繼承同步適配器基類:AbstractThreadedSyncAdapter
要創建同步適配器組件,首先繼承 AbstractThreadedSyncAdapter ,然后編寫它的構造函數。每次你的同步適配器組件創建的時候,構造函數就會執行配置任務,和你使用 Activity.onCreate() 配置Activity是一樣的。例如,如果你的應用使用一個內容提供器來存儲數據,那么使用構造函數來獲取一個 ContentResolver 實例。由于從Android 3.0開始添加了第二種形式的構造函數,來支持 parallelSyncs 參數,所以你需要創建兩種形式的構造函數來保證兼容性。
Note:
同步適配器框架是設計成和同步適配器組件的單例一起工作的。實例化同步適配器組件的更多細節,可以閱讀: Bind the Sync Adapter to the Framework 。
下面的代碼展示了如何實現 AbstractThreadedSyncAdapter 和它的構造函數:
/** * Handle the transfer of data between a server and an * app, using the Android sync adapter framework. */ public class SyncAdapter extends AbstractThreadedSyncAdapter { ... // Global variables // Define a variable to contain a content resolver instance ContentResolver mContentResolver; /** * Set up the sync adapter */ public SyncAdapter(Context context, boolean autoInitialize) { 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 */ public SyncAdapter( Context context, boolean autoInitialize, boolean allowParallelSyncs) { super (context, autoInitialize, allowParallelSyncs); /* * If your app uses a content resolver, get an instance of it * from the incoming Context */ mContentResolver = context.getContentResolver(); ... }
在onPerformSync()中添加數據傳輸代碼
同步適配器組件并不會自動地執行數據傳輸。相反地,它只是對你的數據傳輸代碼進行封裝,所以同步適配器框架可以在后臺執行數據傳輸,而不會涉及到你的應用。當框架準備同步你的應用數據時,它會調用你的
onPerformSync()
方法的實現。
為了便于將你的數據從你的應用程序代碼轉移到同步適配器組件,同步適配器框架調用 onPerformSync() ,它具有下面的參數:
賬戶(Account)
該 Account 對象是和激活同步適配器的事件相關聯的。如果你的服務不需要使用賬戶,你不需要使用這個對象內的信息。
額外數據(Extras)
一個 Bundle 對象,它包含了激活同步適配器的時間所具有的的標識。
權威(Authority)
系統內容提供器的權威。你的應用必須要有訪問它的權限。通常,權威對應于你應用的內容提供器。
內容提供器客戶端( Content provider client )
一個內容提供器的 ContentProviderClient 對象是由權威參數所指定的。一個 ContentProviderClient 是一個內容提供器的輕量級共有接口。它的基本功能和一個 ContentResolver 一樣。如果你正在使用一個內容提供器來存儲你的應用的數據,你可以用該對象和提供器連接。否則的話你可以忽略它。
同步結果(Sync result)
一個 SyncResult 對象,你可以使用它來將信息發送到同步適配器框架。
下面的代碼片段展示了 onPerformSync() 函數的整體架構:
/* * 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. */ @Override public void onPerformSync( Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) { /* * Put the data transfer code here. */ ... }
但是實際的 onPerformSync() 實現是要根據你的應用數據的同步需求以及服務器的連接協議來制定的,有一些你應該要實現的基本任務,如下所示:
連接到一個服務器
盡管你可以假定當你開始傳輸數據時,可以獲取到網絡連接,但是同步適配器框架并不會自動地連接到一個服務器。
下載和上傳數據
一個同步適配器不會自動執行數據傳輸。如果你想要從一個服務器下載數據并將它存儲到一個內容提供器中,你必須提供請求數據,下載數據和將數據插入到提供器里的代碼。同樣地,如果你想把數據發送到一個服務器,你必須要從一個文件,數據庫或者提供器中讀取數據,并且發送必須的上傳請求。你也需要處理在你執行數據傳輸時所發生的網絡錯誤。
處理數據沖突或者確定當前的數據是怎樣的
一個同步適配器不會自動地解決服務器數據與設備數據的沖突。同時,它也不會檢測服務器上的數據是否比設備上的數據要新,反之亦然。因此,你必須提供處理此狀況的算法。
清理
在數據傳輸的尾聲,記得要關閉網絡連接,清除臨時文件盒緩存。
Note:
同步適配器框架在一個后臺線程中執行 onPerformSync() 方法,所以你不需要配置你自己的后臺處理任務。
另外,你應該嘗試將你的定期網絡相關的任務結合起來,并將它們添加到 onPerformSync() 中。通過將所有網絡任務集中到該方法中,你可以節省由啟動和停止網絡接口所造成的電量損失。有關更多如何在進行網絡訪問時更高效地使用電池,可以閱讀: Transferring Data Without Draining the Battery (博客鏈接: http://www.cnblogs.com/jdneo/p/3620713.html ),它描述了一些你的數據傳輸代碼可以包含的網絡訪問任務。
二). 將同步適配器和框架進行綁定
你現在在一個同步適配器框架中已經封裝了你的數據傳輸代碼,但是你必須向框架提供你的代碼。為了做這一點,你需要創建一個捆綁 Service ,它將一個特殊的Android binder對象從同步適配器組件傳遞給框架。有了這一binder對象,框架可以激活 onPerformSync() 方法并將數據傳遞給binder對象。
在你的服務的
onCreate()
方法中將你的同步適配器組件實例化為一個單例。通過在
onCreate()
方法中實例化該組件,你可以延遲到服務啟動后再創建它,這會在框架第一次嘗試執行你的數據傳輸時發生。你需要通過一種線程安全的方法來實例化組件,防止同步適配器框架會將你的同步適配器對于激活和調度的響應排成多個執行隊列。
作為例子,下面的代碼片段展示了你應該如何創建一個捆綁 Service 的類的實現,實例化你的同步適配器組件,并獲取Android binder對象:
package com.example.android.syncadapter; /** * Define a Service that returns an IBinder for the * sync adapter class, allowing the sync adapter framework to call * onPerformSync(). */ public class SyncService extends Service { // Storage for an instance of the sync adapter private static SyncAdapter sSyncAdapter = null ; // Object to use as a thread-safe lock private static final Object sSyncAdapterLock = new Object(); /* * Instantiate the sync adapter object. */ @Override public void onCreate() { /* * Create the sync adapter as a singleton. * Set the sync adapter as syncable * Disallow parallel syncs */ synchronized (sSyncAdapterLock) { if (sSyncAdapter == null ) { sSyncAdapter = new SyncAdapter(getApplicationContext(), true ); } } } /** * Return an object that allows the system to invoke * the sync adapter. * */ @Override public IBinder onBind(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() */ return sSyncAdapter.getSyncAdapterBinder(); } }
Note:
要看更多同步適配器的捆綁服務的例子,可以閱讀樣例代碼。
三). 添加框架所需的賬戶
同步適配器框架需要每個同步適配器擁有一個賬戶類型。在 Add the Authenticator Metadata File 章節中,你聲明了賬戶類型的值。現在你需要在Android系統中配置該賬戶類型。要配置賬戶類型,通過調用 addAccountExplicitly() 添加一個假的賬戶并使用其賬戶類型。
最佳的調用該方法的地方是在你的應用的啟動Activity的 onCreate() 方法中。下面的代碼樣例展示了你應該如何做:
public class MainActivity extends FragmentActivity { ... ... // Constants // The authority for the sync adapter's content provider public static final String AUTHORITY = "com.example.android.datasync.provider" // An account type, in the form of a domain name public static final String ACCOUNT_TYPE = "example.com" ; // The account name public static final String ACCOUNT = "dummyaccount" ; // Instance fields Account mAccount; ... @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); ... // Create the dummy account mAccount = CreateSyncAccount( this ); ... } ... /** * Create a new dummy account for the sync adapter * * @param context The application context */ public static Account CreateSyncAccount(Context context) { // Create the account type and default account Account newAccount = new Account( ACCOUNT, ACCOUNT_TYPE); // Get an instance of the Android account manager AccountManager 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 <provider> 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. */ } } ... }
四). 添加同步適配器的元數據文件
要將你的同步適配器組件添加到框架中,你需要向框架提供描述組件的元數據,以及額外的標識信息。元數據指定了你未你的同步適配器所創建的賬戶類型,聲明了一個和你的應用相關聯的內容提供器權威,對和同步適配器相關的一部分系統用戶接口進行控制,并聲明了其它同步相關的標識。在你的應用工程中的“
/res/xml/
”目錄下的一個特定的文件內聲明這一元數據,你可以為這個文件任意起一個名字,不過通常都叫做:“
syncadapter.xml
”。
在這一文件中包含了一個單一的XML元素“ <sync-adapter> ”,并且它包含了下列的屬性字段:
android:contentAuthority
你的內容提供器的URI權威。如果你在前一節課程中(博客鏈接: http://www.cnblogs.com/jdneo/p/3654420.html )為你的應用創建了一個置空的內容提供器,使用你在清單文件中添加的 <provider> 標簽內的 android:authorities 屬性的值。這一屬性的更多細節在章節 Declare the Provider in the Manifest (博客鏈接: http://www.cnblogs.com/jdneo/p/3655747.html )中有更多的介紹。
如果你正在使用同步適配器,從內容提供器將數據傳輸到服務器,這個值應該和你的數據的URI權威的值是一樣的。這個值也是 你在清單文件中添加的 <provider> 標簽內的 android:authorities 屬性的值。
android:accountType
同步適配器框架所需要的賬戶類型。這個值必須和你創建驗證器的元數據文件中所提供的一致(詳細內容可以閱讀: Add the Authenticator Metadata File ,博客鏈接: http://www.cnblogs.com/jdneo/p/3655747.html )。這也是你在上一節中代碼片段里的常量“ ACCOUNT_TYPE ”的值。
配置相關屬性
android:userVisible
指的是同步適配器框架所需要的賬戶類型。默認地,和賬戶類型相關聯的賬戶圖標和標簽在系統的設置中的賬戶選項中可以看見,所以你需要將你的同步適配器對用戶不可見,除非你擁有一個賬戶類型或者賬戶類型,可以輕松地和你的應用相關聯。如果你將你的賬戶類型設置為不可見,你仍然可以允許用戶通過你的應用的一個activity內的用戶接口來控制你的同步適配器。
android:supportsUploading
允許你將數據上傳到云。如果你的應用僅僅下載數據,那么設置為“ false ”。
android:allowParallelSyncs
允許在同一時間你的同步適配器組件的多個實例運行。如果你的應用支持多個用戶賬戶并且你希望多個用戶并行地傳輸數據,那么使用這個屬性。如果你從不執行多個數據傳輸,這個標識是沒用的。
android:isAlwaysSyncable
指明同步適配器框架可以在任何你指定的時間運行你的永不適配器。如果你希望通過代碼來控制同步適配器的運行,將這個標識設置為“ false ”,然后調用 requestSync() 來執行同步適配器。要學習更多關于運行一個同步適配器的知識,可以閱讀: Running a Sync Adapter 。
下面的例子展示了一個同步適配器的XML,使用了一個假的賬戶并只進行下載:
<? xml version="1.0" encoding="utf-8" ?> < sync-adapter xmlns:android ="http://schemas.android.com/apk/res/android" android:contentAuthority ="com.example.android.datasync.provider" android:accountType ="com.android.example.datasync" android:userVisible ="false" android:supportsUploading ="false" android:allowParallelSyncs ="false" android:isAlwaysSyncable ="true" />
五). 在清單文件中聲明同步適配器
一旦你將同步適配器組件添加到了你的應用中,你需要聲明相關的權限來使用它,并且你需要聲明你所添加的捆綁 Service 。
由于同步適配器組件運行網絡與設備之間傳輸數據的代碼,你需要使用網絡的權限。另外,你的應用需要權限來讀寫同步適配器的配置信息,這樣你才能通過你應用中的其它組件去控制同步適配器。你還需要一個特殊的權限允許你的應用使用你在 Creating a Stub Authenticator (博客鏈接: http://www.cnblogs.com/jdneo/p/3654420.html )中所創建的驗證器組件。
要請求這些權限,將下列內容添加到你的應用清單文件中,并作為 <manifest> 標簽的子標簽:
允許同步適配器訪問網絡,一致于它可以從設備下載和上傳數據到服務器。如果之前請求了該權限,那么你就不需要重復請求了。
android.permission.READ_SYNC_SETTINGS
允許你的應用讀取當前的同步適配器配置。例如,你需要該權限來調用 getIsSyncable() 。
android.permission.WRITE_SYNC_SETTINGS
允許你的應用對同步適配器的配置進行控制。你需要這一權限來執行 addPeriodicSync() ,以此設置執行同步的時間間隔。調用 requestSync() 不 需要用到該權限。更多信息可以閱讀: Running A Sync Adapter 。
android.permission.AUTHENTICATE_ACCOUNTS
允許你使用在 Creating a Stub Authenticator (博客鏈接: http://www.cnblogs.com/jdneo/p/3654420.html )中所創建的驗證器組件。
下面的代碼片段展示了如何添加權限:
< manifest > ... < uses-permission android:name ="android.permission.INTERNET" /> < uses-permission android:name ="android.permission.READ_SYNC_SETTINGS" /> < uses-permission android:name ="android.permission.WRITE_SYNC_SETTINGS" /> < uses-permission android:name ="android.permission.AUTHENTICATE_ACCOUNTS" /> ... </ manifest >
最后,要聲明框架使用的捆綁 Service 和你的同步適配器進行交互,添加下列的XML代碼到你的應用清單文件中,作為 <application> 標簽的子標簽:
< service android:name ="com.example.android.datasync.SyncService" android:exported ="true" android:process =":sync" > < intent-filter > com.example.android.datasync.provider < action android:name ="android.content.SyncAdapter" /> </ intent-filter > < meta-data android:name ="android.content.SyncAdapter" android:resource ="@xml/syncadapter" /> </ service >
<intent-filter> 標簽配置了一個過濾器,它會被帶有“ android.content.SyncAdapter ”這一 action 的 intent 所激活,而這一 intent 一般是由系統為了運行同步適配器而發出的。當過濾器被激活時,系統會啟動你所創建的捆綁服務,在例子中它叫做“ SyncService ”。屬性 android:exported="true" 允許你應用之外的其它進程(包括系統)訪問這一 Service 。屬性 android:process=":sync" 告訴系統在一個全局共享,且稱之為“ sync ”的進程內運行 Service 。如果你的應用中有多個同步適配器,那么它們可以共享該進程,這有助于減少開銷。
<meta-data> 標簽提供了你之前為同步適配器所創建的元數據文件。屬性 android:name 指出這一元數據是針對于同步適配器框架的。而 android:resource 標簽則指定了元數據文件的文字。
現在你擁有了所有同步適配器的相關組件。下一節課將講授如何讓同步適配器框架運行你的同步適配器,既可以通過響應一個事件的方式,也可以通過執行一個定期任務調度的方式。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
