鍍金池/ 教程/ Android/ 執(zhí)行 Sync Adpater
檢測常用的手勢
優(yōu)化layout的層級
用戶輸入
管理應(yīng)用的內(nèi)存
聯(lián)系人信息
開發(fā)輔助程序
Android多媒體
添加語音功能
顯示位置地址
提供向下與橫向?qū)Ш?/span>
支持游戲控制器
訪問可穿戴數(shù)據(jù)層
處理多點(diǎn)觸控手勢
全屏沉浸式應(yīng)用
為多線程創(chuàng)建管理器
數(shù)據(jù)保存
Intent的發(fā)送
更新Notification
優(yōu)化下載以高效地訪問網(wǎng)絡(luò)
打印
打包可穿戴應(yīng)用
接收從其他App傳送來的數(shù)據(jù)
發(fā)送與接收消息
建立靈活動態(tài)的UI
處理鍵盤輸入
Building a Work Policy Controller
建立測試環(huán)境
創(chuàng)建表盤
分享文件
顯示Notification進(jìn)度
實(shí)現(xiàn)自適應(yīng)UI流(Flows)
使用設(shè)備管理策略增強(qiáng)安全性
使用能感知版本的組件
執(zhí)行網(wǎng)絡(luò)操作
建立文件分享
添加移動
更新你的Security Provider來對抗SSL漏洞利用
支持鍵盤導(dǎo)航
創(chuàng)建和監(jiān)視地理圍欄
發(fā)送并同步數(shù)據(jù)
使用BigView樣式
無線連接設(shè)備
提供向上導(dǎo)航與歷史導(dǎo)航
最小化定期更新造成的影響
實(shí)現(xiàn)向下的導(dǎo)航
支持不同的屏幕大小
Android 可穿戴應(yīng)用
添加動畫
顯示聯(lián)系人頭像
使用OpenGL ES顯示圖像
處理輸入法可見性
分享文件
保持設(shè)備喚醒
淡化系統(tǒng)Bar
使用NFC分享文件
保存到Preference
Android聯(lián)系人信息與位置信息
創(chuàng)建標(biāo)準(zhǔn)的網(wǎng)絡(luò)請求
使用Drawables
管理Bitmap的內(nèi)存使用
管理Activity的生命周期
按需加載視圖
傳輸資源
為可穿戴設(shè)備創(chuàng)建自定義UI
在一個線程中執(zhí)行一段特定的代碼
性能優(yōu)化
隱藏導(dǎo)航欄
創(chuàng)建目錄瀏覽器
為多種大小的屏幕進(jìn)行規(guī)劃
View間漸變
使用觸摸手勢
高效加載大圖
使用CursorLoader在后臺加載數(shù)據(jù)
創(chuàng)建抽屜式導(dǎo)航(navigation drawer)
管理音頻焦點(diǎn)
創(chuàng)建后臺服務(wù)
創(chuàng)建功能測試
創(chuàng)建使用Material Design的應(yīng)用
停止與重啟Activity
添加一個簡便的分享功能
啟動Activity時保留導(dǎo)航
TV應(yīng)用清單
創(chuàng)建向后兼容的UI
?# 優(yōu)化自定義View
創(chuàng)建單元測試
在UI上顯示Bitmap
建立OpenGL ES的環(huán)境
構(gòu)建表盤服務(wù)
JNI Tips
建立搜索界面
實(shí)現(xiàn)自定義View的繪制
使用HTTPS與SSL
按需操控BroadcastReceiver
分享簡單的數(shù)據(jù)
繪制形狀
Android位置信息
創(chuàng)建并運(yùn)行可穿戴應(yīng)用
執(zhí)行 Sync Adpater
獲取最后可知位置
創(chuàng)建 Android 項(xiàng)目
實(shí)現(xiàn)高效的導(dǎo)航
退出全屏的Activity
創(chuàng)建Card
兼容音頻輸出設(shè)備
同步數(shù)據(jù)單元
傳輸數(shù)據(jù)時避免消耗大量電量
保存到文件
緩存Bitmap
提供配置 Activity
調(diào)度重復(fù)的鬧鐘
實(shí)現(xiàn)輔助功能
重復(fù)的下載是冗余的
隱藏狀態(tài)欄
實(shí)現(xiàn)自定義的網(wǎng)絡(luò)請求
規(guī)劃界面和他們之間的關(guān)系
使用Sync Adapter傳輸數(shù)據(jù)
TV應(yīng)用內(nèi)搜索
響應(yīng)觸摸事件
使用Google Cloud Messaging(已廢棄)
控制相機(jī)
Android網(wǎng)絡(luò)連接與云服務(wù)
請求分享一個文件
處理TV硬件
響應(yīng)UI可見性的變化
使用網(wǎng)絡(luò)服務(wù)發(fā)現(xiàn)
指定輸入法類型
優(yōu)化電池壽命
創(chuàng)建TV應(yīng)用
獲取聯(lián)系人列表
拖拽與縮放
啟動與停止線程池中的線程
創(chuàng)建 Sync Adpater
使用 WiFi P2P 服務(wù)發(fā)現(xiàn)
開始使用Material Design
代理至新的APIs
使用include標(biāo)簽重用layouts
使得View可交互
高效顯示Bitmap
創(chuàng)建企業(yè)級應(yīng)用
Fragments之間的交互
創(chuàng)建與執(zhí)行測試用例
綜合:設(shè)計(jì)我們的樣例 App
繪制表盤
建立簡單的用戶界面
自定義動畫
開發(fā)輔助服務(wù)
避免出現(xiàn)程序無響應(yīng)ANR(Keeping Your App Responsive)
使用ViewPager實(shí)現(xiàn)屏幕滑動
設(shè)計(jì)高效的導(dǎo)航
Android分享操作(Building Apps with Content Sharing)
提供向后的導(dǎo)航
保持向下兼容
創(chuàng)建TV播放應(yīng)用
縮放View
使用 WiFi 建立 P2P 連接
Android后臺任務(wù)
連接到網(wǎng)絡(luò)
為 Notification 添加頁面
使TV應(yīng)用是可被搜索的
添加Action Bar
使用Material的主題
啟動另一個Activity
顯示正在播放卡片
適配不同的系統(tǒng)版本
輕松錄制視頻
創(chuàng)建可穿戴的應(yīng)用
創(chuàng)建自定義的布局
重新創(chuàng)建Activity
使用CursorLoader執(zhí)行查詢?nèi)蝿?wù)
使用舊的APIs實(shí)現(xiàn)新API的效果
使用備份API
安全要點(diǎn)
Android入門基礎(chǔ):從這里開始
保存并搜索數(shù)據(jù)
根據(jù)網(wǎng)絡(luò)連接類型來調(diào)整下載模式
使用Tabs創(chuàng)建Swipe視圖
SMP(Symmetric Multi-Processor) Primer for Android
解析 XML 數(shù)據(jù)
使用 Volley 傳輸網(wǎng)絡(luò)數(shù)據(jù)
建立ActionBar
Android交互設(shè)計(jì)
使用Intent修改聯(lián)系人信息
增加搜索功能
輕松拍攝照片
定義形狀
測試你的Activity
在 Notifcation 中接收語音輸入
與其他應(yīng)用的交互
管理系統(tǒng)UI
追蹤手勢移動
Android界面設(shè)計(jì)
執(zhí)行 Android 程序
顯示確認(rèn)界面
創(chuàng)建Lists與Cards
打印HTML文檔
創(chuàng)建TV應(yīng)用
為多屏幕設(shè)計(jì)
定義Shadows與Clipping視圖
使用Fragment建立動態(tài)UI
接收Activity返回的結(jié)果
布局變更動畫
定位常見的問題
自定義ActionBar的風(fēng)格
定義Layouts
發(fā)送簡單的網(wǎng)絡(luò)請求
啟動與銷毀Activity
與UI線程通信
非UI線程處理Bitmap
創(chuàng)建TV布局
提升Layout的性能
報(bào)告任務(wù)執(zhí)行狀態(tài)
判斷并監(jiān)測網(wǎng)絡(luò)連接狀態(tài)
兼容不同的設(shè)備
處理按鍵動作
優(yōu)化性能和電池使用時間
給其他App發(fā)送簡單的數(shù)據(jù)
Implementing App Restrictions
向后臺服務(wù)發(fā)送任務(wù)請求
展示Card翻轉(zhuǎn)動畫
管理ViewGroup中的觸摸事件
兼容不同的屏幕密度
通過藍(lán)牙進(jìn)行調(diào)試
為可穿戴設(shè)備創(chuàng)建Notification
控制音量與音頻播放
獲取聯(lián)系人詳情
在表盤上顯示信息
提供向上的導(dǎo)航
滾動手勢動畫
幫助用戶在TV上找到內(nèi)容
創(chuàng)建TV導(dǎo)航
為索引指定App內(nèi)容
ActionBar的覆蓋疊加
Android Wear 上的位置檢測
保護(hù)安全與隱私的最佳策略
Ensuring Compatibility with Managed Profiles
解決云同步的保存沖突
獲取位置更新
創(chuàng)建List
測試程序
管理網(wǎng)絡(luò)的使用情況
為App內(nèi)容開啟深度鏈接
推薦TV內(nèi)容
建立一個Notification
管理音頻播放
設(shè)計(jì)表盤
拍照
處理控制器輸入動作
判斷并監(jiān)測設(shè)備的底座狀態(tài)與類型
處理查詢的結(jié)果
保存到數(shù)據(jù)庫
支持多個游戲控制器
創(chuàng)建 Stub Content Provider
使得ListView滑動順暢
處理數(shù)據(jù)層的事件
創(chuàng)建TV應(yīng)用的第一步
使得你的App內(nèi)容可被Google搜索
將 Notification 放成一疊
創(chuàng)建 Stub 授權(quán)器
暫停與恢復(fù)Activity
管理設(shè)備的喚醒狀態(tài)
Android圖像與動畫
打印照片
云同步
創(chuàng)建TV直播應(yīng)用
為Notification賦加可穿戴特性
提供一個Card視圖
建立請求隊(duì)列(RequestQueue)
適配不同的語言
創(chuàng)建詳情頁
測試UI組件
接收其他設(shè)備的文件
創(chuàng)建自定義View
建立第一個App
創(chuàng)建2D Picker
監(jiān)測電池的電量與充電狀態(tài)
打印自定義文檔
抽象出新的APIs
通知提示用戶
獲取文件信息
運(yùn)用投影與相機(jī)視角
在IntentService中執(zhí)行后臺任務(wù)
多線程操作
創(chuàng)建一個Fragment
添加Action按鈕
在不同的 Android 系統(tǒng)版本支持控制器
維護(hù)兼容性
發(fā)送文件給其他設(shè)備
創(chuàng)建TV游戲應(yīng)用
創(chuàng)建自定義的View類
代碼性能優(yōu)化建議
Intent過濾
適配不同的屏幕

