Posted:
Chrome はとても安全です。これは、Google とリサーチ コミュニティが長年、力を合わせて、700 以上ものセキュリティ バグを修正してきたことも一つの理由です。これまで Chrome Reward プログラムを通じて 125 万ドルを上回る報奨金が支払われ、Chrome が安全になるつれて、セキュリティ バグを見つけるのは難しくなっています。

これはもちろん良い環境になったと言えますが、継続的な皆様からの協力に正当な評価を提供するため、Google は常に報奨金の金額の見直しをしており、このたび最近のChrome の脆弱性を見つける労力に見合うよう報奨レベルを引き上げます。同時に、バグの報告の透明性を向上させるため、当プログラムを一部変更しました。

まずはじめに、1 件のバグに支払われる報酬金が最高で5000 ドルから1 万 5000 ドルへと引き上げられます。(新たに 1 件のバグあたり 500 ドル 〜 1 万 5000 ドルとなります。)また、バグの種類に応じて支払われる金額の詳細も公開します。なお、特に優れた報告については、最高金額以上の報酬がこれまでと変わらずに提供されます。(先日も特に役立った報告について3 万ドルの報酬金が支払われました。)

次に、特定攻撃経路に対するエクスプロイトを提出できる研究者には、より高額の報酬金が支払われます。さらに、脆弱性を報告した後に、追ってエクスプロイトを提出するオプションも選択できるようになります。これによって、提供者はいち早くバグを報告し、Google はより早急にパッチできるように、セキュリティ担当者・研究者の両者に対して好ましい環境が構築でき、結果的に複数のレポートが提出される可能性の減少にもつながります。

そして、Chrome Reward の受賞者はGoogle Hall of Fame に名前が載り功績を称えられます。

なお今回の変更は、2014年7月1日以降に提出された有効な報告すべてが対象となり、新しい報奨金が適用されます。

Trusted Researcher プログラム、Google の哲学、ゼロデイ脆弱性に変わるマーケットなどについて、ルールページでいくつかの想定質問に回答していますので、そちらもご覧ください。

Posted by 北村英志 Developer Relations Team

Posted:
あと 2 週間!

6 月の Google I/O で Google は L Developer Preview として、Nexus 5、Nexus 7、Android TV で動作する開発者用の早期バージョンの Android 5.0 を公開しました。この L Developer Preview を通じて、これまで開発者コミュニティの多くの人たちから素晴らしいフィードバックをいただきました。ご協力に感謝いたします。

先週 Google は Android 5.0 Lollipop を発表いたしました。現在、リリースされたばかりの Android 5.0 SDK がダウンロードできるようになっています。あわせて、Nexus 5、Nexus 7(2013 年モデル)、ADT-1、Android エミュレータの開発者用システム イメージも入手できます。

この新しいバージョンの Android が搭載される最初のデバイス(Nexus 6、Nexus 9、Nexus Player)は、11 月上旬より順次提供開始いたします。また時期をあわせて、全世界の Nexus 4、Nexus 5、Nexus 7(2012 年モデル、2013 年モデル)、Nexus 10、各種の Google Play エディション端末に対して、Android 5.0 アップデートの提供を順次開始していきます。

今がまさに、あなたのアプリを この新しいプラットフォームで動作検証していただきたいタイミングです。準備万端ととのえるまで、あと 2 週間です。

Lollipop が提供する新しい機能

Android 5.0 Lollipop には、多くの新しい API や機能が追加されています。以下はその一部です:
他にも多くの機能があります。全体の概要については、Android 5.0 platform highlights をご参照ください。

Android 5.0 SDK が提供する内容

Android 5.0 SDK には、最新のツール群と、動作検証のための新しい開発者用システム イメージが含まれています。最新の Android プラットフォームに向けて開発していただくために、API Level 21 を活用し、最新の Support Library を使って Material Design 対応を行い、テレビ向けアプリでは Leanback ユーザー インターフェースを活用することができます。

以下のようなコンポーネントを、Android SDK Manager からダウンロードし、Android Studio で利用できます:
  • Android 5.0 SDK Platform と Tools
  • Android 5.0 エミュレータのシステム イメージ、32-bit 版と 64-bit(x86)版
  • Android 5.0 エミュレータの Android TV 版システム イメージ(32-bit)
  • Android v7 appcompat Support Library、Material Design テーマの下位互換機能
  • Android v17 Leanback Library(Android TV 対応用)
C/C++ による開発に Android NDK を利用している開発者のために:
  • NDK r10c による API Level 21 サポート
