1. ホーム
  2. android

[解決済み] なぜContentResolver.requestSyncは同期をトリガーしないのですか?

2022-09-14 23:20:20

質問

で説明されている Content-Provider-Sync Adapter パターンを実装しようとしています。 Google IO - スライド26で説明されているように、Content-Provider-Sync Adapterパターンを実装しようとしています。しかし、ContentProvider から ContentResolver.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>

--編集

私のsyncサービスに関連する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 は、同期を要求する正しい方法でしょうか。 sync tester ツールがサービスに直接バインドして start sync を呼び出すように見えますが、これでは同期アーキテクチャと統合する目的が達成されないように思われます。

これが同期を要求する正しい方法であるなら、なぜ sync tester は機能するのに ContentResolver.requestSync への私の呼び出しは機能しないのでしょうか。 バンドルで渡す必要があるものがあるのでしょうか?

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

どのように解決するのですか?

呼び出し requestSync() を呼び出すと、システムに知られている {Account, ContentAuthority} のペアでのみ機能します。 アプリは、特定の種類のアカウントを使用して特定の種類のコンテンツを同期することが可能であることを Android に伝えるために、多くの手順を実行する必要があります。 これは、AndroidManifest で行います。

1. アプリケーションパッケージが同期を提供することをAndroidに通知する

まず、AndroidManifest.xmlで、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>

のname属性は <service> タグの name 属性は sync を接続するためのクラス名です。 それについては後で説明します。

exportedをtrueにすると、他のコンポーネントから見えるようになります(必要なので ContentResolver がそれを呼び出せるようにするために必要です)。

インテント・フィルタにより、同期を要求するインテントを捕捉することができます。(この Intent から来ています。 ContentResolver を呼び出すと ContentResolver.requestSync() または関連するスケジューリング・メソッドを呼び出したとき)。

<meta-data> タグについては後述します。

2. SyncAdapterを見つけるためのサービスをAndroidに提供します。

では、クラスそのものは... 以下はその例です。

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. アカウントタイプとコンテンツオーソリティの間のバインドを確立する

AndroidManifest をもう一度見てみると、その奇妙な <meta-data> タグは、ContentAuthority とアカウント間のバインディングを確立する重要な部分です。 これは、別の 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" /> 

さて、これは何をするのでしょうか? 定義した同期アダプタ(の name 要素で呼び出されたクラス)を Android に伝えます。 <service> タグに含まれる <meta-data> タグが含まれている場合、com.google スタイルのアカウントを使用して連絡先が同期されます。

すべての ContentAuthority 文字列はすべて一致し、同期するものと一致しなければなりません。独自のデータベースを作成している場合は、これは自分で定義した文字列でなければならず、既知のデータ型(連絡先やカレンダー イベントなど)を同期する場合は、既存のデバイス文字列を使用します。上記の ("com.android.contacts") は、連絡先タイプのデータ用の ContentAuthority 文字列になります(驚きです)。

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

5. 与えられた Account / ContentAuthority のペアで同期を有効にする。

最後に、同期を有効にする必要があります。 コントロール パネルの Accounts & Sync ページで、アプリに移動して、一致するアカウント内のアプリの横にあるチェックボックスを設定することで、これを行うことができます。 代わりに、アプリの設定コードで行うこともできます。

ContentResolver.setSyncAutomatically(account, AUTHORITY, true);

同期を行うには、アカウントと権限のペアが同期可能である必要があります(上記のように) のように、システム上の全体的なグローバル同期フラグが設定されていなければなりません。 デバイスがネットワークに接続されている必要があります。

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

また mgv を設定すると ContentResolver.SYNC_EXTRAS_MANUAL を true に設定すると、グローバルシンクがオフの場合でも強制的に同期するようにアンドロイドに要求します (ここではユーザーを尊重しましょう!)。

最後に、ContentResolver関数を使用して、定期的なスケジュール同期を設定することができます。

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

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

7. ContentResolver.notifyChange()を意識してください。

一つ厄介なことがあります。 ContentResolver.notifyChange() が使用する関数は ContentProvider が使用する関数で、ローカルデータベースが変更されたことを Android に通知します。 これは 2 つの機能を提供します。まず、そのコンテンツ URI に続くカーソルを更新させ、次に再クエリして無効化し、再描画するために ListView などなど...。 これは非常に不思議なことで、データベースが変更されると、あなたの ListView は自動的に更新されるのです。 すごい。 また、データベースが変更されると、通常のスケジュール以外でも、Androidがあなたに代わって同期を要求し、その変更をデバイスから取り出してできるだけ早くサーバーに同期します。 これも素晴らしいことです。

しかし、1つだけエッジケースがあります。 サーバーからプルして、アップデートを ContentProvider を呼び出すと、それは律儀に notifyChange() を呼び出すと、アンドロイドは、「あ、データベースの変更だ、サーバーに置いたほうがいい!」となります(ドヤァ)。 ContentProviders は、変更がネットワークから来たのか、それともユーザーから来たのかを確認するためのテストをいくつか行い、ブール値の syncToNetwork フラグを false に設定し、この無駄な二重同期を防ぎます。 もしあなたがデータを ContentProvider にデータを供給しているのであれば、これを動作させる方法を見つけ出すのが得策です。そうしないと、1 回だけ必要なときに常に 2 回の同期を実行することになってしまうからです。

8. 幸せを感じろ!

すべての xml メタデータを配置し、同期を有効にすると、Android はすべてを接続する方法を認識し、同期が機能し始めます。 この時点で、多くの素晴らしいものが適所に配置され、まるで魔法のように感じられることでしょう。 どうぞお楽しみください。