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


112

我正在尝试实现Google IO-幻灯片26中讨论的Content-Provider-Sync Adapter模式。我的内容提供程序正在运行,当我从Dev Tools Sync Tester应用程序触发它时,我的同步起作用了,但是当我调用ContentResolver时。来自ContentProvider的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的帐户是“ myaccounttype”,并且传递给调用的AUTHORITY匹配我的syc适配器xml。

ContentResolver.requestSync是否是请求同步的正确方法?看起来好像同步测试器工具直接绑定到服务并调用开始同步,但这似乎违反了与同步体系结构集成的目的。

如果这是请求同步的正确方法,那么为什么同步测试器可以工作,而不是我对ContentResolver.requestSync的调用?我需要通过捆绑销售商品吗?

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


3
我的问题是未达到同步适配器中的断点...然后我意识到我正在尝试调试服务...希望这对像我这样的人有所帮助。
丹格2015年

2
同步适配器服务中的断点将无法触发。这是因为同步适配器服务在单独的进程中运行。这就是@danglang所暗示的。另请参阅以下问题:stackoverflow.com/questions/8559458/…–
康斯坦丁·舒伯特

1
在我的情况下,android:process=":sync"从同步服务中删除让调试器击中了尖点。在此之前,同步服务本身就已经在工作,因为我可以onPerformSync代表另一个进程看到来自方法的日志消息。
谢尔盖

另一个原因是WiFi已关闭,因此,如果您尝试与模拟数据进行同步,请检查您的连接。
艾伦·韦洛索

Answers:


280

呼叫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>

<service>标记的name属性是您要连接同步的类的名称。我将在稍后讨论。

将export设置为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在被调用时返回a 。您需要一个type变量AbstractThreadedSyncAdapter。如您所见,这几乎就是该课程中的所有内容。它存在的唯一原因是提供了一个服务,该服务为Android提供了一个标准接口来查询您自己的类SyncAdapter

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

mySyncAdapter是实际同步逻辑本身的存储位置。onPerformSync()需要同步时调用其方法。我认为您已经具备了这一功能。

4.在帐户类型和内容授权机构之间建立绑定

再次回顾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>引用此文件的标记的标记的name元素中调用的类...)将使用com.google样式帐户同步联系人。

您所有的contentAuthority字符串都必须完全匹配,并与您要同步的字符串匹配-如果您要创建自己的数据库,则应为您定义的字符串,如果要同步已知的字符串,则应使用一些现有的设备字符串数据类型(如联系人或日历事件或您拥有的东西。)上面的(“ com.android.contacts”)恰好是联系人类型数据(惊讶,意外)的ContentAuthority字符串。

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

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

最后,必须启用同步。您可以在控制面板的“帐户与同步”页面中执行此操作,方法是转到您的应用,然后在匹配的帐户中选中您应用旁边的复选框。另外,您可以在应用程序中的一些设置代码中完成此操作:

ContentResolver.setSyncAutomatically(account, AUTHORITY, true);

为了进行同步,必须启用您的帐户/授权对进行同步(如上),并且必须设置系统上的总体全局同步标志,并且设备必须具有网络连接性。

如果您的帐户/单位同步或全局同步被禁用,则调用RequestSync()确实会起作用-它设置一个标志,指示已请求同步,并且将在启用同步后立即执行。

此外,每mgvContentResolver.SYNC_EXTRAS_MANUAL在requestSync的extras捆绑包中设置为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之后的游标进行更新,然后重新查询,使a无效并重绘ListView,等等。这非常神奇,数据库会更改并且您会ListView自动更新。太棒了 此外,当数据库发生更改时,Android也会为您请求同步,即使您的正常计划之外也是如此,以便使这些更改从设备中删除并尽快同步到服务器。也很棒

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

8.感到快乐!

一旦所有这些xml元数据都就位并启用了同步,Android将知道如何为您连接所有内容,并且同步应该开始工作。在这一点上,很多很好的事情会随处可见,就像魔术一样。请享用!


11
ContentResolver.setSyncAutomatically(account,AUTHORITY,true);
jcwenger 2011年

1
没问题,很高兴能为您提供帮助。再次从样式的角度来看,如果“ AUTHORITY”和“ myaccounttype”是您正在使用的实际字符串(不仅是复制到站点的示例),您肯定需要清理命名约定。这些字符串在所有设备上必须是唯一的,如果其他程序员懒惰地制作一个带有用于授权的匹配字符串的程序包,则您会遇到真正的麻烦。干杯!
jcwenger 2011年

22
即使全局同步设置已关闭,您也可以请求同步。只需将一个ContentResolver.SYNC_EXTRAS_MANUAL设置为true的Extras捆绑包添加,您将强制进行同步:)
mgv11 2011年

2
@kaciula:我什么都不知道,但是该设备将记住它需要同步,并且一旦打开全局同步,它将立即启动。您真的不应该尝试在这一点上胜过用户-尤其是因为“全局同步关闭”是在紧急情况下节省电池的关键方法之一。如果您真的担心数据无法同步,请考虑一个弹出窗口,告诉用户为什么数据一直处于静止状态(如果已经放置了一段时间)。这样,您可以教育意外配置其设备的用户,并提醒高级用户万一他们忘记了。
jcwenger 2012年

2
可能值得一提的是,如果您想使用addPeriodicSync(),它也只能在您也可以使用setSyncAutomatically()时才起作用-我出于绝望地添加了它,试图使某些东西正常工作。我知道这不是原始问题的一部分,但这是一个完整的答案!
android.weasel 2013年

0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.