Android TV 環境に対応している開発者のために:
  • ADT-1 Developer Kit を対象とした Android 5.0 システム イメージの Over-The-Air(OTA)アップデート。数日以内に配信開始予定です。
前回のプレビュー版リリースと同様に、Nexus 5、Nexus 7(2013 年モデル)での動作検証に利用できる更新版システム イメージのダウンロードを提供いたします。これらのシステム イメージは Android 5.0 SDK に対応しており、開発者による動作検証を可能にするための最小限のアプリのみがプリ インストールされています:
  • Nexus 5(GSM/LTE)「hammerhead」端末用システム イメージ
  • Nexus 7(2013 年 Wi-fi モデル)「razor」端末用システム イメージ
開発者用プレビュー版には、OTA(Over-The-Air)形式でのアップデートの配信は予定しておりません。最新の開発者用プレビュー版を利用するには、端末を再度フラッシュする必要があります。11 月の一般向け OTA アップデートや その他の公式 OTA アップデートを受信するには、Nexus 端末に工場出荷時のイメージを再度フラッシュする必要がありますのでご了承ください。

あなたのアプリを Android 5.0 SDK に対応してください

近日中に予定されている Android 5.0 の一般ユーザーへの提供開始と、Nexus 6、Nexus 9、Nexus Player の提供開始に向けて、いくつかの準備をお願いいたします:
  • Android SDK Manager から最新のエミュレータ イメージを取得、または最新の Nexus 端末用システム イメージをダウンロードしてください。
  • アプリを Android 5.0 SDK を使って再ビルドしてください(もしプレビュー版 API を利用している場合は特に重要です)。プレビュー版 SDK と最終版 SDK の間で、API の一部が変更されています。
  • アプリが API Level 21 の ART 環境で問題なく実行できることをご確認ください。NDK で C/C++ 開発をされている場合は、64-bit エミュレータ上でも ご確認ください。API Level 21 の Android 5.0 端末では、ART がデフォルトの実行環境です。
アプリの動作が問題ないことが確認できましたら、Android 5.0 の新しい API や機能の活用をご検討ください。

あなたの既存アプリを Material Design に移行しましょう

Android 5.0 Lollipop は、Material Design 体験を提供するよう設計されています。あなたのアプリにおいて、さまざまな画面サイズやマルチデバイス環境における適切なユーザー インタラクションを導くための少数の重要な原則に配慮しつつ、先進的で柔軟なデザインを提供していただくことができます。

あなたの現在のアプリが Android 5.0 で動作することが確認できましたら、ぜひ AppCompat Support Library を使って、Material テーマに対応してください。Material Design でアプリの価値を高めるための推奨事項については、 Material Design ガイドラインタブレット最適化ガイドラインをご参照ください。Material Design について初めてプログラミングされる方は、Getting Started ガイドをご参照ください。

Google Play での準備をすすめましょう

いますぐに、Android 5.0 Lollipop をターゲットとするアプリを Google Play で公開していただけます。アプリのマニフェスト(AndroidManifet.xml)の中で、android:targetSdkVersion を "21" に更新し、動作検証して Google Play デベロッパーコンソールにアップロードしてください。

11 月 3 日(米国時間)から、Nexus 9 の提供開始が予定されており、これが一般ユーザーが利用可能な最初の Android 5.0 搭載端末となります。アプリを改修して動作検証が できましたら、お早めに Google Play に更新版を公開することをお勧めします。あなたのアプリが 以前のバージョンの Android をターゲットとしている場合にも、Android 5.0 システム イメージを使って動作確認を実施し、一般ユーザー向けの Android 5.0 提供が始まる前に必要に応じてアプリを更新することをお勧めします。

Nexus 6、Nexus 9 についてのより詳しい情報と、これらの端末上で あなたのアプリを最も美しく表現するための さまざまな手法について、追って順次情報を提供してまいります。

次は、Android TV です

Google は、最初の一般ユーザー向け Android TV 端末である Nexus Player を発表しました。これは、映画や音楽、ビデオ映像のストリーム再生を行うことのできるメディアプレイヤー端末であり、これまでなかった種類の Android ゲーム機でもあります。ユーザーは、ゲームパッドを使って高解像度テレビでゲームを楽しみ、外出時にはスマートフォン上で同じゲーム作品を引き続き楽しむことができます。また、この端末は Google Cast 機能も搭載しており、ユーザーは手元のスマートフォンやタブレット上で実行されている あなたのアプリのコンテンツをテレビ画面にキャストすることができます。

