[go: nahoru, domu]

この記事は Todd Kerpelmen、デベロッパー アドボケートによる The Firebase Blog の記事 "Firebase Remote Config loading strategies" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。


Todd Kerpleman



Todd Kerpelman
Developer Advocate
FirebaseDevelopers チャンネル ファンの皆さんなら、Remote Config からアプリに値を読み込む最善策を紹介した動画が公開されたことをご存じかもしれません。




ですが、動画を見るより記事を読みたい方もいるでしょう。そこで、この内容を便利なブログ形式でもご紹介します。

すでに Remote Config を使ったことがある方なら、アプリでの Remote Config の実装は主に次の 3 つのステップで行われることをご存じでしょう。
  1. Remote Config のさまざまな既定値を設定します。これは、コードで、またはローカルの plist や xml ファイルから値を取り込むことで、端末上でローカルに設定されます。
  2. 次に、fetch() を呼び出して新しい値をクラウドからダウンロードします。
  3. 最後に activateFetched() を呼び出し、最初に設定された既定値に対してダウンロードした値を適用します。
この時点で、Remote Config に特定の値を問い合わせると、クラウドからアップデートされた値か、ローカルに設定されている既定値のどちらかが取得されます。これはすばらしい動作です。アプリの すべての 値を Remote Config 経由で取得できるようになるからです。既定値とは異なる値のみがダウンロードされるため、ネットワークの呼び出しは適切かつ最小限に抑えることができます。しかも、アプリのあらゆる動作を後からクラウド経由で変更できるという柔軟性が得られます。

とても簡単だと思いませんか。基本的には、そのとおりです。問題なのは、先ほどのステップ 2 です。ここではネットワークの呼び出しが必要になりますが、現実の世界では、トンネル、エレベーター、砂漠などのネットワーク接続が不安定な場所でアプリを使う人々がいます。そのため、ネットワークの呼び出しが完了するまでにどのくらい時間がかかるかは予想できません。つまり、ユーザーがアプリを使い始める前にこのプロセスが完了することは保証されません。

これを踏まえて、次の 3 つの戦略が考えられます。

戦略 1: 反映して更新

これはもっとも単純な戦略です。そのため、サンプルアプリやチュートリアルでは、一般的にこの方法が使われています。この考え方では、クラウドから新しい値をダウンロードし終えたタイミングで、完了ハンドラを使って activateFetched() を呼び出し、それをすぐに反映させてから、アプリをアップデートして処理を続けます。


この戦略が優れているのは、ユーザーがすぐにアプリを使い始めることができる点です。しかし、アプリを使用している最中にボタンのテキストや UI 配置などの重要な値が変更されるのは違和感があるでしょう。ユーザー エクスペリエンスの低下につながるかもしれません。そのため、多くのデベロッパーは次のようなソリューションを選ぶ傾向にあります。

戦略 2: ロード中画面の追加

よく使われる戦略として、ユーザーがアプリを開始した際にロード中画面を表示する方法があります。先ほどと同じように、完了ハンドラで activateFetched を呼んで値を即座に適用しますが、アプリのインターフェースを更新するのではなく、ロード中画面を閉じてメイン コンテンツに遷移します。ロード中画面が終わってアプリに入ると、すべてがうまくいっているように見えます。ほとんどの場合、クラウドから最新の値がダウンロードされ、アプリがそれを使う準備も完了していることが保証されます。



しかし、この方式の明らかに大きな欠点は、ロード中画面が追加されたことです。開発にかかる時間や手間もさることながら、ユーザーがアプリに入る際の壁になります。しかし、すでにアプリにロード中画面があり、開始時に他の作業やネットワークの呼び出しを行っているのであれば、これは優れた戦略となるでしょう。その場合は、他の初期化作業と合わせて Remote Config の値を取得しましょう。

しかし、アプリにまだロード中画面がない場合は、別の戦略を試してみることをお勧めします。たとえば、次のようなものです。

戦略 3: 値を読み込み、次回に反映

これはあまり直感的ではないかもしれませんが、ぜひお読みください。ユーザーがアプリを開始した際、即座に activateFetched() を呼び出します。すると、クラウドから取得した前回の値が適用されます。ユーザーはすぐにアプリを使い始めることができます。
その間に、非同期に fetch() を呼び出してクラウドから新しい値を取得します。完了ハンドラでは、何もしません。そもそも、完了ハンドラを追加する必要すらありません。クラウドから取得した値は、ユーザーが次にアプリを起動して activateFetched を呼び出すまで、端末のローカルに保存されたままになります。



これは、ユーザーがすぐにアプリを使い始めることができる優れた戦略で、アプリのインターフェースが突然変わったりするような奇妙な動作も起こりません。明らかな欠点は、ユーザーがアップデートされた Remote Config の値を確認できるまで 2 セッションが必要になることです。この動作がアプリにふさわしいかどうかは、ご自身で判断してください。
タワー ディフェンス ゲームの設定値の微調整に Remote Config を使うような場合は、おそらく問題ないでしょう。しかし、日ごとにメッセージを送信したり、日付に固有のコンテンツを提供したりする場合(アプリのスキンを休日用に変更するなど)にはふさわしくないかもしれません。

戦略 3.5: 戦略 2 と 3、または 1 と 3 の中間

Remote Config が優れている点の 1 つは、最後に fetch() の呼び出しが成功したのはいつなのかがわかることです。そのため、Remote Config データの最終取得日時を最初に確認するという中間戦略をとることができます。それが最近(たとえば、48 時間以内など)であれば、そのまま先に進み、「前回取得した値を適用して、次回のために値を取得する」戦略をとることができます。それ以外の場合は、最初の 2 つの戦略のうちどちらかを実行します。



キャッシュについて

ここまで読み込み戦略の話をしてきたので、関連するキャッシュの話題に進みましょう。Remote Config の値は、fetch() を呼び出すたびに即座に取得されるのではなく、12 時間キャッシュされます。この点を勘違いしているデベロッパーもいるようです。また、キャッシュ時間を短くすることはできますが、ネットワークの呼び出しを頻繁に実行しすぎると、クライアントまたは Remote Config サービスによる制限がアプリに適用されることもあります。

では、そもそもなぜこのようなキャッシュや制限の仕組みが存在しているのでしょうか。

その理由の 1 つは、たとえ何百万というユーザーが使うアプリがあっても、確実にこの無償サービスを維持できるようにするためです。適切に動作するキャッシュの仕組みが追加されているので、アプリがどんなに有名になったとしても、このサービスを無償で利用できることが保証されています。

さらに、この仕組みは質の悪いコードからサービスを守る方法としても有効です。中には、意図せず fetch() を何度も呼び出すようなデベロッパーもいます(もちろん、皆さんのことではありません)。あるデベロッパーが意図せず DDoS を行ったとしても、サービス全体が影響を受けないようにする必要があります。クライアント側のライブラリでキャッシュされた値を提供したり、頻繁に行われるネットワークの呼び出しを制限したりすれば、安全にサービスを維持できます。

これは、ユーザーにとってもメリットになります。優れたキャッシュ戦略があれば、アプリから不要なネットワークの呼び出しを何度も行ってユーザーの電池やデータプランを使い果たすようなことはなくなります。そのため、優れたキャッシュ戦略は常に実装すべきものです。

もちろん、Remote Config の実装の開発やテストの際に、この動作を不便に感じることがあるでしょう。そのため、デベロッパー モードをオンにしてローカルでの制限動作を無視できるようになっています。しかし実際には、たとえユーザーが毎日アプリを使ったとしても、通常、こういったキャッシュ動作はメリットだけをもたらします。

しかし、もっと頻繁に値をプッシュしたい場合はどうすればよいでしょうか。たとえば、Remote Config で「時間ごとのメッセージ」のような機能を提供したいような場合です。率直に言えば、これは Remote Config サービスが適切なソリューションではないことの表れかもしれません。タイムリーに何かを行いたい場合は、Realtime Database の利用を検討するとよいでしょう。

一方で、頻度の少ない Remote Config アップデートを大至急適用したい場合もあるかもしれません。前回の変更で意図せずにアプリ内の秩序を乱してしまった場合や、ただちに新しい値を全員に適用したい場合などが考えられます。キャッシュを回避してクライアントに値の取得を強制させるには、どうすればよいでしょうか。

考えられるソリューションの 1 つは、Firebase Cloud Messaging を使うことです。FCM を使うと、すべての端末にデータのみの通知を送り、保留されている緊急アップデートについて知らせることができます。この通知を受信したアプリは、なんらかのフラグをローカルに保存します。次にユーザーがアプリを起動した際に、そのフラグを確認し、それが true であれば、即時性が保証されるようにキャッシュ時間 0 で Remote Config の値を取得します。完了時には、忘れずにフラグをクリアして通常のキャッシュ動作に戻すようにしてください。

受信した通知に対してクライアントを応答させ、即座にスリープを解除して Remote Config サービスから新しい値を取得させたくなるかもしれませんが、インストールされたすべてのアプリが一斉にこれを行うと、サーバー側の制限の対象になる可能性が高くなります。そのため、このような動作をさせるのではなく、前述の戦略を使用します。そうすることで、すべてのユーザーがアプリを開くまでの期間、ネットワークの呼び出しを分散させることができます。

いかがでしょうか。今回は、Remote Config から値を読み込む際のちょっとしたテクニックについて紹介しました。なにか参考になりましたか?YouTube 動画のコメントでぜひ共有してください。または、Firebase Talk グループでどのように Remote Config を実装しているかをお知らせください。


Posted by Khanh LeViet - Developer Relations Team

この記事は Aurash Mahbod、Google Play ソフトウェア エンジニアによる Android Developers Blog の記事 "Android Instant Apps starts initial live testing" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。

昨年の Google I/O で紹介された Android Instant Apps は、Android アプリをインストールせずに実行できる新たな方法です。ユーザーがスムーズにアプリを見つけて実行できるようにする Google の取り組みにおいて、重要な部分を占めているのがこの Instant Apps です。

