2014/09/21

Android:ImageryHeaderFragment

Intro

Android Developers Blogで紹介されているioschedアプリの機能を一部実装する.
Blog: http://android-developers.blogspot.jp/2014/08/material-design-in-2014-google-io-app.html

実現したい機能のイメージは次の通り. スクロールに追従し, 一定のY座標に到達すると画面上部との隙間を埋める(GapFill)ヘッダバーを実装する.
Imagery Header

First Releaseのデザインではスクロール量によってバナーエリアのα値が変化する. Android Developers Blogの著者はこれについて次のコメントを残している.

Our concern was that this design bent the physics of material design too far. It’s as if the text was sliding along a piece of paper whose transparency changed throughout the animation.

Material Designの重要なファクタである paper と ink の観点から, バナーエリアに描画されたテキストを残す形で透過アニメーションされる点が, Material Designの物理学から外れていると感じたようだ.
これを改善したのがUpdate version. Material Designのpaper と inkの物理学をより推進した形だ. 画像ではわかり辛いが, ヘッダバーエリアの下にコンテンツ詳細が潜り込むため, Z軸の存在を考慮してshadow効果も適用されている.

レイアウト構造は次のようになっている.
Layout structore

実装を始める前に, 今回参考とさせて頂いたサイト, およびリポジトリを紹介する.

また, 今回作成したコードは次のリポジトリで公開している.

ImageryHeaderFragment

Material Designではページのヘッダや背景等にイメージを配置し, コンテキストを表現することを推奨している. 今回作成するImageryHeaderFragmentはこれに倣って画像を配置できる機能を継承する(画像を必ず表示する必要はない). FABについては本コンポーネントの責務から外れるため実装されない. また, “L Preview”のAPIは本Fragmentでは使用しない.

Step by step

実装の手順を紹介する.

  1. Theme, Colorリソースを準備
  2. ImageryHeaderFragmentに必要なView拡張クラスを実装
  3. Layoutリソースを準備
  4. ImageryHeaderFragmentを実装

Step 1: Theme

ImageryHeaderFragment, ActionBarとActivityのためのThemeリソースを定義する.

  • /res/values/colors.xml
<resources>
    <color name="window_background">#eeeeee</color>

    <!-- API v21移行はandroid:colorPrimaryの値に使用 -->
    <color name="colorPrimary">#a00</color>
</resources>
  • /res/values/styles.xml
<!-- ImageryHeader Theme -->
<style name="Theme.ImageryHeader" parent="android:Theme.Holo.Light.DarkActionBar">
    <item name="android:actionBarStyle">@style/Theme.ImageryHeader.ActionBar</item>
    <item name="android:windowBackground">@color/window_background</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:windowActionBarOverlay">true</item>
</style>

<!-- ImageryHeader Theme Action Bar -->
<style name="Theme.ImageryHeader.ActionBar" parent="android:Widget.Holo.ActionBar">
    <item name="android:displayOptions">showHome</item>
    <item name="android:background">@null</item>
</style>
  • /AndroidManifest.xml
<activity
        android:name=".MainActivity"
        android:theme="@style/Theme.ImageryHeader">

ActionBarはApp iconとAction itemのために使用するため消すことはしない. かわりに背景を透過し, 不要な要素(shadowやApp title)を非表示にしておく.

Step 2: Custom View

ObservableScrollView

ImageryHeaderFragmentでは, 各Viewのポジションをスクロール量によって動的に変化させる. 実現するにはScrollViewのスクロールイベントのリスナーが必要であるが, Android純正のScrollViewにはコールバックリスナを受け付ける仕組みがない. これを実現するためにScrollViewを拡張したObservableScrollViewを実装する.

public class ObservableScrollView extends ScrollView {
    private ArrayList<Callbacks> mCallbacks = new ArrayList<Callbacks>();

    public ObservableScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        for (Callbacks c : mCallbacks) {
            c.onScrollChanged(l - oldl, t - oldt);
        }
    }

    @Override
    public int computeVerticalScrollRange() {
        return super.computeVerticalScrollRange();
    }

    public void addCallbacks(Callbacks listener) {
        if (!mCallbacks.contains(listener)) {
            mCallbacks.add(listener);
        }
    }

    public static interface Callbacks {
        public void onScrollChanged(int deltaX, int deltaY);
    }
}

Original source code:
GitHub - ObservableScrollView

Step 3: Layout

リソースとカスタムViewの準備ができたらlayoutリソースを作成する. 画面構成は大きく3つの要素から成る.

  • Header Image
  • Body
  • Header Bar

3つの要素はレイアウト上, (FragmentLayoutによって)重なるように定義される. 実際には画面のスクロールポジションに従って各ViewのY座標を動的に変えていく.
Header Barの定義位置を最後にしてあるのはz-indexを手前にするためである.

  • /res/layout/fragment_imageryheader.xml
<yuki312.android.imageryheaderfragment.ObservableScrollView
        android:id="@+id/scroll_view"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true"
        android:overScrollMode="never">

    <FrameLayout
            android:id="@+id/scroll_view_child"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:clipChildren="false">

        <!-- Header Imagery -->
        <FrameLayout
                android:id="@+id/header_image_container"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

            <ImageView
                    android:id="@+id/header_image"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:scaleType="centerCrop"/>
        </FrameLayout>

        <!-- Body -->
        <LinearLayout
                android:id="@+id/body_container"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@color/window_background"
                android:clipToPadding="false"
                android:orientation="vertical">

            <!-- TODO: Dummy View for scroll. remove this view.-->
            <TextView
                    android:layout_width="match_parent"
                    android:layout_height="777dp"
                    android:text="I`m Dummy"
                    />
        </LinearLayout>

        <!-- Header bar -->
        <FrameLayout
                android:id="@+id/header_bar_container"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:clipChildren="false"
                android:clipToPadding="false">

            <!-- background -->
            <!-- height assigned dynamically, and fill the ActionBar gaps. -->
            <View
                    android:id="@+id/header_bar_background"
                    android:layout_width="match_parent"
                    android:layout_height="0dp"
                    android:background="@color/colorPrimary"/>

            <!-- contents -->
            <LinearLayout
                    android:id="@+id/header_bar_contents"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical"
                    android:paddingBottom="16dp"
                    android:paddingTop="16dp">

                <TextView
                        android:id="@+id/header_bar_title"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="Placeholder"/>

            </LinearLayout>

            <!-- shadow -->
            <View
                    android:id="@+id/header_bar_shadow"
                    android:layout_width="match_parent"
                    android:layout_height="6dp"
                    android:layout_gravity="bottom"
                    android:layout_marginBottom="-6dp"
                    android:background="@drawable/bottom_shadow"/>

        </FrameLayout>
    </FrameLayout>
</yuki312.android.imageryheaderfragment.ObservableScrollView>

@id/header_image_container

ヘッダイメージ画像のコンテナ. ioschedアプリではここにscrim効果を付与するアトリビュートも指定している. scrim効果を実装したい場合は次を追加し, scrim画像を用意する.

<FrameLayout
        android:id="@+id/header_image_container"
        ...
        android:foreground="@drawable/photo_banner_scrim">
  • /res/drawable/photo_banner_scrim
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <gradient
            android:angle="270"
            android:centerColor="#0000"
            android:centerY="0.3"
            android:endColor="#0000"
            android:startColor="#3000"/>
</shape>

Original source code:
GitHub - photo_banner_scrim.xml

@id/header_bar_background

今回のメインViewとなるHeaderBar. 一定量スクロールされるとGapFillアニメーションする. このViewはActionBarが担っていたブランディングカラーのためにcolorPrimaryを指定する.

@id/header_bar_shadow

一定量スクロールされるとHeaderBarに影を落とす効果を付与する. これはMaterial Designの物理学に従いBodyよりもHeaderBarのpaper要素が手前(Z depth)にあることを表現している.

android:clipChildren

このアトリビュートは, 子Viewの描画領域が自領域外であっても実行するものである. これはヘッダ
バーの画面上部へのGapFillアニメーションやshadow効果の描画に必要になる. このアトリビュートを使えば親Viewのサイズに影響することなくshadow等の効果が実現できる.
これはGoogleI/O 2014でも紹介(15:00~)されている(http://youtu.be/lSH9aKXjgt8). ただしhorrible hackの類いである点に留意する.
Android Lから導入されるlight sourceとshadowのサポートはこれらの問題を解決(構築されたView階層に対してZ軸を考慮した物理的にも正しいshadow効果を描画)する.

Fragment

準備は整った. 各ViewはFramelayoutの効果で全て0座標に位置している. 今の状態でアプリ実行すると次のレイアウトになる.

Layout define only

ここからはImageryHeaderFragmentのソースコードで各Viewの配置を調整していく.

public class ImageryHeaderFragment extends Fragment
        implements ObservableScrollView.Callbacks {
    ...
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        ... findViewByID ...
        setupCustomScrolling(rootView);
        return rootView;
    }

    private void setupCustomScrolling(View rootView) {
        scrollView = (ObservableScrollView) rootView.findViewById(R.id.scroll_view);
        scrollView.addCallbacks(this);
        ...
    }

まずはスクロールイベントを検知するために, ObservableScrollView.Callbacksをimplementsする. Fragment.onCreateView()でObservableScrollViewへコールバック登録.

    private ViewTreeObserver.OnGlobalLayoutListener globalLayoutListener
            = new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            recomputeHeaderImageAndScrollingMetrics();
        }
    };

    private void setupCustomScrolling(View rootView) {
        ...
        ViewTreeObserver vto = scrollView.getViewTreeObserver();
        if (vto.isAlive()) {
            vto.addOnGlobalLayoutListener(globalLayoutListener);
        }
    }

    private void recomputeHeaderImageAndScrollingMetrics() {
        final int actionBarSize = calculateActionBarSize();
        headerBarTopClearance = actionBarSize - headerBarContents.getPaddingTop();
        headerBarContentsHeightPixels = headerBarContents.getHeight();

        headerImageHeightPixels = headerBarTopClearance;
        if (showHeaderImage) {
            headerImageHeightPixels =
                    (int) (headerImage.getWidth() / HEADER_IMAGE_ASPECT_RATIO);
            headerImageHeightPixels = Math.min(headerImageHeightPixels,
                    rootView.getHeight() * 2 / 3);
        }

        ViewGroup.LayoutParams lp;
        lp = headerImageContainer.getLayoutParams();
        if (lp.height != headerImageHeightPixels) {
            lp.height = headerImageHeightPixels;
            headerImageContainer.setLayoutParams(lp);
        }

        lp = headerBarBackground.getLayoutParams();
        if (lp.height != headerBarContentsHeightPixels) {
            lp.height = headerBarContentsHeightPixels;
            headerBarBackground.setLayoutParams(lp);
        }

        ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)
                bodyContainer.getLayoutParams();
        if (mlp.topMargin
                 != headerBarContentsHeightPixels + headerImageHeightPixels) {
            mlp.topMargin = headerBarContentsHeightPixels + headerImageHeightPixels;
            bodyContainer.setLayoutParams(mlp);
        }

        onScrollChanged(0, 0); // trigger scroll handling
    }

