ContentResolver.requestSyncが同期をトリガーしないのはなぜですか?


112

Google IO-スライド26 で説明されているように、Content-Provider-Syncアダプターパターンを実装しようとしています。コンテンツプロバイダーは機能しており、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は同期をリクエストする正しい方法ですか?同期テスターツールはサービスに直接バインドしてstart syncを呼び出すように見えますが、同期アーキテクチャとの統合の目的に反するようです。

それが同期を要求する正しい方法である場合、なぜ同期テスターは機能しますが、ContentResolver.requestSyncへの私の呼び出しは機能しませんか?バンドルで渡す必要があるものはありますか?

2.1と2.2を実行しているデバイスのエミュレータでテストしています。


3
私の問題は、同期アダプターのブレークポイントに到達していないことでした...それから私はサービスをデバッグしようとしていることに気付きました...これが私のような他の人を助けることを願っています。
dangalg、2015年

2
同期アダプターサービスのブレークポイントは、トリガーに失敗します。これは、同期アダプターサービスが別のプロセスで実行されるためです。これは@danglangが示唆していたことです。また、この質問を参照してください:stackoverflow.com/questions/8559458/...
コンスタンチンシューベルト

1
私の場合android:process=":sync"、同期サービスから削除して、デバッガーがくちばしポイントをヒットするようにします。onPerformSync別のプロセスに代わってメソッドからのログメッセージを確認できたため、同期サービス自体はそれ以前に機能していました。
セルゲイ

別の原因はWiFiがオフになっていることです。そのため、モックデータと同期しようとしている場合は、接続を確認してください。
Allan Veloso

回答:


280

呼び出す requestSync()は、システムに認識されている{アカウント、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呼び出すことができます)。

インテントフィルターを使用すると、同期をリクエストするインテントをキャッチできます。(このIntentContentResolverあなたが電話したときから来ます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またはそのサブクラスの1つを拡張して実装しpublic IBinder onBind(Intent)SyncAdapterBinderそれが呼び出されたときにを返す必要があります...タイプの変数が必要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>このファイルを参照)がcom.googleスタイルのアカウントを使用して連絡先を同期する通知します。

すべてのcontentAuthority文字列はすべて一致し、同期しているものと一致する必要があります-これは、独自のデータベースを作成している場合は定義した文字列であるか、既知の同期している場合はいくつかの既存のデバイス文字列を使用する必要がありますデータタイプ(連絡先やカレンダーのイベントなど)。上記(「com.android.contacts」)は、たまたま連絡先タイプのデータのContentAuthority文字列です(驚き、驚き)。

accountTypeは、すでに入力されている既知のアカウントタイプのいずれかと一致する必要があります。または、作成中のアカウントタイプと一致する必要があります(これには、サーバーで認証を取得するためのAccountAuthenticatorのサブクラスの作成が含まれます...記事自体に価値があります)。繰り返しになりますが、「com.google」は、定義された文字列を識別する... google.comスタイルのアカウント認証情報です(これも驚きではありません)。

5.指定されたアカウントとContentAuthorityのペアで同期を有効にします

最後に、同期を有効にする必要があります。これを行うには、コントロールパネルの[アカウントと同期]ページで、アプリに移動し、一致するアカウント内のアプリの横にあるチェックボックスをオンにします。または、アプリのいくつかのセットアップコードで行うこともできます。

ContentResolver.setSyncAutomatically(account, AUTHORITY, true);

同期が発生するために、アカウント/権限のペアは同期に有効にする必要があります(上記のような)、システムの全体的なグローバル同期フラグがセットでなければならない、デバイスがネットワーク接続を持っている必要があります。

アカウント/権限の同期またはグローバル同期が無効になっている場合、RequestSync()を呼び出すと効果があります-同期が要求されたことを示すフラグが設定され、同期が有効になるとすぐに実行されます。

また、mgvあたり、設定ContentResolver.SYNC_EXTRAS_MANUALに、requestSyncのextrasバンドルでtrueにすると、グローバル同期がオフの場合でもAndroidに同期を強制するように要求します(ここでユーザーに注意してください!)

最後に、ContentResolver関数を使用して、定期的にスケジュールされた同期をセットアップできます。

6.複数のアカウントの影響を考慮する