もし Android TV 対応アプリを開発されている場合には、Google Play デベロッパーコンソールを使って Android TV ユーザーにアプリを配信する方法についての追加情報を 11 月 3 日(米国時間)頃に公開予定ですのでご参照ください。あなたのアプリが TV Quality Guideline に準拠するように配慮することで、テレビ対応アプリの準備をすすめていただくことができます。

Android 5.0 Lollipop プラットフォームに対応した開発を、さあ始めましょう

もしまだこの新しいバージョンの Android をご覧になったことがない場合には、SDK をダウンロードして、すぐに開発を始めましょう。新機能の概要を Android 5.0 platform highlights でご確認ください。新しい API や挙動の変更点については API Overview をご参照ください。最新の DevBytes ビデオ番組をご覧になって Android 5.0 の新機能について理解を深めていただくこともできます。

この新しい Android リリースを、どうぞよろしくおねがいいたします。




[本記事は Android のプロダクト マネージャー Jamal Eason が 10 月 17 日(米国時間)に Android Developers blog に投稿した "Android 5.0 Lollipop SDK and Nexus Preview Images" という記事を元に、翻訳・作成しています。 - 松内]

Posted:
Posted by 鈴木拓生 Developer Relations Team

いつも Google Developer Relations の活動にご協力いただき誠にありがとうございます。

これまで Google Developer Relations チームは、GDG などのコミュニティと共に、カンファレンスや勉強会、ハッカソンなどの様々なイベントで開発者の皆さまと情報交換をすることで、Google のプラットフォームに対するご意見をいただいてきました。

今後皆さまにより有用なサポートを提供していくため、API やプラットフォームの利用状況、抱えている課題、改善要望などを伺うためのアンケートを作成しました。

アンケートはこちらから : 締め切り 10 月 15 日

また、回答の中で次の 6 つの中から選んでいただく団体に対し、Google が一回答あたり 10 ドルを寄付します : Khan Academy, World Fund, Donors Choose, Girl Rising, Raspberry Pi, Agastya(総額 20,000 US ドルまで)

英語での実施となりますが、日本の開発者の皆さまから貴重なご意見をいただくことで、今後のサポート活動の改善に取り組んでいきたいと考えています。ぜひご協力をお願いいたします。

Posted:
Posted by 北村英志 Developer Relations Team