ObservableScrollViewからViewTreeObserverを取得してGlobalLayoutListenerを登録. ObservableScrollViewのレイアウト構築完了を待つ. レイアウト構築が完了したらheaderImageContainer, bodyContainer, headerBarBackgroundのレイアウトを調整する.

headerBarTopClearance
recomputeHeaderImageAndScrollingMetrics()では, headerBarTopClearance値を設定する. この値はHeaderBarと画面上部まで(あるいはActionBar部の)隙間間隔を表す.
コード上ではActionBarの高さからheaderBarContents.getPaddingTop()つまりHeaderBar領域の上部余白を除いた値を設定することで, HeaderBarのコンテンツ(“Placeholder”)部とActionBarの隙間を小さくしている.

次にImageryHeaderFragmentのコアとなる処理を見る.

    // GapFillアニメ開始位置の調整. 開始位置に"遊び"を持たせる.
    private static final float GAP_FILL_DISTANCE_MULTIPLIER = 1.5f;

    // ヘッダ画像スクロール時のパララックスエフェクト係数
    private static final float HEADER_IMAGE_BACKGROUND_PARALLAX_EFFECT_MULTIPLIER
            = 0.5f;

    @Override
    public void onScrollChanged(int deltaX, int deltaY) {
        final Activity activity = getActivity();
        if (activity == null || activity.isFinishing()) {
            return;
        }

        // Reposition the header bar -- it's normally anchored to the
        // top of the content, but locks to the top of the screen on scroll
        int scrollY = scrollView.getScrollY();

        float newTop = Math.max(headerImageHeightPixels,
                scrollY + headerBarTopClearance);
        headerBarContainer.setTranslationY(newTop);
        headerBarBackground.setPivotY(headerBarContentsHeightPixels);

        int gapFillDistance = 
                (int) (headerBarTopClearance * GAP_FILL_DISTANCE_MULTIPLIER);
        boolean showGapFill = !showHeaderImage || 
                (scrollY > (headerImageHeightPixels - gapFillDistance));
        float desiredHeaderScaleY = showGapFill ?
                ((headerBarContentsHeightPixels + gapFillDistance + 1) * 1f 
                        / headerBarContentsHeightPixels)
                : 1f;
        if (!showHeaderImage) {
            headerBarBackground.setScaleY(desiredHeaderScaleY);
        } else if (gapFillShown != showGapFill) {
            headerBarBackground.animate()
                    .scaleY(desiredHeaderScaleY)
                    .setInterpolator(new DecelerateInterpolator(2f))
                    .setDuration(250)
                    .start();
        }
        gapFillShown = showGapFill;

        // Make a shadow. TODO: Do not need if running on AndroidL
        headerBarShadow.setVisibility(View.VISIBLE);

        if (headerBarTopClearance != 0) {
            // Fill the gap between status bar and header bar with color
            float gapFillProgress = Math.min(Math.max(getProgress(scrollY,
                    headerImageHeightPixels - headerBarTopClearance * 2,
                    headerImageHeightPixels - headerBarTopClearance), 0), 1);
            // TODO: Set elevation properties if running on AndroidL
            headerBarShadow.setAlpha(gapFillProgress);
        }

        // Move background image (parallax effect)
        headerImageContainer.setTranslationY(scrollY *
                HEADER_IMAGE_BACKGROUND_PARALLAX_EFFECT_MULTIPLIER);
    }

onScrollChanged

newTop
HeaderBarの位置を調整する. headerImageHeightPixelsとHeaderBarの隙間(headerBarTopClearance)を考慮したスクロール量(scrollY)とのmaxをとるので, HeaderImageよりもHeaderBarが下に位置することはない.
GAP_FILL_DISTANCE_MULTIPLIER
HeaderBarと画面上部の隙間を埋めるGapFillアニメーションの開始位置に係る定数. ただし, GapFillアニメーション後もHeaderBarはheaderBarTopClearance(隙間間隔)の値に到達するまで移動する点に注意.
画面スクロールのY座標が, headerBarTopClearanceと本係数との乗を超えたときにGapFillアニメーションを開始する. GapFillアニメーションの開始を早めたい場合は本係数を変更する.
gapFillDistance
前述のGAP_FILL_DISTANCE_MULTIPLIERに関係する.

