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

发布于 2024-10-21 03:54:38 字数 1476 浏览 1 评论 0原文

我正在尝试实现 Content-Provider-Sync Adapter 模式,如 Google IO - 幻灯片 26。我的内容提供商正在工作,并且当我从开发工具同步测试器应用程序触发它时,我的同步可以工作,但是当我从以下位置调用 ContentResolver.requestSync(account,authority,bundle) 时我的 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”,并且传递给调用的权限与我的 syc 适配器 xml 匹配。

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

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

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

I am trying to implement the Content-Provider-Sync Adapter pattern as discussed at Google IO - slide 26. My content provider is working, and my sync works when I trigger it from the Dev Tools Sync Tester application, however when I call ContentResolver.requestSync(account, authority, bundle) from my ContentProvider, my sync is never triggered.

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

Edit -- added manifest snippet
My manifest xml contains:

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

--Edit

My syncadapter.xml associated with my sync service contains:

<?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"
/>

Not sure what other code would be useful. The account passed to requestSync is of "myaccounttype" and the AUTHORITY passed to the call matches my syc adapter 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.

If that is the correct way to request a sync then why would the sync tester work, but not my call to ContentResolver.requestSync? Is there something I need to pass in the bundle?

I am testing in the emulator on devices running 2.1 and 2.2.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

百善笑为先 2024-10-28 03:54:38

调用 requestSync() 仅适用于系统已知的 {Account, ContentAuthority} 对。您的应用程序需要执行多个步骤来告诉 Android 您能够使用特定类型的帐户同步特定类型的内容。它在 AndroidManifest.xml 中执行此操作。

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 属性是用于连接同步的班级名称...我稍后会谈到这个问题。

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

意图过滤器可以捕获请求同步的意图。 (当您调用 ContentResolver.requestSync() 或相关调度方法时,此 Intent 来自 ContentResolver。)

; 标签将在下面讨论。

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 (意图),并且在调用时必须返回 SyncAdapterBinder...您需要一个 AbstractThreadedSyncAdapter 类型的变量。正如您所看到的,这几乎就是该课程中的所有内容。它存在的唯一原因是提供一个服务,该服务为 Android 提供一个标准接口来查询您的类,了解您的 SyncAdapter 本身是什么。

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

mySyncAdapter是真正的同步逻辑本身的存储位置。当需要同步时,它的 onPerformSync() 方法会被调用。我想你已经准备好了。

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

再次回顾 AndroidManifest,我们服务中那个奇怪的 标记是在帐户类型和内容权限之间建立绑定的关键部分一个 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 我们已经定义了同步适配器(在 标记的 name 元素中调用的类,其中包含 引用此文件的标签...)将使用 com.google 风格的帐户同步联系人。

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

accountType 也必须与已知的其中之一匹配已输入的帐户类型,或者它必须与您正在创建的帐户类型相匹配(这涉及创建 AccountAuthenticator 的子类以在您的服务器上获得身份验证...本身就值得一篇文章。)同样,“com.google”是定义的字符串标识... google.com 风格的帐户凭据(同样,这不应该是一个惊喜。)

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

最后,必须启用同步。您可以在“帐户和帐户”中执行此操作。通过转到您的应用程序并在匹配帐户中设置应用程序旁边的复选框来同步控制面板中的页面。或者,您可以在应用程序中的某些设置代码中执行此操作:

ContentResolver.setSyncAutomatically(account, AUTHORITY, true);

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

如果您的帐户/权限同步或全局同步被禁用,则调用 RequestSync() 确实会产生影响 - 它设置一个已请求同步的标志,并且在启用同步后立即执行。

另外,根据 mgv,在 requestSync 的额外包中将 ContentResolver.SYNC_EXTRAS_MANUAL 设置为 true 会询问 android即使全局同步关闭也可以强制同步(在这里尊重您的用户!)

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

6.考虑多个帐户的影响

可以拥有多个相同类型的帐户(在一台设备上设置两个 @gmail.com 帐户或两个 Facebook 帐户或两个 Twitter 帐户等...)应该考虑这样做的应用程序影响...如果您有两个帐户,您可能不想尝试将它们同步到相同的数据库表中。也许您需要指定一次只能有一个处于活动状态,并在切换帐户时刷新表并重新同步。 (通过查询存在哪些帐户的属性页)。也许您为每个帐户创建不同的数据库,也许不同的表,也许每个表中的关键列。所有的应用都是特定的,值得思考。 ContentResolver.setIsSyncable(Account account, Stringauthority, intsyncable) 这里可能会感兴趣。 setSyncAutomatically() 控制帐户/权限对是选中还是未选中,而 setIsSyncable() 提供了一种方法取消选中并使该行变灰,以便用户无法打开它。您可以将一个帐户设置为可同步,而将另一个帐户设置为不可同步 (dsabled)。

