为什么 ContentResolver.requestSync 不触发同步? [英] Why does ContentResolver.requestSync not trigger a sync?

查看:31
本文介绍了为什么 ContentResolver.requestSync 不触发同步?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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 -- 添加清单片段我的清单 xml 包含:

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>

--编辑

与我的同步服务关联的 syncadapter.xml 包含:

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

不确定还有哪些其他代码会有用.传递给 requestSync 的帐户是myaccounttype",传递给调用的 AUTHORITY 与我的 syc 适配器 xml 匹配.

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.

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

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 的调用却不起作用?有什么我需要在包中传递的吗?

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?

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

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

推荐答案

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

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.通知 Android 您的应用程序包提供同步

首先,在 AndroidManifest.xml 中,你必须声明你有一个同步服务:

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>

<service> 标签的 name 属性是您要连接同步的类的名称...我稍后会谈到.

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.

设置导出的 true 使其对其他组件可见(需要这样 ContentResolver 才能调用它).

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

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

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

标签将在下面讨论.

2.为 Android 提供用于查找您的 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();
    }
}

您的类必须扩展 Service 或其子类之一,必须实现 public IBinder onBind(Intent),并且必须在以下情况下返回 SyncAdapterBinder这就是所谓的...您需要一个 AbstractThreadedSyncAdapter 类型的变量.如您所见,这几乎是该类中的所有内容.它存在的唯一原因是提供一个服务,该服务为 Android 提供了一个标准接口来查询您的类,了解您的 SyncAdapter 本身是什么.

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.提供一个 class SyncAdapter 来实际执行同步.

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

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

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.在 Account-type 和 Content Authority 之间建立绑定

再次回顾 AndroidManifest,我们服务中那个奇怪的 标签是建立 ContentAuthority 和帐户之间绑定的关键部分.它在外部引用了另一个 xml 文件(您可以随意称呼它,与您的应用程序相关的名称.)让我们看一下 sync_myapp.xml:

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 我们定义的同步适配器(在 标签的 name 元素中调用的类,该标签包含 code> 标记引用此文件...) 将使用 com.google 样式帐户同步联系人.

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.

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

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

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.在给定的帐户/ContentAuthority 对上启用同步

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

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.

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

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.

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

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

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

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

6.考虑多个帐户的影响

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

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.注意 ContentResolver.notifyChange()

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

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.

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

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.感到快乐!

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

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!

这篇关于为什么 ContentResolver.requestSync 不触发同步?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