ここではAndroidLが正式リリースされた際の対応をTODOコメントとして残しておく(実際にはAndroidL前後で動作を変えるラッパを用意しておくのが望ましいが, 本件の主旨ではないので割愛.

Run & improvement

これで, MaterialDesignの物理学に従った対応ができた. ここまでの実装でアプリを動作させた場合のレイアウトが下記:

enter image description here

実際に動作させてアニメーションやshadowの具合を確認してみてほしい.
Material Designは他にもTypography(Roboto, Font size/style), Bold(color, FAB), Layout baselineといった項目があり, これだけではMaterial Designに則ったデザインであるとは到底言えないが, 本稿の目的は達成したのでこれ以上は実装しない. 必要であれば残るMaterial Designの要素を実装していく必要がある.

以上.


Copyright 2014 yuki312 All Right Reserved.

Licensed under the Apache License, Version 2.0 (the “License”);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an “AS IS” BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


License
Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.

2014/09/19

Android:SyncAdapter

Intro

SyncAdapterについて.

  • SyncAdapterはスケジュールされた同期タスクに向いている
  • 同期のポリシーを指定するだけで後はAndroidがよしなにスケジュールする
  • 採用できる同期のポリシーは次の通り
    • サーバデータ変更時のメッセージ通知受信時(use GCM)
    • デバイスデータ変更時
    • TCP/IPコネクション接続時
    • 一定時間or指定時間 (厳密ではない(効率優先))
    • オンデマンド (not require)
  • バッテリーやネットワークリソースに易しい設計である
  • Authenticator機能を使用してログインが必要なサーバとも同期可能
  • Authenticator機能が不要であればスタブでよい(アカウントは必須ではない)
  • ContentProviderと連携したデータ管理, 操作をサポートする
  • ContentProviderの使用は必須ではないが強く推奨されている

Transferring Data Using Sync Adapters

Synchronizing data between an Android device and web servers can make your application significantly more useful and compelling for your users. For example, transferring data to a web server makes a useful backup, and transferring data from a server makes it available to the user even when the device is offline. In some cases, users may find it easier to enter and edit their data in a web interface and then have that data available on their device, or they may want to collect data over time and then upload it to a central storage area.

AndroidデバイスとWebサーバー間のデータ同期を行うアプリケーションは魅力的に映る. 例えばデータをサーバに移してバックアップをとったり, サーバからデータを取得してオフライン操作を可能にする. Webインタフェースを使ってデータを簡単に見つけたり編集したりでき, それらのデータをAndroidデバイス上でも使うことができる. 時間をかけて集めたデータをサーバにアップロードして管理することも可能だ.

Although you can design your own system for doing data transfers in your app, you should consider using Android’s sync adapter framework. This framework helps manage and automate data transfers, and coordinates synchronization operations across different apps. When you use this framework, you can take advantage of several features that aren’t available to data transfer schemes you design yourself:

アプリでデータ転送を行う場合, Android標準のsync adapter frameworkの使用を検討すべきである. このframeworkはデータの管理と自動転送の手段を提供する. そして, 端末にあるアプリ全体のデータ同期タイミングを調整してくれる. このframeworkを使用するといくつかのアドバンテージを得られる.

Plug-in architecture
Allows you to add data transfer code to the system in the form of callable components.
Plug-in architecture
データ転送のロジックを呼出し可能なコンポーネントにしてシステムへ追加できる
Automated execution
Allows you to automate data transfer based on a variety of criteria, including data changes, elapsed time, or time of day. In addition, the system adds transfers that are unable to run to a queue, and runs them when possible.
Automated execution
データが更新された時や一定間隔置き, 指定時間といった様々な契機でデータ転送を自動実行できる. また, 実行できなかったものはキューイングされ良しなに再開される.
Automated network checking
The system only runs your data transfer when the device has network connectivity.
Automated network checking
システムはネットワーク接続が有効な時にデータ転送を実行する.
Improved battery performance
Allows you to centralize all of your app’s data transfer tasks in one place, so that they all run at the same time. Your data transfer is also scheduled in conjunction with data transfers from other apps. These factors reduce the number of times the system has to switch on the network, which reduces battery usage.
improved battery performance
システムはデータ転送タスクを一元管理して, 同時実行されてもシステムはうまくタスクスケジューリングしてくれる. これによりネットワーク接続の回数を減らしてバッテリー消費を抑える.
Account management and authentication
If your app requires user credentials or server login, you can optionally integrate account management and authentication into your data transfer.
Account management and authentication
もし, ユーザのサーバログインが必要であればオプションでAccount ManagementとAuthenticatorとも統合されることができる.

This class shows you how to create a sync adapter and the bound Service that wraps it, how to provide the other components that help you plug the sync adapter into the framework, and how to run the sync adapter to run in various ways.

ここでは, どうやってsync adapterとバインドされたServiceを作成するのか, frameworkにsync adapterを差し込むために必要なコンポーネントをどうやって提供するのか, そして, sync adapterを実行する方法をいくつか紹介する.

Note: Sync adapters run asynchronously, so you should use them with the expectation that they transfer data regularly and efficiently, but not instantaneously. If you need to do real-time data transfer, you should do it in an AsyncTask or an IntentService.

Note: Sync adapterは非同期実行され, 定期的かつ効率的なデータ転送を用途としており, 即時同期を期待すべきではない. もし即時性を求めるならばAsyncTaskIntentServiceの使用を検討すべき.

since: 2014.09.19
source: http://developer.android.com/training/sync-adapters/index.html

Creating a Stub Authenticator

The sync adapter framework assumes that your sync adapter transfers data between device storage associated with an account and server storage that requires login access. For this reason, the framework expects you to provide a component called an authenticator as part of your sync adapter. This component plugs into the Android accounts and authentication framework and provides a standard interface for handling user credentials such as login information.

sync adapter frameworkはログインとアカウント情報が必要なWebサービス/サーバとのデータ転送も想定している. frameworkはあなたから提供されるsync adapterがauthenticatorを持っていることを期待する. このコンポーネントはaccountとauthentication frameworkとログイン画面で使われる.

Even if your app doesn’t use accounts, you still need to provide an authenticator component. If you don’t use accounts or server login, the information handled by the authenticator is ignored, so you can provide an authenticator component that contains stub method implementations. You also need to provide a bound Service that allows the sync adapter framework to call the authenticator’s methods.

もしアプリがアカウントを使用しない場合でもauthenticatorコンポーネントを提供する必要がある. もしアカウントやサーバログインを必要としないのであればauthenticatorをスタブ化すればよい. いずれにせよframeworkがauthenticatorへアクセスするために必要なメソッドはバインドサービスに実装する必要がある.

This lesson shows you how to define all the parts of a stub authenticator that you need to satisfy the requirements of the sync adapter framework. If you need to provide a real authenticator that handles user accounts, read the reference documentation for AbstractAccountAuthenticator.

ここではスタブなauthenticatorの実装方法について紹介する. もし認証機構を提供したいのであればAbstractAccountAuthenticatorのドキュメントを参照すること.

Add a Stub Authenticator Component

To add a stub authenticator component to your app, create a class that extends AbstractAccountAuthenticator, and then stub out the required methods, either by returning null or by throwing an exception.

スタブ用コンポーネントにAbstractAccountAuthenticatorクラスを継承させ, スタブメソッドを実装し, メソッドでnullや例外を返すようにする.

The following snippet shows an example of a stub authenticator class:

authenticatorクラスのスニペット:

/*
 * Implement AbstractAccountAuthenticator and stub out all
 * of its methods
 */
public class Authenticator extends AbstractAccountAuthenticator {
    // Simple constructor
    public Authenticator(Context context) {
        super(context);
    }
    // Editing properties is not supported
    @Override
    public Bundle editProperties(
            AccountAuthenticatorResponse r, String s) {
        throw new UnsupportedOperationException();
    }
    // Don't add additional accounts
    @Override
    public Bundle addAccount(
            AccountAuthenticatorResponse r,
            String s,
            String s2,
            String[] strings,
            Bundle bundle) throws NetworkErrorException {
        return null;
    }
    // Ignore attempts to confirm credentials
    @Override
    public Bundle confirmCredentials(
            AccountAuthenticatorResponse r,
            Account account,
            Bundle bundle) throws NetworkErrorException {
        return null;
    }
    // Getting an authentication token is not supported
    @Override
    public Bundle getAuthToken(
            AccountAuthenticatorResponse r,
            Account account,
            String s,
            Bundle bundle) throws NetworkErrorException {
        throw new UnsupportedOperationException();
    }
    // Getting a label for the auth token is not supported
    @Override
    public String getAuthTokenLabel(String s) {
        throw new UnsupportedOperationException();
    }
    // Updating user credentials is not supported
    @Override
    public Bundle updateCredentials(
            AccountAuthenticatorResponse r,
            Account account,
            String s, Bundle bundle) throws NetworkErrorException {
        throw new UnsupportedOperationException();
    }
    // Checking features for the account is not supported
    @Override
    public Bundle hasFeatures(
        AccountAuthenticatorResponse r,
        Account account, String[] strings) throws NetworkErrorException {
        throw new UnsupportedOperationException();
    }
}

Bind the Authenticator to the Framework

In order for the sync adapter framework to access your authenticator, you must create a bound Service for it. This service provides an Android binder object that allows the framework to call your authenticator and pass data between the authenticator and the framework.

sync adapter frameworkがあなたのauthenticatorにアクセスできるようにバインドサービスを作成する必要がある. このサービスはframeworkがあなたのauthenticatorを呼び出し, frameworkとauthenticator間でデータ通知できるようにするbinder objectを提供する.

Since the framework starts this Service the first time it needs to access the authenticator, you can also use the service to instantiate the authenticator, by calling the authenticator constructor in the Service.onCreate() method of the service.

frameworkがこのサービスを開始した時, まず初めにauthenticatorへのアクセスを必要とする. authenticatorをインスタンス化するためにService.onCreate()でauthenticatorのコンストラクタを呼ぶ方法がある.

The following snippet shows you how to define the bound Service:

サービスの定義は下記.

/**
 * A bound Service that instantiates the authenticator
 * when started.
 */
public class AuthenticatorService extends Service {
    ...
    // Instance field that stores the authenticator object
    private Authenticator mAuthenticator;
    @Override
    public void onCreate() {
        // Create a new authenticator object
        mAuthenticator = new Authenticator(this);
    }
    /*
     * When the system binds to this Service to make the RPC call
     * return the authenticator's IBinder.
     */
    @Override
    public IBinder onBind(Intent intent) {
        return mAuthenticator.getIBinder();
    }
}

Add the Authenticator Metadata File

To plug your authenticator component into the sync adapter and account frameworks, you need to provide these framework with metadata that describes the component. This metadata declares the account type you’ve created for your sync adapter and declares user interface elements that the system displays if you want to make your account type visible to the user. Declare this metadata in a XML file stored in the /res/xml/ directory in your app project. You can give any name to the file, although it’s usually called authenticator.xml.

authenticatorをsync adapterとaccount frameworkに結合するには, コンポーネントを定義したmetadataをframeworkに提供する必要がある. metadataはあなたがsync adapterで使用するアカウント種別を宣言し, ユーザに表示するアカウント情報にも使用される. metadataはプロジェクトの/res/xmlにXMLファイルとして格納する. ファイル名は任意だが, 一般的にはauthenticator.xmlが使われる.

This XML file contains a single element <account-authenticator> that has the following attributes:

このXMLファイルには<account-authenticator>タグが1つだけ含まれる. 属性については下記.

android:accountType

The sync adapter framework requires each sync adapter to have an account type, in the form of a domain name. The framework uses the account type as part of the sync adapter’s internal identification. For servers that require login, the account type along with a user account is sent to the server as part of the login credentials.

If your server doesn’t require login, you still have to provide an account type. For the value, use a domain name that you control. While the framework uses it to manage your sync adapter, the value is not sent to your server.

android:accountType

sync adapter frameworkに登録するsync adapterにはaccount typeが必須. account typeはドメイン名の形式となる. frameworkはsync adapterの識別子としてaccount typeを使用する. サーバに対してはログインアカウントとしてログイン検証で使用される.

もしサーバがログインを必要としない場合でもaccount typeをframeworkに提供する必要がある. その時は制御できるドメイン名を指定する. frameworkはsync adapterの管理にaccount typeを使用するに留め, サーバにこれを送信することはしない.

android:icon
Pointer to a Drawable resource containing an icon. If you make the sync adapter visible by specifying the attribute android:userVisible="true" in res/xml/syncadapter.xml, then you must provide this icon resource. It appears in the Accounts section of the system’s Settings app.
android:icon
Drawableリソースを指定する. もしsync adapterの属性としてandroid:userVisible="true"/res/xml/syncadapter.xmlで定義したならば, このリソースの提供は必須である. このアイコンはシステムの設定アプリにあるAccountセクションで表示される.
android:smallIcon
Pointer to a Drawable resource containing a small version of the icon. This resource may be used instead of android:icon in the Accounts section of the system’s Settings app, depending on the screen size.
android:smallIcon
Drawableリソースアイコン(小さい版)を指定する.このアイコンはシステムの設定アプリにあるAccountセクションで画面サイズに依存して表示される.
android:label
Localizable string that identifies the account type to users. If you make the sync adapter visible by specifying the attribute android:userVisible="true" in res/xml/syncadapter.xml, then you should provide this string. It appears in the Accounts section of the system’s Settings app, next to the icon you define for the authenticator.
android:label
ローカライズされたaccount typeの識別子でユーザに表示される. もしsync adapterの属性としてandroid:userVisible="true"/res/xml/syncadapter.xmlで定義したならば, この文字列リソースの提供は必須である. このアイコンはシステムの設定アプリにあるAccountセクションでアカウントのラベルで使用される.

The following snippet shows the XML file for the authenticator you created previously:

authenticatorのXMLファイル定義は下記.

<?xml version="1.0" encoding="utf-8"?>
<account-authenticator
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:accountType="example.com"
        android:icon="@drawable/ic_launcher"
        android:smallIcon="@drawable/ic_launcher"
        android:label="@string/app_name"/>

Declare the Authenticator in the Manifest

In a previous step, you created a bound Service that links the authenticator to the sync adapter framework. To identify this service to the system, declare it in your app manifest by adding the following <service> element as a child element of <application>:

次にsync adapter frameworkへ登録する, ahtuenticatorと紐づいたバインドサービスを作成する. システムがこのサービスを識別できるようにアプリのマニフェストファイルに<service>タグで登録する.

    <service
            android:name="com.example.android.syncadapter.AuthenticatorService">
        <intent-filter>
            <action android:name="android.accounts.AccountAuthenticator"/>
        </intent-filter>
        <meta-data
            android:name="android.accounts.AccountAuthenticator"
            android:resource="@xml/authenticator" />
    </service>

The <intent-filter> element sets up a filter that’s triggered by the intent action android.accounts.AccountAuthenticator, which sent by the system to run the authenticator. When the filter is triggered, the system starts AuthenticatorService, the bound Service you have provided to wrap the authenticator.

<intent-filter>タグはandroid.accounts.AccountAuthenticatorのIntentアクションをトリガできるように宣言する. このIntentアクションはsystemがauthenticatorを実行するときに送信される. このフィルタがトリガされたとき, システムはAuthenticatorServiceを開始する. このAuthenticatorServiceはあなたが定義したauthenticatorをラップしたサービスである.

The <meta-data> element declares the metadata for the authenticator. The android:name attribute links the meta-data to the authentication framework. The android:resource element specifies the name of the authenticator metadata file you created previously.

<meta-data>タグはauthenticatorのmetadataを定義する. android:name属性はauthentication frameworkのmeta-dataとリンクする. android:resource属性にはauthenticator metadataファイルを指定する.

Besides an authenticator, a sync adapter also requires a content provider. If your app doesn’t use a content provider already, go to the next lesson to learn how to create a stub content provider; otherwise, go to the lesson Creating a Sync Adapter.

authenticatorの補足として, sync adapterはContent Providerを必要とする. もしアプリで Content Providerを使用していない場合, Content Providerのスタブを作成する次のレッスンに進めばよい. そうでなければCreating a Sync Adapterに進む.

since: 2014.09.19
source: http://developer.android.com/training/sync-adapters/creating-authenticator.html

Creating a Stub Content Provider

The sync adapter framework is designed to work with device data managed by the flexible and highly secure content provider framework. For this reason, the sync adapter framework expects that an app that uses the framework has already defined a content provider for its local data. If the sync adapter framework tries to run your sync adapter, and your app doesn’t have a content provider, your sync adapter crashes.

sync adapter frameworkはデバイスデータを柔軟かつセキュアに管理するためにContent Providerを使うようデザインされている. そのためsync adapter frameworkはアプリがローカルデータをContent Providerを使って管理していることを期待する. もしsync adapter frameworkがあなたのsync adapterを実行したときにContent Providerを持っていないとクラッシュする.

If you’re developing a new app that transfers data from a server to the device, you should strongly consider storing the local data in a content provider. Besides their importance for sync adapters, content providers offer a variety of security benefits and are specifically designed to handle data storage on Android systems. To learn more about creating a content provider, see Creating a Content Provider.

もしサーバからデバイスへデータ転送するアプリを開発しているならContent Providerを使ってデータを保存することを強く検討すべきである. もう1つ, sync adapterの重要なポイントは, Content Providerはセキュリティ面で様々なメリットがあり, Androidシステムのデータストレージに特化した設計であることだ.

However, if you’re already storing local data in another form, you can still use a sync adapter to handle data transfer. To satisfy the sync adapter framework requirement for a content provider, add a stub content provider to your app. A stub provider implements the content provider class, but all of its required methods return null or 0. If you add a stub provider, you can then use a sync adapter to transfer data from any storage mechanism you choose.

ローカルデータをContent Providerで管理していなくても, sync adapterを使う方法はまだある. sync adapter frameworkのContent Providerに対する要件を満たすために, stub content providerの追加を検討する. stub content providerはContentProviderクラスを継承し, すべてのメソッドでnull0を返却する. stub content providerを追加した場合でも任意のストレージ機構を使用してsync adapterを使用できる.

If you already have a content provider in your app, you don’t need a stub content provider. In that case, you can skip this lesson and proceed to the lesson Creating a Sync Adapter. If you don’t yet have a content provider, this lesson shows you how to add a stub content provider that allows you to plug your sync adapter into the framework.

すでにContent Providerを持っているならstub content providerは必要ない. その場合, このレッスンはスキップしてCreating a Sync Adapterに進むといい. もしcontent providerを持っていないならここでstub content providerの作成方法を紹介する.

Add a Stub Content Provider

To create a stub content provider for your app, extend the class ContentProvider and stub out its required methods. The following snippet shows you how to create the stub provider:

stub content providerを作成するためにContentProviderクラスを継承し, 実装必須のメソッドをスタブ定義する.

/*
 * Define an implementation of ContentProvider that stubs out
 * all methods
 */
public class StubProvider extends ContentProvider {
    /*
     * Always return true, indicating that the
     * provider loaded correctly.
     */
    @Override
    public boolean onCreate() {
        return true;
    }
    /*
     * Return an empty String for MIME type
     */
    @Override
    public String getType() {
        return new String();
    }
    /*
     * query() always returns no results
     *
     */
    @Override
    public Cursor query(
            Uri uri,
            String[] projection,
            String selection,
            String[] selectionArgs,
            String sortOrder) {
        return null;
    }
    /*
     * insert() always returns null (no URI)
     */
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }
    /*
     * delete() always returns "no rows affected" (0)
     */
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }
    /*
     * update() always returns "no rows affected" (0)
     */
    public int update(
            Uri uri,
            ContentValues values,
            String selection,
            String[] selectionArgs) {
        return 0;
    }
}