[#http2study 代表の Jxck さんから ハイパフォーマンス ブラウザネットワーキング ミートアップ についての寄稿を頂きました。- 北村]

■概要

11 月 4 日(火)、今年の 5 月に日本語翻訳版が出版された、ハイパフォーマンス ブラウザネットワーキング(HPBN)の原著者である Ilya Grigorik (Google Developer Advocate)を迎え、ちょっと遅めの出版記念イベントとしてミートアップを開催します。

■イベント内容

名称: ハイパフォーマンス ブラウザネットワーキング ミートアップ(HPBN Meetup)
日時: 2014 年 11 月 4 日(火) 19:00 - 22:00 (受付 18:30 - 19:00)
場所: Google 東京オフィス 六本木ヒルズ森タワー
会費: 無料
主催: Google、#http2study
協力: オライリージャパン
ハッシュタグ: #hpbn
内容:

第一部: キーノート
著者である Ilya Grigorik によるキーノートを実施します。
著書の紹介や昨今のブラウザネットワーキングのアップデートなどについてお話いただく予定です。

第二部: Podcast Mozaicfm の公開収録
 Ilya Grigorik へのインタビューを Podcast 形式で実施します。
 Ilya 自身の話や、執筆に関する話、最近のネットワークテクノロジーなどをお話いただく予定です。また、Ilya への Q&A セッションも行う予定です。質問は下記より事前に募集します。この機会に是非色々質問してください(質問は日本語で構いません)。

質問がある方はこちらに投稿をお願いします:
http://qiita.com/Jxck_/items/a0cea350cc982915d092
  • 終了後、懇親会(軽食付き)を行う予定です。
  • 英日逐次通訳を行う予定です。
  • 当日はライブ配信を行う予定です(ただし諸事情により実施しない場合があります)。
  • トークの内容は後日 http://mozaic.fm という Podcast にて公開する予定です(ただし録音の状態などによっては公開しない場合があります)。当日会場に来られない方も是非質問を寄せてください。

■ 申し込み方法

申し込みフォームよりお申し込みください:
http://goo.gl/vG8RNT
※定員に達したため、受付を終了しました。

先着順による受付です。定員は 80 名です。定員になり次第、受付を締め切ります。
なお、参加証は10月28日(火)までに順次ご登録いただいたメールアドレス宛にお送りする予定です。

多くの皆様のご参加をお待ちしております。

Posted:
Posted by 北村英志 Developer Relations Team



日本人の開発者にとってひとつの課題は「英語」です。アメリカを発祥とする IT 関連のテクノロジーは、ほぼすべてのドキュメントがまず英語で記述され、それが各国語へと翻訳され、みなさんに届けられます。しかし、Google から提供される技術ドキュメントはなかなか日本語化できていないのが現状です。

もちろん、私たちの努力が足りないことは把握しています。すべての技術ドキュメントを日本語に翻訳することが理想ですが、あまりにも速く変化し続ける仕様、そしてそれにキャッチアップできるだけの技術的知識を持った翻訳者の確保は簡単ではありません。

実際にやってみると分かるのですが、翻訳のプロではないいち技術者が一本の記事を翻訳するには、短めの記事でも数時間を要することすらあります。それに加えて、高い技術的知識が求められるのです。

それにも関わらず、日本の開発者のみなさんが世界的に見ても高い技術力を保ち続けているのはなぜか?考えてみると、そこには独自に翻訳を行い、それを公開し、他の開発者の役に立てていただいている方々の存在があります。

RFC などを検索してみると、日本語版の一覧 が公開されており、それらが実は様々な人の手によって草の根で翻訳されてきたものであることが分かります。他にも、ネット上を探せば、様々な技術ドキュメントの翻訳が公開されており、我々の今の技術力が、そういった方たちの努力に支えられてきたものであることに気付かされます。

私の観測範囲で最近では、CSSRadar こと Yuya Saito さんの立ち上げた enja-oss というコミュニティ で、フロントエンドに関わる技術ドキュメントが多く翻訳されています。また、日本最大の HTML5 コミュニティである html5j では、英語部という支部が立ち上がり、英語ドキュメントの翻訳イベントを開催されたりしていました。

翻訳された日本語ドキュメントは、多くの場合ネット上の口コミで広まり、検索で見つけやすくなって様々な人の目に触れる機会を得ますが、中には埋もれていってしまうものもあるでしょう。せっかく貴重な時間を使って作業をしたにも関わらずです。

そんなみなさんの努力を無駄にすべきではありません。できることなら皆さんの翻訳を公式なものとして取り込み、ドキュメント提供側、翻訳者、読者すべてがハッピーになれる状況が理想です。

そこで実験な取り組みとして我々が始めたのが、ドキュメント本体をオープンソース化して、翻訳をソースコードと同様にして Contribute (貢献) してもらうことです。開発者コミュニティの力で翻訳されたドキュメントを公式に取り込むことで、間違いなく発見もされますし、そこからさらに改善されていくことも期待できます。これを試してみない手はありません。

我々は手始めに、元々オープンソースとして始まった HTML5Rocks翻訳を募集しました。(翻訳方法についての公式ドキュメントはこちら)

※ HTML5Rocksは、近年めまぐるしく変化し続けるウェブ標準技術の最新情報を機能ごとに紹介し、チュートリアルという形で仕様やテクニックについて解説するサイトです。

HTML5Rocks の日本語記事一覧

蓋を開けてみれば、実に多くの Contribution を頂きました。そこでこの機会に、これまでに集まった日本語記事を、翻訳者のみなさんへの感謝と、読者のみなさんへの周知の意味を込めて、ここでご紹介したいと思います。分かる範囲でどなたの手による翻訳かをまとめたつもりですが、もし抜けや漏れがありましたら @googledevjp までお知らせ下さい。(一部 Google による翻訳も含まれますが、HTML5Rocks のすべての日本語記事を一覧できる機会ですので、ぜひ合わせてご覧下さい。)
なお、最近クレジットを記載するフォーマットを用意しましたので、もし過去に Contribute してクレジットが記載されていない方がいらっしゃいましたら、ぜひ追記して Pull Request を送って下さい。
改めまして、Contribute くださったみなさま、ありがとうございました。また、古くなってしまった記事がありましたら、翻訳だけでなく内容についても Pull Request を受け付けておりますので、ぜひご検討下さい。

Posted:
Posted by 荒木 佑一 Developer Relations Team

「本記事は Google I/O 2014 アプリのテクニカル リード Bruno Oliveira が 8 月 27 日に Android Developers blog に投稿した「Conference Data Sync and GCM in the Google I/O App」という記事を元に、翻訳・作成しています。詳しくは元記事をご覧ください。 - 荒木」

クラウドとのデータ同期は多くのアプリで重要な部分です。Google I/O アプリも例外ではありません。これにあたって標準の Android 機能を存分に活用しました。SyncAdapter です。SyncAdapter を使うことにより、例えば定期的にアラームを仕掛けたりするような原始的なやり方に比べてたくさんのメリットがあります。システムがバッテリー効率を勘案して自動的に SyncAdapter のスケジューリングを行ってくれるからです。

データはローカルの SQLite データベースに格納してあります。ただし、アプリのあらゆる部分が直接データベースにアクセスするようなことはせず、Android 標準のメカニズムに従ってデータへのアクセスを管理しています。皆さんお分かりの通り、ContentProvider を利用しています。SQLite データベースに直接アクセスするのは ContentProvider だけです。アプリの他の部分は ContentProvider を通してのみデータにアクセスするようにします。これにより、データベース内でのデータ表現とアプリ中での表示を切り離して、柔軟に構成することができます。

I/O アプリは大きく分けて 2 種類のデータを扱います。カンファレンス データ(セッション、講演者、部屋など)と、ユーザー データ(ユーザー個人のスケジュール)です。カンファレンス データが Google Cloud Storage に格納された JSON ファイルからデバイスへの一方向にのみ同期されるのに対して、ユーザー データはユーザーの Google Drive AppData フォルダーとの間で双方向に同期されます。



カンファレンス データの効率的なダウンロード

Google I/O ともなると、カンファレンス データはかなり巨大です。セッション、部屋、講演者、地図の位置情報、ソーシャル ハッシュタグ、ビデオ ライブラリの項目など、すべての情報が入っています。すべてのデータを繰り返しダウンロードするのはバッテリー使用料や通信容量の観点から見て無駄です。そこで我々は、どうすればダウンロードしたり処理したりするデータの量を最小化できるか検討しました。

検討の結果、データをいくつかの JSON ファイルに分割し、それらをメインの JSON ファイル(「マニフェスト ファイル」)から参照するようにしました。マニフェスト ファイルの URL が唯一アプリの中に埋め込まれている URL です(Config.javaMANIFEST_URL 定数)。JSON ファイルを置いておくための場所として、今回の I/O アプリでは Google Cloud Storage を利用しましたが、HTTP でアクセスできる同様のホスティング サービスであれば同じように利用できます。

同期プロセスの最初の部分でマニフェスト ファイルを見に行きます。前回ダウンロードしたときから変更があるかチェックし、ある場合のみ処理します。このロジックを実装しているのは RemoteConferenceDataFetcher クラスの fetchConfenceDataIfNewer メソッドです。

public class RemoteConferenceDataFetcher {
    // (...)
    public String[] fetchConferenceDataIfNewer(String refTimestamp) throws IOException {
        BasicHttpClient httpClient = new BasicHttpClient();
        httpClient.setRequestLogger(mQuietLogger);
        // (...)

        // データが refTimestamp より新しいときのみダウンロード
        if (!TextUtils.isEmpty(refTimestamp) && TimeUtils
            .isValidFormatForIfModifiedSinceHeader(refTimestamp)) {
                httpClient.addHeader("If-Modified-Since", refTimestamp);
            }
        }

        HttpResponse response = httpClient.get(mManifestUrl, null);
        int status = response.getStatus();
        if (status == HttpURLConnection.HTTP_OK) {
            // データに変更があったので処理する
        } else if (status == HttpURLConnection.HTTP_NOT_MODIFIED) {
            // サーバーのデータに変更はないので何もしない
            return null;
        } else {
            // (エラー処理)
        }
    }
    // (...)
}