私たちは、少数のデベロッパーと共同で、ユーザーやデベロッパーのエクスペリエンスを改善する作業を続けてきました。本日(*原文公開当時)より Android ユーザーは限定テスト中の Instant Apps を初めて利用できるようになりました。たとえば BuzzFeed、Wish、Periscope、Viki がリリースしたアプリが含まれています。ユーザーのフィードバックを収集してリリースを重ねることで、今後、この体験をさらに多くのアプリやユーザーに展開できる予定です。

Instant Apps 対応アプリを開発するには、既存の Android アプリをアップデートして Instant Apps 機能に対応させ、アプリをモジュール化して、アプリの一部を即座にダウンロードして実行できるようにする必要があります。Android API や Android Studio プロジェクトは同じものを使うことができます。本日から、Instant Apps の開発準備に向けた重要なステップを開始できます。完全な SDK は、数か月以内に公開予定です。

Instant Apps には、すでに多くのデベロッパーから絶大な関心が寄せられています。皆さんからフィードバックをいただき、今年中にさらにすばらしい体験を共有できることが待ちきれません。ご期待ください。



Posted by Yuichi Araki - Developer Relations Team

この記事は Suzanne van Tienen、Google Play プロダクト マネージャーによる Android Developers Blog の記事 "Manage paid orders and payments settings from the Google Play Developer Console" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。

現在私たちはコミュニティからのフィードバックに基づき、有料アプリやアプリ内購入、定期購入が可能なアプリを開発したデベロッパーの販売関連の操作を改善し、シンプルにしようとしています。

まず、注文管理を Google Payments Center から Google Play Developer Console に移動し、いくつかの機能を改善しています。課金設定は、引き続き payments.google.com からも利用できますが、Developer Console からもアクセスできるようになります。新機能には適切なアクセス制御を設定できるため、必要なツールへのアクセス権のみをユーザーに与えることができます。



Google Play Developer Console の新しい注文管理タブ

今まで Google Payments Center で行っていたタスクは、Developer Console から実行できます。さらに、いくつかの改善も行っています。
  • 一括返金: 複数の注文を選択して同時に返金できるようになり、個々に返金する必要はなくなります。
  • 定期購入のキャンセル: 注文管理タブから、直接(別の UI を開かずに)返金と定期購入の取り消しを行うことができます。
  • パーミッション: Developer Console に「注文の管理」という新しいユーザー アクセス パーミッションを追加しました。このパーミッションを持つユーザーは、注文の検索、返金、定期購入のキャンセルが可能です。その他の機能は読み取り専用となり、売上レポートは表示されません(売上データは、「売上レポートの表示」パーミッションを持つユーザーのみ参照できます)。Developer Console からアクセスする場合、課金設定にアクセスできるのはアカウント所有者のみです。

注文管理の Developer Console への移行

Developer Console から注文管理にアクセスできるようになり、1 月 23 日以降、Payments Center からは注文管理にアクセスできなくなりました。Developer Console アカウントに新しいユーザーを追加する方法を次に示します。
  1. Google Payments Center にログインし、すべての既存ユーザーを確認します。
  2. Developer Console にログインし、Developer Console の注文管理にアクセスする必要があるすべてのユーザーに以下のいずれかまたは両方のパーミッションを追加します。
    1. 売上レポートの表示: 売上レポートへのアクセスと参照に必要なアクセス権を与えます。
    2. 注文の管理: 注文の参照や返金に必要なアクセス権を与えますが、売上の集計や統計の表示、販売および支払レポートのダウンロードはできません。
  3. 注文管理を行う新しい場所をユーザーに知らせます。


Posted by Takeshi Hagikura - Developer Relations Team

この記事は Megan Ruthven、ソフトウェア エンジニアによる Android Developers Blog の記事 "Silence speaks louder than words when finding malware | Android Developers Blog" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。

Android セキュリティ チームでは、端末をさらに安全かつスムーズに操作できるようにする方法を模索しています。Google Play を利用する全端末を対象としたセキュリティ ソリューションの 1 つがアプリの確認です。アプリの確認では、端末上に有害な可能性があるアプリ(Potentially Harmful App、PHA)がないかどうかを確認します。PHA が見つかった場合、アプリの確認で警告を表示し、ユーザーがそのアプリをアンインストールできるようにします。

しかし、アプリの確認が行われない端末もあります。新しいスマートフォンを買った場合などセキュリティとは関係ない理由でそうなることもあれば、心配すべきことが起きていることもあります。アプリの確認が行われない場合、その端末は利用不能または安全ではない(Dead or Insecure、DOI)と見なされます。あるアプリをダウンロードした端末が高確率で DOI 端末となった場合、そのアプリは DOI アプリと見なされます。私たちは Android ユーザーを保護するため、アプリが PHA かどうかを DOI 指標などのセキュリティ システムから判断しています。また脆弱性を発見した際には、セキュリティ アップデート システムを使って Android 端末にパッチを適用しています。

本ブログ投稿では、セキュリティに起因して端末が動作しなくなる原因を特定し、今後それが起こらないようにするための Android セキュリティ チームの研究について紹介します。
DOI アプリの判別

この問題をさらに深く理解してユーザーを保護するため、Android セキュリティ チームはアプリのインストール試行回数と DOI 端末との関連を調査し、端末に危害を与えるアプリを探しています。
そのような要素を念頭に置き、「継続利用率」に着目しました。アプリのダウンロード後もアプリの確認によるセキュリティ チェックが定期的に行われていれば、その端末は利用継続中と見なされます。そうでない場合、DOI の可能性があると見なされます。特定の日にアプリをダウンロードし、継続利用中のすべての端末の割合がアプリの継続利用率となります。継続利用は端末の健全性を示す強力なインジケーターなので、私たちはエコシステムの継続利用率が最大化されるよう努めています。

そこで、端末の継続利用率はすべてのアプリで同じになるはずという前提のもと、アプリの DOI スコアラーを利用します。あるアプリの継続利用率が平均よりも低い標準偏差を示す場合、DOI スコアラーはそのアプリにフラグを立てます。平均から標準偏差の数値を計算する一般的な方法に、Z スコアがあります。Z スコアの方程式を次に示します。

  • N = アプリをダウンロードした端末数
  • x = アプリをダウンロードした継続利用中の端末数
  • p = 何らかのアプリをダウンロードした端末の継続利用率

ここで、アプリの継続利用率の Z スコアを DOI スコアと呼びます。Z スコアが -3.7 より低い場合、DOI スコアはそのアプリが統計的に著しく低い継続利用率であるという指標になります。つまり、帰無仮説を真とする場合、Z スコアの大きさがそこまで大きくなる確率は 0.01% 以下しかありません。この場合の帰無仮説とは、アプリが低い継続利用率を示すのは、アプリの動作とは関係なく偶然であるということです。
これによって、極端な値(継続利用率が低くダウンロード数が多い)を示すアプリが DOI リストのトップに現れることになります。そこから DOI スコアと他の情報を組み合わせて、アプリを PHA に分類するかどうかを判断します。次に、アプリの確認を使用してインストール済みのアプリを削除し、今後はそのアプリがインストールされないようにします。

同じ端末にダウンロードされた通常のアプリと DOI アプリの違い


実際の結果
DOI スコアを使うと、3 つの有名な不正ソフトウェアである HummingbadGhost PushGooligan を含むアプリを他の手段に比べて多く判別できました。各マルウェアの動作は異なるにもかかわらず、DOI スコアラーはこの 3 つの不正ソフトウェアに関連する 25,000 以上のアプリを判別しています。これらのマルウェアが Android の機能を損ない、無視できない数のユーザーがファクトリー リセットを行ったり、端末の利用をやめたりしているためです。このアプローチは、PHA を検出し、それが広まる前にブロックするもう 1 つの有望な手段となっています。DOI スコアラーがなければ、これらのアプリの大半が人手によるレビューをすり抜けていたはずです。
DOI スコアラーを含む Android の不正ソフトウェア対策機能は、Android のユーザーやデベロッパーを保護するための何層にもわたる取り組みの 1 つです。Android のセキュリティや透過性に関する取り組みの概要については、こちらのページをご覧ください。



Posted by Yuichi Araki - Developer Relations Team

[この記事は Paul Bakaus、Google AMP デベロッパー アドボケートによる Accelerated Mobile Pages Project の記事 "Why AMP Caches exist – Accelerated Mobile Pages Project" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。]


以下の記事は、Google の AMP デベロッパー アドボケート、Paul Bakaus が Medium に投稿したものです。

AMP(Accelerated Mobile Pages)プロジェクトにおいて、キャッシュは必要不可欠な仕組みです。にもかかわらず、大きく誤解されているコンポーネントの 1 つでもあります。キャッシュにリンクするのではなく、AMP サーフェス(たとえば Google)に AMP ページを掲載できないのはなぜかと、毎日のようにデベロッパーから聞かれます。キャッシュ モデルがウェブのオリジン モデルを破壊しているのではないかと心配する方もいれば、アナリティクス属性や canonical リンクの共有について心配する方もいます。また、制御できないサーバー上にページがホストされることについて心配する方もいます。本記事では、これらすべてを取り上げ、キャッシュの存在意義についての理解を深めます。

AMP Cache によって多少のトレードオフは生じるものの、これによって一貫性のある高速で使いやすい仕組みをユーザーに提供できます。キャッシュは、次の目的で設計されています。

  • すべての AMP ページが実際に有効な AMP であることを保証する
  • AMP ページが効率的かつ安全にプリロードできるようにする
  • コンテンツに対して、ユーザーにメリットをもたらすさまざまなパフォーマンスの最適化を追加で実施する

まずは、次の点から見てみましょう。

基本: アナリティクス属性とリンク共有
AMP Cache モデルはオリジン モデル(ページは自身のドメインで提供する)に従ってはいませんが、すべてのトラフィックはサイトオーナーに帰結します。AMP は <amp-analytics> タグを通してさまざまなアナリティクス プロバイダ(現時点で 26 あり、さらに増加中)をサポートしており、成功の度合いの計測や、トラフィックが正しく関連づけられていることを確認できます。