Declare the Provider in the Manifest

The sync adapter framework verifies that your app has a content provider by checking that your app has declared a provider in its app manifest. To declare the stub provider in the manifest, add a <provider> element with the following attributes:

sync adapter frameworkはあなたのアプリがContent Providerを持っているかどうかをマニフェストファイルを参照して検証する. マニフェストでstub content providerを定義するには<provider>タグに次の属性を指定する.

android:name="com.example.android.datasync.provider.StubProvider"
Specifies the fully-qualified name of the class that implements the stub content provider.
android:name="com.example.android.datasync.provider.StubProvider"
絶対参照名でstub content providerのクラス名を指定
android:authorities="com.example.android.datasync.provider"
A URI authority that identifies the stub content provider. Make this value your app’s package name with the string “.provider” appended to it. Even though you’re declaring your stub provider to the system, nothing tries to access the provider itself.
android:authorities="com.example.android.datasync.provider"
stub content providerを識別するURIのAuthorityを指定. Authorityはパッケージ名に”.provider”を追加する形で作成する. stub content providerを定義してシステムに提供しても自身でこれにアクセスすることはしないだろう.
android:exported="false"
Determines whether other apps can access the content provider. For your stub content provider, set the value to false, since there’s no need to allow other apps to see the provider. This value doesn’t affect the interaction between the sync adapter framework and the content provider.
android:exported="false"
他のアプリがこのcontent providerにアクセスできるかどうかを指定する. stub content providerであれば他アプリからアクセスさせる必要がないのでfalseを設定する. この値はsync adapter frameworkとcontent providerとのインタラクションに影響しない.
android:syncable="true"
Sets a flag that indicates that the provider is syncable. If you set this flag to true, you don’t have to call setIsSyncable() in your code. The flag allows the sync adapter framework to make data transfers with the content provider, but transfers only occur if you do them explicitly.
android:syncable="true"
このflagはproviderが同期可能であることを示唆する. もしtrueを設定したならば, コード上でsetIsSyncable()を呼ぶ必要がなくなる. このflagはsync adapter frameworkがcontent providerを使って転送データを作るのを許可する. ただし, あなたが望む場合にだけ転送が発生する.

The following snippet shows you how to add the <provider> element to the app manifest:

<provider>タグの書き方は下記.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.network.sync.BasicSyncAdapter"
    android:versionCode="1"
    android:versionName="1.0" >
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
    ...
    <provider
        android:name="com.example.android.datasync.provider.StubProvider"
        android:authorities="com.example.android.datasync.provider"
        android:exported="false"
        android:syncable="true"/>
    ...
    </application>
</manifest>

Now that you have created the dependencies required by the sync adapter framework, you can create the component that encapsulates your data transfer code. This component is called a sync adapter. The next lesson shows you how to add this component to your app.

sync adapter frameworkに必要なものを作成したら, 次はデータ転送をカプセル化したコンポーネントを作成する. このコンポーネントはsync adapterと呼ばれており, 次のレッスンでコンポーネントの追加方法を紹介する.

since: 2014.09.19
source: http://developer.android.com/training/sync-adapters/creating-stub-provider.html

Creating a Sync Adapter

The sync adapter component in your app encapsulates the code for the tasks that transfer data between the device and a server. Based on the scheduling and triggers you provide in your app, the sync adapter framework runs the code in the sync adapter component. To add a sync adapter component to your app, you need to add the following pieces:

sync adapter componentはサーバとデバイス間のデータ転送を行うタスクのコードをカプセル化する. スケジューリングとアプリが提供するトリガを契機に, sync adapter frameworkがsync adapter componenetのデータ転送コードを実行する. sync adapter componentをアプリに追加するには次のコンポーネントが必要になる:

Sync adapter class.
A class that wraps your data transfer code in an interface compatible with the sync adapter framework.
Sync adapter class
sync adapter frameworkと互換性のあるインタフェースで実装されたデータ転送コードをもつクラス.
Bound Service.
A component that allows the sync adapter framework to run the code in your sync adapter class.
Bound Service
sync adapter frameworkがsync adapterクラスを実行できるようにするコンポーネント
Sync adapter XML metadata file.
A file containing information about your sync adapter. The framework reads this file to find out how to load and schedule your data transfer.
Sync adapter XML metadata file.
sync adapterの情報が含まれたファイル. frameworkはこのファイルからデータトランスファの読み込み方とスケジュールを参照する.
Declarations in the app manifest.
XML that declares the bound service and points to sync adapter-specific metadata.
Declarations in the app manifest.
XMLにはサービスとsync adapterのmetadataファイルを定義する.

This lesson shows you how to define these elements.

このレッスンでは上記の要素をどう定義するかを紹介する.

Create a Sync Adapter Class

In this part of the lesson you learn how to create the sync adapter class that encapsulates the data transfer code. Creating the class includes extending the sync adapter base class, defining constructors for the class, and implementing the method where you define the data transfer tasks.

この章では, データ転送コードをカプセル化したsync adapterクラスの作成方法を紹介する. クラスを作成するにはsync adapterを拡張し, コンストラクタを作成し, データ転送タスクのメソッドを実装する.

Extend the base sync adapter class AbstractThreadedSyncAdapter

To create the sync adapter component, start by extending AbstractThreadedSyncAdapterand writing its constructors. Use the constructors to run setup tasks each time your sync adapter component is created from scratch, just as you use Activity.onCreate() to set up an activity. For example, if your app uses a content provider to store data, use the constructors to get a ContentResolver instance. Since a second form of the constructor was added in Android platform version 3.0 to support the parallelSyncs argument, you need to create two forms of the constructor to maintain compatibility.

sync adapter componentを作成するために, AbstractThreadedSyncAdapterクラスを継承し, コンストラクタを定義する. コンストラクタではActivity.onCreate()のように, sync adapter componentを初期化・構築する. 例えば, アプリがContent Providerを使用してデータを保存する場合, コンストラクタでContentResolverインスタンスを取得する. 2つめのコンストラクタはAndroid3.0からサポートされた引数parallelSyncsが追加されている. あなたは互換性のためにこの2つのコンストラクタをメンテする必要がある.

Note: The sync adapter framework is designed to work with sync adapter components that are singleton instances. Instantiating the sync adapter component is covered in more detail in the section Bind the Sync Adapter to the Framework.

Note: sync adapter frameworkはsync adapter componentsがsingletonインスタンスとして動作するようにデザインされている. sync adapter componentについてはBind the Sync Adapter to the Frameworkでより詳細に扱っている.

The following example shows you how to implement AbstractThreadedSyncAdapterer and its constructors:

AbstractTreadedSyncAdapterのコンストラクタを実装したサンプル:

/**
 * Handle the transfer of data between a server and an
 * app, using the Android sync adapter framework.
 */
