为什么ContentResolver.requestSync不会触发同步?

我尝试实现Content-Provider-Sync Adapter模式,如Google IO-幻灯片26中讨论的。我的内容提供者正在工作,当我从Dev Tools Sync Tester应用程序中触发它时,我的同步工作正常,但是当我调用ContentResolver时。请求同步(帐户,权限,捆绑)从我的ContentProvider,我的同步永远不会触发。

ContentResolver.requestSync( account, AUTHORITY, new Bundle()); 

编辑 – 添加清单片段我的清单xml包含:

 <service android:name=".sync.SyncService" android:exported="true"> <intent-filter> <action android:name="android.content.SyncAdapter" /> </intent-filter> <meta-data android:name="android.content.SyncAdapter" android:resource="@xml/syncadapter" /> </service> 

– 编辑

与我的同步服务关联的我的syncadapter.xml包含:

 <?xml version="1.0" encoding="utf-8"?> <sync-adapter xmlns:android="http://schemas.android.com/apk/res/android" android:contentAuthority="AUTHORITY" android:accountType="myaccounttype" android:supportsUploading="true" /> 

不知道其他代码会有用。 传递给requestSync的帐户是“myaccounttype”,传递给该调用的AUTHORITY与我的syc适配器xml匹配。

ContentResolver.requestSync是请求同步的正确方法吗? 它看起来像同步testing仪工具直接绑定到服务和调用开始同步,但是这似乎失败了与同步体系结构集成的目的。

如果这是请求同步的正确方法,那么为什么同步testing工作,但不是我对ContentResolver.requestSync的调用? 有什么我需要通过捆绑?

我正在模拟器上运行2.1和2.2的设备上进行testing。

调用requestSync()仅适用于系统已知的{Account,ContentAuthority}对。 您的应用需要经过多个步骤才能告诉Android,您可以使用特定types的帐户同步特定types的内容。 它在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> 

<service>标签的名称属性是您的类的名称,以连接同步…我会在一秒钟之内谈到。

设置导出的true使其对其他组件可见( ContentResolver可以调用它)。

意图filter让它捕捉一个意图请求同步。 (当您调用ContentResolver.requestSync()或相关的调度方法时,此Intent来自ContentResolver 。)

下面将讨论<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时返回…您需要一个AbstractThreadedSyncAdaptertypes的variables。 所以,正如你所看到的那样,这几乎是该class的一切。 它的唯一原因是提供一个服务,它提供了一个标准的Android接口来查询你的类,你的SyncAdapter本身是什么。

3.提供一个class SyncAdapter来实际执行同步。

mySyncAdapter是存储真正的同步逻辑的地方。 它的onPerformSync()方法在需要同步的时候被调用。 我想你已经有了这个地方。

4.build立帐户types和内容授权机构之间的绑定

再回顾一下AndroidManifest,我们的服务中那个奇怪的<meta-data>标签是build立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样式帐户。

所有的contentAuthoritystring必须全部匹配,并与您正在同步的内容匹配 – 如果您要创build自己的数据库,则应该是您定义的string;如果您正在同步已知数据库,则应该使用一些现有的设备string数据types(如联系人或日历事件或你有什么)。上述(“com.android.contacts”)碰巧是联系人types数据的ContentAuthoritystring(惊喜,惊喜)。

accountType还必须匹配已经input的已知帐户types之一,或者必须匹配您正在创build的帐户types(这涉及到创buildAccountAuthenticator的子类以在您的服务器上获得授权…值得一文,本身。)同样,“com.google”是定义的string标识… google.com风格的帐户凭据(同样,这不应该是一个惊喜。)

5.在给定的Account / ContentAuthority对上启用同步

最后,同步必须启用。 您可以在控制台的“帐户和同步”页面中执行此操作,方法是转到您的应用,并在匹配的帐户中将您的应用旁边的checkbox设置。 或者,您可以在应用程序的某些设置代码中执行此操作:

 ContentResolver.setSyncAutomatically(account, AUTHORITY, true); 

要进行同步,必须启用帐户/权限对,才能同步(如上所述), 并且必须设置系统上的全局同步标志, 并且设备必须具有networking连接。

如果您的帐户/权限同步或全局同步被禁用,则调用RequestSync()确实会产生影响 – 它会设置一个已经请求同步的标志,一旦启用同步就会执行该标志。

此外,每mgv ,在你的requestSync的extras bundle中设置ContentResolver.SYNC_EXTRAS_MANUAL为true将会要求android强制同步,即使全局同步closures(尊敬你的用户在这里!)

最后,您可以使用ContentResolver函数再次设置定期计划同步。

6.考虑多个帐户的影响

可以有多个相同types的帐户(在一台设备上设置两个@ gmail.com帐户或两个Facebook帐户,或者两个Twitter帐户等)。您应该考虑这样做的应用含义。 ..如果您有两个帐户,您可能不想尝试将它们同步到同一个数据库表中。 也许你需要指定一次只能有一个是活动的,如果你切换帐户,刷新表并重新同步。 (通过查询帐户的属性页面)。 也许你为每个帐户创build一个不同的数据库,也许不同的表,也许每个表中的关键列。 所有的应用程序具体和值得一些想法。 ContentResolver.setIsSyncable(Account account, String authority, int syncable)可能在这里感兴趣。 setSyncAutomatically()控制是否选中某个帐户/权限对,而setIsSyncable()提供了取消选中该行并使其灰色的方法,以便用户无法将其打开。 您可以设置一个帐户Syncable,另一个不可以Syncable(dsabled)。

7.注意ContentResolver.notifyChange()

一件棘手的事情。 ContentResolver.notifyChange()ContentProvider用来通知Android本地数据库已被更改的函数。 这有两个function,首先,它会导致内容uri更新后的游标,并进而重新查询和无效和重绘一个ListView等…这是非常神奇的,数据库更改,您的ListView只是自动更新。 真棒。 另外,当数据库发生变化时,Android会为您请求Sync,即使超出您的正常时间表,也会将这些更改从设备中取出并尽快同步到服务器。 也真棒。

尽pipe有一个边缘案例。 如果你从服务器拉出来,并将更新推送到ContentProvider ,它将尽职调用notifyChange() ,android会去,“哦,数据库更改,更好地把它们放在服务器上! (Doh!)编写良好的ContentProviders将进行一些testing,以查看更改是来自networking还是来自用户,并且将设置布尔值syncToNetwork标志为false,以防止这种浪费的双重同步。 如果您要将数据提供给ContentProvider ,那么您应该了解如何使其正常工作 – 否则,只有在需要时才会执行两次同步。

8.感到开心!

一旦你拥有所有这些XML元数据,并启用了同步function,Android将知道如何为你连接所有的东西,同步应该开始工作。 在这一点上,很多不错的东西只要点击到位就会感觉很像魔术。 请享用!