執(zhí)行 Sync Adpater

編寫:jdneo - 原文:http://developer.android.com/training/sync-adapters/running-sync-adapter.html

在本節(jié)課之前,我們已經(jīng)學(xué)習(xí)了如何創(chuàng)建一個封裝了數(shù)據(jù)傳輸代碼的 Sync Adapter 組件,以及如何添加其它的組件,使得我們可以將 Sync Adapter 集成到系統(tǒng)當(dāng)中?,F(xiàn)在我們已經(jīng)擁有了所有部件,來安裝一個包含有 Sync Adapter 的應(yīng)用了,但是這里還沒有任何代碼是負(fù)責(zé)去運(yùn)行 Sync Adapter。

執(zhí)行 Sync Adapter 的時機(jī),一般應(yīng)該基于某個計(jì)劃任務(wù)或者一些事件的間接結(jié)果。例如,我們可能希望 Sync Adapter 以一個定期計(jì)劃任務(wù)的形式運(yùn)行(比如每隔一段時間或者在每天的一個固定時間運(yùn)行)?;蛘咭部赡芟M?dāng)設(shè)備上的數(shù)據(jù)發(fā)生變化后,執(zhí)行 Sync Adapter。我們應(yīng)該避免將運(yùn)行 Sync Adapter 作為用戶某個行為的直接結(jié)果,因?yàn)檫@樣做的話我們就無法利用 Sync Adapter 框架可以按計(jì)劃調(diào)度的特性。例如,我們應(yīng)該在 UI 中避免使用刷新按鈕。