public class SyncAdapter extends AbstractThreadedSyncAdapter {
    ...
    // Global variables
    // Define a variable to contain a content resolver instance
    ContentResolver mContentResolver;
    /**
     * Set up the sync adapter
     */
    public SyncAdapter(Context context, boolean autoInitialize) {
        super(context, autoInitialize);
        /*
         * If your app uses a content resolver, get an instance of it
         * from the incoming Context
         */
        mContentResolver = context.getContentResolver();
    }
    ...
    /**
     * Set up the sync adapter. This form of the
     * constructor maintains compatibility with Android 3.0
     * and later platform versions
     */
    public SyncAdapter(
            Context context,
            boolean autoInitialize,
            boolean allowParallelSyncs) {
        super(context, autoInitialize, allowParallelSyncs);
        /*
         * If your app uses a content resolver, get an instance of it
         * from the incoming Context
         */
        mContentResolver = context.getContentResolver();
        ...
    }

Add the data transfer code to onPerformSync()

The sync adapter component does not automatically do data transfer. Instead, it encapsulates your data transfer code, so that the sync adapter framework can run the data transfer in the background, without involvement from your app. When the framework is ready to sync your application’s data, it invokes your implementation of the method onPerformSync().

sync adapter componentが勝手にデータ転送することはしない. 代わりに, あなたのアプリの関与なしにカプセル化されたデータ転送コードをsync adapter frameworkがバックグラウンドで実行する. frameworkがデータ転送の準備が整ったら, onPerformSync()メソッドを呼び出す.

To facilitate the transfer of data from your main app code to the sync adapter component, the sync adapter framework calls onPerformSync() with the following arguments:

sync adapter componentにデータ転送させるため, sync adapter frameworkはonPerformSync()に次の引数を渡す.

Account
An Account object associated with the event that triggered the sync adapter. If your server doesn’t use accounts, you don’t need to use the information in this object.
Account
Accountオブジェクトはsync adapterがトリガされたイベントに関連したオブジェクト. もしあなたのサーバがアカウントを必要としないのであれば, このオブジェクトの情報も必要ない.
Extras
A Bundle containing flags sent by the event that triggered the sync adapter.
Extras
sync adapterをトリガしたイベントから送信されたflagを持つBundle.
Authority
The authority of a content provider in the system. Your app has to have access to this provider. Usually, the authority corresponds to a content provider in your own app.
Authority
システムにあるContent ProviderのAuthority. あなたのアプリはこのContent Providerにアクセスできる必要がある. 通常自アプリのContent Providerのものを指定する.
Content provider client
A ContentProviderClient for the content provider pointed to by the authority argument. A ContentProviderClient is a lightweight public interface to a content provider. It has the same basic functionality as a ContentResolver. If you’re using a content provider to store data for your app, you can connect to the provider with this object. Otherwise, you can ignore it.
Content provider client
ContentProviderClientは引数authorityに対応するContente Providerのクライアントである. ContentProviderClientは軽量なContentProviderの公開インタフェースである. ContentResolverが持つ基本的な機能を備える. もしContent providerを使ってデータを保存したいならContentProviderClientを使って接続できる. 不要ならこれを無視できる.
Sync result
A SyncResult object that you use to send information to the sync adapter framework.
Sync result
SyncResultオブジェクトはsync adapter frameworkへ情報を送信するのに使用する.

The following snippet shows the overall structure of onPerformSync():

onPerformSync()について記す.

    /*
     * Specify the code you want to run in the sync adapter. The entire
     * sync adapter runs in a background thread, so you don't have to set
     * up your own background processing.
     */
    @Override
    public void onPerformSync(
            Account account,
            Bundle extras,
            String authority,
            ContentProviderClient provider,
            SyncResult syncResult) {
    /*
     * Put the data transfer code here.
     */
    ...
    }

While the actual implementation of onPerformSync() is specific to your app’s data synchronization requirements and server connection protocols, there are a few general tasks your implementation should perform:

onPerformSync()の実装はアプリデータ同期要件とサーバ接続プロトコルによって固有のものとなるが, いくつか一般的なタスクもある.

Connecting to a server
Although you can assume that the network is available when your data transfer starts, the sync adapter framework doesn’t automatically connect to a server.
Connecting to a server
あなたはネットワーク接続が有効の前提でデータ転送を開始することができるが, sync adapter frameworkはサーバへ自動接続するようなことはしない.
Downloading and uploading data
A sync adapter doesn’t automate any data transfer tasks. If you want to download data from a server and store it in a content provider, you have to provide the code that requests the data, downloads it, and inserts it in the provider. Similarly, if you want to send data to a server, you have to read it from a file, database, or provider, and send the necessary upload request. You also have to handle network errors that occur while your data transfer is running.
Downloading andr uploading data
sync adapterはいかなるデータ転送タスクも自動化しない. もしサーバからデータをダウンロードしてcontent providerを使って保存したい場合, データをリクエストして, ダウンロードして, providerでinsertするコードを提供する必要がある. 同じように, サーバへデータを送信したい場合はファイル,データベース, content providerを使って読み込み, 必要なアップロードリクエストを送信する. あなたはデータ転送の実行時に発生するネットワークエラーもハンドルする必要がある.
Handling data conflicts or determining how current the data is
A sync adapter doesn’t automatically handle conflicts between data on the server and data on the device. Also, it doesn’t automatically detect if the data on the server is newer than the data on the device, or vice versa. Instead, you have to provide your own algorithms for handling this situation.
Handling data conflicts or determining how current the data is
sync adapter はデバイスとサーバ間データのコンフリクトを自動でハンドルしない. またサーバとデバイスどちらのデータが新しいかについても検出しない. 代わりに独自のアルゴリズムを作ってこれを解決する必要がある.
Clean up.
Always close connections to a server and clean up temp files and caches at the end of your data transfer.
Clean up.
データ転送が終了したらサーバとのコネクションや添付ファイル, キャッシュの後始末をすること.

Note: The sync adapter framework runs onPerformSync() on a background thread, so you don’t have to set up your own background processing.

Note sync adapter framework はonPerformSync()をバックグラウンドスレッドで実行する. あなたはバックグラウンドプロセスをわざわざ作成する必要がない.

In addition to your sync-related tasks, you should try to combine your regular network-related tasks and add them to onPerformSync(). By concentrating all of your network tasks in this method, you conserve the battery power that’s needed to start and stop the network interfaces. To learn more about making network access more efficient, see the training class Transferring Data Without Draining the Battery, which describes several network access tasks you can include in your data transfer code.

onPerformSync()には同期関連タスクに加えて, 通常のネットワーク関連タスクも実装する. ネットワーク関連のタスクはこのメソッドに集約される. ネットワーク接続のstart/stopはバッテリーを消耗する. 効率的なネットワークアクセスについてはTransferring Data Without Draining the Batteryを参照. ネットワークアクセスについて有用なコードが紹介されている.

Bind the Sync Adapter to the Framework

You now have your data transfer code encapsulated in a sync adapter component, but you have to provide the framework with access to your code. To do this, you need to create a bound Service that passes a special Android binder object from the sync adapter component to the framework. With this binder object, the framework can invoke the onPerformSync() method and pass data to it.

ここまでで, データ転送コードはsync adapter componentにカプセル化されたわけだが, frameworkにアクセス手段を提供するコードを提供しなければならない. これをするにはsync adapter componentのBinder Objectをframeworkに渡すバインドサービスを作成する. このBinderはframeworkがonPerformSync()メソッドを実行してデータを渡せるものであること.

Instantiate your sync adapter component as a singleton in the onCreate() method of the service. By instantiating the component in onCreate(), you defer creating it until the service starts, which happens when the framework first tries to run your data transfer. You need to instantiate the component in a thread-safe manner, in case the sync adapter framework queues up multiple executions of your sync adapter in response to triggers or scheduling.

サービスのonCreate()メソッドでsync adapter componentをsingletonインスタンスとしてインスタンス化する. onCreate()でコンポーネントがインスタンス化されるため, frameworkが最初にデータ転送をしようとサービスを開始するまでsync adapter componentの作成は延期される. コンポーネントのインスタンス化はスレッドセーフにすること. sync adapter frameworkは応答へのトリガやスケジューリングによるsync adapterの複数の実行をキューイングする.

For example, the following snippet shows you how to create a class that implements the bound Service, instantiates your sync adapter component, and gets the Android binder object:

サービスを実装してsync adapter componentをインスタンス化し, Binder objectを取得するコードを例示する.

package com.example.android.syncadapter;
/**
 * Define a Service that returns an IBinder for the
 * sync adapter class, allowing the sync adapter framework to call
 * onPerformSync().
 */
public class SyncService extends Service {
    // Storage for an instance of the sync adapter
    private static SyncAdapter sSyncAdapter = null;
    // Object to use as a thread-safe lock
    private static final Object sSyncAdapterLock = new Object();
    /*
     * Instantiate the sync adapter object.
     */
    @Override
    public void onCreate() {
        /*
         * Create the sync adapter as a singleton.
         * Set the sync adapter as syncable
         * Disallow parallel syncs
         */
        synchronized (sSyncAdapterLock) {
            if (sSyncAdapter == null) {
                sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
            }
        }
    }
    /**
     * Return an object that allows the system to invoke
     * the sync adapter.
     *
     */
    @Override
    public IBinder onBind(Intent intent) {
        /*
         * Get the object that allows external processes
         * to call onPerformSync(). The object is created
         * in the base class code when the SyncAdapter
         * constructors call super()
         */
        return sSyncAdapter.getSyncAdapterBinder();
    }
}

Note: To see a more detailed example of a bound service for a sync adapter, see the sample app.

Note: sync adapterのためのバインドサービスについての詳細な例はサンプルアプリを参照.

Add the Account Required by the Framework

The sync adapter framework requires each sync adapter to have an account type. You declared the account type value in the section Add the Authenticator Metadata File. Now you have to set up this account type in the Android system. To set up the account type, add a dummy account that uses the account type by calling addAccountExplicitly().

sync adapter frameworkはsync adapterがaccount typeを持っていることを期待する. account typeの定義についてはAdd the Authenticator Metadata Fileのセクションを参照. account typeをセットアップするためにダミーのアカウントを追加するにはaddAccountExplicitly()を使えきる.

The best place to call the method is in the onCreate() method of your app’s opening activity. The following code snippet shows you how to do this:

ActivityのonCreate()メソッドはこのメソッドを呼ぶのに都合のいい場所になる.

public class MainActivity extends FragmentActivity {
    ...
    ...
    // Constants
    // The authority for the sync adapter's content provider
    public static final String AUTHORITY = "com.example.android.datasync.provider"
    // An account type, in the form of a domain name
    public static final String ACCOUNT_TYPE = "example.com";
    // The account name
    public static final String ACCOUNT = "dummyaccount";
    // Instance fields
    Account mAccount;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        // Create the dummy account
        mAccount = CreateSyncAccount(this);
        ...
    }
    ...
    /**
     * Create a new dummy account for the sync adapter
     *
     * @param context The application context
     */
    public static Account CreateSyncAccount(Context context) {
        // Create the account type and default account
        Account newAccount = new Account(
                ACCOUNT, ACCOUNT_TYPE);
        // Get an instance of the Android account manager
        AccountManager accountManager =
                (AccountManager) context.getSystemService(
                        ACCOUNT_SERVICE);
        /*
         * Add the account and account type, no password or user data
         * If successful, return the Account object, otherwise report an error.
         */
        if (accountManager.addAccountExplicitly(newAccount, null, null))) {
            /*
             * If you don't set android:syncable="true" in
             * in your <provider> element in the manifest,
             * then call context.setIsSyncable(account, AUTHORITY, 1)
             * here.
             */
        } else {
            /*
             * The account exists or some other error occurred. Log this, report it,
             * or handle it internally.
             */
        }
    }
    ...
}

Add the Sync Adapter Metadata File

