【问题标题】:Why does ContentResolver.requestSync not trigger a sync?为什么 ContentResolver.requestSync 不触发同步?
【发布时间】:2011-03-09 23:56:42
【问题描述】:

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

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

编辑——添加清单 sn-p 我的清单 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 的设备上的模拟器中进行测试。

【问题讨论】:

  • 我的问题是我在同步适配器中的断点没有到达......然后我意识到我正在尝试调试服务......希望这可以帮助像我这样的其他人。
  • 同步适配器服务中的断点将无法触发。那是因为同步适配器服务在一个单独的进程中运行。这就是@danglang 所暗示的。另请参阅此问题:stackoverflow.com/questions/8559458/…
  • 在我的情况下,从同步服务中删除 android:process=":sync" 让调试器达到了临界点。同步服务本身在此之前工作,因为我可以看到来自 onPerformSync 方法代表另一个进程的日志消息。
  • 另一个原因是 WiFi 已关闭,因此如果您尝试与模拟数据同步,请检查您的连接。

标签: android android-contentprovider android-syncadapter


【解决方案1】:

调用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>

&lt;service&gt; 标记的 name 属性是要连接同步的类的名称...我稍后再谈。

设置exported true 使其对其他组件可见(需要ContentResolver 可以调用它)。

意图过滤器让它捕获请求同步的意图。 (当你调用ContentResolver.requestSync()或相关调度方法时,这个Intent来自ContentResolver。)

&lt;meta-data&gt; 标签将在下面讨论。

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,我们服务中奇怪的 &lt;meta-data&gt; 标签是建立 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 我们定义的同步适配器(在 &lt;service&gt; 标记的 name 元素中调用的类,包括引用此文件的 &lt;meta-data&gt; 标记...)将使用 com.谷歌风格的帐户。

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

accountType 还必须与已输入的已知帐户类型之一匹配,或者必须与您正在创建的帐户类型匹配(这涉及创建 AccountAuthenticator 的子类以在您的服务器上获取身份验证...值得一文,本身。)同样,“com.google”是定义的字符串,用于标识... google.com 样式的帐户凭据(同样,这应该不足为奇。)

5.在给定的帐户/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() 控制帐户/权限对是选中还是未选中,而setIsSyncable() 提供了一种取消选中和灰显该行的方法,这样用户就无法转动它在。您可以将一个帐户设置为可同步,而将另一个帐户设置为不可同步(禁用)。

7.注意 ContentResolver.notifyChange()

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

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

8.开心!

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

【讨论】:

  • ContentResolver.setSyncAutomatically(account, AUTHORITY, true);
  • 没问题,很高兴能帮上忙。不过,再次从样式的角度来看,如果“AUTHORITY”和“myaccounttype”是您正在使用的实际字符串(而不仅仅是用于复制到站点的示例),您肯定需要清理您的命名约定。这些字符串在所有设备中必须是唯一的,如果其他程序员懒惰地用匹配的字符串创建一个包以获得权限并且你遇到冲突,你会遇到真正的麻烦。干杯!
  • 即使全局同步设置已关闭,您也可以请求同步。只需将 ContentResolver.SYNC_EXTRAS_MANUAL 设置为 true 到 extras Bundle 即可强制同步 :)
  • @kaciula:我不知道,但是设备会记住它需要同步,并且一旦打开全局同步,它就会启动一个。您真的不应该试图在这一点上胜过用户——尤其是因为“全局同步关闭”是在关键情况下节省电池的关键方法之一。如果你真的担心数据没有同步,考虑一个弹出窗口告诉用户为什么数据没有移动,如果它已经坐了一段时间。这样您就可以教育那些意外错误配置其设备的用户,并提醒高级用户以防他们忘记了。
  • 可能值得补充的是,如果您想使用 addPeriodicSync(),它似乎只有在您还 setSyncAutomatically() 时才有效 - 我出于绝望添加了它,试图让 SOMETHING 工作。我知道这不是原始问题的一部分,但这是一个完整的答案!
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-27
    • 2011-10-12
    • 2015-06-16
    • 2022-08-03
    相关资源
    最近更新 更多