下列情況可以作為運(yùn)行 Sync Adapter 的時機(jī):

當(dāng)服務(wù)端數(shù)據(jù)變更時:

當(dāng)服務(wù)端發(fā)送消息告知服務(wù)端數(shù)據(jù)發(fā)生變化時,運(yùn)行 Sync Adapter 以響應(yīng)這一來自服務(wù)端的消息。這一選項(xiàng)允許從服務(wù)器更新數(shù)據(jù)到設(shè)備上,該方法可以避免由于輪詢服務(wù)器所造成的執(zhí)行效率下降,或者電量損耗。

當(dāng)設(shè)備的數(shù)據(jù)變更時:

當(dāng)設(shè)備上的數(shù)據(jù)發(fā)生變化時,運(yùn)行 Sync Adapter。這一選項(xiàng)允許我們將修改后的數(shù)據(jù)從設(shè)備發(fā)送給服務(wù)器。如果需要保證服務(wù)器端一直擁有設(shè)備上最新的數(shù)據(jù),那么這一選項(xiàng)非常有用。如果我們將數(shù)據(jù)存儲于 Content Provider,那么這一選項(xiàng)的實(shí)現(xiàn)將會非常直接。如果使用的是一個 Stub Content Provider,檢測數(shù)據(jù)的變化可能會比較困難。