ユーザーやデベロッパーがキャッシュされた AMP の結果から canonical ページへの「クリックスルー」を行いたい理由を尋ねてみると、多くの人がリンク共有と答えます。また、当然ながら、canonical URL の代わりに google.com の URL をコピーするのは気が進みません。しかし、これは皆さんが考えるほど大きな問題ではありません。Google はキャッシュされた AMP 検索結果を Schema.org や OpenGraph のメタデータを使って補正するので、それに従うプラットフォームにリンクを投稿すれば、canonical URL が共有されることになります。しかし、共有フローを改善する機会は他にもあります。ネイティブのウェブビューでは、アプリがサポートしていれば canonical を直接共有できます。また、ユーザーからのフィードバックに基づき、Google のチームはすべてのサーフェスで canonical URL に簡単にアクセスできる方法を作成しています。

この点が解決したところで、次はもう少し深く掘り下げてみましょう。

AMP ラベルが表示されていれば、それは AMP である
AMP Project は厳格な検証が必要なエコシステムで構成されており、それによって高パフォーマンスと高品質を保証しています。検証ツールの中には開発中に使えるものもありますが、コンテンツをユーザーに提示する最終段階で妥当性を保証するのは AMP Cache です。

AMP Cache から AMP ページが初めてリクエストされると、まずキャッシュがドキュメントを検証します。そこで問題が検出された場合、ページはユーザーに提供されません。AMP と統合されているプラットフォーム(Bing、Pinterest、Google など)では、トラフィックを直接オリジン上の AMP ページに送るか、AMP Cache に送るかを選択できます。しかし、妥当性が保証されるのは、ページがキャッシュから提供されるときだけです。ユーザーに AMP ラベルが表示されている場合、ほぼ確実に高速でユーザー フレンドリーな操作ができることが保証されています(低速で有効な AMP ページを作ることができた場合は例外です。これは難しいことですが、不可能ではありません。たとえば、巨大なウェブフォントがあると、このようなことが起きるかもしれません)。

想像以上に重要なプリレンダリング
この投稿で一番のポイントになるのは、プリレンダリングです。とりわけ AMP で行われるプリレンダリング的な動作は、オリジン サーバーで直接ホスティングすることで理論的に可能なあらゆる高速化をはるかに上回る効果があります。オリジン サーバーがユーザーの近くにあれば、まれに数ミリ秒ほど高速化できる可能性もあります。しかし、プリレンダリングはほぼ確実に最大の効果を発揮します。

体感速度を大幅に改善
実際、プリレンダリングを使うと、ミリ秒単位ではなく秒単位での高速化が可能です。AMP JS ライブラリによるさまざまなパフォーマンス最適化とは異なり、プリレンダリングの効果はかなり劇的です。この点が「即時的に表示される感覚」に大きく貢献しています。
cache_post
完全なプリレンダリングよりもはるかに効率的
もしそれだけの話であれば、オリジン サーバーから簡単に AMP ページをプリレンダリングできるはずです。しかし、オリジン上では、ページが有効な AMP であることは保証できません。有効な AMP であることは、AMP JS ライブラリが行うカスタム プリレンダリングにとって非常に重要です。リンクのプリフェッチのような単なるページ全体のプリレンダリングとは異なり、AMP のプリレンダリングではユーザーの帯域幅や CPU、電池の利用も制限されます。

有効な AMP ドキュメントは、プリレンダリングの段階で「協調的」に振る舞います。最初に表示されるビューポートのアセットのみがプリロードされ、サードパーティ製のスクリプトは実行されません。これによって、はるかに低コストで、帯域幅や CPU の負荷も低いプリロードを実現しています。さらに、プラットフォームは最初のページだけでなく、ユーザーがクリックしそうないくつかの AMP ページのプリレンダリングもできるようになります。

安全な埋め込み
AMP Cache はブラウザのプリレンダリングに依存していないため(上記のセクションを参照)、通常のナビゲーションでページからページへの移動を行うことはできません。そのため、AMP キャッシュ モデルでは、プラットフォームのページ上でインラインでページをオープンする必要があります。リクエストされた AMP ページで安全にその処理を実行できることが、AMP Cache によって保証されます。


  • 検証ツールによって、メイン ドキュメント内にクロスサイト スクリプティング(XSS)がないことが保証されます。
  • さらに、AMP Cache はドキュメントを解析して明確な方法でシリアライズし直します(つまり、HTML5 のエラー訂正には依存しません)。これによって確実に、バグや差異を解析するブラウザで XSS を防ぐことができます。
  • キャッシュは、コンテンツ セキュリティ ポリシー(CSP)を適用します。これによって、XSS 攻撃に対する多層防御が提供されます。


プライバシーの強化
さらに、AMP Cache はプリレンダリングの際に重要な問題となり得るものを取り除きます。AMP ページをプリロードしているコンテンツ プラットフォームの結果ページで検索を行った場合、プリロードされた AMP ページは自分がプリロードされていることがわかりません。

次のように考えてみましょう。たとえば、「breakfast burrito」で検索を行うとします。私のことをよく知っている方なら、同じタイトルの Parry Gripp の曲を検索していることは自明でしょう。しかし、検索結果ページには、実際に朝食のブリトーを売っているファストフード チェーンの AMP 検索結果も表示されます。翌月には、リンクをクリックしていないのに(ひょっとするとクリックするかもしれませんが…)あちこちに表示される実際の朝食のブリトーは見たくないと思うようになるでしょう。広告主も、ブリトーに関する意味のないマーケティングを繰り返して無駄なお金を使いたいとは思いません。AMP のプリロードは AMP ページのサイトオーナーや関連するサードパーティには隠蔽されるため、これはユーザーと広告主の双方にとってメリットになります。

劇的な高速化につながる自動最適化
AMP Cache はまず以上のようなことを行いますが、その後もたくさんの変換を行っています。具体的には、次のような最適化が挙げられます。

  • (大規模サイトオーナーだけでなく)すべてのコンテンツに対して、一貫性のある高速なコンテンツ配信ネットワークを無償で提供
  • スクリプトの理想的な順番への並び替え、重複するスクリプトタグの削除、不要な引用符や空白文字の削除などによる HTML の最適化
  • キャッシュ時間を無限にするための JavaScript URL の書き換え
  • イメージの最適化(帯域幅を平均 40% 削減)


イメージ圧縮という側面だけでも、Google はキャッシュを通してロスレス圧縮(EXIF データの削除など、視覚的変化のない圧縮)やロス圧縮(視覚的変化がある圧縮)を行っています。さらに、対応ブラウザに対してはイメージを WebP に変換し、srcset 属性(いわゆるレスポンシブ イメージ)がない場合は自動生成して、各端末で正しいサイズのイメージを生成、表示します。

さらなる改善に向けて
皆さんの言いたいことはわかります。AMP Cache プロバイダは、コンテンツのミラーリングを行っています。これは重要な役割で、大きな責任が伴います。キャッシュ プロバイダがすべての AMP ページに不快な広告を挿入するといったとんでもないことをしでかせば、AMP はサイトオーナーにとって有用なソリューションではなくなり、衰退してゆくことになるでしょう。

AMP は、サイトオーナーやユーザー、プラットフォームにとってモバイルウェブをよりよいものにするために、皆さんとともに作り上げたものです。AMP チームが AMP Cache の厳格なガイドラインをリリースしたのはそのためです。興味深い点を 2 つ抜粋しましょう。ガイドラインでは、コンテンツは「ソース ドキュメントを視覚的、UX 的に忠実に再現」できる必要があり、キャッシュ プロバイダはたとえキャッシュ自身が使われなくなったとしても、 URL はいつまでも使えるようにしなければならないことが明示されています。これらを始めとするさまざまなルールによって、キャッシュが皆さんのコンテンツを壊さないことが保証されています。

何よりも重要なのは、AMP Cache は 1 つだけではなく、そこには大きな余地があることです。先日、実際に Cloudflare が独自のキャッシュを発表しています。AMP Cache ガイドラインがリリースされたので、そのルールに従う限り、他のインフラ会社も新しい AMP Cache を作ることができます。どのキャッシュを選ぶかは、AMP を統合したプラットフォーム次第になります。

キャッシュからウェブ標準へ
以上で、AMP Cache がユーザー フレンドリーなモバイルウェブを即時的に提供するメリットやトレードオフについて理解していただけたと思います。しかし、同じようなさまざまな劇的な最適化をトレードオフなしに、あるいはキャッシュを使わずに行えたらどうでしょう。

個人的には、将来そういったことを実現し、キャッシュ モデルの先を行く(たとえば、アセットが読み込まれる前にページがどのように見えるかがわかる静的レイアウト システムなど)まだ発明されていないウェブ標準ができることを夢見ています。

2016 年、私たちは CPP でその最初の 1 歩を踏み出しました。現在、CPP は Feature Policy となっています。これは、「このサイトでは document.write や iframe でのサードパーティ ページの読み込みは許可しない」といったことを指定する方法を提供します。静的レイアウトや安全なプリレンダリングのような高度な概念には、さらなるウェブ プラットフォームの変化が必要です。しかし、未来へのタイムトラベルのように、不可能なことではありません。ただ非常に難しいだけです。

TwitterSlack で連絡と取りながら、一緒にこれを探求してみませんか。また、私はいつも皆さんの質問やアイデア、懸念に耳を傾けていることもお忘れなく。前進あるのみ!



Posted by Yoshifumi Yamaguchi - Developer Relations Team

この記事は セキュリティおよびプライバシー エンジニアリング、Ryan Hurst、Gary Belvin による Google Security Blog の記事 "Google Online Security Blog: Security Through Transparency" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。

暗号化はウェブの基礎をなすテクノロジーです。私たちは多くの時間をかけて、暗号化されたアプリを使いやすくするという複雑な作業を行ってきました。その課程で、メッセージの宛先を正確に指定するために、受信者の公開鍵を見つける安全で汎用的な方法が重要であることに気づきました。この仕組みはさまざまに応用できるだけでなく、これを行う汎用テクノロジーはどこにも存在していません。

確実にインターネット規模まで拡張でき、信頼されていないサーバーを経由して安全な通信を確立する方法を提供するソリューションが必要になります。Certificate Transparency から得た着想と CONIKS を組み合わせれば、私たちが望む以上の特性を持つシステムを構築できることがわかりました。

その結果として生まれたのが、オープンソース プロトタイプとして本日(*原文公開当時)公開する Key Transparency です。