HTTP リクエストに If-Modified-Since ヘッダーがついていることに注意してください。もしマニフェストに前回から変更がない場合、レスポンス コードとして HTTP_OK ではなく HTTP_NOT_MODIFIED が返ってくるので、後はダウンロードもパースも必要ありません。つまり、マニフェスト ファイルに変更がない限り同期プロセスは実に経済的です。HTTP リクエストが 1 回で、レスポンスもとても短いものです。

マニフェスト ファイルのフォーマットは単純明快です。カンファレンス データを含んだ JSON ファイルへの参照が並んでいます。

{
  "format": "iosched-json-v1",
  "data_files": [
    "past_io_videolibrary_v5.json",
    "experts_v11.json",
    "hashtags_v8.json",
    "blocks_v10.json",
    "map_v11.json",
    "keynote_v10.json",
    "partners_v2.json",
    "session_data_v2.681.json"
  ]
}

同期プロセスは次にデータ ファイルを順に処理していきます。この部分も経済的に実装してあります。すでにデータ ファイルのキャッシュがある場合は処理を省略してローカルのキャッシュを使います。この部分は processManifest メソッドで行っています。

JSON ファイルをそれぞれパースして情報をメモリに乗せ、最後にまとめて ContentProvider に書き込みます。

ContentProvideOperation の効率的な実行