To plug your sync adapter component into the framework, you need to provide the framework with metadata that describes the component and provides additional flags. The metadata specifies the account type you’ve created for your sync adapter, declares a content provider authority associated with your app, controls a part of the system user interface related to sync adapters, and declares other sync-related flags. Declare this metadata in a special XML file stored in the /res/xml/ directory in your app project. You can give any name to the file, although it’s usually called syncadapter.xml.

frameworkにsync adapterを組み込むのに, コンポーネントを指定するmetadataと追加のflagグを提供する必要がある. metadataはaccount typeを指定し, sync adapterを生成し, アプリに関連するcontent providerのauthorityを解決し, systemUIでsync adapterに関連する一部を制御し, 他の同期に関連するflagを宣言する. metadataの宣言は/res/xmlディレクトリにあるXMLファイルで行う. ファイル名は任意だが, 一般的にsyncadapter.xmlが使用される.

This XML file contains a single XML element <sync-adapter> that has the following attributes:

XMLファイルには<sync-adapter>要素だけが含まれる. 次はそれぞれの属性:

android:contentAuthority
The URI authority for your content provider. If you created a stub content provider for your app in the previous lesson Creating a Stub Content Provider, use the value you specified for the attribute android:authorities in the <provider> element you added to your app manifest. This attribute is described in more detail in the section Declare the Provider in the Manifest.
If you’re transferring data from a content provider to a server with your sync adapter, this value should be the same as the content URI authority you’re using for that data. This value is also one of the authorities you specify in the android:authorities attribute of the <provider> element that declares your provider in your app manifest.
android:contentAuthority
Content ProviderのURI Authorityを指定する. もしstub content providerを作成しているのであればCreating a Stub Content Providerのレッスンを参照. ここで使用する値はマニフェストで宣言される<provider>タグのandroid:authoritiesの値を指定する. この属性についての詳細はDeclare the Provider in the Manifestのセクションを参照.
もし, sync adapterでContent Providerを使ってデータ転送するのであれば, この値はコンテンツURIのauthorityと揃えるべきである. この値はandroid:authoritiesで指定されたcontent providerを解決する.
android:accountType
The account type required by the sync adapter framework. The value must be the same as the account type value you provided when you created the authenticator metadata file, as described in the section Add the Authenticator Metadata File. It’s also the value you specified for the constant ACCOUNT_TYPE in the code snippet in the section Add the Account Required by the Framework.
android:accountType
account typeはsync adapter frameworkが必要とする. この値はあなたが作成したauthenticator metadataで提供されるaccount typeの値となる. この説明はAdd the Authenticator Metadata Fileを参照. またはAdd the Account required by the Frameworkセクションのコードスニペットで定義したACCOUNT_TYPE定数.

Settings attributes

android:userVisible
Sets the visibility of the sync adapter’s account type. By default, the account icon and label associated with the account type are visible in the Accounts section of the system’s Settings app, so you should make your sync adapter invisible unless you have an account type or domain that’s easily associated with your app. If you make your account type invisible, you can still allow users to control your sync adapter with a user interface in one of your app’s activities.
android:userVisible
sync adapterのaccount typeの可視性を設定する. デフォルトではSystem設定アプリのAccountsセクションでアカウントアイコンとラベルが表示される. あなたのsync adapterに紐づくアカウントやドメインがないのであれば不可視に設定すべき. アカウントを不可視に設定しても自前のActivityでアカウントを制御できるように作ることは可能.
android:supportsUploading
Allows you to upload data to the cloud. Set this to false if your app only downloads data.
android:supportsUploading
クラウドへのデータアップロードを許可するかどうか. falseにするとダウンロードのみ可能となる.
android:allowParallelSyncs
Allows multiple instances of your sync adapter component to run at the same time. Use this if your app supports multiple user accounts and you want to allow multiple users to transfer data in parallel. This flag has no effect if you never run multiple data transfers.
android:allowParallelSyncs
sync adapter componentの複数インスタンス化と同時実行を許可するかどうか. マルチユーザをサポートして並列にデータ転送をサポートしたいのであればこのフラグが使える. 複数のデータ転送をしない限りこのフラグには効果がない.
android:isAlwaysSyncable
Indicates to the sync adapter framework that it can run your sync adapter at any time you’ve specified. If you want to programmatically control when your sync adapter can run, set this flag to false, and then call requestSync() to run the sync adapter. To learn more about running a sync adapter, see the lesson Running a Sync Adapter
android:isAlwaysSyncable
sync adapter frameworkがsync adapterをいつでも実行できることを示す. もしsync adapterの実行スケジュールをプログラム制御したい場合は, フラグにfalseをセットしてrequestSync()を呼べばsync adapterが実行される. sync adapterの実行についてはRunning a Sync Adapterを参照.

The following example shows the XML for a sync adapter that uses a single dummy account and only does downloads.

sync adapterがダミーアカウントを使用してダウンロードのみサポートする場合のXMLを例示する.

<?xml version="1.0" encoding="utf-8"?>
<sync-adapter
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:contentAuthority="com.example.android.datasync.provider"
        android:accountType="com.android.example.datasync"
        android:userVisible="false"
        android:supportsUploading="false"
        android:allowParallelSyncs="false"
        android:isAlwaysSyncable="true"/>

Declare the Sync Adapter in the Manifest

Once you’ve added the sync adapter component to your app, you have to request permissions related to using the component, and you have to declare the bound Service you’ve added.

sync adapter componentをアプリに追加するには, これに関連するpermissionを要求し, バインドサービスの宣言を追加する必要がある.

Since the sync adapter component runs code that transfers data between the network and the device, you need to request permission to access the Internet. In addition, your app needs to request permission to read and write sync adapter settings, so you can control the sync adapter programmatically from other components in your app. You also need to request a special permission that allows your app to use the authenticator component you created in the lesson Creating a Stub Authenticator.

sync adapter componentが実行するコードでネットワークとデバイス間のデータ転送を行うため, Internetへアクセスするpermissionが必要になる. 追加でsync adapter設定のread/write permissionも必要になる. これでsync adapterをプログラムから制御できる. Creating a Stub Authenticatorのレッスンで作成したようなauthenticator componentを作成するには特別なpermissionが必要になる.

To request these permissions, add the following to your app manifest as child elements of <manifest>:

これらのpermissionを要求するための<manifest>の子要素:

android.permission.INTERNET
Allows the sync adapter code to access the Internet so that it can download or upload data from the device to a server. You don’t need to add this permission again if you were requesting it previously.
android.permission.INTERNET
sync adapterがInternetにアクセスするのを許可する. これによりダウンロード/アップロードが可能になる. 既に定義済みなら不要.
android.permission.READ_SYNC_SETTINGS
Allows your app to read the current sync adapter settings. For example, you need this permission in order to call getIsSyncable().
android.permission.READ_SYNC_SETTINGS
現在のsync adapter設定を読み取るのに必要. 例えばgetIsSyncable()メソッドの実行に必要となる.
android.permission.WRITE_SYNC_SETTINGS
Allows your app to control sync adapter settings. You need this permission in order to set periodic sync adapter runs using addPeriodicSync(). This permission is not required to call requestSync(). To learn more about running the sync adapter, see Running A Sync Adapter.
android.permission.WRITE_SYNC_SETTINGS
sync adapter設定の書き込みに必要. このpermissionは定期的にsync adapterを実行するためのaddPeriodicSync()の実行に必要. requestSync()メソッドの実行にこのパーミッションは必要ない. より詳細を学びたいならRunning A Sync Adapterを参考.
android.permission.AUTHENTICATE_ACCOUNTS
Allows you to use the authenticator component you created in the lesson Creating a Stub Authenticator.
android.permission.AUTHENTICATE_ACCOUNTS
Creating a Stub Authenticatorで作成したauthenticator componentの生成に必要.

The following snippet shows how to add the permissions:

permission追加の方法は下記:

<manifest>
...
    <uses-permission
            android:name="android.permission.INTERNET"/>
    <uses-permission
            android:name="android.permission.READ_SYNC_SETTINGS"/>
    <uses-permission
            android:name="android.permission.WRITE_SYNC_SETTINGS"/>
    <uses-permission
            android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
...
</manifest>

Finally, to declare the bound Service that the framework uses to interact with your sync adapter, add the following XML to your app manifest as a child element of <application>:

最後に, frameworkと相互通信するsync adapterを持つバインドサービスを定義.

        <service
                android:name="com.example.android.datasync.SyncService"
                android:exported="true"
                android:process=":sync">
            <intent-filter>
                <action android:name="android.content.SyncAdapter"/>
            </intent-filter>
            <meta-data android:name="android.content.SyncAdapter"
                    android:resource="@xml/syncadapter" />
        </service>

The <intent-filter> element sets up a filter that’s triggered by the intent action android.content.SyncAdapter, sent by the system to run the sync adapter. When the filter is triggered, the system starts the bound service you’ve created, which in this example is SyncService. The attribute android:exported="true" allows processes other than your app (including the system) to access the Service. The attribute android:process=":sync" tells the system to run the Service in a global shared process named sync. If you have multiple sync adapters in your app they can share this process, which reduces overhead.

システムがsync adapterを実行できるようにするため<intent-filter>android.content.SyncAdapterを定義. システムがバインドサービスを開始するときにトリガされる. 上記例ではSyncServiceにあたる. android:exported="true"属性はシステムを含めた他のプロセスがServiceにアクセスできることを許可するものである. android:process=":sync"属性はサービスをグローバル共有プロセスであるsyncで動作させることを指定している. もし複数のsync adapterをもっているなら, このプロセスリソースを共有することで, オーバヘッドを軽減させる.

The <meta-data> element provides provides the name of the sync adapter metadata XML file you created previously. The android:name attribute indicates that this metadata is for the sync adapter framework. The android:resource element specifies the name of the metadata file.

<meta-data>要素にはsync adapterのmetadata XMLファイルを指定する. android:nameの値はこれがsync adapter frameworkのものであることを示す. android:resouce要素はmetadataファイルの名前を指定する.

You now have all of the components for your sync adapter. The next lesson shows you how to tell the sync adapter framework to run your sync adapter, either in response to an event or on a regular schedule.

これですべてのsync adapterのためのコンポーネントを学習した. 次はsync adapter frameworkにsync adapterを実行させる契機(イベントに反応させるor定期的に実行させる)を登録する方法を見ていく.

since: 2014.09.19
source: http://developer.android.com/training/sync-adapters/creating-sync-adapter.html

Running a Sync Adapter

In the previous lessons in this class, you learned how to create a sync adapter component that encapsulates data transfer code, and how to add the additional components that allow you to plug the sync adapter into the system. You now have everything you need to install an app that includes a sync adapter, but none of the code you’ve seen actually runs the sync adapter.

前のレッスンでsync adapter componentのイロハを学んだ. これでsync adapterをアプリに追加できるがこれを実行する術を身につけていない.