Key Transparency が役立つ理由
危殆化された(compromised)サーバーからユーザーを守る既存の手法では、ユーザーが手動で受信者のアカウントを検証する必要がありますが、うまく機能しているとは言えません。PGP での暗号メールの web-of-trust がその例です。これが発明されて 20 年以上たっていますが、発明者本人も含め、ほとんどの人々はこれを使えないまたは使おうとしていませんメッセージング アプリ、ファイル共有、ソフトウェアのアップデートも、同じ課題に悩まされています。

Key Transparency を開発した目的の 1 つは、このプロセスを簡素化し、専門家以外のユーザーでも使えるインフラを作ることでした。オンライン上の個人と公開鍵との関係は、自動的に検証可能かつ広く監査可能であるべきです。ユーザーはアカウントに紐付けられているすべての鍵を参照できるべきですし、同時にレコード改ざんの試みも可視化する必要があります。これにより送信者は、アカウントの所有者による検証済みの鍵を常に使い回せるようになります。

Key Transparency は汎用的で透過性のあるディレクトリです。これを活用すれば、独立してアカウント データを監査できるさまざまなシステムを簡単に構築できるようになります。Key Transparency は、データの暗号化や認証を行う必要があるさまざまなシナリオで利用でき、ユーザーにわかりやすいセキュリティ機能を実現するとともに、アカウント復旧などの重要なユーザーニーズにも対応できます。

将来の方向性
Key Transparency はまだ生まれたばかりです。Key Transparency は最初のオープンソース リリースを迎えましたが、私たちは暗号コミュニティやその他の業界のリーダーとの会話を継続し、フィードバックをお願いし、誰もが高度なセキュリティを使えるようにするための標準の作成に向けて作業を進めています。

複数年にわたる Key Transparency の開発期間中は、CONIKS チーム、Open Whisper Systems、Yahoo! や Google 内部のセキュリティ エンジニアリング チームなどと協力してきました。その皆様に感謝を捧げます。

私たちの目的は、Key Transparency を汎用的で拡張や相互運用が可能なオープンソース公開鍵ディレクトリへと進化させ、相互に監査可能なディレクトリのエコシステムを構築することです。KeyTransparency.org は、この新しいテクノロジーに関連する皆さんのアプリや提案、貢献をお待ちしています。


Posted by Eiji Kitamura - Developer Relations Team

2017 年 2 月 9 日(木)、Chrome Tech Talk Night #9 を開催します。今回は Chrome セキュリティチーム の Product Manager である Emily Schechter の来日に合わせ、ウェブをよりセキュアで安心できる環境にする HTTPS への移行について考えるセッションを集めました。


  • Case for HTTPS everywhere - Emily Schechter (@emschec), Google
  • CSP a Powerful Security Steroid - Jxck (@Jxck_), Google Developer Expert
  • なぜ/どうやってクックパッドは HTTPS に移行したのか - 星 北斗 (@kani_b), クックパッド株式会社 インフラストラクチャー部
なお、今回のイベントに通訳は付きません。Emily Schechter の講演は英語であることを予めご了承の上ご参加下さい。

イベント概要

名称:Chrome Tech Talk Night #9
日時:2017 年 2 月 9 日(木) 19:00 - 21:00 (受付 18:30 〜 19:30)
   ※ 終了後、懇親会(軽食付き)を行う予定です。
場所:グーグル株式会社 六本木オフィス
   東京都港区六本木 6-10-1 六本木ヒルズ 森タワー
会費:無料
定員:100 名
主催:グーグル株式会社
ハッシュタグ: #chromejp

申し込み方法

参加を希望される方は以下のフォームよりお申込みをお願いします。
https://goo.gl/forms/tMDQvaYAq6W7hyha2

プログラム

18:30 - 19:00 受付開始
19:00 - 21:00 セッション
21:00 - 22:00 懇親会

内容は変更になる可能性がございます。予めご了承ください。
皆様のご参加をお待ちしております。

Posted by Eiji Kitamura - Developer Relations Team

この記事は Matthew Prince、Cloudflare CEO による Accelerated Mobile Pages Project の記事 "Introducing Accelerated Mobile Links: Making the Mobile Web App-Quick – Accelerated Mobile Pages Project" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。

以下の記事は、Cloudflare の CEO、Matthew Prince が Cloudflare のブログに投稿したものです。

2017 年は、Cloudflare のネットワーク トラフィックの半分以上がモバイル端末からのものになると予測しています。モバイルウェブは、小さな画面に表示できる形式になっているとはいえ、従来のウェブのプロトコルや技術を使って PC の CPU やネットワーク接続、画面向けに構築されたものです。そのため、モバイルウェブのブラウジングは、ネイティブのモバイルアプリに比べると低速に感じられます。

2015 年 10 月、Google のチームが AMP(Accelerated Mobile Pages)を発表しました。これは、モバイルウェブをネイティブ アプリと同じくらい高速にする新しいオープン テクノロジーです。それ以来、多数のサイトオーナーが AMP を採用しています。現在は、700,000 ドメインの 6 億ページが AMP 形式で利用できます。

この AMP コンテンツに対するトラフィックの大半は、Google.com で検索したユーザーによるものです。訪問者が Google 検索以外のソースでコンテンツを見つけた場合、コンテンツを AMP で提供できるはずでも、通常はそうはなりません。そのため、モバイルウェブは求められる速度よりも遅いままになります。

モバイル ウェブアプリを高速に

Cloudflare の Accelerated Mobile Links を使うと、コンテンツを見つけた方法によらずアプリを高速化できるので、この問題の解決につながります。Accelerated Mobile Links を有効にすると、Cloudflare のお客様のサイト上で、AMP 版が利用できるコンテンツへのリンクを自動的に識別します。モバイル端末でリンクをクリックすると、ほぼ瞬時にAMP コンテンツが読み込まれます。





動作を確認するには、モバイル端末でこの投稿を表示し、以下のいずれかのリンクをクリックしてみてください。





ユーザー エンゲージメントの向上

Accelerated Mobile Links のメリットの 1 つは、AMP コンテンツが、コンテンツにリンクしているサイトのビューアーに直接読み込まれることです。そのため、閲覧者が AMP コンテンツを読み終えてビューアーを閉じると、リンク元のソースに戻ることができます。このように、Cloudflare のお客様のサイトはネイティブ モバイルアプリに近くなり、それによってユーザー エンゲージメントも向上します。

ブランドに合わせたユーザー エクスペリエンスを提供したい大規模なサイトオーナーに対しては、サイトオーナーのドメインに一致するようにビューアーのドメインをカスタマイズできる機能を提供する予定です。訪問者を Google のドメインにリダイレクトせずに AMP コンテンツを表示するシームレスな操作が初めて実現しました。Accelerated Mobile Links ビューアーのカスタマイズに興味を持っている大規模なサイトオーナーの皆様は、Cloudflare チームにご連絡ください。

AMP の革新

AMP の初代チャンピオンは Google ですが、AMP 関連技術はオープンです。私たちは、Cloudflare の Accelerated Mobile Links や独自の AMP キャッシュの開発にあたり、Google チームと密接に連携してきました。Google で AMP プロジェクトのテクニカル リードを務める Malte Ubl 氏は、私たちとの協業について次のように話しています。

「AMP キャッシュ ソリューションに関する Cloudflare との協業は、最高にシームレスなオープンソース開発でした。Cloudflare はプロジェクトの恒常的な貢献者になり、AMP のすべてのユーザーのためにコードベースを改善してくれました。ソフトウェア プロジェクトにとって、特定のキャッシュのサポートを多くのキャッシュのサポートへと進化させることは、常に大きな進展です。これを実現した Cloudflare のエレガントなソリューションは実に見事です」

現在、Cloudflare は Google 以外で唯一互換性がある AMP キャッシュを開発しており、Google と同じパフォーマンスやセキュリティを提供しています。

私たちは、サイトオーナーやエンドユーザーの懸念に対処するために、オープンソース精神に則ってプロジェクトの開発を進めています。とりわけ、AMP に関して寄せられている懸念に対処するために、以下の機能の開発を行っています。 

  • サイトオーナーのオリジナル ドメインを使って AMP コンテンツを簡単に共有する仕組み 
  • PC から AMP 版にアクセスした訪問者を自動的にオリジナル版のコンテンツにリダイレクト 
  • AMP 版コンテンツにリダイレクトしたくないエンドユーザーがオプトアウトできる仕組み 
  • サイトオーナーが AMP ビューアーにブランドを表示し、それを自社ドメインから提供する機能 

Cloudflare は AMP プロジェクトに貢献しています。Accelerated Mobile Links は、私たちがリリースする最初の AMP 機能ですが、今後数か月でさらに実績を重ねる予定です。現在のところ、すべての Cloudflare のお客様が無料で Accelerated Mobile Links を利用できます。この機能は、Cloudflare Performance ダッシュボードから有効化できます。モバイルウェブのさらなる高速化を実現する AMP 機能にご期待ください。


Posted by Yoshifumi Yamaguchi - Developer Relations Team

この記事は Tom Ambrose、AdMob パブリッシャー品質チームによる Inside AdMob の記事 "Inside AdMob: Archiving Ad Units" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。

 本日(*原文公開当時)は、今まで見逃されがちだった AdMob フロントエンド インターフェースの機能についてお話ししましょう。その機能とは、アプリでの広告ユニットのアーカイブです。広告ユニットをアーカイブすると、その広告ユニットに関連づけられているすべての広告の提供や設定が無効になり、広告ユニットにリンクされているアクティブなキャンペーンがある場合、その実行も停止されます。AdMob ユーザー インターフェースの [MONETIZE] タブからその広告ユニットにアクセスすることもできなくなりますが、レポートの履歴は参照できます。この手順は取り消すことができないため、注意してください。一度アーカイブした広告ユニットは、永遠にアーカイブ状態のままになります。

最初、この操作はありえないように思えるかもしれません。収益をもたらしてくれる可能性があるのに、なぜアプリの広告ユニットをアーカイブしてアプリの広告提供を無効化する必要があるのでしょうか。たくさんのアプリを管理している場合、ものごとがいつも単純に進むとは限りません。アプリのソースコードにアクセスできなくなっていたり、アプリの数が多すぎてすべての状況を把握し続けるのが難しい可能性もあるでしょう。このような「以前のアプリ」によって、すべてのアプリをポリシーに準拠させ続けるのが困難になる場合があります。ポリシー違反が修正されないと、AdMob アカウントが一時的に停止される可能性もあります。