カンファレンス データの同期は効率的でなければなりませんが、それはダウンロードするデータの容量だけでなく、データベースに対して実行する処理の量についても同様です。出来る限り経済的に行うために、この部分も最適化されています。データベース全体を新しいデータで上書きするのではなく、既存のローカル データを活かして変更のあるデータだけ更新するようにするのです。

これは 3 階層目の重要な最適化です。これを実現するためには、アプリがメモリ上の情報と ContentProvider 内の情報を比較して、更新の必要があるかどうか判別できなければなりません。メモリーとデータベースのフィールドを 1 つずつ見ていってもいいのですが、手間ですし、すべてのフィールドを見ていくのでパフォーマンスが劣ります。その代わりに、今回は各エンティティーに「インポート ハッシュコード」というフィールドを付け加えることにしました。インポート ハッシュコードはデータから生成される弱いハッシュです。例えば、講演者のデータに対するインポート ハッシュは以下のように求められます。

public class Speaker {
    public String id;
    public String publicPlusId;
    public String bio;
    public String name;
    public String company;
    public String plusoneUrl;
    public String thumbnailUrl;

    public String getImportHashcode() {
        StringBuilder sb = new StringBuilder();
        sb.append("id").append(id == null ? "" : id)
                .append("publicPlusId")
                .append(publicPlusId == null ? "" : publicPlusId)
                .append("bio")
                .append(bio == null ? "" : bio)
                .append("name")
                .append(name == null ? "" : name)
                .append("company")
                .append(company== null ? "" : company)
                .append("plusoneUrl")
                .append(plusoneUrl == null ? "" : plusoneUrl)
                .append("thumbnailUrl")
                .append(thumbnailUrl == null ? "" : thumbnailUrl);
        String result = sb.toString();
        return String.format(Locale.US, "%08x%08x", 
            result.hashCode(), result.length());
    }
}

データベースでエンティティーが更新されるたびにそのインポート ハッシュがデータベースのカラムに保存されます。後から更新されている可能性のあるバージョンがあるときは、そのインポート ハッシュを計算してデータベースに保存されているバージョンのインポート ハッシュと比較するだけでいいのです。値が異なる場合は、データベースのエンティティーを更新する ContentProviderOperation を実行します。値が同一の場合、そのエンティティーの更新は必要ないので飛ばします。この部分の処理は SpeakersHandler クラスの makeContentProviderOperations メソッドなどで見ることができます。