當(dāng)系統(tǒng)發(fā)送了一個網(wǎng)絡(luò)消息:

當(dāng) Android 系統(tǒng)發(fā)送了一個網(wǎng)絡(luò)消息來保持 TCP/IP 連接開啟時,運(yùn)行 Sync Adapter。這個消息是網(wǎng)絡(luò)框架(Networking Framework)的一個基本部分??梢詫⑦@一選項(xiàng)作為自動運(yùn)行 Sync Adapter 的一個方法。另外還可以考慮將它和基于時間間隔運(yùn)行 Sync Adapter 的策略結(jié)合起來使用。

每隔一定時間:

可以每隔一段指定的時間間隔后,運(yùn)行 Sync Adapter,或者在每天的固定時間運(yùn)行它。

根據(jù)需求:

運(yùn)行 Sync Adapter 以響應(yīng)用戶的行為。然而,為了提供最佳的用戶體驗(yàn),我們應(yīng)該主要依賴那些更加自動式的選項(xiàng)。使用自動式的選項(xiàng),可以節(jié)省大量的電量以及網(wǎng)絡(luò)資源。

本課程的后續(xù)部分會詳細(xì)介紹每個選項(xiàng)。

當(dāng)服務(wù)器數(shù)據(jù)變化時,運(yùn)行 Sync Adapter

如果我們的應(yīng)用從服務(wù)器傳輸數(shù)據(jù),且服務(wù)器的數(shù)據(jù)會頻繁地發(fā)生變化,那么可以使用一個 Sync Adapter 通過下載數(shù)據(jù)來響應(yīng)服務(wù)端數(shù)據(jù)的變化。要運(yùn)行 Sync Adapter,我們需要讓服務(wù)端向應(yīng)用的 BroadcastReceiver 發(fā)送一條特殊的消息。為了響應(yīng)這條消息,可以調(diào)用 ContentResolver.requestSync() 方法,向 Sync Adapter 框架發(fā)出信號,讓它運(yùn)行 Sync Adapter。

谷歌云消息(Google Cloud Messaging,GCM)提供了我們需要的服務(wù)端組件和設(shè)備端組件,來讓上述消息系統(tǒng)能夠運(yùn)行。使用 GCM 觸發(fā)數(shù)據(jù)傳輸比通過向服務(wù)器輪詢的方式要更加可靠,也更加有效。因?yàn)檩喸冃枰粋€一直處于活躍狀態(tài)的 Service,而 GCM 使用的 BroadcastReceiver 僅在消息到達(dá)時會被激活。另外,即使沒有更新的內(nèi)容,定期的輪詢也會消耗大量的電池電量,而 GCM 僅在需要時才會發(fā)出消息。