以前のアプリのすべての広告をアーカイブすれば、一番簡単にこの問題を回避できます。アプリでユーザーに広告が表示されず、そこから収益が生まれなければ、おそらく AdMob のポリシー違反になることはないでしょう。次の手順に従えば、すべてを AdMob フロントエンドで管理できるため、アプリのソースコードを変更する必要はありません。

AdMob ユーザー インターフェースで、トップバーの [MONETIZE] に移動します。





画面の左側にアプリのリストが表示されます。任意のアプリをクリックすると、アプリの広告ユニットが表示されます。

広告ユニットをアーカイブするには、広告ユニットの横にあるボックスを選択し、[ARCHIVE] をクリックします。





広告をアーカイブすると、ユーザーに広告が表示されなくなり、収益も生まれなくなります。この処理は永続的なものであり、アーカイブを取り消すことはできません。この点は重要なので、覚えておいてください。

次の投稿までの間、TwitterLinkedInGoogle+ でお届けする AdMob の情報をご覧ください。


Posted by Rikako Katayama - AdMob Team

この記事は Aditya Tendulkar、Google マイビジネス プロダクト マネージャーによる Google Geo Developers Blog の記事 "Google Geo Developers Blog: Introducing insights in the Google My Business API" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。

Google My Business API の営業拠点・店舗分析機能を紹介します。これは、サードパーティのアプリケーション デベロッパーや大規模な多拠点ブランドが、検索数や閲覧数、ユーザーのアクション数の合計を求めるなどの営業拠点・店舗に関する分析をプログラムから簡単に行う機能です。ビジネス オーナーはこうした情報を追跡し、分析することによって、ユーザーが Google のサービスを使って、どこでどのようにして、営業拠点や店舗を見つけたのかを把握できます。

デベロッパーは Google My Business API を使って、各店舗の場所に関する最大 18 か月分のデータをリクエストし、それを分析して実用的な形で集計や視覚化を行うアプリケーションを構築できます。たとえば、数百の店舗を抱えるコーヒー ショップであれば、ユーザーの閲覧数、店舗へのルート 検索のクリック回数、電話の回数などの傾向を容易に把握し、店舗間で比較することができます。この分析結果は、店舗間のリソース割り当ての改善やマーケティング効果の追跡に活用できます。

この新しい API は、Google マイビジネス ダッシュボードの機能を皆さんのデータ アナリティクス ツールから使えるようにするものです。次の図に示すようにウェブ インターフェースを利用するユーザーは、直近 90 日の Google マイビジネス情報からグラフを生成できます。

このデータは API からも利用できます。最新の ドキュメントをご覧いただければ、この API を簡単に試してみることができます。次に示す簡単な HTML リクエストでは、Google 検索と Google マップで実行された、ビジネスリスティングの検索結果の数を取得します。

次の例では、利用者がある営業拠点・店舗までの運転ルートをリクエストした際の分析結果です。




この新機能によって、Google My Business API ユーザーは、顧客が Google のサービスを使って、どのように営業拠点・店舗を検索しているのか、さらにはその場所を見つけた後にどのようなアクションをとっているのかを理解した上で、ユーザーのアクションを引き起こす最適なビジネスリスティングを得ることができます。このような分析は、Google マイビジネス ウェブやモバイルでも利用できるため、場所を問わずに重要なトレンドを把握することができます。

Google My Business API の詳細や利用申請については、デベロッパー ページをご覧ください。質問やフィードバックがある方は、Google My Business API フォーラムで API チームにご連絡ください。

[この記事は Huibao Lin、Eyal Peled、Google ソフトウェア エンジニアによる Google Developers Blog の記事 "Google AMP Cache, AMP Lite, and the need for speed" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。]
Google は、基本方針として高速なサービスの設計を追求しています。Accelerated Mobile Pages(AMP)形式を使うと、コンテンツを確実かつ高速に読み込むことができますが、これだけではありません。

Google 検索や Google ニュースと天気などでは、ほぼ瞬時にユーザーに画面が表示される AMP が使われています。その鍵となる技術のひとつがスマート キャッシュです。一般的に、キャッシュを活用すると、コンテンツとそれをリクエストするユーザーを物理的に近づけることができ、コンテンツのデータがユーザーのもとに届くまでに移動する距離が短くなります。さらに、キャッシュのような単一の共通インフラを使うことで、ページの提供時間の一貫性が高まります。一方、多くのホストから配信されるコンテンツは、キャッシュに比べるとコンテンツを提供するレイテンシが大幅に異なり、かなり長くなります。

Google 検索では、Google AMP Cache から AMP ページを提供しています。その大きな理由は、一貫した高速のコンテンツ配信にあります。このキャッシュの統合コンテンツ提供インフラは、数億件の規模でドキュメント提供を改善し、最適化を図れるというすばらしい可能性をもたらしています。Google AMP Cache は誰でも無料で使うことができます。その主な理由の 1 つは、すべてのドキュメントがこのようなメリットを受けられるようにするためです。

本投稿では、最近導入された 2 つの改善点、(1)イメージ配信の最適化と(2)「AMP Lite」プロジェクトによる帯域幅が限られた状態でのコンテンツ提供の実現について取り上げます。

Google AMP Cache によるイメージの最適化


ウェブ全体の平均で、イメージはページの全データ量の 64% を占めています。つまり、効果的に最適化するには、イメージを対象にするとよいということになります。

イメージの最適化は、通信に必要なデータ量を削減する効果的な方法です。Google AMP Cache は、PageSpeed モジュールChrome データ圧縮で利用されているイメージ最適化スタックを使っています(この変換を行うために、Google AMP Cache は「Cache-Control: no-transform」ヘッダーを無視している点に注意してください)。サーバーに PageSpeed をインストールすると、サイトは元のイメージに対して同じ最適化を行うことができます。

以下に、私たちが行った最適化の概要を示します。

1)見えない、または見えにくいデータを削除する
サムネイルや位置情報メタデータなど、ユーザーに見えないイメージデータを削除します。画質とカラーサンプルが必要以上に高い JPEG イメージでは、そのレベルを下げます。厳密に言えば、JPEG 画質を 85、カラーサンプルを 4:2:0(4 ピクセルごとに 1 つのカラーサンプル)に減らします。JPEG 圧縮イメージでは、これより画質が高い、またはカラーサンプルが多い場合、データ量は増えますが目視ではほとんど違いがわかりません。

データ量を減らしたイメージデータは大幅に圧縮されます。以上の最適化によって、ユーザーの見た目には影響を与えずに 40% 以上のデータ量を削減できることがわかりました。

2)イメージを WebP 形式に変換する
特定の画像形式では、モバイルと親和性が高まります。WebP がサポートされているブラウザの場合、JPEG を WebP に変換します。この変換によって、画質を変えずにさらに 25% 以上のデータ量を削減できます。

3)srcset を追加する
「srcset」が含まれていない場合は追加します。これは、「src」属性が存在し、「srcset」属性が存在しない「amp-img」タグに適用されます。この操作では、「amp-img」タグを拡張するとともに、イメージを複数のサイズに変更しています。これによって、画面が小さい端末でさらにデータ量が削減されます。

4)状況によって画質の低いイメージを利用する
ユーザーが望む場合や、ネットワークが非常に遅い場合には、(後述の AMP Lite の一環として)JPEG イメージの画質を下げます。たとえば、データセーバーをオンにしている Chrome ユーザーに対して、JPEG イメージの画質を 50 に下げます。この変換によって、さらに JPEG イメージのデータ量を 40% 以上削減できます。

次の例は、最適化前(左)と最適化後(右)のイメージを示しています。元のイメージは 241,260 バイトでしたが、1、2、4 の最適化を行った後は、25,760 バイトになっています。最適化後もイメージはほとんど同じに見えますが、データ量の 89% が削減されています。



低速ネットワーク向けの AMP Lite


世界では、多くのユーザーが低速な接続や RAM の少ない端末でインターネットにアクセスしています。しかし、AMP ページの中には、このように帯域幅が厳しく制限されたユーザー向けに最適化されていないものもありました。そのため、Google は AMP Lite を立ち上げ、このようなユーザーのためにさらに AMP ページのデータ量を削減することにしました。

AMP Lite では、イメージに対して前述のすべての最適化を行います。特に、画質レベルは常に低いものを使用します(上記の 4 つ目の項目を参照)。

さらに、amp-font タグを使って外部フォントを最適化し、外部フォントがキャッシュ済みかどうかにかかわらずページが即座に表示されるように、フォント読み込みタイムアウトを 0 秒に設定します。

AMP Lite は、ベトナムやマレーシアなど、いくつかの国の帯域幅が限られたユーザーや、全世界の RAM が少ない端末所有者のために提供されています。イメージによっては、これらの最適化によって細部が変わってしまう場合もある点に注意してください。ただし、広告など、ページの他の部分には影響しません。

* * *

上記のすべての最適化を組み合わせると、合計 45% のデータ量を削減できます。
私たちは、今まで以上に高速な AMP を提供するために、ユーザーがさらにデータを効率的に利用できるようにしたいと考えています。


Posted by Yoshifumi Yamaguchi - Developer Relations Team

[この記事は AMP Project プロジェクト マネージャー、Eric Lindley による Accelerated Mobile Pages Project の記事 "AMP Roadmap Update for End-Q4 2016 – Accelerated Mobile Pages Project" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。]

2016 年第 4 四半期末の AMP ロードマップ アップデート

2017 年の到来とともに、第 4 四半期に提供を開始した機能や、ここ数か月でスタートして今年も継続するプロジェクトを振り返る時期となりました。以下に概要を記載しますが、さらに詳しい内容をお伝えするため、AMP ロードマップもアップデートしています。

第 4 四半期に提供を開始した機能

この四半期には、<amp-form> によるフォームのサポート、<amp-app-banner> によるアプリのインストール プロモーション、amp-videoamp-youtube によるミュート状態での自動再生など、AMP フォーマットの機能向上が図られました。加えて、AMP-in-PWA のサポートも強化しました。これには、PWA(Progressive Web App)による AMP ドキュメントの段階的な機能拡張や、PWA 内で AMP を使うためのリファレンス実装が含まれています。

