113

我正在尝试实现在Google IO中讨论的 Content-Provider-Sync Adapter 模式- 幻灯片 26。我的内容提供程序正在工作,当我从 Dev Tools Sync Tester 应用程序触发它时,我的同步工作正常,但是当我调用 ContentResolver 时。来自我的 ContentProvider 的 requestSync(account, authority, bundle),我的同步永远不会被触发。

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 是请求同步的正确方法吗?看起来同步测试工具直接绑定到服务并调用启动同步,但这似乎违背了与同步架构集成的目的。

如果这是请求同步的正确方法,那么为什么同步测试器可以工作,但我对 ContentResolver.requestSync 的调用却不行?我需要在捆绑包中传递一些东西吗?

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

4

2 回答 2

283

调用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()ContentProviders用来通知Android本地数据库发生变化的函数。这有两个功能,首先,它会导致跟随该内容 uri 的游标更新,然后重新查询和无效并重绘 aListView等......这非常神奇,数据库更改并且您ListView只是自动更新。惊人的。此外,当数据库发生更改时,Android 会为您请求同步,即使在您的正常计划之外,以便这些更改从设备上删除并尽快同步到服务器。也很棒。

不过有一个极端情况。如果你从服务器拉取更新,并将更新推送到ContentProvider,它会尽职尽责地调用notifyChange(),android 会说:“哦,数据库更改,最好将它们放在服务器上!” (Doh!)写得好的ContentProviders将进行一些测试,看看更改是来自网络还是来自用户,syncToNetwork如果是,则将布尔标志设置为 false,以防止这种浪费的双同步。如果您将数据输入到 中ContentProvider,那么您应该弄清楚如何使其工作 - 否则您最终将总是在只需要一个时执行两个同步。

8. 开心!

一旦你准备好所有这些 xml 元数据并启用同步,Android 将知道如何为你连接所有内容,并且同步应该开始工作。在这一点上,很多不错的东西都会点击到位,感觉很像魔术。享受!

于 2011-03-10T04:09:54.520 回答
0

我正在使用setIsSyncableAccountManagersetAuthToken方法。但setAuthToken之前返回的功能setIsSyncable已达到。订单更改后一切正常!

于 2019-09-13T09:53:29.620 回答