同じタイプのアカウントを複数持つことができます(1つのデバイスに2つの@ gmail.comアカウントが設定されているか、2つのFacebookアカウント、または2つのTwitterアカウントなど)。アプリケーションの影響を考慮する必要があります。 .. 2つのアカウントがある場合、両方を同じデータベーステーブルに同期しようとは思わないでしょう。一度にアクティブにできるのは1つだけであることを指定し、アカウントを切り替えた場合はテーブルをフラッシュして再同期する必要があるかもしれません。(存在するアカウントを照会するプロパティページを介して)。たぶん、アカウントごとに異なるデータベースを作成したり、異なるテーブルを作成したり、各テーブルのキー列を作成したりできます。すべてのアプリケーション固有で、いくつかの検討に値します。 ContentResolver.setIsSyncable(Account account, String authority, int syncable)ここで興味深いかもしれません。 setSyncAutomatically()アカウント/権限ペアがかどうかを制御しますがチェックますuncheckedsetIsSyncable()ユーザーがオンにできないようにラインのチェックを外して灰色にする方法を提供します。1つのアカウントを同期可能に設定し、もう1つを同期不可能(dsabled)に設定する場合があります。

7. ContentResolver.notifyChange()に注意してください

1つのトリッキーなこと。ローカルデータベースが変更されたことをAndroidに通知するためにsがContentResolver.notifyChange()使用する関数ContentProviderです。これは2つの機能を提供します。最初に、そのコンテンツURIに続くカーソルを更新し、次にを再クエリして無効化して再描画しListViewます。...データベースの変更とListViewです。自動的に更新されます。驚くばかり。また、データベースが変更されると、Androidは通常のスケジュール外であっても同期を要求します。これにより、これらの変更がデバイスから取得され、サーバーにできるだけ迅速に同期されます。また素晴らしい。

ただし、エッジケースが1つあります。サーバーからプルしてアップデートをにプッシュするとContentProvider、それが忠実に呼び出されnotifyChange()、Androidは「ああ、データベースの変更、サーバーに配置するほうがいい!」(ドー!)よく書かれているContentProvidersている場合、変更がネットワークからのものかユーザーからのものかを確認するためのテストがいくつかあり、ブール値syncToNetworkフラグをfalseに設定して、無駄な二重同期を防止します。データをにフィードする場合ContentProvider、これを機能させる方法を理解する必要があります。そうしないと、1つだけ必要なときに常に2つの同期を実行することになります。

8.幸せを感じてください。

このすべてのxmlメタデータを配置し、同期を有効にすると、Androidがすべてを接続する方法を認識し、同期が機能するようになります。この時点で、多くの優れた点がクリックされるだけで、まるで魔法のように感じられます。楽しい!


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

1
問題ありません。お手伝いできてうれしいです。ただし、スタイルの観点から見ると、 "AUTHORITY"と "myaccounttype"が実際に使用している文字列である場合(そして、サイトへのコピー貼り付けのサンプルだけではない場合)、命名規則を明確にする必要があります。これらの文字列はすべてのデバイスで一意である必要があり、他のプログラマーが権限の一致する文字列を使用してパッケージを怠惰に作成し、競合が発生した場合、実際に問題が発生します。乾杯!
jcwenger、2011年

22
グローバル同期設定がオフになっている場合でも、同期をリクエストできます。ContentResolver.SYNC_EXTRAS_MANUALエクストラバンドルにセットをtrueに追加するだけで、強制的に同期されます:)
mgv

2
@kaciula:わかりませんが、デバイスは同期する必要があることを覚えており、グローバル同期がオンになるとすぐにデバイスが起動します。あなたは本当にこれでユーザーを打ち負かそうとするべきではありません-特に「グローバル同期オフ」は重要な状況でバッテリーを節約するための重要な方法の一つなので。データが同期されないことが本当に心配な場合は、しばらくの間データが移動していない理由をユーザーに知らせるポップアップを検討してください。そうすることで、誤ってデバイスを誤って設定したユーザーを教育し、忘れた場合に備えてパワーユーザーに思い出させることができます。
jcwenger、2012年

2
addPeriodicSync()を使用したい場合は、setSyncAutomatically()も機能している場合にのみ機能するように見えるので、追加する価値があるかもしれません。元の質問の一部ではなかったのはわかっていますが、これは完全な答えです。
android.weasel

0

私はAccountManagerメソッドのsetIsSyncable後に落ち着いていたsetAuthToken。しかし、setAuthToken戻る前の関数にsetIsSyncable到達しました。注文変更後、すべてうまくいきました!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.