Note:如果我們使用 GCM,將廣播消息發(fā)送到所有安裝了我們的應(yīng)用的設(shè)備,來激活 Sync Adapter。要記住他們會在同一時間(粗略地)收到我們的消息。這會導(dǎo)致在同一時段內(nèi)有多個 Sync Adapter 的實(shí)例在運(yùn)行,進(jìn)而導(dǎo)致服務(wù)器和網(wǎng)絡(luò)的負(fù)載過重。要避免這一情況,我們應(yīng)該考慮為不同的設(shè)備設(shè)定不同的 Sync Adapter 來延遲啟動時間。

下面的代碼展示了如何通過 requestSync() 響應(yīng)一個接收到的 GCM 消息:

public class GcmBroadcastReceiver extends BroadcastReceiver {
    ...
    // Constants
    // Content provider authority
    public static final String AUTHORITY = "com.example.android.datasync.provider"
    // Account type
    public static final String ACCOUNT_TYPE = "com.example.android.datasync";
    // Account
    public static final String ACCOUNT = "default_account";
    // Incoming Intent key for extended data
    public static final String KEY_SYNC_REQUEST =
            "com.example.android.datasync.KEY_SYNC_REQUEST";
    ...
    @Override
    public void onReceive(Context context, Intent intent) {
        // Get a GCM object instance
        GoogleCloudMessaging gcm =
                GoogleCloudMessaging.getInstance(context);
        // Get the type of GCM message
        String messageType = gcm.getMessageType(intent);
        /*
         * Test the message type and examine the message contents.
         * Since GCM is a general-purpose messaging system, you
         * may receive normal messages that don't require a sync
         * adapter run.
         * The following code tests for a a boolean flag indicating
         * that the message is requesting a transfer from the device.
         */
        if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)
            &&
            intent.getBooleanExtra(KEY_SYNC_REQUEST)) {
            /*
             * Signal the framework to run your sync adapter. Assume that
             * app initialization has already created the account.
             */
            ContentResolver.requestSync(ACCOUNT, AUTHORITY, null);
            ...
        }
        ...
    }
    ...
}

當(dāng) Content Provider 的數(shù)據(jù)變化時,運(yùn)行 Sync Adapter

如果我們的應(yīng)用在一個 Content Provider 中收集數(shù)據(jù),并且希望當(dāng)我們更新了 Content Provider 的時候,同時更新服務(wù)器的數(shù)據(jù),我們可以配置 Sync Adapter 來讓它自動運(yùn)行。要做到這一點(diǎn),首先應(yīng)該為 Content Provider 注冊一個 Observer。當(dāng) Content Provider 的數(shù)據(jù)發(fā)生了變化之后,Content Provider 框架會調(diào)用 Observer。在 Observer 中,調(diào)用 requestSync() 來告訴框架現(xiàn)在應(yīng)該運(yùn)行 Sync Adapter 了。

Note:如果我們使用的是一個 Stub Content Provider,那么在 Content Provider 中不會有任何數(shù)據(jù),并且不會調(diào)用 onChange() 方法。在這種情況下,我們不得不提供自己的某種機(jī)制來檢測設(shè)備數(shù)據(jù)的變化。這一機(jī)制還要負(fù)責(zé)在數(shù)據(jù)發(fā)生變化時調(diào)用 requestSync()

為了給 Content Provider 創(chuàng)建一個 Observer,繼承 ContentObserver 類,并且實(shí)現(xiàn) onChange() 方法的兩種形式。在 onChange() 中,調(diào)用 requestSync() 來啟動 Sync Adapter。

要注冊 Observer,需要將它作為參數(shù)傳遞給 registerContentObserver()。在該方法中,我們還要傳遞一個我們想要監(jiān)視的 Content URI。Content Provider 框架會將這個需要監(jiān)視的 URI 與其它一些 Content URIs 進(jìn)行比較,這些其它的 Content URIs 來自于 ContentResolver 中那些可以修改 Provider 的方法(如 ContentResolver.insert())所傳入的參數(shù)。如果出現(xiàn)了變化,那么我們所實(shí)現(xiàn)的 ContentObserver.onChange() 將會被調(diào)用。

