Google Gearsはまだまだ発展途上
Google Gearsについて聞きかじったことがある、もしくはよく知っていると言う読者の方も、今や多いことでしょう。Google Gearsは2007年5月のGoogle Developer Dayで発表された技術で、「Webアプリケーションをオフライン化する」技術として一躍注目を集めました(現在Gearsを利用しているサービスはこちらにまとめられています)。
その実現方法は実に単純で、Gearsの本質はブラウザプラグインです。つまり、Internet ExplorerやFirefoxが持つ拡張メカニズムを利用して、オフライン機能とJavaScript APIを提供しています。
しかし、本当にGearsは単なる「Webサイトのオフライン化プラグイン」なのでしょうか?「ブラウザにJavaScript APIを追加する」と言うコンセプトが持つ可能性は、果たしてオフラインにとどまるもので終わるのでしょうか。
そうではありません。一般的な認識とは異なり、実際にはGearsは「オフライン機能の提供」をゴールとしてはいません。
Gearsのブログによれば、Gearsを開発した目的は「Webブラウザの進化が遅いことに業を煮やして開始された」と言うことです。つまりGearsの目指すものは、あらゆるWebブラウザの奥深くに食い込み、Gearsが重視する機能を提供することで、ブラウザベンダの思惑に左右されることなく、開発者が欲しいと思う機能を追加していくことにあると言っても良いと思います。
こうした目標は、実はかなり早い段階から表明されていました。そして最近では、HTMLの次期バージョンである「HTML5」がその姿を現し始めたことも、Gearsの方向性を大きく左右しようとしています。この記事では、現在Gearsが提供している機能を学び直すとともに、Gearsが将来備える可能性のある機能を紹介することで、Gearsが目指すものを明らかにしていきたいと思います。そして最後に筆者の私見も交えつつ、Web技術の将来像について少し想像を巡らせたいと思います。
Gearsが持つ現在の機能
まずは、Gearsが持つ現在の機能をざっと復習することから始めましょう。
現状のAPIは、やはり「オフライン」と言うキーワードを中心に据えた方が理解し易いと思います。従って、ここでは「オフライン動作を実現するための機能」と言う視点から、各機能を説明していきたいと思います。
ローカルサーバ
ローカルサーバとは、Webページの「キャッシュ」を提供する仕組みです。基本的なキャッシュ機能はWebブラウザ自身も保持していますが、残念ながらそれらはオフラインの状態では利用できませんし、プログラマティックに操作することもできません。
ローカルサーバは、ブラウザがWebサイトにアクセスしようとする動作を横取りし、ローカルのキャッシュからリソースを供給する、と言う仕組みで動作します。例えば、「http://example.com/index.html」と言うURLのリソースがキャッシュ内にあるなら、そのURLにブラウザからアクセスしようとすると、サーバへのアクセスが横取りされ、キャッシュからそのファイルが読み出されることになります。
これならサイトがダウンしていたり、クライアントがオフラインの状態でも、問題なくWebページを表示することができます。また、サイトへのHTTPリクエストを行わずにすむことから、サイトの素早い表示やトラフィックの低減と言った嬉しい副作用も発生します。
以下のサンプルコードは、同期が自動的に行われるタイプのキャッシュ、「ManagedResourceStore」を用いたコードの例です。キャッシュ同期は、まずManagedResourceStoreが「マニフェストファイル」と呼ばれるキャッシュ定義ファイルをダウンロードすることから始まります。マニフェストファイルはJSON形式のファイルで、キャッシュのバージョンを表す番号や、キャッシュするURLを設定できます。以前キャッシュしたバージョンと、現在のバージョンが異なっていれば、キャッシュの内容を自動的にダウンロードします。
// ローカルサーバのインスタンスを作成する var localServer = google.gears.factory.create("beta.localserver"); // ManagedResourceStoreのインスタンス作成 var store = localServer.createManagedStore("EXAMPLE_STORE"); // マニフェストファイルのURLを指定 store.manifestUrl = "manifest.json"; // キャッシュ同期の進捗状況を検知するハンドラをセット store.onprogress = function(details) { console.log(details.filesComplete + "/" + details.filesTotal + "完了..."); }; // キャッシュ同期のエラーを検知するハンドラをセット store.onerror = function(details) { console.error(details.errorMessage); }; // キャッシュ同期の完了を検知するハンドラをセット store.oncomplete = function(details) { console.log("完了しました!現在のバージョン:" + details.newVersion); }; // キャッシュの同期 store.checkForUpdate();
ローカルデータベース
データ更新などの機能も提供する動的なWebアプリケーションをオフライン化するためには、オフライン状態でもデータ操作を行えるようにする必要があります。そうした目的のためにGearsが提供している機能が「ローカルデータベース」です。前述のような、動的Webアプリケーションは、オフライン時のデータ操作をローカルDBに記録しておき、後にオンラインになったときにサーバと同期を取るのです。
ローカルDBは、C言語で書かれた組み込みデータベースSQLiteによって実装されており、SQL92のほぼ全機能を利用できます。
ローカルDBをいかにうまく扱うかが、オフラインアプリの使い勝手を大きく左右します。最も単純な実装方法では、アプリは「モード」に基づき動作するようにします。そして「オンラインモード」ではサーバと、「オフラインモード」ではローカルDBとデータのやり取りを行うのです。この方法は実現が容易ですが、モード切り替えをユーザが意識する必要があり、それほど利便性は高くありません。
実装は複雑ですが、モードを持たないオフラインアプリを作ることも可能です。モードレスなアプリでは、データ操作は常にローカルDBに対して行われ、任意のタイミングでローカルDBとサーバ上のデータの同期をとります。この方法による最大の利点は、オフライン状態とオンライン状態をシームレスに遷移できることです。ユーザはモードを意識する必要がなく、アプリの操作中に「いきなり」オフラインになったとしても、問題なくアプリは動作し続けます。また、本来サーバに対して行うデータ操作を、ローカルDBに対する操作で完了することができるため、実質上のパフォーマンスが大きく改善すると言う利点もあります。
以下に示すサンプルコードは、ローカルDBの利用方法を簡潔に表しています。
// ローカルDBのインスタンスを作成 var db = google.gears.factory.create("beta.database"); // データベースをオープン(なければ作成される) db.open("EXAMPLE_DB"); // messageテーブルを作成(なければ作成される) db.execute("create table if not exists message(content)"); // insert文を発行 db.execute("insert into message values(?)", ["test"]); // select文を利用して検索 var rs = db.execute("select content from message"); // 結果セットをループ処理 while (rs.isValidRow()) { alert(rs.fieldByName("content")); rs.next(); } // 結果セットのクローズ rs.close();
ワーカプール
ブラウザ上で動作するJavaScriptアプリケーションの大きな悩みは、時間のかかる処理をJavaScriptで実現すると、その間UIも停止してしまうと言うことです。それを解決するための仕組みがワーカプールです。
ワーカプールは、その名の通り「ワーカ」と呼ばれるプログラム実行単位のプールです。各ワーカは、別個のJavaScript「プロセス」と言えるもので、スレッドとは異なります。よって、ワーカ間で変数を共有することはできません(メッセージングによってワーカ間の通信を行うことはできます)。
ワーカを用いて、時間のかかる処理をバックグラウンドで実行するようにし、アプリの使い勝手を大きく向上させることが可能です。
以下のサンプルコードは、「worker1.js」と言うJavaScriptファイルからワーカを生成し、"start"と言う文字列をメッセージ送信しています。ワーカ側は受け取ったメッセージが"start"であれば、メッセージの発信元に対して「Hello from worker」と言う文字列を返信します。
// main.html(ワーカを作成する元となるHTMLファイル) // ワーカのソースコード var workerPool = google.gears.factory.create('beta.workerpool'); // メッセージ受信時のハンドラを追加 workerPool.onmessage = function(a, b, message) { alert("ワーカからのメッセージ:" + message.text); }; // ワーカを作成し、メッセージを送信 var workerId = workerPool.createWorkerFromUrl('worker1.js'); workerPool.sendMessage("start", workerId); // worker1.js(ワーカのソースコード) var wp = google.gears.workerPool; // メッセージ受信時のハンドラを追加 wp.onmessage = function(a, b, message) { if (message.text == "start") wp.sendMessage("Hello from worker", message.sender); }
HttpRequest、Timer
バックグラウンドのワーカは、ブラウザ固有のJavaScript環境にアクセスすることができません。つまり、WebページのDOMにアクセスするのはもちろんのこと、windowオブジェクトが持つ様々な関数を利用することもできません。
そこで、ワーカプールからもXMLHttpRequestやwindow.setTimeout()/window.setInterval()を利用できるようにするため、Gearsが提供しているのがこれらのクラス(HttpRequest、Timer)です。多少の差異はありますが、使い勝手は元になったAPIと全く変わりません。
Desktop API
開発者向けの最新バージョンである0.3で、新しく追加されたのが「Desktop API」です。このAPIは非常に単純で、「createShortcut()」と言うメソッドが一つ定義されているだけです。
var desktop = google.gears.factory.create("beta.desktop"); // デスクトップ上にショートカットを作成する // 引数は上から、 // ショートカットの名称 // ショートカットが参照するURL // アイコンの指定(32x32, 48x48, 64x64, 128x128のどれかを指定) // アプリケーションの説明 desktop.createShortcut( "Desktop API Example", "http://gears.google.com/", {"48x48": "http://gears.google.com/images/gears_generic_48.png"}, "64x64": "http://gears.google.com/images/gears_generic_64.png"}, "An application at http://gears.google.com/");
このコードを実行すると、以下のようなダイアログが表示されます。
ダイアログで「Yes」をクリックすると、メソッドの引数に指定したURLへのショートカットが、デスクトップ上に作成されます(ショートカットをダブルクリックするとブラウザが立ち上がり、Webサイトが表示される)。
Gearsに搭載されることが予定されている機能
さて、ここまで現状のGearsが持つ機能についてざっと見てきました。ここからは、現在のGearsには搭載されていませんが、将来的に搭載される可能性のある機能を紹介していきます。
ここで紹介する機能は、2008/4/21時点でGoogle GearsのWikiに一覧されているものから抜粋しています。同Wikiは非常に更新が激しく、次々と新しいアイデアが提案されては、以前のアイデアが消えていくと言う感じなので、Gearsの将来が気になる方は定期的にチェックすることをおすすめします。
BlobAPI
Gearsでバイナリデータを取り扱えるようにするためのAPI。バイナリデータを(ローカルサーバにより)キャッシュする、ローカルDBに対するバイナリの読み書き、ワーカ間でバイナリデータを通信する(現在は文字列のみ)、HttpRequestでバイナリをアップロード/ダウンロード可能に、などの用途が想定されています。
Blobは、以下のようなメンバを持つ単純なオブジェクトです。
- length・・・データ長を表す
- slice(start, length)・・・バイナリを切り出し、新しいBlobを返す
Audio API
HTML5で搭載される予定の「HTMLAudioElement」を基にしたAPI。WAV/OGG/MP3と言った音声データの再生を行えるだけではなく、音声データをBlobとして取得したり、録音を行うことも可能です。
以下のコードは、Wikiからの抜粋で、WAVファイルのダウンロードと再生を行うものです。
// Audioクラスのインスタンスを取得 var audio = google.gears.factory.create('beta.audio'); // 音声データのURLを指定 audio.src = 'http://blahblahblob.com/sampleaudio.wav'; // 音声データをロード audio.load(); // 音声データを再生(非同期で実行される) audio.play(); // Blobデータとして取得 var blob = audio.getMediaBlob();
ConsoleAPI
JavaScriptプログラムからのロギングを可能にするAPI。「デバッグ(debug)」や「エラー(error)」と言ったログレベルの指定を行えるのみならず、ログが出力されたことをイベント通知APIによって捕捉することができるため、ログ文字列をサーバに送信したりローカルDBに書き込んだり、と言ったことが可能です。
以下のコードは、現在時刻をデバッグ出力するだけではなく、そのイベントを捕捉してアラート表示も行います。
// コンソールのインスタンスを取得 var console = google.gears.factory.create('beta.console'); // ログ出力のイベントを処理 console.onlog = function(logEvent) { alert(logEvent.type + ":" + logEvent.message); }; // ログ出力 console.log("debug", "現在時刻:%s", [new Date()]);
以上のようにして出力したログを見るには、ブラウザに組み込まれたGearsのサブメニューか、URL(http://localhost/gears/log_viewer.htmlなど)を通して閲覧できるようにする、などの方法が考えられています。
Database2 API
HTML5で提案されているクライアントサイド・データベースの実装です。現在Gearsに実装されているデータベースとは別個に提供されます。
この提案の目的は、HTML5と互換性のあるAPIをGearsが提供することで、標準的なプログラミングモデルを用いてデータベースを操作できるだけではなく、HTML5がサポートされていないブラウザ上でも同APIを利用できるようにする、と言うものです。
以下のサンプルコードは、前述のローカルDBと同じ動作をデータベース2のAPIで書き直したものです。DB操作をトランザクション中で行わねばならないこと、検索処理が非同期で行われることなど、様々な違いがあるのがお分かりでしょう。
// ローカルDBのインスタンスを作成 var dbManager = google.gears.factory.create("beta.databasemanager"); // データベースをオープン(なければ作成される) // 引数は「名前, スキーマのバージョン、説明、最大サイズ」 var db = dbManager.open("EXAMPLE_DB", "1", "EXAMPLE", 100000); // トランザクション処理 db.transaction(function(tx) { // messageテーブルを作成(なければ作成される) tx.executeSql("create table if not exists message(content)"); // insert文を発行 tx.executeSql("insert into message values(?)", ["test"]); // select文を利用して検索。非同期コールバックを利用して値を取得 tx.executeSql("select content from message", [], function(rs) { for (var i = 0; i < rs.rows.length; i++) { alert(rs.rows.item(i)); } }); });
FileSystem API
拡張されたファイルアップロードフォームを利用するためのAPI。複数ファイルの選択や、選択可能なファイル種別のフィルタリングなどを行うことが可能です。
以下のサンプルは、Wikiページからの抜粋です。ユーザが選択した複数の画像ファイルを、imgタグを用いて貼付けます。また、ここで使用されているgetLocalFiles()メソッドが、前述したDesktopクラスに含まれることにも注意が必要です。ユーザのローカル環境にアクセスするタイプのAPIが、今後もDesktopクラスに追加されていく可能性を示唆しています。
// Desktopクラスのインスタンスを作成 var desktop = google.gears.factory.create('beta.desktop'); // ファイル選択ダイアログを表示。 // 引数はファイルフィルタのラベルと、ファイル名のワイルドカード var files = desktop.getLocalFiles( ['Images (*.jpg, *.gif)', '*.jpg;*.gif', 'All files', '*']); // 戻り値はファイル名の配列 for (var i = 0; i < files.length; ++i) { var img = document.createElement("img"); img.src = files[i]; document.body.appendChild(img); }
HttpRequestの機能拡張
現状、Gearsが提供しているHttpRequestクラスは、ワーカでも使えると言うこと以外はほとんど標準的なXMLHttpRequestと変わりありません。ですが、将来的には大きく機能拡張することが予定されています。追加される可能性のある機能は、以下のようなものです。
- multipart/x-mixed-replaceによるサーバプッシュの実現
- Gzip圧縮されたPOSTリクエストのサポート
- クロスドメインのリクエスト
- 多くのWebブラウザが持つ、HTTP同時接続数の上限が2と言う制限の緩和
- Blobデータのサポート
- 進捗状況やリダイレクトを検知するためのイベントハンドラ
また、巨大なファイルをHTTP上でやり取りするために、中断と再開が可能なHTTPリクエスト/レスポンスの仕様を標準として提出しようと言う動きもあるようです。
HTML5にワーカを追加する提案
現在提案されているHTML5には、Gearsが持つワーカプールに該当する機能は存在しません。そこでGearsチームは、Gearsのワーカプールと同じコンセプトで、さらにスマートなAPIをHTML5に提案しようとしています。
以下のソースは、Wikiで提案されているAPIを基に、先ほど示したワーカプールのサンプルを書き直したものです。
// main.html(ワーカを作成する元となるHTMLファイル) // ワーカのソースコード var worker = createWorker("worker1.js"); // メッセージ受信時のハンドラを追加 worker.addEventListener("worker-message", function(e) { alert("ワーカからのメッセージ:" + e.arguments.message); }; worker.sendMessage("start"); //worker1.js(ワーカのソースコード) workerContext.addEventListener("worker-message", function(e) { if (e.arguments == "start") { workerContext.parent.sendMessage({message: "Hello from worker"}); } });
Imaging API
画像操作用のAPIで、画面にあらかじめ表示されているイメージを操作することも、画像を表示することなしにバックグラウンドで取り扱うことも可能です。
HTML5で追加される予定のcanvas要素を基にしてAPIが組み立てられており、画像の塗りつぶし、移動、拡大/縮小、回転、テキストの描画など、基本的な画像操作はほぼすべてサポートされます。
以下のコードは、Wikiページからの抜粋です。BlobデータからCanvasを作成し、画像を操作した後、Blobに変換しています(原文ではさらに、ローカルサーバへの保存と、HttpRequestを用いたアップロードを行っています)。
// キャンバスの作成 var canvas = google.gears.factory.create('canvas'); // Blobデータをキャンバスに読み込み canvas.load(srcBlob); // 2D操作を行うためのコンテキストを取得 var context = canvas.getContext('2d'); // 各辺を10ピクセル切り落とす context.crop(10, 10, canvas.width - 20, canvas.height - 20); // 画像のサイズ変更 context.resize(1024, 768); // Blobに変換 var destBlob = canvas.toBlob();
Location API
モバイルデバイス上で、現在地の情報を取得するためのAPI。
以下のサンプルはWikiページからの抜粋です。getCurrentPosition()は現在地点を返すメソッド、watchPosition()は継続して現在地点を監視するメソッドです。どちらのメソッドも非同期で実行されるため、結果を処理するためのコールバックを引数に渡しています。
// Geolocationクラスを作成 var geo = google.gears.factory.create('beta.geolocation'); // 現在地点を取得 geo.getCurrentPosition(function(position) { updateMap(position.latitude, position.longitude); }); // 現在地点を継続して監視 var watchId = geo.watchPosition(function(position) { updateMap(position.latitude, position.longitude, position.accuracy); }); // 監視終了 geo.clearWatch(watchId);
NotificationAPI
ユーザに対して通知メッセージを表示するためのAPI。デスクトップの端に一定時間表示された後、フェードアウトする小窓のようなイメージです(Mac OS XのGrowlが持つ機能と同等)。
以下のサンプルは、APIの使い方を簡潔に示したものです。register()で通知プラットフォームへの登録、notify()で実際の通知を行います。
// 通知APIの取得 notification = google.gears.factory.create("beta.notification"); // 登録 notification.register({ url: "http://mail.google.com", name: "Google Mail", notificationNames: ["New mail", "Chat message"] }); // 通知 notification.notify({ name: "New mail", title: "サンプルタイトル", description: "本文" });
Gears、そしてWebクライアントはどこへ向かうのか?
さて、Gearsに搭載される可能性のある様々な機能をこうして見てきました。これまで提案されては消えていったアイデア、そしてこれから登場するであろうアイデアを含めると、ここに示したのはほんの一部だと言えます。それだけGearsには広く大きな可能性があると言うことです。
逆に言えば、現在Gearsで実現されている機能(オフライン)は、Gearsが持つ可能性のうちのほんのわずかな一部に過ぎない、とも言えます。では、この先Gearsはどのような方向に向かうのか。今回紹介した数々のアイデアを基に、多少筆者の私見も交えつつ、想像していきたいと思います。
数々のアイデアから気づくことの一つ目は - 非常に陳腐ですが - 、魅力的な機能が多い、と言うことです。開発者によっては、バイナリを送受信できる(XML)HttpRequestを欲しくてしょうがないかもしれませんし、またある開発者に取っては、複数ファイルを選択できるダイアログに魅力を感じるかも知れません。ここで問題となるのは、機能があまりに魅力的で、Gearsに依存したくなる気持ちが生じてしまうかもしれない、と言うことです。
現在のGearsが利用されている状況は、非常に限定的です。「オフライン」と言うコンセプトが、あくまで既存のWebアプリに対する付加価値でしかないため、GearsがクライアントにインストールされていようといまいとWebアプリは問題なく動作しなくてはなりません。おかげで、ユーザは「Google Gears」と言うプロダクトに関する知識が全くなくとも、快適なWebブラウジングを行うことができます。
しかしGearsに今後搭載されるAPIの中には、一度依存してしまうと、アプリがGearsなしでは動作しないようになる可能性が高いものが多くあります。つまり、「このサイトを閲覧するには、Google Gearsが必要です」と言うダイアログを表示する必要が生じるかもしれないし、そんなサイトがこれからたくさん出てくるかもしれないのです。これが良いことか悪いことかはまだわかりませんが、Webを取り巻く環境に大きなインパクトをもたらすのは間違いないように思えます。
またもう一つ気づくことは、HTML5を非常に意識した設計となりつつあることです。アイデアの多くに「HTML5」と言う単語が出てきたことにお気づきでしょう。そう、Gearsは標準を尊重する姿勢を明確に打ち出しています。
HTML5に搭載される機能は、開発者に取って魅力的なものが数多くあります。しかし、すべてのブラウザがHTML5に対応するようになるまでには、長い時間を必要とするでしょう。そこでGearsの出番です。「当サイトはHTML5に準拠したブラウザ、もしくはGoogle Gearsがインストールされたブラウザでご利用ください」となるわけです。
つまり、近い将来にGoogle Gearsは「HTML5の実装+魅力的な独自機能」と言う位置づけのプロダクトになるのは間違いないと考えます。だとしたら、「HTML5の機能を使いたい」と言うWebサイトの数が臨界点に達したとき、Gearsは一気に普及するかもしれません。
また、デスクトップAPIが持つショートカット作成機能に見られるように、GearsはデスクトップアプリとWebアプリの境界線を曖昧にしつつあります。
つまり、「オフライン動作が可能なWebアプリは、デスクトップアプリとほとんど変わらない」と言う事実を端的に表すAPIだと言ってよいでしょう。
「スタンドアローンで動作するWebアプリ」と言う分野には、Adobe AIRやMozilla Prismと言った競合が存在しますが、それぞれアプローチは大きく異なります。これらのプロダクト間での競争がこれからどうなっていくのか、非常に楽しみです。
さらに筆者の個人的な見解としては、種々雑多なブラウザが存在する現在、機能的な差異を吸収し、さらに高次元の機能を提供しようとするGearsの姿勢は、OS間の差異が問題だった時代にクロスプラットフォームを強く打ち出したJavaと通ずるものがあるように思えます。将来的には、「Write Once, Run Any Browser (一度書けばどのブラウザでも動く)」と言うようなメッセージを打ち出し始めるかも知れません。そうなった場合は恐らく、同様の利点を持つAdobe FlashやMicrosoft Silverlightと競合することになるでしょう。その場合、HTMLやJavaScriptと言う標準を後ろ盾にし、オープンソースを基盤とするGearsが、どこまで強みを発揮できるかを想像するのも、また一つの楽しみです。
著者について
フリーエンジニア兼テクニカルライターとして、マイコミジャーナルや ZDNet などの Web 媒体、Web Designing誌やシステム開発ジャーナル誌などの雑誌媒体を中心に、Java/Ajax/RIA を中心とした技術記事を執筆しています。著書に「GoogleGears スタートアップガイド」(技術評論社)があります。