広告でも、複数サイズの広告リクエスト「フライング カーペット」型広告、amp-sticky-ad のユーザー エクスペリエンスの向上が行われました。それとともに、DoubleClick、TripleLift、Adsense などの広告サーバーがサポートされ、A4A(AMP for Ads)による AMP 形式の広告クリエイティブの配信数も増加を続けています。

また、コミュニティからのフィードバックに基づいて、アナリティクスのサポートも拡大しました。amp-carouselamp-form の機能では、トリガーが利用できるようになりました。

継続中の作業

新年を迎えても、第 4 四半期以前から始まった多くのプロジェクトでは、まだ多くの作業が残されています。今後数か月間は、そういった作業に集中的に取り組み続ける予定です。

現在は、要素の動作をユーザー アクションにバインドする仕組みの開発を進めています。これは、AMP ページのインタラクティブ性の向上につながります。また、デベロッパーが簡単に見栄えのよい AMP ページを作れるように、一連のクイックスタート サンプルコード(「AMP スタート」)も提供したいと考えています。

さらに、製品ギャラリータブ型のコンテンツ ナビゲーションのサポート、e コマース アナリティクスの強化など、すばらしい e コマース体験を提供するための作業も続けています。

アナリティクス面の強化を継続するため、amp-analytics での動画プレーヤーに対するユーザー インタラクションのネイティブ サポートや、可変フィルターのサポートなど、さらにきめ細かいアナリティクス機能も構築したいと考えています。

また、近い将来には、広告読み込み時の UX 改善や A4A への継続的な投資によって、サポートする広告ネットワークやコンテキストの数を増加させ、非 AMP ページでも AMP 広告を表示できるようにする予定です。


* * *

作業やフィードバックをいただいた AMP 開発コミュニティの方々に感謝いたします。いつものように、問題や機能リクエストがありましたら遠慮なくお知らせください。


Posted by Yoshifumi Yamaguchi - Developer Relations Team

この記事はゲスト ブロガー Jen Looper による The Firebase Blog の記事 "The Firebase Blog: Building a NativeScript + Angular 2 Mobile App using Firebase" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。




Jen Looper
Developer Advocate at ProgressSW

NativeScript、Firebase、Angular 2 という強力な組み合わせを活用すれば、アプリの構築を一気に加速させることができます。クリスマス休暇の期間中は、アプリの開発をスピードアップさせて、プレゼントを贈りたいという家族のニーズに応えなければならないため、特にその点が重要になります。ちょうどよいことに、Angular 2 ベースの NativeScript アプリで Firebase を使う方法のデモを皆さんにプレゼントすることができます(以下をご覧ください 🎁)。これは、Eddy Verbruggen 氏による有名な NativeScript-Firebase プラグインのいくつかのコンポーネントを使ったものです。


このチュートリアルでは、よく使われている次の 4 つの Firebase 機能を NativeScript アプリで使用する方法を説明します。4 つの機能には、ログインとユーザー登録の機能を提供する Authentication、リアルタイムにアップデートされるデータ ストレージの Realtime Database、リモートでアプリを変更できる Remote Config、写真を保存する Storage があります。説明するにあたって、以前に Ionic で書いた Giftler アプリを書き直すことにしました。

実際のプロジェクトで作業に着手する前に、ドキュメントに目を通し、以下の前提事項について確認することをお勧めします。

  • ローカルマシンに NativeScript がインストールされており、CLI が問題なく動作していることを確認します。
  • お好みの IDE で、NativeScript と Angular の開発環境を設定します。TypeScript が必要になるので、トランスパイル プロセスが動作することを確認しておきます。Visual StudioVisual Studio CodeJetbrains 対応の IDE などで利用できる優れた NativeScript プラグインがありますが、中でも Visual Studio Code には、開発を加速させる便利なスニペットがあります。
  • Firebase アカウントにログインし、コンソールを開きます。
  • Firebase コンソールで新しいプロジェクトを作成します。ここでは、「Giftler」という名前にしました。さらに、Firebase コンソールで iOS と Android のアプリを作成します。その際に、GoogleServices-Info.plist ファイルと google-services.json ファイルをダウンロードします。後ほど必要になるので、ファイルを配置した場所は覚えておいてください。


依存モジュールのインストール

Giftler は、認証が必要な NativeScript アプリのサンプルとして作成しました。このアプリでは、ユーザーがクリスマス休暇に受け取りたいギフトを、写真や説明とともに一覧表示できます。現時点で、このアプリには iOS 版と Android 版があり、以下の機能があります。

  • ログイン、ログアウト、ユーザー登録、および「パスワードを忘れた」場合の処理の実行
  • ユーザーによるリストへのギフト項目の登録
  • ユーザーによるリストからのギフト項目の削除
  • ユーザーによるリストの各ギフト項目の編集(説明や写真の追加)
  • バックエンドですぐに変更できる Firebase の Remote Config サービスによるメッセージング

それでは、Giftler のソースコードをフォークしましょう。これは、問題なく動作している完全版のアプリです。アプリをクローンできたら、アプリを作成した際にダウンロードした、現在の Firebase 関連のアプリのファイルを置き換えます。

  • Firebase からダウンロードした google.services.json ファイルを /app/App_Resources/Android folder に配置します。
  • 同様に、Firebase からダウンロードした GoogleService-Info.plist ファイルを /app/App_Resources/iOS folder に配置します。


これらのファイルは、アプリで Firebase を初期化し、関連する外部サービスに接続するために必要です。

次に、アプリのルートにある package.json を確認します。このファイルには、アプリで使用するプラグインが含まれています。ここでは、NativeScript に関連するプラグインに注目してください。

"nativescript-angular":"1.2.0",
"nativescript-camera": "^0.0.8",
"nativescript-iqkeyboardmanager": "^1.0.1",
"nativescript-plugin-firebase": "^3.8.4",
"nativescript-theme-core": "^1.0.2",


NativeScript-Angular プラグインは、Angular と NativeScript を統合するプラグインです。Camera プラグインを使うと、少しカメラを管理しやすくなります。IQKeyboardManager は iOS 専用のプラグインで、厄介な iOS のキーボードを管理してくれます。Theme プラグインは、スキンを完全に自作しなくてもデフォルトのスタイルをアプリに追加できるすばらしいプラグインです。最後に、このアプリでもっとも重要なのが Firebase プラグインです。

以上の依存モジュールを配置して、プラグインをインストールする準備ができたら、iOS と Android のそれぞれのコードが格納された platforms フォルダを作成し、Firebase プラグインやその他の npm ベースのプラグインを初期化して、アプリを構築できるようにします。NativeScript CLI を使ってクローンしたアプリのルートに移動し、tns run ios または tns run android を実行します。これによってプラグインをビルドするルーチンが開始されます。中でも、Firebase プラグインのさまざまなパーツのインストールが始まることが確認できるはずです。インストール スクリプトが実行されると、さまざまな Firebase サービスを統合するためのコンポーネントをインストールするかどうかの確認が表示されます。ここでは、Messaging とソーシャル認証を除くすべてを選択します。ここでの大きな特徴は、firebase.nativescript.json ファイルがアプリのルートにインストールされることです。そのため、今後プラグインの新しいパーツをインストールしたい場合、このファイルを編集するだけでプラグインを再インストールできます。


ここで、tns livesync ios --watch または tns livesync android --watch を実行し、エミュレータでアプリが起動して変更が監視されることを確認すると、アプリが実行されて新しくログインする準備ができていることがわかります。ただし、ログインを行う前に、Firebase コンソールの [Authentication] タブからメール / パスワードによるログインを有効にして、Firebase がこのタイプのログインを処理できるようにする必要があります。


この裏で何が起こっているかを確認するために、少しばかり内部の仕組みを見てみましょう。Firebase にログインするには、インストールした Firebase サービスを初期化しておく必要があります。app/main.ts には、いくつか興味深い点があります。

// this import should be first in order to load some required settings (like globals and reflect-metadata)
 import { platformNativeScriptDynamic } from "nativescript-angular/platform";

 import { AppModule } from "./app.module";
 import { BackendService } from "./services/backend.service"; 
 

 import firebase = require("nativescript-plugin-firebase"); 


 firebase.init({
   //persist should be set to false as otherwise numbers aren't returned during 
 livesync
  persist: false,
  storageBucket: 'gs://giftler-f48c4.appspot.com',
  onAuthStateChanged: (data: any) => {
    console.log(JSON.stringify(data))
    if (data.loggedIn) {
      BackendService.token = data.user.uid;
    }
    else {
      BackendService.token = "";
    }
   }
 }).then(
   function (instance) {
     console.log("firebase.init done");
   },
   function (error) {
     console.log("firebase.init error: " + error);
   }
   );
 platformNativeScriptDynamic().bootstrapModule(AppModule); 


最初に、プラグインから firebase をインポートし、.init() を呼び出します。次に storageBucket プロパティを編集し、Firebase コンソールの [Storage] タブに表示されている値を貼り付けます。

これで、お使いの Firebase アカウントに合わせてアプリがカスタマイズされ、アプリで新しいユーザーの登録とログインが可能になるはずです。必要に応じて、app/login/login.component.ts ファイルの user.email と password 変数を編集して、デフォルトのログイン認証情報を user@nativescript.org からご自身のログインとパスワードに変更することもできます。

iOS と Android のログイン画面
注: iOS では、Xcode シミュレータを使うとすぐにアプリをエミュレートできます。ただし Android では、アプリをエミュレータにインストールするために、Google Services の有効化など、追加でいくつかの手順が必要になる場合があります。

コードの構造と認証

Angular 2 のデザイン パターンでは、コードをモジュール化する必要があります。そのため、以下のコード構造に従うものとします。

—login
  1. login.component.ts
  2. login.html
  3. login.module.ts
  4. login.routes.ts
—list …

—list-detail …

—models

  1. gift.model.ts
  2. user.model.ts
  3. index.ts
—services
  1. backend.service.ts
  2. firebase.service.ts
  3. utils.service.ts
  4. index.ts