7.请注意 ContentResolver.notifyChange()

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

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

8.高兴吧!

一旦您准备好所有这些 xml 元数据并启用同步,Android 将知道如何为您连接所有内容,并且同步应该开始工作。此时,很多美好的事情就会水到渠成,感觉就像魔术一样。享受!

Calling requestSync() will only work on an {Account, ContentAuthority} pair that is known to the system. Your app needs to go through a number of steps to tell Android that you are capable of synchronizing a specific kind of content using a specific kind of account. It does this in the AndroidManifest.

1. Notify Android that your application package provides syncing

First off, in AndroidManifest.xml, you have to declare that you have a Sync Service:

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

The name attribute of the <service> tag is the name of your class to connect up sync... I'll talk to that in a second.

Setting exported true makes it visible to other components (needed so ContentResolver can call it).

The intent filter lets it catch an intent requesting sync. (This Intent comes from ContentResolver when you call ContentResolver.requestSync() or related scheduling methods.)

The <meta-data> tag will be discussed below.

2. Provide Android a service used to find your SyncAdapter

So the class itself... Here's an example:

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();
    }
}

Your class must extend Service or one of its subclasses, must implement public IBinder onBind(Intent), and must return a SyncAdapterBinder when that's called... You need a variable of type AbstractThreadedSyncAdapter. So as you can see, that's pretty much everything in that class. The only reason it's there is to provide a Service, that offers a standard interface for Android to query your class as to what your SyncAdapter itself is.

3. Provide a class SyncAdapter to actually perform the sync.

mySyncAdapter is where the real sync logic itself is stored. Its onPerformSync() method gets called when it's time to sync. I figure you already have this in place.

4. Establish a binding between an Account-type and a Content Authority

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" /> 

Okay, so what does this do? It tells Android that the sync adapter we've defined (the class that was called out in the name element of the <service> tag that includes the <meta-data> tag that references this file...) will sync contacts using a com.google style account.

All your contentAuthority strings have to all match, and match with what you're syncing -- This should be a string you define, if you're creating your own database, or you should use some existing device strings if you're syncing known data types (like contacts or calendar events or what have you.) The above ("com.android.contacts") happens to be the ContentAuthority string for contacts type data (surprise, surprise.)

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. Enable Sync on a given Account / ContentAuthority pair

Finally, sync has to be enabled. You can do this in the Accounts & Sync page in the control panel by going to your app and setting the checkbox next to your app within the matching account. Alternately, you can do it in some setup code in your app:

ContentResolver.setSyncAutomatically(account, AUTHORITY, true);

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

If your account/authority sync or the global sync are disabled, calling RequestSync() does have an effect -- It sets a flag that sync has been requested, and will be performed as soon as sync is enabled.

Also, per mgv, setting ContentResolver.SYNC_EXTRAS_MANUAL to true in the extras bundle of your requestSync will ask android to force a sync even if global sync is off (be respectful of your user here!)

Finally, you can setup a periodic scheduled sync, again with ContentResolver functions.

6. Consider implications of multiple accounts

It is possible to have more than one account of the same type (two @gmail.com accounts set up on one device or two facebook accounts, or two twitter accounts, etc...) You should consider the application implications of doing that... If you have two accounts, you probably don't want to try to sync both of them into the same database tables. Maybe you need to specify that only one can be active at a time, and flush the tables and resync if you switch accounts. (through a property page that queries what accounts are present). Maybe you create a different database for each account, maybe different tables, maybe a key column in each table. All application specific and worthy of some thought. ContentResolver.setIsSyncable(Account account, String authority, int syncable) might be of interest here. setSyncAutomatically() controls whether an account/authority pair is checked or unchecked, whereas setIsSyncable() provides a way to uncheck and grey out the line so the user can't turn it on. You might set one account Syncable and the other not Syncable (dsabled).

7. Be aware of ContentResolver.notifyChange()

One tricky thing. ContentResolver.notifyChange() is a function used by ContentProviders to notify Android that the local database has been changed. This serves two functions, first, it will cause cursors following that content uri to update, and in turn requery and invalidate and redraw a ListView, etc... It's very magical, the database changes and your ListView just updates automatically. Awesome. Also, when the database changes, Android will request Sync for you, even outside your normal schedule, so that those changes get taken off the device and synced to the server as rapidly as possible. Also awesome.

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!

Once you have all this xml metadata in place, and sync enabled, Android will know how to connect everything up for you, and sync should start working. At this point, a lot of things that are nice will just click into place and it will feel a lot like magic. Enjoy!

唠甜嗑 2024-10-28 03:54:38

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

I was caling setIsSyncable after AccountManager setAuthToken method. But setAuthToken returned function before setIsSyncable was reached. After order changes everything worked fine!

红衣飘飘貌似仙 2024-10-28 03:54:38

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

I noticed the that the requestSync will trigger the sync implementation when there is internet connection

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文