You should try to run your sync adapter based on a schedule or as the indirect result of some event. For example, you may want your sync adapter to run on a regular schedule, either after a certain period of time or at a particular time of the day. You may also want to run your sync adapter when there are changes to data stored on the device. You should avoid running your sync adapter as the direct result of a user action, because by doing this you don’t get the full benefit of the sync adapter framework’s scheduling ability. For example, you should avoid providing a refresh button in your user interface.

sync adapterをスケジュールベースまたは何かのイベントを契機に実行させてみる. 例えば, 一定間隔置きあるいは指定時間に実行させるといった具合に. デバイスデータの変更に合わせてsync adapterを実行したい場合に, ユーザの更新アクションを契機に直接sync adapterを実行するような設計は避けるべきである. これではsync adapter frameworkのスケジューリング能力を引き出すことができない. sync adapterを使用するためにリフレッシュボタンを置くようなことは避けるべき.

You have the following options for running your sync adapter:

sync adapterを使用する上でのオプション:

When server data changes
Run the sync adapter in response to a message from a server, indicating that server-based data has changed. This option allows you to refresh data from the server to the device without degrading performance or wasting battery life by polling the server.
When server data changes
サーバ上のデータが変更され, その変更通知を受けてsync adapterを実行する. この方法は, ポーリングによるサーバパフォーマンスの低下や無駄なバッテリー消費を避けることができる.
When device data changes
Run a sync adapter when data changes on the device. This option allows you to send modified data from the device to a server, and is especially useful if you need to ensure that the server always has the latest device data. This option is straightforward to implement if you actually store data in your content provider. If you’re using a stub content provider, detecting data changes may be more difficult.
When device data changes
デバイス上のデータが変更された時にsync adapterを実行する. この方法ではデバイスからサーバへ更新データを送信して、サーバが最新の情報を持つようにできる. これをContent Providerで実装すれば簡単に実現できる. もしStud content providerを使っているならデータ変更の検知から実装する必要があり複雑である.
When the system sends out a network message
Run a sync adapter when the Android system sends out a network message that keeps the TCP/IP connection open; this message is a basic part of the networking framework. Using this option is one way to run the sync adapter automatically. Consider using it in conjunction with interval-based sync adapter runs.
When the system sends out a network message
Androidシステムがネットワーク通信のためにTCP/IP 接続したのを契機にsync adapterを実行する. このオプションは自動的にsync adapterを実行するための方法でもある. 間隔をおいて同期実行するsync adapterの方法とあわせて検討できる.
At regular intervals
Run a sync adapter after the expiration of an interval you choose, or run it at a certain time every day.
At regular intervals
一定時間後であったり, 毎日特定時刻になるとsync adapterを実行する.
On demand
Run the sync adapter in response to a user action. However, to provide the best user experience you should rely primarily on one of the more automated options. By using automated options, you conserve battery and network resources.
On demand
ユーザアクションに応じてsync adapterを実行する. ただし, よりよいUXを提供するには他のいずれかのオプションを指定すべきである. 自動化オプションはバッテリーとネットワークリソースを節約する.

The rest of this lesson describes each of the options in more detail.

残りのレッスンは各オプションの詳細についてみていく.

Run the Sync Adapter When Server Data Changes

If your app transfers data from a server and the server data changes frequently, you can use a sync adapter to do downloads in response to data changes. To run the sync adapter, have the server send a special message to a BroadcastReceiver in your app. In response to this message, call ContentResolver.requestSync() to signal the sync adapter framework to run your sync adapter.

もしアプリがサーバからデータを受け取り, かつサーバデータが頻繁に更新されるのであればsync adapterをデータ変更時のダウンローダとして使える. サーバは特別なメッセージをBroadcastReceiverに対して送信しsync adapterを実行する. メッセージを受けたらContentResolver.requestSync()を実行する. この命令でsync adapter frameworkがsync adapterを実行する.

Google Cloud Messaging (GCM) provides both the server and device components you need to make this messaging system work. Using GCM to trigger transfers is more reliable and more efficient than polling servers for status. While polling requires a Service that is always active, GCM uses a BroadcastReceiver that’s activated when a message arrives. While polling at regular intervals uses battery power even if no updates are available, GCM only sends messages when needed.

サーバでメッセージを作りデバイスにこれを送信する. GCMはポーリングより効率的かつ信頼できる. ポーリングでは常にサービスが稼働しているが, GCMではBroadcastReceiverを使用して必要なメッセージがある時にしかサービスを稼働させない. ポーリングはメッセージや更新がないときもバッテリーを消費し続けるがGCMではメッセージが送られた時にしか消費しない.

Note: If you use GCM to trigger your sync adapter via a broadcast to all devices where your app is installed, remember that they receive your message at roughly the same time. This situation can cause multiple instance of your sync adapter to run at the same time, causing server and network overload. To avoid this situation for a broadcast to all devices, you should consider deferring the start of the sync adapter for a period that’s unique for each device.

Note: GCMでbroadcastを経由してsync adapterのトリガとする時, GCMが世界中のアプリインストールされたデバイスに一斉配信されてサーバへ同時アクセスしてくるシチュエーションを想定すること. サーバとネットワークリソースの過負荷を回避するため, GCMをトリガとする場合はアクセスが集中しないように分散させること.

The following code snippet shows you how to run requestSync() in response to an incoming GCM message:

GCM受信を契機にrequestSync()するコード:

public class GcmBroadcastReceiver extends BroadcastReceiver {
    ...
    // Constants
    // Content provider authority
    public static final String AUTHORITY = "com.example.android.datasync.provider"
    // Account type
    public static final String ACCOUNT_TYPE = "com.example.android.datasync";
    // Account
    public static final String ACCOUNT = "default_account";
    // Incoming Intent key for extended data
    public static final String KEY_SYNC_REQUEST =
            "com.example.android.datasync.KEY_SYNC_REQUEST";
    ...
    @Override
    public void onReceive(Context context, Intent intent) {
        // Get a GCM object instance
        GoogleCloudMessaging gcm =
                GoogleCloudMessaging.getInstance(context);
        // Get the type of GCM message
        String messageType = gcm.getMessageType(intent);
        /*
         * Test the message type and examine the message contents.
         * Since GCM is a general-purpose messaging system, you
         * may receive normal messages that don't require a sync
         * adapter run.
         * The following code tests for a a boolean flag indicating
         * that the message is requesting a transfer from the device.
         */
        if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)
            &&
            intent.getBooleanExtra(KEY_SYNC_REQUEST)) {
            /*
             * Signal the framework to run your sync adapter. Assume that
             * app initialization has already created the account.
             */
            ContentResolver.requestSync(ACCOUNT, AUTHORITY, null);
            ...
        }
        ...
    }
    ...
}

Run the Sync Adapter When Content Provider Data Changes

If your app collects data in a content provider, and you want to update the server whenever you update the provider, you can set up your app to run your sync adapter automatically. To do this, you register an observer for the content provider. When data in your content provider changes, the content provider framework calls the observer. In the observer, call requestSync() to tell the framework to run your sync adapter.

Content Providerでデータを管理している場合に, Content Providerが更新されるたびにサーバもアップデートしたい時, sync adapterを自動化して同期させることができる. これをするには, Content ProviderのObserverを登録する. Content Providerのデータが更新されたとき, Content Provider frameworkはobserverを呼び出す. observerはreqyestSync()を呼び出してframeworkにsync adapterを実行させる.

Note: If you’re using a stub content provider, you don’t have any data in the content provider and onChange() is never called. In this case, you have to provide your own mechanism for detecting changes to device data. This mechanism is also responsible for calling requestSync() when the data changes.

Note: stub content providerを使っている場合はcontent providerでデータを扱うことができないし, observerのonChange()は決して呼ばれない. この場合, 独自の機構を作ってデバイスデータの変更を検出するしかない. この機構でのデータ変更時はrequestSync()を呼び出す必要がある.

To create an observer for your content provider, extend the class ContentObserver and implement both forms of its onChange() method. In onChange(), call requestSync() to start the sync adapter.

content providerのobserverを作成するにはContentObserverクラスを継承し, onChange()メソッドを実装する. onChange()ではrequestSync()を呼びsync adapterを実行する.

To register the observer, pass it as an argument in a call to registerContentObserver(). In this call, you also have to pass in a content URI for the data you want to watch. The content provider framework compares this watch URI to content URIs passed in as arguments to ContentResolver methods that modify your provider, such as ContentResolver.insert(). If there’s a match, your implementation of ContentObserver.onChange() is called.

observerの登録にはregisterContentObserver()の引数にそれを渡す. もう一つの引数に監視対象のURIも必要になる. Content Provider Frameworkは ContentResolver.insert()のようなコンテンツを変更するメソッドが呼ばれた時に, メソッドに渡されるURIを比較してcontent providerの変更を監視する. マッチした場合は実装したContentObserver.onchange()が呼び出される.

The following code snippet shows you how to define a ContentObserver that calls requestSync() when a table

requestSync()を呼ぶContentObserverの実装:

