> 編寫:[jdneo](https://github.com/jdneo) - 原文:[http://developer.android.com/training/sync-adapters/running-sync-adapter.html](http://developer.android.com/training/sync-adapters/running-sync-adapter.html)
在本節課之前,你已經學習了如何創建一個封裝了數據傳輸代碼的Sync Adapter組件,以及如何添加其它的組件,使得你可以將Sync Adapter集成到系統當中。現在你已經擁有了所有部件,來安裝一個包含有Sync Adapter的應用了,但是這里還沒有任何代碼是負責去運行Sync Adapter的。
執行Sync Adapter的時機,一般應該基于某個計劃任務或者一些事件的間接結果。例如,你可能希望你的Sync Adapter以一個定期計劃任務的形式運行(比如每隔一段時間或者在每天的一個固定時間運行)。或者你也可能希望當設備上的數據發生變化后,執行你的Sync Adapter。你應該避免將運行Sync Adapter作為用戶某個行為的直接結果,因為這樣做的話你就無法利用Sync Adapter框架可以按計劃調度的特性。例如,你應該在UI中避免使用刷新按鈕。
下列情況可以作為運行Sync Adapter的時機:
**當服務端數據變更時:**
當服務端發送消息告知服務端數據發生變化時,運行Sync Adapter以響應這一來自服務端的消息。這一選項允許從服務器更新數據到設備上,該方法可以避免由于輪詢服務器所造成的執行效率下降,或者電量損耗。
**當設備的數據變更時:**
當設備上的數據發生變化時,運行Sync Adapter。這一選項允許你將修改后的數據從設備發送給服務器,如果你需要保證服務器端一直擁有設備上最新的數據,那么這一選項非常有用。如果你將數據存儲于你的Content Provider,那么這一選項的實現將會非常直接。如果你使用的是一個Stub Content Provider,檢測數據的變化可能會比較困難。
**當系統發送了一個網絡消息:**
當Android系統發送了一個網絡消息來保持TCP/IP連接開啟時,運行Sync Adapter。這個消息是網絡框架(Networking Framework)的一個基本部分。可以將這一選項作為自動運行Sync Adapter的一個方法。另外還可以考慮將它和基于時間間隔運行Sync Adapter的策略結合起來使用。
**每隔固定的時間間隔后:**
可以每隔一段你指定的時間間隔后,運行Sync Adapter,或者在每天的固定時間運行它。
**根據需求:**
運行Sync Adapter以響應用戶的行為。然而,為了提供最佳的用戶體驗,你應該主要依賴那些更加自動式的選項。使用自動式的選項,你可以節省大量的電量以及網絡資源。
本課程的后續部分會詳細介紹每個選項。
### 當服務器數據變化時,運行Sync Adapter
如果你的應用從服務器傳輸數據,且服務器的數據會頻繁地發生變化,你可以使用一個Sync Adapter通過下載數據來響應服務端數據的改變。要運行Sync Adapter,我們需要讓服務端向應用的[BroadcastReceiver](http://developer.android.com/reference/android/content/BroadcastReceiver.html)發送一條特殊的消息。為了響應這條消息,可以調用[ContentResolver.requestSync()](http://developer.android.com/reference/android/content/ContentResolver.html#requestSync(android.accounts.Account, java.lang.String, android.os.Bundle))方法,向Sync Adapter框架發出信號,讓它運行你的Sync Adapter。
谷歌云消息([Google Cloud Messaging](http://developer.android.com/google/gcm/index.html),GCM)提供了你需要的服務端組件和設備端組件,來讓上述消息系統能夠運行。使用GCM觸發數據傳輸比通過向服務器輪詢的方式要更加可靠,也更加有效。因為輪詢需要一個一直處于活躍狀態的[Service](http://developer.android.com/reference/android/app/Service.html),而GCM使用的[BroadcastReceiver](http://developer.android.com/reference/android/content/BroadcastReceiver.html)僅在消息到達時會被激活。另外,即使沒有更新的內容,定期的輪詢也會消耗大量的電池電量,而GCM僅在需要時才會發出消息。
> **Note:**如果你使用GCM,將廣播消息發送到所有安裝了你的應用的設備,來激活你的Sync Adapter,要記住他們會在同一時間(粗略地)收到你的消息。這會導致在同一時段內有多個Sync Adapter的實例在運行,進而導致服務器和網絡的負載過重。要避免這一情況,你應該考慮為不同的設備設定不同的Sync Adapter延遲啟動時間。
下面的代碼展示了如何通過[requestSync()](http://developer.android.com/reference/android/content/ContentResolver.html#requestSync(android.accounts.Account, java.lang.String, android.os.Bundle))響應一個接收到的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);
...
}
...
}
...
}
~~~
### 當Content Provider的數據變化時,運行Sync Adapter
如果你的應用在一個Content Provider中收集數據,并且你希望當你更新了Content Provider的時候,同時更新服務器的數據,你可以配置你的Sync Adapter來讓它自動運行。要做到這一點,你首先應該為Content Provider注冊一個Observer。當Content Provider的數據發生了變化之后,Content Provider框架會調用Observer。在Observer中,調用[requestSync()](http://developer.android.com/reference/android/content/ContentResolver.html#requestSync(android.accounts.Account, java.lang.String, android.os.Bundle))來告訴框架現在應該運行你的Sync Adapter了。
> **Note:**如果你使用的是一個Stub Content Provider,那么你不會在Content Provider中有任何數據,并且[onChange()](http://developer.android.com/reference/android/database/ContentObserver.html#onChange(boolean))方法也從來不會被調用。在這種情況下,你不得不提供自己的某種機制來檢測設備數據的變化。這一機制還要負責在數據發生變化時調用[requestSync()](http://developer.android.com/reference/android/content/ContentResolver.html#requestSync(android.accounts.Account, java.lang.String, android.os.Bundle))。
為了給你的Content Provider創建一個Observer,繼承[ContentObserver](http://developer.android.com/reference/android/database/ContentObserver.html)類,并且實現[onChange()](http://developer.android.com/reference/android/database/ContentObserver.html#onChange(boolean))方法的兩種形式。在[onChange()](http://developer.android.com/reference/android/database/ContentObserver.html#onChange(boolean))中,調用[requestSync()](http://developer.android.com/reference/android/content/ContentResolver.html#requestSync(android.accounts.Account, java.lang.String, android.os.Bundle))來啟動Sync Adapter。
要注冊Observer,需要將它作為參數傳遞給[registerContentObserver()](http://developer.android.com/reference/android/content/ContentResolver.html#registerContentObserver(android.net.Uri, boolean, android.database.ContentObserver))。在該方法中,你還要傳遞一個你想要監視的Content URI。Content Provider框架會將這個需要監視的URI與其它一些Content URIs進行比較,這些其它的Content URIs來自于[ContentResolver](http://developer.android.com/reference/android/content/ContentResolver.html)中那些可以修改Provider的方法(如[ContentResolver.insert()](http://developer.android.com/reference/android/content/ContentResolver.html#insert(android.net.Uri, android.content.ContentValues)))所傳入的參數,如果出現了變化,那么你所實現的[ContentObserver.onChange()](http://developer.android.com/reference/android/database/ContentObserver.html#onChange(boolean))將會被調用。
下面的代碼片段展示了如何定義一個[ContentObserver](http://developer.android.com/reference/android/database/ContentObserver.html),它在表數據發生變化后調用[requestSync()](http://developer.android.com/reference/android/content/ContentResolver.html#requestSync(android.accounts.Account, java.lang.String, android.os.Bundle)):
~~~
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);
...
}
...
}
~~~
### 在一個網絡消息之后,運行Sync Adapter
當可以獲得一個網絡連接時,Android系統會每隔幾秒發送一條消息來保持TCP/IP連接處于開啟狀態。這一消息也會傳遞到每個應用的[ContentResolver](http://developer.android.com/reference/android/content/ContentResolver.html)中。通過調用[setSyncAutomatically()](http://developer.android.com/reference/android/content/ContentResolver.html#setSyncAutomatically(android.accounts.Account, java.lang.String, boolean)),你可以在[ContentResolver](http://developer.android.com/reference/android/content/ContentResolver.html)收到消息后,運行Sync Adapter。
每當網絡消息被發送后運行你的Sync Adapter,通過這樣的調度方式可以保證每次運行Sync Adapter時都可以訪問網絡。如果不是每次數據變化時就要以數據傳輸來響應,但是又希望自己的數據會被定期地更新,那么你可以用這一選項。類似地,如果你不想要定期執行你的Sync Adapter,但你希望經常運行它,你也可以使用這一選項。
由于[setSyncAutomatically()](http://developer.android.com/reference/android/content/ContentResolver.html#setSyncAutomatically(android.accounts.Account, java.lang.String, boolean))方法不會禁用[addPeriodicSync()](http://developer.android.com/reference/android/content/ContentResolver.html#addPeriodicSync(android.accounts.Account, java.lang.String, android.os.Bundle, long)),所以你的Sync Adapter可能會在一小段時間內重復地被觸發激活。如果你想要定期地運行你的Sync Adapter,應該禁用[setSyncAutomatically()](http://developer.android.com/reference/android/content/ContentResolver.html#setSyncAutomatically(android.accounts.Account, java.lang.String, boolean))。
下面的代碼片段向你展示如何配置你的[ContentResolver](http://developer.android.com/reference/android/content/ContentResolver.html),利用它來響應網絡消息,從而運行你的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);
...
}
...
}
~~~
### 定期地運行Sync Adapter
你可以設置一個每次運行之間的時間間隔來定期運行你的Sync Adapter,或者在每天的固定時間運行它,還可以兩種策略同時使用。定期地運行你的Sync Adapter可以讓你與服務器的更新間隔大致保持一致。
同樣地,當你的服務器相對來說比較空閑時,你可以通過在夜間定期調用Sync Adapter,把設備上的數據上傳到服務器。大多數用戶在晚上不會關機,并為手機充電,所以這一方法是可行的。而且,通常來說,設備不會在深夜運行除了你的Sync Adapter之外的其他的任務。然而,如果你使用這個方法的話,你需要注意讓每臺設備在略微不同的時間觸發數據傳輸。如果所有設備在同一時間運行你的Sync Adapter,那么你的服務器和移動運營商的網絡將很有可能負載過重。
一般來說,當你的用戶不需要實時更新,而希望定期更新時,使用定期運行的策咯會很有用。如果你希望在數據的實時性和Sync Adapter的資源消耗之間進行一個平衡,那么定期執行是一個不錯的選擇。
要定期運行你的Sync Adapter,調用[addPeriodicSync()](http://developer.android.com/reference/android/content/ContentResolver.html#addPeriodicSync(android.accounts.Account, java.lang.String, android.os.Bundle, long))。這樣每隔一段時間,Sync Adapter就會運行。由于Sync Adapter框架會考慮其他Sync Adapter的執行,并嘗試最大化電池效率,所以間隔時間會動態地進行細微調整。同時,如果當前無法獲得網絡連接,框架不會運行你的Sync Adapter。
注意,[addPeriodicSync()](http://developer.android.com/reference/android/content/ContentResolver.html#addPeriodicSync(android.accounts.Account, java.lang.String, android.os.Bundle, long))方法不會讓Sync Adapter每天在某個時間自動運行。要讓你的Sync Adapter在每天的某個時刻左右自動執行,可以使用一個重復計時器作為觸發器。重復計時器的更多細節可以閱讀:[AlarmManager](http://developer.android.com/reference/android/app/AlarmManager.html)。如果你使用[setInexactRepeating()](http://developer.android.com/reference/android/app/AlarmManager.html#setInexactRepeating(int, long, long, android.app.PendingIntent))方法設置了一個每天的觸發時刻會有粗略變化的觸發器,你仍然應該將不同設備的Sync Adapter的運行時間隨機化,使得它們的執行交錯開來。
[addPeriodicSync()](http://developer.android.com/reference/android/content/ContentResolver.html#addPeriodicSync(android.accounts.Account, java.lang.String, android.os.Bundle, long))方法不會禁用[setSyncAutomatically()](http://developer.android.com/reference/android/content/ContentResolver.html#setSyncAutomatically(android.accounts.Account, java.lang.String, boolean)),所以你可能會在一小段時間內產生多個Sync Adapter的運行實例。另外,僅有一部分Sync Adapter的控制標識可以在調用[addPeriodicSync()](http://developer.android.com/reference/android/content/ContentResolver.html#addPeriodicSync(android.accounts.Account, java.lang.String, android.os.Bundle, long))時使用。不被允許的標識在該方法的[文檔](http://developer.android.com/reference/android/content/ContentResolver.html#addPeriodicSync(android.accounts.Account,%20java.lang.String,%20android.os.Bundle,%20long))中可以查看。
下面的代碼樣例展示了如何定期執行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);
...
}
...
}
~~~
### 按需求執行Sync Adapter
以響應用戶請求的方式運行Sync Adapter是最不推薦的策略。要知道,該框架是被特別設計的,它可以讓Sync Adapter在根據某個調度規則運行時,能夠盡量最高效地使用手機電量。顯然,在數據改變的時候執行同步可以更有效的使用手機電量,因為電量都消耗在了更新新的數據上。
相比之下,允許用戶按照自己的需求運行Sync Adapter意味著Sync Adapter會自己運行,這將無法有效地使用電量和網絡資源。如果根據需求執行同步,會誘導用戶即便沒有證據表明數據發生了變化也請求一個更新,這些無用的更新會導致對電量的低效率使用。一般來說,你的應用應該使用其它信號來觸發一個同步更新或者讓它們定期地去執行,而不是依賴于用戶的輸入。
不過,如果你仍然想要按照需求運行Sync Adapter,可以將Sync Adapter的配置標識設置為手動執行,之后調用[ContentResolver.requestSync()](http://developer.android.com/reference/android/content/ContentResolver.html#requestSync(android.accounts.Account, java.lang.String, android.os.Bundle))來觸發一次更新。
通過下列標識來執行按需求的數據傳輸:
[**SYNC_EXTRAS_MANUAL**](http://developer.android.com/reference/android/content/ContentResolver.html#SYNC_EXTRAS_MANUAL)
強制執行手動的同步更新。Sync Adapter框架會忽略當前的設置,比如通過[setSyncAutomatically()](http://developer.android.com/reference/android/content/ContentResolver.html#setSyncAutomatically(android.accounts.Account, java.lang.String, boolean))方法設置的標識。
[**SYNC_EXTRAS_EXPEDITED**](http://developer.android.com/reference/android/content/ContentResolver.html#SYNC_EXTRAS_EXPEDITED)
強制同步立即執行。如果你不設置此項,系統可能會在運行同步請求之前等待一小段時間,因為它會嘗試將一小段時間內的多個請求集中在一起調度,目的是為了優化電量的使用。
下面的代碼片段將向你展示如何調用[requestSync()](http://developer.android.com/reference/android/content/ContentResolver.html#requestSync(android.accounts.Account, java.lang.String, android.os.Bundle))來響應一個按鈕點擊事件:
~~~
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);
}
~~~
- 序言
- Android入門基礎:從這里開始
- 建立第一個App
- 創建Android項目
- 執行Android程序
- 建立簡單的用戶界面
- 啟動其他的Activity
- 添加ActionBar
- 建立ActionBar
- 添加Action按鈕
- 自定義ActionBar的風格
- ActionBar的覆蓋層疊
- 兼容不同的設備
- 適配不同的語言
- 適配不同的屏幕
- 適配不同的系統版本
- 管理Activity的生命周期
- 啟動與銷毀Activity
- 暫停與恢復Activity
- 停止與重啟Activity
- 重新創建Activity
- 使用Fragment建立動態的UI
- 創建一個Fragment
- 建立靈活動態的UI
- Fragments之間的交互
- 數據保存
- 保存到Preference
- 保存到文件
- 保存到數據庫
- 與其他應用的交互
- Intent的發送
- 接收Activity返回的結果
- Intent過濾
- Android分享操作
- 分享簡單的數據
- 給其他App發送簡單的數據
- 接收從其他App返回的數據
- 給ActionBar增加分享功能
- 分享文件
- 建立文件分享
- 分享文件
- 請求分享一個文件
- 獲取文件信息
- 使用NFC分享文件
- 發送文件給其他設備
- 接收其他設備的文件
- Android多媒體
- 管理音頻播放
- 控制音量與音頻播放
- 管理音頻焦點
- 兼容音頻輸出設備
- 拍照
- 簡單的拍照
- 簡單的錄像
- 控制相機硬件
- 打印
- 打印照片
- 打印HTML文檔
- 打印自定義文檔
- Android圖像與動畫
- 高效顯示Bitmap
- 高效加載大圖
- 非UI線程處理Bitmap
- 緩存Bitmap
- 管理Bitmap的內存
- 在UI上顯示Bitmap
- 使用OpenGL ES顯示圖像
- 建立OpenGL ES的環境
- 定義Shapes
- 繪制Shapes
- 運用投影與相機視圖
- 添加移動
- 響應觸摸事件
- 添加動畫
- View間漸變
- 使用ViewPager實現屏幕側滑
- 展示卡片翻轉動畫
- 縮放View
- 布局變更動畫
- Android網絡連接與云服務
- 無線連接設備
- 使得網絡服務可發現
- 使用WiFi建立P2P連接
- 使用WiFi P2P服務
- 執行網絡操作
- 連接到網絡
- 管理網絡
- 解析XML數據
- 高效下載
- 為網絡訪問更加高效而優化下載
- 最小化更新操作的影響
- 避免下載多余的數據
- 根據網絡類型改變下載模式
- 云同步
- 使用備份API
- 使用Google Cloud Messaging
- 解決云同步的保存沖突
- 使用Sync Adapter傳輸數據
- 創建Stub授權器
- 創建Stub Content Provider
- 創建Sync Adpater
- 執行Sync Adpater
- 使用Volley執行網絡數據傳輸
- 發送簡單的網絡請求
- 建立請求隊列
- 創建標準的網絡請求
- 實現自定義的網絡請求
- Android聯系人與位置信息
- Android聯系人信息
- 獲取聯系人列表
- 獲取聯系人詳情
- 使用Intents修改聯系人信息
- 顯示聯系人頭像
- Android位置信息
- 獲取最后可知位置
- 獲取位置更新
- 顯示位置地址
- 創建和監視地理圍欄
- Android可穿戴應用
- 賦予Notification可穿戴特性
- 創建Notification
- 在Notifcation中接收語音輸入
- 為Notification添加顯示頁面
- 以Stack的方式顯示Notifications
- 創建可穿戴的應用
- 創建并運行可穿戴應用
- 創建自定義的布局
- 添加語音功能
- 打包可穿戴應用
- 通過藍牙進行調試
- 創建自定義的UI
- 定義Layouts
- 創建Cards
- 創建Lists
- 創建2D-Picker
- 創建確認界面
- 退出全屏的Activity
- 發送并同步數據
- 訪問可穿戴數據層
- 同步數據單元
- 傳輸資源
- 發送與接收消息
- 處理數據層的事件
- Android TV應用
- 創建TV應用
- 創建TV應用的第一步
- 處理TV硬件部分
- 創建TV的布局文件
- 創建TV的導航欄
- 創建TV播放應用
- 創建目錄瀏覽器
- 提供一個Card視圖
- 創建詳情頁
- 顯示正在播放卡片
- 幫助用戶在TV上探索內容
- TV上的推薦內容
- 使得TV App能夠被搜索
- 使用TV應用進行搜索
- 創建TV游戲應用
- 創建TV直播應用
- TV Apps Checklist
- Android企業級應用
- Ensuring Compatibility with Managed Profiles
- Implementing App Restrictions
- Building a Work Policy Controller
- Android交互設計
- 設計高效的導航
- 規劃屏幕界面與他們之間的關系
- 為多種大小的屏幕進行規劃
- 提供向下和橫向導航
- 提供向上和歷史導航
- 綜合:設計樣例 App
- 實現高效的導航
- 使用Tabs創建Swipe視圖
- 創建抽屜導航
- 提供向上的導航
- 提供向后的導航
- 實現向下的導航
- 通知提示用戶
- 建立Notification
- 當啟動Activity時保留導航
- 更新Notification
- 使用BigView風格
- 顯示Notification進度
- 增加搜索功能
- 建立搜索界面
- 保存并搜索數據
- 保持向下兼容
- 使得你的App內容可被Google搜索
- 為App內容開啟深度鏈接
- 為索引指定App內容
- Android界面設計
- 為多屏幕設計
- 兼容不同的屏幕大小
- 兼容不同的屏幕密度
- 實現可適應的UI
- 創建自定義View
- 創建自定義的View類
- 實現自定義View的繪制
- 使得View可交互
- 優化自定義View
- 創建向后兼容的UI
- 抽象新的APIs
- 代理至新的APIs
- 使用舊的APIs實現新API的效果
- 使用版本敏感的組件
- 實現輔助功能
- 開發輔助程序
- 開發輔助服務
- 管理系統UI
- 淡化系統Bar
- 隱藏系統Bar
- 隱藏導航Bar
- 全屏沉浸式應用
- 響應UI可見性的變化
- 創建使用Material Design的應用
- 開始使用Material Design
- 使用Material的主題
- 創建Lists與Cards
- 定義Shadows與Clipping視圖
- 使用Drawables
- 自定義動畫
- 維護兼容性
- Android用戶輸入
- 使用觸摸手勢
- 檢測常用的手勢
- 跟蹤手勢移動
- Scroll手勢動畫
- 處理多觸摸手勢
- 拖拽與縮放
- 管理ViewGroup中的觸摸事件
- 處理鍵盤輸入
- 指定輸入法類型
- 處理輸入法可見性
- 兼容鍵盤導航
- 處理按鍵動作
- 兼容游戲控制器
- 處理控制器輸入動作
- 支持不同的Android系統版本
- 支持多個控制器
- Android后臺任務
- 在IntentService中執行后臺任務
- 創建IntentService
- 發送工作任務到IntentService
- 報告后臺任務執行狀態
- 使用CursorLoader在后臺加載數據
- 使用CursorLoader執行查詢任務
- 處理查詢的結果
- 管理設備的喚醒狀態
- 保持設備的喚醒
- 制定重復定時的任務
- Android性能優化
- 管理應用的內存
- 代碼性能優化建議
- 提升Layout的性能
- 優化layout的層級
- 使用include標簽重用layouts
- 按需加載視圖
- 使得ListView滑動順暢
- 優化電池壽命
- 監測電量與充電狀態
- 判斷與監測Docking狀態
- 判斷與監測網絡連接狀態
- 根據需要操作Broadcast接受者
- 多線程操作
- 在一個線程中執行一段特定的代碼
- 為多線程創建線程池
- 啟動與停止線程池中的線程
- 與UI線程通信
- 避免出現程序無響應ANR
- JNI使用指南
- 優化多核處理器(SMP)下的Android程序
- Android安全與隱私
- Security Tips
- 使用HTTPS與SSL
- 為防止SSL漏洞而更新Security
- 使用設備管理條例增強安全性
- Android測試程序
- 測試你的Activity
- 建立測試環境
- 創建與執行測試用例
- 測試UI組件
- 創建單元測試
- 創建功能測試
- 術語表