> 编写:[jdneo](https://github.com/jdneo) - 原文:[http://developer.android.com/training/sync-adapters/creating-sync-adapter.html](http://developer.android.com/training/sync-adapters/creating-sync-adapter.html)
设备和服务器之间执行数据传输的代码会封装在应用的Sync Adapter组件中。Sync Adapter框架会基于你的调度和触发操作,运行Sync Adapter组件中的代码。要将同步适配组件添加到应用当中,你需要添加下列部件:
**Sync Adapter类**
将你的数据传输代码封装到一个与Sync Adapter框架兼容的接口当中。
**绑定[Service](http://developer.android.com/reference/android/app/Service.html)**
通过一个绑定服务,允许Sync Adapter框架运行Sync Adapter类中的代码。
**Sync Adapter的XML元数据文件**
该文件包含了有关Sync Adapter的信息。框架会根据该文件确定应该如何加载并调度你的数据传输任务。
**应用清单文件的声明**
需要在应用的清单文件中声明绑定服务;同时还需要指出Sync Adapter的元数据。
这节课将会向你展示如何定义他们。
### 创建一个Sync Adapter类
在这部分课程中,你将会学习如何创建封装了数据传输代码的Sync Adapter类。创建该类需要继承Sync Adapter的基类;为该类定义构造函数;以及实现相关的方法,在这些方法中,我们定义数据传输任务。
### 继承Sync Adapter基类:AbstractThreadedSyncAdapter
要创建Sync Adapter组件,首先继承[AbstractThreadedSyncAdapter](http://developer.android.com/reference/android/content/AbstractThreadedSyncAdapter.html),然后编写它的构造函数。同使用[Activity](# "An activity represents a single screen with a user interface.").onCreate()配置[Activity](# "An activity represents a single screen with a user interface.")时一样,每次你的Sync Adapter组件重新被创建的时候,使用构造函数执行相关的配置。例如,如果你的应用使用一个Content Provider来存储数据,那么使用构造函数来获取一个[ContentResolver](http://developer.android.com/reference/android/content/ContentResolver.html)实例。由于从Android 3.0开始添加了第二种形式的构造函数,来支持`parallelSyncs`参数,所以你需要创建两种形式的构造函数来保证兼容性。
> **Note:** Sync Adapter框架是设计成和Sync Adapter组件的单例一起工作的。实例化Sync Adapter组件的更多细节,会在后面的章节中展开。
下面的代码展示了如何实现[AbstractThreadedSyncAdapter](http://developer.android.com/reference/android/content/AbstractThreadedSyncAdapter.html)和它的构造函数:
~~~
/**
* 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()中添加数据传输代码
Sync Adapter组件并不会自动地执行数据传输。它对你的数据传输代码进行封装,使得Sync Adapter框架可以在后台执行数据传输,而不会牵连到你的应用。当框架准备同步你的应用数据时,它会调用你所实现的[onPerformSync()](http://developer.android.com/reference/android/content/AbstractThreadedSyncAdapter.html#onPerformSync(android.accounts.Account, android.os.Bundle, java.lang.String, android.content.ContentProviderClient, android.content.SyncResult))方法。
为了便于将数据从你的应用程序转移到Sync Adapter组件中,Sync Adapter框架调用[onPerformSync()](http://developer.android.com/reference/android/content/AbstractThreadedSyncAdapter.html#onPerformSync(android.accounts.Account, android.os.Bundle, java.lang.String, android.content.ContentProviderClient, android.content.SyncResult)),它具有下面的参数:
**Account**
该[Account](http://developer.android.com/reference/android/accounts/Account.html)对象与触发Sync Adapter的事件相关联。如果服务端不需要使用账户,那么你不需要使用这个对象内的信息。
**Extras**
一个Bundle对象,它包含了一些标识,这些标识由触发Sync Adapter的事件所发送。
**Authority**
系统中某个Content Provider的Authority。你的应用必须要有访问它的权限。通常,该Authority对应于你的应用的Content Provider。
**Content provider client**
[ContentProviderClient](http://developer.android.com/reference/android/content/ContentProviderClient.html)针对于由`Authority`参数所指向的Content Provider。[ContentProviderClient](http://developer.android.com/reference/android/content/ContentProviderClient.html)是一个Content Provider的轻量级共有接口。它的基本功能和[ContentResolver](http://developer.android.com/reference/android/content/ContentResolver.html)一样。如果你正在使用Content Provider来存储你的应用数据,你可以利用它连接Content Provider。反之,则将其忽略。
**Sync result**
一个[SyncResult](http://developer.android.com/reference/android/content/SyncResult.html)对象,你可以使用它将信息发送给Sync Adapter框架。
下面的代码片段展示了[onPerformSync()](http://developer.android.com/reference/android/content/AbstractThreadedSyncAdapter.html#onPerformSync(android.accounts.Account, android.os.Bundle, java.lang.String, android.content.ContentProviderClient, android.content.SyncResult))函数的整体结构:
~~~
/*
* 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()](http://developer.android.com/reference/android/content/AbstractThreadedSyncAdapter.html#onPerformSync(android.accounts.Account, android.os.Bundle, java.lang.String, android.content.ContentProviderClient, android.content.SyncResult))实现是要根据应用数据的同步需求以及服务器的连接协议来制定,但是你的实现应当包含下列这些基本任务:
**连接到一个服务器**
尽管你可以假定在你开始传输数据时,已经获取到了网络连接,但是Sync Adapter框架并不会自动地连接到一个服务器。
**下载和上传数据**
Sync Adapter不会自动执行数据传输。如果你想要从服务器下载数据并将它存储到Content Provider中,你必须提供请求数据,下载数据和将数据插入到Provider中的代码。类似地,如果你想把数据发送到服务器,你需要从一个文件,数据库或者Provider中读取数据,并且发送必需的上传请求。同时你还需要处理在你执行数据传输时所发生的网络错误。
**处理数据冲突或者确定当前数据是否最新**
Sync Adapter不会自动地解决服务器数据与设备数据之间的冲突。同时,它也不会自动检测服务器上的数据是否比设备上的数据要新,反之亦然。因此,你必须自己提供处理这些状况的算法。
**清理**
在数据传输的尾声,记得要关闭网络连接,清除临时文件和缓存。
> **Note:** Sync Adapter框架会在一个后台线程中执行[onPerformSync()](http://developer.android.com/reference/android/content/AbstractThreadedSyncAdapter.html#onPerformSync(android.accounts.Account, android.os.Bundle, java.lang.String, android.content.ContentProviderClient, android.content.SyncResult))方法,所以你不需要配置后台处理任务。
除了和同步相关的任务之外,你应该尝试将一些周期性的网络相关的任务合并起来,并将它们添加到[onPerformSync()](http://developer.android.com/reference/android/content/AbstractThreadedSyncAdapter.html#onPerformSync(android.accounts.Account, android.os.Bundle, java.lang.String, android.content.ContentProviderClient, android.content.SyncResult))中。将所有网络任务集中到该方法内处理,可以节省由启动和停止网络接口所造成的电量损失。有关更多如何在进行网络访问时更高效地使用电池方面的知识,可以阅读:[Transferring Data Without Draining the Battery](#),它描述了一些在数据传输代码中可以包含的网络访问任务。
### 将Sync Adapter绑定到框架上
现在,你已经将你的数据传输代码封装在了Sync Adapter组建中,但是你必须让框架可以访问你的代码。为了做到这一点,你需要创建一个绑定[Service](http://developer.android.com/reference/android/app/Service.html),它将一个特殊的Android Binder对象从Sync Adapter组件传递给框架。有了这一Binder对象,框架就可以调用[onPerformSync()](http://developer.android.com/reference/android/content/AbstractThreadedSyncAdapter.html#onPerformSync(android.accounts.Account, android.os.Bundle, java.lang.String, android.content.ContentProviderClient, android.content.SyncResult))方法并将数据传递给它。
在服务的[onCreate()](http://developer.android.com/reference/android/app/Service.html#onCreate())方法中将你的Sync Adapter组件实例化为一个单例。通过在[onCreate()](http://developer.android.com/reference/android/app/Service.html#onCreate())方法中实例化该组件,你可以推迟到服务启动后再创建它,这会在框架第一次尝试执行你的数据传输时发生。你需要通过一种线程安全的方法来实例化组件,以防止Sync Adapter框架在响应触发和调度时,形成含有多个Sync Adapter执行的队列。
作为例子,下面的代码片段展示了你应该如何实现一个绑定[Service](http://developer.android.com/reference/android/app/Service.html)的类,实例化你的Sync Adapter组件,并获取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:**要看更多Sync Adapter绑定服务的例子,可以阅读样例代码。
### 添加框架所需的账户
Sync Adapter框架需要每个Sync Adapter拥有一个账户类型。在[创建Stub授权器](#)章节中,你已经声明了账户类型的值。现在你需要在Android系统中配置该账户类型。要配置账户类型,通过调用[addAccountExplicitly()](http://developer.android.com/reference/android/accounts/AccountManager.html#addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle))添加一个使用其账户类型的虚拟账户。
调用该方法最合适的地方是在应用的启动[Activity](# "An activity represents a single screen with a user interface.")的[onCreate()](http://developer.android.com/reference/android/app/Service.html#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.
*/
}
}
...
}
~~~
### 添加Sync Adapter的元数据文件
要将你的Sync Adapter组件集成到框架中,你需要向框架提供描述组件的元数据,以及额外的标识信息。元数据指定了你为Sync Adapter所创建的账户类型,声明了一个和你的应用相关联的Content Provider Authority,对和Sync Adapter相关的一部分系统用户接口进行控制,同时还声明了其它同步相关的标识。在你的项目中的`/res/xml/`目录下的一个特定的文件内声明这一元数据,你可以为这个文件命名,不过通常来说我们将其命名为`syncadapter.xml`。
在这一文件中包含了一个XML标签`<sync-adapter>`,它包含了下列的属性字段:
**android:contentAuthority**
Content Provider的URI Authority。如果你在前一节课程中为你的应用创建了一个Stub Content Provider,那么请使用你在清单文件中添加在 [`<provider>`](http://developer.android.com/guide/topics/manifest/provider-element.html)标签内的[android:authorities](http://developer.android.com/guide/topics/manifest/provider-element.html#auth)属性值。这一属性的更多细节在本章后续章节中有更多的介绍。
如果你正使用Sync Adapter将数据从Content Provider传输到服务器上,该属性的值应该和数据的Content URI Authority保持一致。这个值也是你在清单文件中添加在[`<provider>`](http://developer.android.com/guide/topics/manifest/provider-element.html)标签内的`android:authorities`属性的值。
**android:accountType**
Sync Adapter框架所需要的账户类型。这个值必须和你所创建的验证器元数据文件内所提供的账户类型一致(详细内容可以阅读:[创建Stub授权器](#))。这也是在上一节的代码片段中。常量`ACCOUNT_TYPE`的值。
**配置相关属性****android:userVisible**:该属性设置Sync Adapter框架的账户类型是否可见。默认地,和账户类型相关联的账户图标和标签在系统设置的账户选项中可以看见,所以你应该将你的Sync Adapter设置为对用户不可见,除非你确实拥有一个账户类型或者域名,它们可以轻松地和你的应用相关联。如果你将你的账户类型设置为不可见,你仍然可以允许用户通过一个[Activity](# "An activity represents a single screen with a user interface.")中的用户接口来控制你的Sync Adapter。**android:supportsUploading**:允许你将数据上传到云。如果你的应用仅仅下载数据,那么请将该属性设置为`false`。**android:allowParallelSyncs**:允许多个Sync Adapter组件的实例同时运行。如果你的应用支持多个用户账户并且你希望多个用户并行地传输数据,那么你可以使用该特性。如果你从不执行多个数据传输,这个选项是没用的。**android:isAlwaysSyncable**:指明Sync Adapter框架可以在任何你指定的时间运行你的Sync Adapter。如果你希望通过代码来控制Sync Adapter的运行时机,请将该属性设置为`false`,然后调用[requestSync()](http://developer.android.com/reference/android/content/ContentResolver.html#requestSync(android.accounts.Account, java.lang.String, android.os.Bundle))来运行Sync Adapter。要学习更多关于运行Sync Adapter的知识,可以阅读:[执行Sync Adapter](#)。
下面的代码展示了应该如何通过XML配置一个使用单个虚拟账户,并且只执行下载的Sync Adapter:
~~~
<?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"/>
~~~
### 在Manifest清单文件中声明Sync Adapter
一旦你将Sync Adapter组件集成到了你的应用中,你需要声明相关的权限来使用它,并且你还需要声明你所添加的绑定[Service](http://developer.android.com/reference/android/app/Service.html)。
由于Sync Adapter组件会运行设备与网络之间传输数据的代码,所以你需要请求使用网络的权限。同时,你的应用还需要读写Sync Adapter配置信息的权限,这样你才能通过应用中的其它组件去控制Sync Adapter。另外,你还需要一个特殊的权限,来允许你的应用使用你在[创建Stub授权器](#)中所创建的授权器组件。
要请求这些权限,将下列内容添加到应用清单文件中,并作为[`<manifest>`](http://developer.android.com/guide/topics/manifest/manifest-element.html)标签的子标签:
[**android.permission.INTERNET**](http://developer.android.com/reference/android/Manifest.permission.html#INTERNET)
允许Sync Adapter访问网络,使得它可以从设备下载和上传数据到服务器。如果之前已经请求了该权限,那么你就不需要重复请求了。
[**android.permission.READ_SYNC_SETTINGS**](http://developer.android.com/reference/android/Manifest.permission.html#READ_SYNC_SETTINGS)
允许你的应用读取当前的Sync Adapter配置。例如,你需要该权限来调用[getIsSyncable()](http://developer.android.com/reference/android/content/ContentResolver.html#getIsSyncable(android.accounts.Account, java.lang.String))。
[**android.permission.WRITE_SYNC_SETTINGS**](http://developer.android.com/reference/android/Manifest.permission.html#WRITE_SYNC_SETTINGS)
允许你的应用对Sync Adapter的配置进行控制。你需要这一权限来通过[addPeriodicSync()](http://developer.android.com/reference/android/content/ContentResolver.html#addPeriodicSync(android.accounts.Account, java.lang.String, android.os.Bundle, long))方法设置执行同步的时间间隔。另外,调用[requestSync()](http://developer.android.com/reference/android/content/ContentResolver.html#requestSync(android.accounts.Account, java.lang.String, android.os.Bundle))方法不需要用到该权限。更多信息可以阅读:[执行Sync Adapter](#)。
[**android.permission.AUTHENTICATE_ACCOUNTS**](http://developer.android.com/reference/android/Manifest.permission.html#AUTHENTICATE_ACCOUNTS)
允许你使用在[创建Stub授权器](#)中所创建的验证器组件。
下面的代码片段展示了如何添加这些权限:
~~~
<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>
~~~
最后,要声明框架用来和你的Sync Adapter进行交互的绑定[Service](http://developer.android.com/reference/android/app/Service.html),添加下列的XML代码到应用清单文件中,作为[`<application>`](http://developer.android.com/guide/topics/manifest/application-element.html)标签的子标签:
~~~
<service
android:name="com.example.android.datasync.SyncService"
android:exported="true"
android:process=":sync">
<intent-filter>
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter" />
</service>
~~~
[`<intent-filter>`](http://developer.android.com/guide/topics/manifest/intent-filter-element.html)标签配置了一个过滤器,它会被带有`android.content.SyncAdapter`这一Action的Intent所触发,该Intent一般是由系统为了运行Sync Adapter而发出的。当过滤器被触发后,系统会启动你所创建的绑定服务,在本例中它叫做`SyncService`。属性[android:exported="true"](http://developer.android.com/guide/topics/manifest/service-element.html#exported)允许你应用之外的其它进程(包括系统)访问这一[Service](http://developer.android.com/reference/android/app/Service.html)。属性[android:process=":sync"](http://developer.android.com/guide/topics/manifest/service-element.html#proc)告诉系统应该在一个全局共享的,且名字叫做`sync`的进程内运行该[Service](http://developer.android.com/reference/android/app/Service.html)。如果你的应用中有多个Sync Adapter,那么它们可以共享该进程,这有助于减少开销。
[`<meta-data>`](http://developer.android.com/guide/topics/manifest/meta-data-element.html)标签提供了你之前为Sync Adapter所创建的元数据XML文件的文件名。属性[android:name](http://developer.android.com/guide/topics/manifest/meta-data-element.html#nm)指出这一元数据是针对于Sync Adapter框架的。而[android:resource](http://developer.android.com/guide/topics/manifest/meta-data-element.html#rsrc)标签则指定了元数据文件的名称。
现在你已经为Sync Adapter准备好所有相关的组件了。下一节课将讲授如何让Sync Adapter框架运行你的Sync Adapter,要实现这一点,既可以通过响应一个事件的方式,也可以通过执行一个周期性任务的方式。
- 序言
- 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组件
- 创建单元测试
- 创建功能测试
- 術語表