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

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

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 的帐户是“ myaccount 类型”,传递给调用的 AUTHORITY 与我的 syc 适配器 xml 匹配。

Is ContentResolver.requestSync the correct way to request a sync? It looks like the sync tester tool binds directly to the service and calls start sync, but that seems like it defeats the purpose of integrating with the sync architecture.

如果这是请求同步的正确方式,那么为什么同步测试工作,而不是我对 ContentResolver.requestSync 的调用呢?包裹里有什么东西要递给我吗?

我正在模拟器中测试运行2.1和2.2的设备。

47248 次浏览

调用 requestSync()只能在系统已知的{ Account,ContentAuthority }对上工作。你的应用程序需要经过一系列步骤来告诉 Android,你可以使用一个特定类型的账户来同步一个特定类型的内容。它在 Android 清单中这样做。

1. 通知 Android 你的应用程序包提供了同步功能

首先,在 AndroidManifest.xml 中,您必须声明您拥有一个 Sync 服务:

<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>标记的 name 属性是用于连接 sync 的类的名称... ... 我将在后面讨论这个问题。

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

意图过滤器允许它捕获请求同步的意图。(当您调用 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时返回 SyncAdapterBinder... 您需要一个 AbstractThreadedSyncAdapter类型的变量。如你所见,这几乎是这门课的全部内容。它存在的唯一原因是为了提供一个服务,为 Android 提供一个标准的接口来查询你的类,你的 SyncAdapter本身是什么。

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

MySyncAdapter 是存储真正的同步逻辑本身的地方。它的 onPerformSync()方法在同步时被调用。我想你已经准备好了。

4. 在帐户类型和内容管理机构之间建立一个绑定

Looking back again at AndroidManifest, that strange <meta-data> tag in our service is the key piece that establishes the binding between a ContentAuthority and an account. It externally references another xml file (call it whatever you like, something relevant to your app.) Let's look at 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>标记的 name 元素中调用的类,其中包括引用该文件的 <meta-data>标记...)将使用 com.google 风格的帐户同步联系人。

所有 contentAuthority 字符串都必须匹配,并且匹配您正在同步的内容——如果您正在创建自己的数据库,那么这应该是您定义的字符串,或者如果您正在同步已知的数据类型(比如联系人或日历事件等等) ,那么您应该使用一些现有的设备字符串上面的(“ com.android.contact”)恰好是用于联系人类型数据的 ContentAuthority 字符串(令人惊讶,令人惊讶)

accountType also has to match one of those known account types that are already entered, or it has to match one you're creating (This involves creating a subclass of AccountAuthenticator to get auth on your server... Worth an article, itself.) Again, "com.google" is the defined string identifying... google.com style account credentials (again, this should not be a surprise.)

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

最后,必须启用同步。你可以在控制面板的“帐户与同步”页面中进行这项操作,方法是进入你的应用程序并在匹配的帐户中设置应用程序旁边的复选框。或者,你也可以在你的应用程序中使用一些安装代码:

ContentResolver.setSyncAutomatically(account, AUTHORITY, true);

For sync to occur, your account/authority pair must be enabled to sync (like above) 还有 the overall global sync flag on the system must be set, 还有 the device must have network connectivity.

如果您的帐户/授权同步或全局同步被禁用,调用 RequestSync ()确实有效果——它设置了一个已请求同步的标志,并将在启用同步后立即执行。

另外,根据 MGV,在 requestSync 的附加包中将 ContentResolver.SYNC_EXTRAS_MANUAL设置为 true,即使全局同步关闭,也会要求 android 强制同步(在这里要尊重用户!)

最后,还可以使用 ContentResolver 函数设置定期调度的同步。

6. Consider implications of multiple accounts

可以有多个同类型的帐户(在一个设备上设置两个@gmail.com 帐户,或者两个 facebook 帐户,或者两个 twitter 帐户,等等。.)您应该考虑这样做对应用程序的影响... ... 如果您有两个帐户,您可能不想尝试将它们同步到同一个数据库表中。也许您需要指定一次只能激活一个,并在切换帐户时刷新表并重新同步。(通过查询存在哪些帐户的属性页)。也许您为每个帐户创建一个不同的数据库,也许是不同的表,也许是每个表中的一个键列。所有的应用都是特定的,值得思考。ContentResolver.setIsSyncable(Account account, String authority, int syncable)可能是这里的兴趣所在。setSyncAutomatically()控制帐户/权限对是 检查过了还是 不受约束,而 setIsSyncable()提供了一种取消检查并使线路灰化的方法,这样用户就不能打开它。您可以设置一个帐户同步,另一个帐户不同步(dsable)。

7. 注意 ContentResolver.notifyChange ()

有件事很棘手。ContentResolver.notifyChange()ContentProvider用来通知 Android 本地数据库已更改的函数。这有两个功能,首先,它会导致内容 uri 后面的游标进行更新,然后查询并使 ListView失效和重绘,等等。.这是非常神奇的,数据库的变化和您的 ListView只是自动更新。太棒了。此外,当数据库发生变化时,Android 会为您请求同步,甚至超出您的正常时间表,这样这些变化就可以从设备上移除,并尽可能快地同步到服务器上。也很棒。

There's one edge case though. If you pull from the server, and push an update into the ContentProvider, it will dutifully call notifyChange() and android will go, "Oh, database changes, better put them on the server!" (Doh!) Well-written ContentProviders will have some tests to see if the changes came from the network or from the user, and will set the boolean syncToNetwork flag false if so, to prevent this wasteful double-sync. If you're feeding data into a ContentProvider, it behooves you to figure out how to get this working -- Otherwise you'll end up always performing two syncs when only one is needed.

8. Feel happy!

一旦您准备好了所有的 xml 元数据并启用了同步,Android 就会知道如何为您连接所有的东西,同步应该就可以开始工作了。在这一点上,很多东西是好的将只是点击到位,它会感觉很像魔术。好好享受吧!

我在 AccountManager setAuthToken方法之后调用 setIsSyncable。但是 setAuthToken在到达 setIsSyncable之前返回了功能。订单更改后,一切工作正常!

我注意到,当有互联网连接时,requestSync将触发 sync实现