app.component.ts

app.css

app.module.ts

app.routes.ts

auth-guard.service.ts

main.ts

Firebase の認証が Angular 2 の auth-guard.service と連携する方法に注目してください。前述のように、アプリの app/main.ts で Firebase が初期化されます。その際に、onAuthStateChanged 関数が呼び出されます。

onAuthStateChanged: (data: any) => { 
    console.log(JSON.stringify(data))
    if (data.loggedIn) {
      BackendService.token = data.user.uid;
    }
    else {
      BackendService.token = "";
    }
  }

アプリが起動する際に、Firebase から返されるデータを文字列化したものをコンソールで確認してください。そのユーザーに loggedIn フラグが立っている場合、Firebase から送り返された userId を token として設定します。NativeScript アプリケーション設定モジュールを使って、この userId を保存するかつ、これから作成するデータと関連付けます。このモジュールは、localStorage のような機能を持つモジュールです。このトークンとトークンを使う認証テストは app/services/backend.service.ts ファイルで管理されており、app/auth-guard.service.ts ファイルで利用できます。auth-guard ファイルは、アプリのログインおよびログアウト状態を適切に管理する方法を提供しています。

AuthGuard クラスは、Angular Router モジュールの CanActivate インターフェースを実装しています。

export class AuthGuard implements CanActivate {
  constructor(private router:Router) { } 

  canActivate() {
    if (BackendService.isLoggedIn()) {
      return true;
    }
    else {
      this.router.navigate(["/login"]);
      return false;
    }
  }


基本的には、前述のログイン ルーチンでトークンが設定され、かつ BackendService.isLoggedIn 関数が true を返す場合、アプリでデフォルトのルートであるウィッシュリストへのナビゲーションが許可されます。しかし、それ以外の場合、ユーザーはログイン画面に戻されます。

const listRoutes:Routes = [
  { path: "", component:ListComponent, canActivate: [AuthGuard] },
];

これで、Firebase を使う NativeScript アプリを初期化できました。次は、アプリにデータを読み込み、Firebase のすばらしいリアルタイム性を活用して、アップデートされるデータベースの内容を監視する方法について学んでゆきましょう。

リストの作成とダブルチェック

ウィッシュリストのベースとなっている app/list/list.html から説明します。ここには、テキスト項目と空のリストが表示されているはずです。では、サンタさんに欲しいものを伝えてみましょう。項目がデータベースに送信され、リアルタイムにリストに追加されます。これをどうやって実現しているかを見てみましょう。

app/list/list.component.ts では、最初にギフトのリストを保持するための監視可能オブジェクトを設定しています。 public gifts$: Observable; 次に、コンポーネントの初期化の際に、データベースからリストを読み込みます。

ngOnInit(){
  this.gifts$ = this.firebaseService.getMyWishList();
}


おもしろいのは firebaseService ファイルです。この関数では、リスナーを追加して Firebase Database の Gifts コレクションの変更を監視する監視可能オブジェクト rxjs を返しています。この方法に注目してください。

getMyWishList():Observable {
   return new Observable((observer: any) => { 
       let path = 'Gifts';
         let  any) => {
           this.ngZone.run(() => {
             let results = this.handleSnapshot(snapshot.value);
             console.log(JSON.stringify(results))
              observer.next(results);
           });
         };
         firebase.addValueEventListener(onValueEvent, `/${path}`);
     }).share(); 
   }


このクエリの結果は、次に示す handleSnapshot 関数で処理されます。この関数は、ユーザーがフィルタリングしたデータを _allItems 配列に設定しています。

handleSnapshot(data: any) {
    //empty array, then refill and filter 
    this._allItems = [];
    if (data) {
      for (let id in data) {
        let result = (Object).assign({id: id}, data[id]);
        if(BackendService.token === result.UID){
          this._allItems.push(result);
        }
      }
      this.publishUpdates();
    }
    return this._allItems;
 }


最後に、publishUpdates が呼び出されます。この関数は、新しい項目が先頭に表示されるように、日付でデータを並び替えています。

publishUpdates() {
    // here, we sort must emit a *new* value (immutability!)
    this._allItems.sort(function(a, b){
         if(a.date < b.date) return -1;
         if(a.date > b.date) return 1;
       return 0;
    })
    this.items.next([...this._allItems]);
  }


監視可能オブジェクト $gifts にデータが設定されると、要素の編集や削除を行うたびにリスナーが呼ばれ、フロントエンドが適切にアップデートされます。getMyWishList メソッド内の onValueEvent 関数で ngZone が使われていることに注意してください。これによって、非同期に行われるデータのアップデートに応じて UI が適切にアップデートされます。NativeScript アプリの ngZone の概要については、こちらをご覧ください。

メッセージのリモート設定


もう 1 つの優れた Firebase サービスに「Remote Config」があります。これは、Firebase のバックエンドからアプリをアップデートできる機能です。Remote Config を使うと、アプリの機能のオン、オフを切り替えたり、UI を変更したりできます。ここでは、サンタさんからメッセージを送る機能を追加してみましょう。

app/list/list.html には、次のメッセージ ボックスがあります。 <Label class="gold card" textWrap="true" [text]="message$ | async"></Label> 監視可能オブジェクト message$ は、データリストとほぼ同じ方法で組み込まれていますが、ここではアプリが新しく初期化されるたびに変更が適用されます。

ngOnInit(){
  this.message$ = this.firebaseService.getMyMessage();
}


秘密は、次に示すサービスレイヤ(app/services/firebase.service.ts)にあります。
getMyMessage():Observable{
    return new Observable((observer:any) => {
      firebase.getRemoteConfig({ 
      developerMode: false,
      cacheExpirationSeconds: 300,
      properties: [{
      key: "message",
      default:"Happy Holidays!"
    }]
  }).then(
        function (result) {
          console.log("Fetched at " + result.lastFetch + (result.throttled ? " (throttled)" : "")); 
          for (let entry in result.properties)
            {
              observer.next(result.properties[entry]);
            }
          }
       );
     }).share();
 }



好きなだけ新しいメッセージを発行可能

注: アプリから必要のない高頻度で Remote Config の値を取得すると、Remote Config の利用が制限されてしまう場合がありますので、慎重に開発してください。

写真を撮る



このプロジェクトのおもしろいところは、選んだプレゼントの写真を撮って Firebase Storage に格納できる点でしょう。前述のように、この部分には Camera プラグインを使いました。これでハードウェアの管理が少し楽になります。最初に、app/list-detail/list-detail.component.ts の ngOnInit() メソッドでパーミッション セットを取得し、アプリがカメラデバイスにアクセスできるようにします。

ngOnInit() {
   camera.requestPermissions();
   ...
  }

ユーザーが詳細画面の [Photo] ボタンをクリックすると、イベント チェーンが始まります。最初に呼ばれるのは次のコードです。

takePhoto() {
  let options = {
            width:300,
            height:300,
            keepAspectRatio: true,
            saveToGallery: true
         };
     camera.takePicture(options)
          .then(imageAsset => {
              imageSource.fromAsset(imageAsset).then(res => {
                  this.image = res;
                  //save the source image to a file, then send that file path to firebase
                  this.saveToFile(this.image);
              })
          }).catch(function (err) {
              console.log("Error -> " + err.message);
          });
}


カメラで写真が撮影され、その写真が imageAsset として保存されて画面に表示されます。このイメージはファイルとしてローカルに保存されます。その際に、日付のタイムスタンプに従った名前が付けられます。また、今後の利用に備えて、パスも保存されます。

saveToFile(res){
  let imgsrc = res;
        this.imagePath =
this.utilsService.documentsPath(`photo-${Date.now()}.png`);
        imgsrc.saveToFile(this.imagePath, enums.ImageFormat.png); 
}


[Save] ボタンが押されると、イメージはローカルパス経由で Firebase に送信され、ストレージ モジュール内に保存されます。アプリに返される Firebase のフルパスは、/Gifts データベース コレクションに保存されます。

editGift(id: string){
  if(this.image){
    //upload the file, then save all
    this.firebaseService.uploadFile(this.imagePath).then((uploadedFile: any) => 
{
          this.uploadedImageName = uploadedFile.name;
          //get downloadURL and store it as a full path;
this.firebaseService.getDownloadUrl(this.uploadedImageName).then((downloadUrl: string) => {
this.firebaseService.editGift(id,this.description,downloadUrl).then((result:any) => { 
               alert(result)
             }, (error: any) => {
                 alert(error);
             });
           })
          }, (error: any) => {
            alert('File upload error: ' + error);
          });
        }
        else {
          //just edit the description
          this.firebaseService.editDescription(id,this.description).then((result:any) => {
               alert(result)
            }, (error: any) => {
               alert(error);
            });
          }
        }


このイベント チェーンは一見複雑そうですが、最終的に実行されるのは、Firebase サービス ファイルの中の数行です。

uploadFile(localPath: string, file?: any): Promise {
      let filename = this.utils.getFilename(localPath);
      let remotePath = `${filename}`;
      return firebase.uploadFile({
        remoteFullPath: remotePath,
        localFullPath: localPath,
        onProgress: function(status) {
            console.log("Uploaded fraction: " + status.fractionCompleted);
            console.log("Percentage complete: " + status.percentageCompleted);
        }
      });
  }
  getDownloadUrl(remoteFilePath: string): Promise {
      return firebase.getDownloadUrl({
     remoteFullPath: remoteFilePath})
      .then(
        function (url:string) {
          return url;
        },
        function (errorMessage:any) {
          console.log(errorMessage);
        });
}
editGift(id:string, description: string, imagepath: string){
    this.publishUpdates();
    return firebase.update("/Gifts/"+id+"",{
        description: description,
        imagepath: imagepath})
      .then(
        function (result:any) {
          return 'You have successfully edited this gift!';
        },
        function (errorMessage:any) {
          console.log(errorMessage);
        });
  }


最終的な結果では、ウィッシュリストに表示するギフトの写真と説明の両方がうまく取得できています。これでもうサンタさんは、カイリーのどのアイライナーを買えばいいのかわからないという言い訳はできません。強力な NativeScript と Angular を組み合わせれば、数分で iOS と Android のネイティブ アプリを作ることができます。これに Firebase を追加すれば、アプリのユーザー、イメージ、データを保存する強力な機能を利用でき、さらに端末間でリアルタイムにデータをアップデートすることも可能になります。すばらしいと思いませんか。このアプリは、次のようになります。