下面的代碼片段展示了如何定義一個 ContentObserver,它在表數(shù)據(jù)發(fā)生變化后調(diào)用 requestSync()

public class MainActivity extends FragmentActivity {
    ...
    // Constants
    // Content provider scheme
    public static final String SCHEME = "content://";
    // Content provider authority
    public static final String AUTHORITY = "com.example.android.datasync.provider";
    // Path for the content provider table
    public static final String TABLE_PATH = "data_table";
    // Account
    public static final String ACCOUNT = "default_account";
    // Global variables
    // A content URI for the content provider's data table
    Uri mUri;
    // A content resolver for accessing the provider
    ContentResolver mResolver;
    ...
    public class TableObserver extends ContentObserver {
        /*
         * Define a method that's called when data in the
         * observed content provider changes.
         * This method signature is provided for compatibility with
         * older platforms.
         */
        @Override
        public void onChange(boolean selfChange) {
            /*
             * Invoke the method signature available as of
             * Android platform version 4.1, with a null URI.
             */
            onChange(selfChange, null);
        }
        /*
         * Define a method that's called when data in the
         * observed content provider changes.
         */
        @Override
        public void onChange(boolean selfChange, Uri changeUri) {
            /*
             * Ask the framework to run your sync adapter.
             * To maintain backward compatibility, assume that
             * changeUri is null.
            ContentResolver.requestSync(ACCOUNT, AUTHORITY, null);
        }
        ...
    }
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        // Get the content resolver object for your app
        mResolver = getContentResolver();
        // Construct a URI that points to the content provider data table
        mUri = new Uri.Builder()
                  .scheme(SCHEME)
                  .authority(AUTHORITY)
                  .path(TABLE_PATH)
                  .build();
        /*
         * Create a content observer object.
         * Its code does not mutate the provider, so set
         * selfChange to "false"
         */
        TableObserver observer = new TableObserver(false);
        /*
         * Register the observer for the data table. The table's path
         * and any of its subpaths trigger the observer.
         */
        mResolver.registerContentObserver(mUri, true, observer);
        ...
    }
    ...
}

在一個網(wǎng)絡(luò)消息之后,運(yùn)行 Sync Adapter

當(dāng)可以獲得一個網(wǎng)絡(luò)連接時,Android 系統(tǒng)會每隔幾秒發(fā)送一條消息來保持 TCP/IP 連接處于開啟狀態(tài)。這一消息也會傳遞到每個應(yīng)用的 ContentResolver 中。通過調(diào)用 setSyncAutomatically(),我們可以在 ContentResolver 收到消息后,運(yùn)行 Sync Adapter。

每當(dāng)網(wǎng)絡(luò)消息被發(fā)送后運(yùn)行 Sync Adapter,通過這樣的調(diào)度方式可以保證每次運(yùn)行 Sync Adapter 時都可以訪問網(wǎng)絡(luò)。如果不是每次數(shù)據(jù)變化時就要以數(shù)據(jù)傳輸來響應(yīng),但是又希望自己的數(shù)據(jù)會被定期地更新,那么我們可以用這一選項(xiàng)。類似地,如果我們不想要定期執(zhí)行 Sync Adapter,但希望經(jīng)常運(yùn)行它,我們也可以使用這一選項(xiàng)。

由于 setSyncAutomatically() 方法不會禁用 addPeriodicSync(),所以 Sync Adapter 可能會在一小段時間內(nèi)重復(fù)地被觸發(fā)激活。如果我們想要定期地運(yùn)行 Sync Adapter,應(yīng)該禁用 setSyncAutomatically()。

下面的代碼片段展示如何配置 ContentResolver,利用它來響應(yīng)網(wǎng)絡(luò)消息,從而運(yùn)行 Sync Adapter:

public class MainActivity extends FragmentActivity {
    ...
    // Constants
    // Content provider authority
    public static final String AUTHORITY = "com.example.android.datasync.provider";
    // Account
    public static final String ACCOUNT = "default_account";
    // Global variables
    // A content resolver for accessing the provider
    ContentResolver mResolver;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        // Get the content resolver for your app
        mResolver = getContentResolver();
        // Turn on automatic syncing for the default account and authority
        mResolver.setSyncAutomatically(ACCOUNT, AUTHORITY, true);
        ...
    }
    ...
}