public class MainActivity extends FragmentActivity {
    ...
    // Constants
    // Content provider scheme
    public static final String SCHEME = "content://";
    // Content provider authority
    public static final String AUTHORITY = "com.example.android.datasync.provider";
    // Path for the content provider table
    public static final String TABLE_PATH = "data_table";
    // Account
    public static final String ACCOUNT = "default_account";
    // Global variables
    // A content URI for the content provider's data table
    Uri mUri;
    // A content resolver for accessing the provider
    ContentResolver mResolver;
    ...
    public class TableObserver extends ContentObserver {
        /*
         * Define a method that's called when data in the
         * observed content provider changes.
         * This method signature is provided for compatibility with
         * older platforms.
         */
        @Override
        public void onChange(boolean selfChange) {
            /*
             * Invoke the method signature available as of
             * Android platform version 4.1, with a null URI.
             */
            onChange(selfChange, null);
        }
        /*
         * Define a method that's called when data in the
         * observed content provider changes.
         */
        @Override
        public void onChange(boolean selfChange, Uri changeUri) {
            /*
             * Ask the framework to run your sync adapter.
             * To maintain backward compatibility, assume that
             * changeUri is null.
            ContentResolver.requestSync(ACCOUNT, AUTHORITY, null);
        }
        ...
    }
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        // Get the content resolver object for your app
        mResolver = getContentResolver();
        // Construct a URI that points to the content provider data table
        mUri = new Uri.Builder()
                  .scheme(SCHEME)
                  .authority(AUTHORITY)
                  .path(TABLE_PATH)
                  .build();
        /*
         * Create a content observer object.
         * Its code does not mutate the provider, so set
         * selfChange to "false"
         */
        TableObserver observer = new TableObserver(false);
        /*
         * Register the observer for the data table. The table's path
         * and any of its subpaths trigger the observer.
         */
        mResolver.registerContentObserver(mUri, true, observer);
        ...
    }
    ...
}

Run the Sync Adapter After a Network Message

When a network connection is available, the Android system sends out a message every few seconds to keep the device’s TCP/IP connection open. This message also goes to the ContentResolver of each app. By calling setSyncAutomatically(), you can run the sync adapter whenever the ContentResolver receives the message.

ネットワーク接続が有効な時, AndroidはTCP/IP接続を維持するために数秒ごとにメッセージを送信する. このメッセージはContentResolverに届き, setSyncAutomatically()を呼ぶことでContentResolverがメッセージを受信する度にsync adapterを実行させることもできる.

By scheduling your sync adapter to run when the network message is sent, you ensure that your sync adapter is always scheduled to run while the network is available. Use this option if you don’t have to force a data transfer in response to data changes, but you do want to ensure your data is regularly updated. Similarly, you can use this option if you don’t want a fixed schedule for your sync adapter, but you do want it to run frequently.

ネットワークメッセージが送信された際にsync adapterを実行するようにスケジュールすると, sync adapterはネットワーク接続が有効になる都度実行される. このオプションはデータ変更時の強制同期を必要とはしないものの, それなりの頻度でデータをアップデートしたい時に使用できる. 定期的なスケジューリングは望まないが頻繁なアップデートを望む場合に向いている.

Since the method setSyncAutomatically() doesn’t disable addPeriodicSync(), your sync adapter may be triggered repeatedly in a short period of time. If you do want to run your sync adapter periodically on a regular schedule, you should disable setSyncAutomatically().

setSyncAutomatically()メソッドはaddPeriodicSync()を無効化しない. sync adapterは短いスパンで繰り返しトリガされることになる. 一定期間置きに同期実行されたい場合はsetSyncAutomatically()を無効化すべき.

The following code snippet shows you how to configure your ContentResolver to run your sync adapter in response to a network message:

下記はネットワークメッセージに応じてContentResolverからsync adapterを実行させるコード:

public class MainActivity extends FragmentActivity {
    ...
    // Constants
    // Content provider authority
    public static final String AUTHORITY = "com.example.android.datasync.provider";
    // Account
    public static final String ACCOUNT = "default_account";
    // Global variables
    // A content resolver for accessing the provider
    ContentResolver mResolver;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        // Get the content resolver for your app
        mResolver = getContentResolver();
        // Turn on automatic syncing for the default account and authority
        mResolver.setSyncAutomatically(ACCOUNT, AUTHORITY, true);
        ...
    }
    ...
}

Run the Sync Adapter Periodically

You can run your sync adapter periodically by setting a period of time to wait between runs, or by running it at certain times of the day, or both. Running your sync adapter periodically allows you to roughly match the update interval of your server.

一定の間隔でsync adapterを実行させることができる. これによりサーバとの更新間隔を一定にできる.

Similarly, you can upload data from the device when your server is relatively idle, by scheduling your sync adapter to run at night. Most users leave their powered on and plugged in at night, so this time is usually available. Moreover, the device is not running other tasks at the same time as your sync adapter. If you take this approach, however, you need to ensure that each device triggers a data transfer at a slightly different time. If all devices run your sync adapter at the same time, you are likely to overload your server and cell provider data networks.

夜間にアップロードするようにスケジュールすれば比較的アイドル状態の時に同期させることができる. ほとんどのユーザが夜に充電するため, デバイスが有効である可能性も高い. このアプローチをとるとき, 各端末からアクセスが集中してネットワークやサーバが過負荷とならないよう配慮すること.

In general, periodic runs make sense if your users don’t need instant updates, but expect to have regular updates. Periodic runs also make sense if you want to balance the availability of up-to-date data with the efficiency of smaller sync adapter runs that don’t over-use device resources.

一般的に, ユーザがインスタントアップデートを望まないが, 定期的なアップデートを望むような場合に使える. データ可用性のバランスを保つために効率的かつ小さくsync adapterを定期実行してデータをアップデートすることは, デバイスのリソースを占有しすぎることをせず有効である.

To run your sync adapter at regular intervals, call addPeriodicSync(). This schedules your sync adapter to run after a certain amount of time has elapsed. Since the sync adapter framework has to account for other sync adapter executions and tries to maximize battery efficiency, the elapsed time may vary by a few seconds. Also, the framework won’t run your sync adapter if the network is not available.

sync adapterを定期実行するためにaddPeriodicSync()を呼ぶ. このスケジュールはsync adapterを一定時間経過後に実行させる. ただし, sync adapter frameworkはバッテリ効率化のために間近にスケジュールされているsync adapterをまとめて実行する. そのため, 予定時刻からずれて実行させるケースがある. また指定時間にネットワークが有効でない場合は同期実行されない.

Notice that addPeriodicSync() doesn’t run the sync adapter at a particular time of day. To run your sync adapter at roughly the same time every day, use a repeating alarm as a trigger. Repeating alarms are described in more detail in the reference documentation for AlarmManager. If you use the method setInexactRepeating() to set time-of-day triggers that have some variation, you should still randomize the start time to ensure that sync adapter runs from different devices are staggered.

覚書として, addPeriodicSync()は一日の特定の時間にsync adapterを実行しない. 毎日決まった時間に同期実行したいのであればAlarmManagerを参照. setInexactrepeating()メソッドを使って同期時間を集中させることを避けるために実行時間をランダマイズして分散させることを検討すべき.

The method addPeriodicSync() doesn’t disable setSyncAutomatically(), so you may get multiple sync runs in a relatively short period of time. Also, only a few sync adapter control flags are allowed in a call to addPeriodicSync(); the flags that are not allowed are described in the referenced documentation for addPeriodicSync().

addPeriodicSync()メソッドは setSyncAutomatically()を無効化しない. そのため短期間で複数回同期実行される可能性がある. いくつかのsync adapter制御フラグがaddPeriodicSync()で許可される. 許可されないフラグについてはaddPeriodicSync()のドキュメントで説明されている.

The following code snippet shows you how to schedule periodic sync adapter runs:

sync adapterを定期実行させるコード:

public class MainActivity extends FragmentActivity {
    ...
    // Constants
    // Content provider authority
    public static final String AUTHORITY = "com.example.android.datasync.provider";
    // Account
    public static final String ACCOUNT = "default_account";
    // Sync interval constants
    public static final long MILLISECONDS_PER_SECOND = 1000L;
    public static final long SECONDS_PER_MINUTE = 60L;
    public static final long SYNC_INTERVAL_IN_MINUTES = 60L;
    public static final long SYNC_INTERVAL =
            SYNC_INTERVAL_IN_MINUTES *
            SECONDS_PER_MINUTE *
            MILLISECONDS_PER_SECOND;
    // Global variables
    // A content resolver for accessing the provider
    ContentResolver mResolver;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        // Get the content resolver for your app
        mResolver = getContentResolver();
        /*
         * Turn on periodic syncing
         */
        ContentResolver.addPeriodicSync(
                ACCOUNT,
                AUTHORITY,
                null,
                SYNC_INTERVAL);
        ...
    }
    ...
}

Run the Sync Adapter On Demand

Running your sync adapter in response to a user request is the least preferable strategy for running a sync adapter. The framework is specifically designed to conserve battery power when it runs sync adapters according to a schedule. Options that run a sync in response to data changes use battery power effectively, since the power is used to provide new data.

ユーザリクエストを契機にsync adapterを実行させるのが最後の戦略になる. sync adapter frameworkはsync adapterをスケジュール実行させてバッテリー節約するようデザインされている. 基本的にデータの変更を契機に同期実行するのがバッテリーにやさしい設計である.

In comparison, allowing users to run a sync on demand means that the sync runs by itself, which is inefficient use of network and power resources. Also, providing sync on demand leads users to request a sync even if there’s no evidence that the data has changed, and running a sync that doesn’t refresh data is an ineffective use of battery power. In general, your app should either use other signals to trigger a sync or schedule them at regular intervals, without user input.

他のオプションと比較して, ユーザに同期タイミングを委ねるネットワークやバッテリーリソースの面でも非効率的である. また, 同期を実行したからといってデータが変更されている保証もなく空振りに終わる可能性もあり, リソースが無駄に使われる. 一般的にユーザの入力に頼らず, 定期的あるいは他のシグナルをトリガに同期を実行するべきである.

However, if you still want to run the sync adapter on demand, set the sync adapter flags for a manual sync adapter run, then call ContentResolver.requestSync().

しかし, それでもまだ都度sync adapterを実行したいという場合は都度実行用のフラグを設定した上で, ContentResolver.requestSync()を使って手動で同期実行させることができる.

Run on demand transfers with the following flags:

都度実行させるためのフラグ設定:

SYNC_EXTRAS_MANUAL
Forces a manual sync. The sync adapter framework ignores the existing settings, such as the flag set by setSyncAutomatically().
SYNC_EXTRAS_MANUAL
強制同期実行する. sync adapter frameworkはこの時, setSyncAutomatically()で設定されている値を無視する.
SYNC_EXTRAS_EXPEDITED
Forces the sync to start immediately. If you don’t set this, the system may wait several seconds before running the sync request, because it tries to optimize battery use by scheduling many requests in a short period of time.
SYNC_EXTRAS_EXPEDITED
即時同期実行を強制する. これを設定しない場合, sync adapter frameworkはバッテリ効率化のために同期リクエストをスケジューリングしようとするため即時同期実行されない場合がある.

The following code snippet shows you how to call requestSync() in response to a button click:

ボタン押下でrequestSync()メソッドを呼ぶコード:

public class MainActivity extends FragmentActivity {
    ...
    // Constants
    // Content provider authority
    public static final String AUTHORITY =
            "com.example.android.datasync.provider"
    // Account type
    public static final String ACCOUNT_TYPE = "com.example.android.datasync";
    // Account
    public static final String ACCOUNT = "default_account";
    // Instance fields
    Account mAccount;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        /*
         * Create the dummy account. The code for CreateSyncAccount
         * is listed in the lesson Creating a Sync Adapter
         */

        mAccount = CreateSyncAccount(this);
        ...
    }
    /**
     * Respond to a button click by calling requestSync(). This is an
     * asynchronous operation.
     *
     * This method is attached to the refresh button in the layout
     * XML file
     *
     * @param v The View associated with the method call,
     * in this case a Button
     */
    public void onRefreshButtonClick(View v) {
        ...
        // Pass the settings flags by inserting them in a bundle
        Bundle settingsBundle = new Bundle();
        settingsBundle.putBoolean(
                ContentResolver.SYNC_EXTRAS_MANUAL, true);
        settingsBundle.putBoolean(
                ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
        /*
         * Request the sync for the default account, authority, and
         * manual sync settings
         */
        ContentResolver.requestSync(mAccount, AUTHORITY, settingsBundle);
    }

since: 2014.09.19
source: http://developer.android.com/training/sync-adapters/running-sync-adapter.html


License
Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.