调用requestSync()
仅适用于系统已知的 {Account, ContentAuthority} 对。您的应用需要通过多个步骤来告诉 Android 您能够使用特定类型的帐户同步特定类型的内容。它在 AndroidManifest 中执行此操作。
1.通知Android你的应用包提供同步
首先,在 AndroidManifest.xml 中,您必须声明您有一个同步服务:
<service android:name=".sync.mySyncService" android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/sync_myapp" />
</service>
标记的 name 属性<service>
是要连接同步的类的名称...我稍后再谈。
设置导出的 true 使其对其他组件可见(需要所以ContentResolver
可以调用它)。
意图过滤器让它捕获请求同步的意图。(这Intent
来自ContentResolver
您调用ContentResolver.requestSync()
或相关的调度方法时。)
<meta-data>
标签将在下面讨论。
2. 为 Android 提供一个用于查找 SyncAdapter 的服务
所以类本身......这是一个例子:
public class mySyncService extends Service {
private static mySyncAdapter mSyncAdapter = null;
public SyncService() {
super();
}
@Override
public void onCreate() {
super.onCreate();
if (mSyncAdapter == null) {
mSyncAdapter = new mySyncAdapter(getApplicationContext(), true);
}
}
@Override
public IBinder onBind(Intent arg0) {
return mSyncAdapter.getSyncAdapterBinder();
}
}
您的类必须扩展Service
或其子类之一,必须实现public IBinder onBind(Intent)
,并且必须SyncAdapterBinder
在调用时返回...您需要一个类型的变量AbstractThreadedSyncAdapter
。如您所见,这几乎就是该课程中的所有内容。它存在的唯一原因是提供一个服务,它为 Android 提供一个标准接口来查询你的类,以了解你SyncAdapter
自己是什么。
3. 提供一个class SyncAdapter
来实际执行同步。
mySyncAdapter 是存储真正同步逻辑本身的地方。它的onPerformSync()
方法在需要同步时被调用。我想你已经有了这个。
4. 在 Account-type 和 Content Authority 之间建立绑定
再次回顾 AndroidManifest,<meta-data>
我们服务中的那个奇怪的标签是建立 ContentAuthority 和帐户之间绑定的关键部分。它从外部引用另一个 xml 文件(你可以随意调用它,与你的应用程序相关的东西。)让我们看看 sync_myapp.xml:
<?xml version="1.0" encoding="utf-8" ?>
<sync-adapter
xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="com.android.contacts"
android:accountType="com.google"
android:userVisible="true" />
好的,那么这有什么作用?它告诉 Android 我们定义的同步适配器(在<service>
包含<meta-data>
引用此文件的标签的标签的名称元素中调用的类...)将使用 com.google 样式帐户同步联系人。
您的所有 contentAuthority 字符串必须全部匹配,并且与您正在同步的内容相匹配——如果您正在创建自己的数据库,这应该是您定义的字符串,或者如果您正在同步已知的,您应该使用一些现有的设备字符串数据类型(如联系人或日历事件或你有什么。)上面的(“com.android.contacts”)恰好是联系人类型数据的 ContentAuthority 字符串(惊喜,惊喜。)
accountType 还必须与已输入的已知帐户类型之一匹配,或者它必须与您正在创建的帐户类型匹配(这涉及创建 AccountAuthenticator 的子类以在您的服务器上获取身份验证......值得一篇文章本身。)同样,“com.google”是定义的字符串,用于识别... google.com 样式的帐户凭据(同样,这应该不足为奇。)
5. 在给定的 Account/ContentAuthority 对上启用同步
最后,必须启用同步。您可以在控制面板的 Accounts & Sync 页面中执行此操作,方法是转到您的应用并在匹配帐户中设置您的应用旁边的复选框。或者,您可以在应用程序的一些设置代码中执行此操作:
ContentResolver.setSyncAutomatically(account, AUTHORITY, true);
要进行同步,您的帐户/权限对必须启用同步(如上),并且必须设置系统上的整体全局同步标志,并且设备必须具有网络连接。
如果您的帐户/权限同步或全局同步被禁用,调用 RequestSync() 确实会产生影响——它会设置一个已请求同步的标志,并且将在启用同步后立即执行。
此外,根据mgv,在您的 requestSync 的附加包中设置ContentResolver.SYNC_EXTRAS_MANUAL
为 true 将要求 android 强制同步,即使全局同步已关闭(请在此处尊重您的用户!)
最后,您可以再次使用 ContentResolver 函数设置定期计划同步。
6.考虑多个账户的影响
可能有多个相同类型的帐户(在一台设备上设置两个 @gmail.com 帐户或两个 facebook 帐户,或两个 twitter 帐户等)您应该考虑这样做对应用程序的影响。 .. 如果您有两个帐户,您可能不想尝试将它们同步到同一个数据库表中。也许您需要指定一次只能激活一个,并在切换帐户时刷新表并重新同步。(通过查询存在哪些帐户的属性页)。也许您为每个帐户创建不同的数据库,也许是不同的表,也许是每个表中的一个键列。所有应用程序特定且值得深思。 ContentResolver.setIsSyncable(Account account, String authority, int syncable)
可能对这里感兴趣。 setSyncAutomatically()
控制是否检查帐户/权限对或unchecked,而setIsSyncable()
提供了一种方法来取消选中该行并将其变灰,因此用户无法打开它。您可以将一个帐户设置为可同步,而将另一个帐户设置为不可同步 (dsabled)。
7. 注意 ContentResolver.notifyChange()
一件棘手的事情。ContentResolver.notifyChange()
是ContentProvider
s用来通知Android本地数据库发生变化的函数。这有两个功能,首先,它会导致跟随该内容 uri 的游标更新,然后重新查询和无效并重绘 aListView
等......这非常神奇,数据库更改并且您ListView
只是自动更新。惊人的。此外,当数据库发生更改时,Android 会为您请求同步,即使在您的正常计划之外,以便这些更改从设备上删除并尽快同步到服务器。也很棒。
不过有一个极端情况。如果你从服务器拉取更新,并将更新推送到ContentProvider
,它会尽职尽责地调用notifyChange()
,android 会说:“哦,数据库更改,最好将它们放在服务器上!” (Doh!)写得好的ContentProviders
将进行一些测试,看看更改是来自网络还是来自用户,syncToNetwork
如果是,则将布尔标志设置为 false,以防止这种浪费的双同步。如果您将数据输入到 中ContentProvider
,那么您应该弄清楚如何使其工作 - 否则您最终将总是在只需要一个时执行两个同步。
8. 开心!
一旦你准备好所有这些 xml 元数据并启用同步,Android 将知道如何为你连接所有内容,并且同步应该开始工作。在这一点上,很多不错的东西都会点击到位,感觉很像魔术。享受!