定期地運(yùn)行Sync Adapter

我們可以設(shè)置一個在運(yùn)行之間的時間間隔來定期運(yùn)行 Sync Adapter,或者在每天的固定時間運(yùn)行它,還可以兩種策略同時使用。定期地運(yùn)行 Sync Adapter 可以讓服務(wù)器的更新間隔大致保持一致。

同樣地,當(dāng)服務(wù)器相對來說比較空閑時,我們可以通過在夜間定期調(diào)用 Sync Adapter,把設(shè)備上的數(shù)據(jù)上傳到服務(wù)器。大多數(shù)用戶在晚上不會關(guān)機(jī),并為手機(jī)充電,所以這一方法是可行的。而且,通常來說,設(shè)備不會在深夜運(yùn)行除了 Sync Adapter 之外的其他的任務(wù)。然而,如果我們使用這個方法的話,我們需要注意讓每臺設(shè)備在略微不同的時間觸發(fā)數(shù)據(jù)傳輸。如果所有設(shè)備在同一時間運(yùn)行我們的 Sync Adapter,那么我們的服務(wù)器和移動運(yùn)營商的網(wǎng)絡(luò)將很有可能負(fù)載過重。

一般來說,當(dāng)我們的用戶不需要實(shí)時更新,而希望定期更新時,使用定期運(yùn)行的策咯會很有用。如果我們希望在數(shù)據(jù)的實(shí)時性和 Sync Adapter 的資源消耗之間進(jìn)行一個平衡,那么定期執(zhí)行是一個不錯的選擇。

要定期運(yùn)行我們的 Sync Adapter,調(diào)用 addPeriodicSync()。這樣每隔一段時間,Sync Adapter 就會運(yùn)行。由于 Sync Adapter 框架會考慮其他 Sync Adapter 的執(zhí)行,并嘗試最大化電池效率,所以間隔時間會動態(tài)地進(jìn)行細(xì)微調(diào)整。同時,如果當(dāng)前無法獲得網(wǎng)絡(luò)連接,框架不會運(yùn)行我們的 Sync Adapter。

注意,addPeriodicSync() 方法不會讓 Sync Adapter 每天在某個時間自動運(yùn)行。要讓我們的 Sync Adapter 在每天的某個時刻自動執(zhí)行,可以使用一個重復(fù)計(jì)時器作為觸發(fā)器。重復(fù)計(jì)時器的更多細(xì)節(jié)可以閱讀:AlarmManager。如果我們使用 setInexactRepeating() 方法設(shè)置了一個每天的觸發(fā)時刻會有粗略變化的觸發(fā)器,我們?nèi)匀粦?yīng)該將不同設(shè)備 Sync Adapter 的運(yùn)行時間隨機(jī)化,使得它們的執(zhí)行交錯開來。

addPeriodicSync() 方法不會禁用 setSyncAutomatically(),所以我們可能會在一小段時間內(nèi)產(chǎn)生多個 Sync Adapter 的運(yùn)行實(shí)例。另外,僅有一部分 Sync Adapter 的控制標(biāo)識可以在調(diào)用 addPeriodicSync() 時使用。不被允許的標(biāo)識在該方法的文檔中可以查看。

下面的代碼樣例展示了如何定期執(zhí)行 Sync Adapter:

public class MainActivity extends FragmentActivity {
    ...
    // Constants
    // Content provider authority
    public static final String AUTHORITY = "com.example.android.datasync.provider";
    // Account
    public static final String ACCOUNT = "default_account";
    // Sync interval constants
    public static final long SECONDS_PER_MINUTE = 60L;
    public static final long SYNC_INTERVAL_IN_MINUTES = 60L;
    public static final long SYNC_INTERVAL =
            SYNC_INTERVAL_IN_MINUTES *
            SECONDS_PER_MINUTE;
    // Global variables
    // A content resolver for accessing the provider
    ContentResolver mResolver;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        // Get the content resolver for your app
        mResolver = getContentResolver();
        /*
         * Turn on periodic syncing
         */
        ContentResolver.addPeriodicSync(
                ACCOUNT,
                AUTHORITY,
                Bundle.EMPTY,
                SYNC_INTERVAL);
        ...
    }
    ...
}

