[解決済み] なぜContentResolver.requestSyncは同期をトリガーしないのですか?
質問
で説明されている 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" />
さて、これは何をするのでしょうか? 定義した同期アダプタ(<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 はすべてを接続する方法を認識し、同期が機能し始めます。 この時点で、多くの素晴らしいものが適所に配置され、まるで魔法のように感じられることでしょう。 どうぞお楽しみください。
関連
-
[解決済み] BottomSheetDialogFragmentの状態をexpandedに設定する。
-
[解決済み] ViewPagerのアイテムを強制的に再インスタンス化する方法 [重複].
-
[解決済み] Android Studio - あいまいなメソッド呼び出し getClass()
-
[解決済み] バイト配列の画像ファイルをビットマップに変換するには?
-
[解決済み] これはどういうことですか?失敗 [INSTALL_FAILED_CONTAINER_ERROR]?
-
[解決済み] XMLで矩形を描画できますか?
-
[解決済み] アンドロイドのクライアントでヒープアップデートを有効にする方法
-
[解決済み] PendingIntentの "requestCode "は何に使うのですか?
-
[解決済み] LayoutParamsの高さを密度に依存しないピクセル数でプログラム的に設定する。
-
[解決済み] 文字列リソースにHTML?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] AndroidにおけるViewPager2の適切な実装
-
[解決済み] Android Navigation Architecture Component - 現在表示されているフラグメントを取得する
-
[解決済み] データベースでリサイクルビューを使用する
-
[解決済み] Android: ランドスケープモード用の代替レイアウト xml
-
[解決済み] APKが署名済みかデバッグビルドかを確認するには?
-
[解決済み] Gradleでビルドタイプを使用し、ContentProviderを使用する同じアプリを1つのデバイスで実行する。
-
[解決済み] 非推奨のandroid.support.v4.app.ActionBarDrawerToggleの置き換え方法
-
[解決済み] FABアイコンの色を設定する
-
[解決済み] google-services.jsonって実際何してるの?
-
[解決済み] ViewPager2でスワイプを無効にするには?