ここまで、充実したウィッシュリスト管理アプリの作成に向けて順調に進んできました。しかし、サンタさんに願いごとを伝える最高の方法はまだ模索中と言えるでしょう。次のステップとしては、Mailgun のメールとの統合やプッシュ通知の利用が考えられます。それまでの間、すばらしいクリスマス休暇をお過ごしください。皆さんが Firebase を利用してすてきな NativeScript アプリを作れるようにお祈りします。

NativeScript についてさらに詳しく学びたい方は、http://www.nativescript.org をご覧ください。質問がある方は、こちらから NativeScript の Slack チャンネルに参加してください。

Posted by Khanh LeViet - Developer Relations Team

[この記事は、Ken Nevarez, Industry Solutions Lead at Google による Geo Developers Blog の記事 "Geolocation and Firebase for the Internet of Things" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。]

GPS は位置情報サービスの要ですが、GPS ハードウェアのコストや消費電力を抑えたいケースや、市街地や建物内など GPS の精度に欠ける場所にあるデバイスを探すといったケースも存在します。

近年、アセットの追跡や盗難防止、使用状況の最適化、アセットの保守点検などを目的として、GPS の代わりに Google Maps Geolocation API を用いた IoT(モノのインターネット)アプリケーションが増えています。そこで、Industry Solutions プロジェクトの一環として、周辺の Wi-Fi ネットワークと Google Maps Geolocation API を使って自身の位置を特定できる IoT デバイスのプロトタイプ作成に取り組みました。この記事では、実装した機能の一部を紹介すると共に、皆さんがご自分でもプロトタイプを作成できるよう、その方法を簡単に説明します。

作成した IoT デバイスは、周辺の Wi-Fi を探してその結果(Wi-Fi ホットスポットとその信号強度)を Firebase Realtime Database へ書き込みます。次に、バックエンド サービスがこのデータを読み出し、Google Maps Geolocation API を使ってそのデータから実際の位置を導き出します。導き出された位置は地図上にプロットすることができます。

デバイスのセットアップとローカルでの書き込み

これを実現するために、Intel EdisonをLinux ベースのコンピューティング プラットフォームとして使い、これに Sparkfun の Edison Blocks を加えました。なお、デバイスの作成には Intel EdisonBase BlockBattery BlockHardwareパックがそれぞれ 1 つずつ必要です。



Edison の開発は Intel XDK IDE を使えば簡単です。JavaScript でシンプルな Node.js アプリケーションを作っていきます。今回、3 つのライブラリ(データベース接続には Firebase、Wi-Fiネットワークの取得には wireless-tools/iwlist、デバイスのMAC 取得には macaddress)を使用しました。実装手順はそれぞれのリンク先のページを参照してください。

ステップ 1: デバイスの MAC アドレスを取得して Firebase に接続する
function initialize() {
    macaddress.one('wlan0', function (err, mac) {
        mac_address = mac;
        if (mac === null) {
            console.log('exiting due to null mac Address');
            process.exit(1);
        }
        firebase.initializeApp({
            serviceAccount: '/node_app_slot/<service-account-key>.json',
            databaseURL: 'https://<project-id>.firebaseio.com/'
        });
        var db = firebase.database();
        ref_samples = db.ref('/samples');
        locationSample();
    });
}


上記のコードに含まれている 2 つのプレースホルダーは次のとおりです。

  1. service-account-key は Firebase コンソールで作成するプライベート キーです。コンソールの左上部にある歯車アイコンから「settings」を選択して、Generate New Private Key をクリックします。このキーを Edison の /node_app_slot/ ディレクトリに入れます。詳しくは、Firebase のドキュメントをご覧ください。
  2. データベース URLの中の project-id は、Google プロジェクトを Firebase と連携させた後、Firebase コンソールのデータベース ページに表示されます。


ステップ 2: 10秒毎に Wi-Fiネットワークをスキャンしてローカルに書き込む
function locationSample() {
    var t = new Date();
    iwlist.scan('wlan0', function(err, networks) {
        if(err === null) {
            ref_samples.push({
                mac: mac_address,
                t_usec: t.getTime(),
                t_locale_string: t.toLocaleString(),
                networks: networks,
            });
        } else {
            console.log(err);
        }        
    });
    setTimeout(locationSample, 10000);
}


クラウドへの書き込み

上記の locationSample() は、検出可能なWi-Fi ネットワークを Firebase のデータベースに書き込む関数で、データベースはネットワーク接続時にクラウドと同期します。

補足説明: Firebase へのアクセス権と認証の設定では、デバイスを「サーバー」として設定しました。この設定方法については Firebase ウェブサイトをご覧ください。今回、認証情報の保存に関しデバイスのセキュリティは十分であると仮定しましたが、皆さんが実装する際にこの点が該当しない場合は、クライアント JavaScript SDK の設定手順に従ってください。

データベースではワークロード管理に 3 つのキュー(Wi-Fi サンプル キュー、ジオロケーション結果キュー、視覚化データ キュー)を使用します。ワークフローは次のとおりです。デバイスからのサンプルがサンプル キューに入り、これを使ってジオロケーションが生成され、生成されたジオロケーションはジオロケーション キューに入れられます。ジオロケーションは取得されて表示用にフォーマット化され、デバイスによって整理された後、出力が視覚化バケットに保管され、フロントエンドのウェブサイトがこれを使用するという流れです。

以下に、Firebase Database コンソール上に表示されるサンプル、ジオロケーション、デバイスによって書き込まれた視覚化データの例を示します。

データを Google App Engine で処理する

サンプルデータの処理の実行には、長期実行中の Google App Engine Backend Module と Java Client for Google Maps Services のカスタム版を使用しました。

補足説明: Firebase と App Engine を併用する場合は、必ず手動スケーリングを使用してください。Firebase はバックグラウンド スレッドを使って変更をリッスンしていて、App Engine は手動でスケーリングされたバックエンド インスタンスに対する長期のバックグラウンド スレッドしか許可しません。

Java Client for Google Maps Services は Google Maps API を利用する際に必要な大量の通信コードを扱い、エラー処理について公開されているベスト プラクティスおよびレート制限に従ったリトライ方針に従います。次に示す GeolocateWifiSample() 関数は、イベント リスナーとしてFirebase に登録されています。この関数はデバイスから報告される各ネットワークをループし、それをジオロケーション リクエストに組み込みます。

private void GeolocateWifiSample(DataSnapshot sample,  Firebase db_geolocations, Firebase db_errors) {
    // initalize the context and request
    GeoApiContext context = new GeoApiContext(new GaeRequestHandler()).setApiKey("");
    GeolocationApiRequest request = GeolocationApi.newRequest(context)
            .ConsiderIp(false);
    // for every network that was reported in this sample...
    for (DataSnapshot wap : sample.child("networks").getChildren()) {
        // extract the network data from the database so it’s easier to work with
        String wapMac = wap.child("address").getValue(String.class);
        int wapSignalToNoise = wap.child("quality").getValue(int.class);
        int wapStrength = wap.child("signal").getValue(int.class);
        // include this network in our request
        request.AddWifiAccessPoint(new WifiAccessPoint.WifiAccessPointBuilder()
                .MacAddress(wapMac)
                .SignalStrength(wapStrength)
                .SignalToNoiseRatio(wapSignalToNoise)
                .createWifiAccessPoint());
    }
    ...
    try {
        // call the api
        GeolocationResult result = request.CreatePayload().await();
        ...
        // write results to the database and remove the original sample
    } catch (final NotFoundException e) {
        ...
    } catch (final Throwable e) {
        ...
    }
}


GeolocateWifiSample() 関数をイベント ハンドラーとして登録します。ジオロケーションの結果を処理して視覚化データを生成するそれ以外のリスナーも、同じようなパターンで作成します。
ChildEventListener samplesListener = new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
        // geolocate and write to new location
        GeolocateWifiSample(dataSnapshot, db_geolocations, db_errors);
    }
    ...
};
db_samples.addChildEventListener(samplesListener);


データの視覚化

デバイスの位置の視覚化には、Google App Engine を使います。保存されたデータを Firebase から渡して、Google Maps JavaScript API を使って結果表示用のシンプルなウェブページを作成しました。index.html ページの中身は空の <div> とID “map” です。この <div> を初期化して Google Map オブジェクトを入れました。また、イベントハンドラーの “child_added” と “child_removed” を追加して、時間の経過に伴ってデータが変化したときにマップを更新するようにしています。

function initMap() {
    // attach listeners
    firebase.database().ref('/visualization').on('child_added', function(data) {
        ...
        data.ref.on('child_added', function(vizData) {
            circles[vizData.key]= new CircleRoyale(map,
                                vizData.val().lat,
                                vizData.val().lng,
                                vizData.val().accuracy,
                                color);
          set_latest_position(data.key, vizData.val().lat, vizData.val().lng);
        });
        data.ref.on('child_removed', function(data) {
            circles[data.key].removeFromMap();
        });
    });
    // create the map
    map = new google.maps.Map(document.getElementById('map'), {
      center: get_next_device(),
      zoom: 20,
      scaleControl: true,
    });
    ...
}


API は位置だけでなく精度表示も返すため、精度のコンポーネントを示すカスタム マーカー(半径内の範囲が脈動する)を作成しました。

2 個のデバイス(赤と青)と判明した位置の直近 5 か所

次に進むには

この記事では、インターネットに接続されたデバイス(ロボットからウェアラブル端末まで)を追跡するために、Google Maps Geolocation API を使った IoT デバイスの構築方法を紹介しました。App Engine の処理モジュールを拡張し、他の Google Maps API を使ってルート案内標高プレイスタイムゾーン情報などの地図データを提供することもできます。皆さん、ぜひ作成してみてください。

さらに、Firebase の代わりに Google Cloud Platform を使って同様のソリューションを実現することも可能です。手順はこちらの記事をご覧ください。




Posted by 丸山 智康 (Tomoyasu Maruyama) - Google Maps Solution Architect, Google Maps