按需求執(zhí)行 Sync Adapter

以響應(yīng)用戶請求的方式運(yùn)行 Sync Adapter 是最不推薦的策略。要知道,該框架是被特別設(shè)計(jì)的,它可以讓 Sync Adapter 在根據(jù)某個調(diào)度規(guī)則運(yùn)行時,能夠盡量最高效地使用手機(jī)電量。顯然,在數(shù)據(jù)改變的時候執(zhí)行同步可以更有效的使用手機(jī)電量,因?yàn)殡娏慷枷脑诹烁滦碌臄?shù)據(jù)上。

相比之下,允許用戶按照自己的需求運(yùn)行 Sync Adapter 意味著 Sync Adapter 會自己運(yùn)行,這將無法有效地使用電量和網(wǎng)絡(luò)資源。如果根據(jù)需求執(zhí)行同步,會誘導(dǎo)用戶即便沒有證據(jù)表明數(shù)據(jù)發(fā)生了變化也請求一個更新,這些無用的更新會導(dǎo)致對電量的低效率使用。一般來說,我們的應(yīng)用應(yīng)該使用其它信號來觸發(fā)一個同步更新或者讓它們定期地去執(zhí)行,而不是依賴于用戶的輸入。

不過,如果我們?nèi)匀幌胍凑招枨筮\(yùn)行 Sync Adapter,可以將 Sync Adapter 的配置標(biāo)識設(shè)置為手動執(zhí)行,之后調(diào)用 ContentResolver.requestSync() 來觸發(fā)一次更新。

通過下列標(biāo)識來執(zhí)行按需求的數(shù)據(jù)傳輸:

SYNC_EXTRAS_MANUAL

強(qiáng)制執(zhí)行手動的同步更新。Sync Adapter 框架會忽略當(dāng)前的設(shè)置,比如通過 setSyncAutomatically() 方法設(shè)置的標(biāo)識。

SYNC_EXTRAS_EXPEDITED

強(qiáng)制同步立即執(zhí)行。如果我們不設(shè)置此項(xiàng),系統(tǒng)可能會在運(yùn)行同步請求之前等待一小段時間,因?yàn)樗鼤L試將一小段時間內(nèi)的多個請求集中在一起調(diào)度,目的是為了優(yōu)化電量的使用。

下面的代碼片段將展示如何調(diào)用 requestSync() 來響應(yīng)一個按鈕點(diǎn)擊事件:

public class MainActivity extends FragmentActivity {
    ...
    // Constants
    // Content provider authority
    public static final String AUTHORITY =
            "com.example.android.datasync.provider"
    // Account type
    public static final String ACCOUNT_TYPE = "com.example.android.datasync";
    // Account
    public static final String ACCOUNT = "default_account";
    // Instance fields
    Account mAccount;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        /*
         * Create the dummy account. The code for CreateSyncAccount
         * is listed in the lesson Creating a Sync Adapter
         */

        mAccount = CreateSyncAccount(this);
        ...
    }
    /**
     * Respond to a button click by calling requestSync(). This is an
     * asynchronous operation.
     *
     * This method is attached to the refresh button in the layout
     * XML file
     *
     * @param v The View associated with the method call,
     * in this case a Button
     */
    public void onRefreshButtonClick(View v) {
        ...
        // Pass the settings flags by inserting them in a bundle
        Bundle settingsBundle = new Bundle();
        settingsBundle.putBoolean(
                ContentResolver.SYNC_EXTRAS_MANUAL, true);
        settingsBundle.putBoolean(
                ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
        /*
         * Request the sync for the default account, authority, and
         * manual sync settings
         */
        ContentResolver.requestSync(mAccount, AUTHORITY, settingsBundle);
    }