public class SpeakersHandler extends JSONHandler {
    private HashMap mSpeakers = new HashMap();
    // (...)
    @Override
    public void makeContentProviderOperations(ArrayList list) {
        // (...)
        int updatedSpeakers = 0;
        for (Speaker speaker : mSpeakers.values()) {
            String hashCode = speaker.getImportHashcode();
            speakersToKeep.add(speaker.id);

            if (!isIncrementalUpdate || !speakerHashcodes.containsKey(speaker.id) ||
                    !speakerHashcodes.get(speaker.id).equals(hashCode)) {
         // スピーカーが更新されているので、ContentProviderOperation を発行する
                ++updatedSpeakers;
                boolean isNew = !isIncrementalUpdate || 
                 !speakerHashcodes.containsKey(speaker.id);
                buildSpeaker(isNew, speaker, list);
            }
        }

 // 古いスピーカーを削除
        int deletedSpeakers = 0;
        if (isIncrementalUpdate) {
            for (String speakerId : speakerHashcodes.keySet()) {
                if (!speakersToKeep.contains(speakerId)) {
                    buildDeleteOperation(speakerId, list);
                    ++deletedSpeakers;
                }
            }
        }
    }

buildSpeaker()buildDeleteOperation() というメソッド(ここでは中身を省略しています)はただ単に ContentProviderOperation を組み立てるだけです。ContentProvider にスピーカーの情報を挿入、更新、削除する ContentProviderOperation です。ここで注目していただきたいのは、ContentProviderOperation でスピーカー情報を更新するのはインポート ハッシュコードが変わったときだけだということです。また、以前は存在していたのに今はデータ上で参照されていないスピーカー情報はもう必要ないので、削除を行っています。

同期の信頼性を高める

I/O アプリの SyncAdapter はカンファレンス データの同期、ユーザーのスケジュールの同期、ユーザーからのフィードバックの同期など、いくつかのタスクを担っています。ネットワークの状態によっては、そういったタスクが途中で失敗することもあり得ます。それでも、あるタスクが失敗したからといって他のタスクを巻き添えにするのは好ましくありません。そのため、同期のプロセスはそれぞれ独立したタスクで構成されています。つまり、それぞれが try/catch ブロックで保護されています。これは SyncHelper クラスの performSync() メソッドでご覧いただけます。

// リモート同期を構成するタスク。
// 1 つづつ実行される(どれかが失敗しても他に影響を与えない)。
final int OP_REMOTE_SYNC = 0;
final int OP_USER_SCHEDULE_SYNC = 1;
final int OP_USER_FEEDBACK_SYNC = 2;

int[] opsToPerform = userDataOnly ?
        new int[] { OP_USER_SCHEDULE_SYNC } :
        new int[] { OP_REMOTE_SYNC, OP_USER_SCHEDULE_SYNC, OP_USER_FEEDBACK_SYNC};

for (int op : opsToPerform) {
    try {
        switch (op) {
            case OP_REMOTE_SYNC:
                dataChanged |= doRemoteSync();
                break;
            case OP_USER_SCHEDULE_SYNC:
                dataChanged |= doUserScheduleSync(account.name);
                break;
            case OP_USER_FEEDBACK_SYNC:
                doUserFeedbackSync();
                break;
        }
    } catch (AuthException ex) {
        // (... 認証エラーの後始末 ...)
    } catch (Throwable throwable) {
        // (... その他のエラーの後始末 ...)

        // 例外の発生をシステムに知らせる
        if (syncResult != null && syncResult.stats != null) {
            ++syncResult.stats.numIoExceptions;
        }
    }
}

同期プロセスの一部が失敗したときは syncResult.stats.numIoExceptions をインクリメントしてシステムに知らせます。こうしておけば、システムがあとで指数バックオフを利用しつつ同期を再試行します。

同期をいつ行うか。GCM の世界にようこそ。

カンファレンス データの更新を迅速に受け取ることができるかどうかは、ユーザーにとってとても重要です。特に Google I/O の最中(と、その前数日)はそうです。これを実現する方法として安易に思いつくのはアプリがサーバーをポーリングすることですが、当然のごとくそれでは通信帯域やバッテリーを多量に消費してしまいます。

より洗練された解決法として、我々は GCM (Google Cloud Messaging) を利用することにしました。サーバー側に新しいデータがあるときは、サーバーからすべての登録済みデバイスに GCM メッセージを送るのです。GCM メッセージを受け取ったデバイスは同期を開始して新しいカンファレンス データをダウンロードします。GCM メッセージを処理しているのは GCMIntentService クラスです。

public class GCMIntentService extends GCMBaseIntentService {

    private static final String TAG = makeLogTag("GCM");

    private static final Map MESSAGE_RECEIVERS;
    static {
        // 既知のメッセージと GCM メッセージの受信部
        Map  receivers = new HashMap();
        receivers.put("test", new TestCommand());
        receivers.put("announcement", new AnnouncementCommand());
        receivers.put("sync_schedule", new SyncCommand());
        receivers.put("sync_user", new SyncUserCommand());
        receivers.put("notification", new NotificationCommand());
        MESSAGE_RECEIVERS = Collections.unmodifiableMap(receivers);
    }

    // (...)

    @Override
    protected void onMessage(Context context, Intent intent) {
        String action = intent.getStringExtra("action");
        String extraData = intent.getStringExtra("extraData");
        LOGD(TAG, "Got GCM message, action=" + action + ", extraData=" + extraData);

        if (action == null) {
            LOGE(TAG, "Message received without command action");
            return;
        }

        action = action.toLowerCase();
        GCMCommand command = MESSAGE_RECEIVERS.get(action);
        if (command == null) {
            LOGE(TAG, "Unknown command received: " + action);
        } else {
            command.execute(this, action, extraData);
        }

    }
    // (...)
}

ここでは onMessage() メソッドが GCM メッセージの "action" フィールドに従ってメッセージを適切なハンドラーに引き渡しています。action フィールドが "sync_schedule" なら、メッセージは SyncCommand クラスのインスタンスに引き渡され、同期が開始します。ちなみに、SyncCommand クラスに対する GCM メッセージには jitter というパラメーターを指定することができます。同期は即座に行われるのではなく、jitter で指定された猶予期間までのランダムなタイミングで実行されます。これにより、全てのクライアントが一斉にリクエストを投げてサーバー側が大量のリクエストにさらされることを防いでいます。

ユーザー データの同期

I/O アプリではユーザーが興味のあるセッションを選択して自分個人のスケジュールを作ることができるようになっています。これは「私のスケジュール」画面で見ることができます。

このデータはユーザーが利用している複数の Android 端末間で同期され、さらに I/O のウェブサイトとも同期される必要があります。つまり、ユーザーの Google アカウントを使ってクラウド上にデータを保存する必要があります。今回は Google Drive AppData フォルダーを利用することにしました。

ユーザー データは SyncHelper クラスの doUserScheduleSync() メソッドで Google Drive に同期されます。ソース コードを見ていただくとわかりますが、このメソッドでは Google Drive HTTP API を使って Google Drive AppData フォルダーにアクセスし、ユーザーが登録したセッションのデータをクラウド上と端末内とですり合わせた上で、必要に応じてクラウド上のデータにローカルでの変更を反映しています。

つまり、ユーザーが Android 端末であるセッションを登録し、I/O のウェブサイト上で別のセッションを登録した場合、Android 端末と I/O ウェブサイトともに、両方のセッションが登録されている状態に同期されます。

これに加えて、ユーザーが I/O のウェブサイトでセッションを登録したり解除したりした場合にデータがすべての Android 端末に同期される必要があります。逆方向も同様です。このため、ユーザーがスケジュールに変更を行うたび、I/O ウェブサイトから GCM サーバーに通知を送ります。GCM サーバーはユーザーが利用しているすべてのデバイスに GCM メッセージを送信し、同期が開始されます。同じメカニズムが複数の端末の間でも働きます。つまり、あるデバイスでデータが更新されれば、他のすべてのデバイスに GCM メッセージが送信されるのです。

まとめ

データを新鮮に保つことは多くの Android アプリで肝心な機能です。ネットワーク通信やデータベースへの書き込みを最小限にとどめつつデータを最新に保ち、Google Cloud Storage や Google Drive や Google Cloud Messaging を活用して、複数のプラットフォーム、複数の端末間でデータの同期を取るなど、I/O アプリの開発での我々の取り組みについてご紹介しました。

Posted:
Posted by 鈴木拓生 Developer Relations Team

Google Developer Group(GDG)コミュニティの皆さんが今年の 8 月 〜 9 月に Android Wear の勉強会を日本各地で開催します。

今年の Google I/O で新たな SDK も公開され、日本でも実機の発売が開始されたのをきっかけに、GDG のコミュニティが主導となって 実際に Android Wear で 「どんなアプリが作れるのか」、「実際にアプリを作ってみる」を開発者の皆さまと一緒に学ぶ勉強会になっています。

参加を希望される方は、会場毎に詳細と参加お申し込みフォームが用意されていますので、ご確認のうえ登録ください。

イベント概要

■Android Wear Hackathon in Tokyo 2014-8
日程:2014 年 8 月 30 日(土)11:00 - 18:00
場所:Google 東京オフィス、六本木ヒルズ森タワー
定員:60 名
主催:GDG 東京
詳細:http://goo.gl/vo8jVh


■Global Android Dev Camp in 信州
日程:2014 年 9 月 12 日(金)18:00 - 21:30(アイデアソン)、9 月 13 日(土)09:00-18:00 (ハッカソン)
場所:9 月 12 日:塩尻インキュベーションプラザ SIP、9 月 13 日:扉温泉檜の湯(松本市)
定員:15 名
主催:GDG 信州
詳細:世界同時期開催の Global Android Dev Camp を GDG 信州でも行います。
9 月 12 日はアイデアソン、9 月 13 日はハッカソンを予定しています。
申込みhttp://goo.gl/mQNUvF


■ Global Android Dev Camp Kyoto 2014
日程:2014 年 9 月 13 日(土)10:00 - 17:30 ハッカソン
場所:京都リサーチパーク
定員:20 名
主催:GDG 京都
詳細:http://goo.gl/yLARA9
申込み:http://goo.gl/PKXwiD


■DevFest Shikoku - Android Wear Hackathon in Shikoku 2nd
日程:2014 年 9 月 28 日(日)10:00 - 17:00
場所:JR 高松駅前 e -とぴあ・かがわ クラスルーム C
定員:15 名
主催:GDG 四国
共催:日本 Android の会 香川支部
詳細:http://goo.gl/HRHSRF
申込み:http://goo.gl/aXsr80


開発者の皆さまのご参加をお待ちしております。

※四国会場の情報を追記いたしました