HTML 5 を使ってモバイル Web アプリケーションを作成する: 第 4 回、Web ワーカーを使ってモバイル Web アプリケーションを高速化する

マルチスレッドの JavaScript を HTML 5 に追加し、成功する

Web アプリケーションは従来からシングル・スレッドの世界に押し込まれていました。そのため、コードの中で行えることは非常に制限されていました。あまりにも複雑なことをすると、アプリケーションの UI がフリーズする危険性があるからです。Web ワーカーによって Web アプリケーションにマルチスレッドが導入され、そうした様相が大きく変わりました。アプリケーション・ロジックの大部分がクライアント・サイドにあるモバイル Web アプリケーションにとって、Web ワーカーは特に便利です。この記事では、Web ワーカーの扱い方と、どんなタスクが Web ワーカーに最も適切なのかについて学びます。また、他の HTML 5 技術と組み合わせ、Web ワーカー技術を効率的に使う方法についても学びます。

Michael Galpin, Software architect, eBay

Michael Galpin's photoMichael Galpin は eBay のアーキテクトであり、developerWorks に頻繁に寄稿しています。彼は JavaOne、EclipseCon、AjaxWorld など、さまざまな技術カンファレンスで講演を行っています。彼が次に取り組もうとしていることを知るには、Twitter で @michaelg をフォローしてください。



2010年 6月 08日

この連載について

HTML 5 は大きな話題になっている技術ですが、それには十分な理由があります。HTML 5 は、デスクトップ・アプリケーションの機能をブラウザー上に実現する上での技術的な転換点となります。従来のブラウザーにとっての転換点となるだけでなく、HTML 5 はモバイル・ブラウザーにとってはさらに大きな転換点となる可能性があります。さらに良いことに、最もよく使われるモバイル・ブラウザーは HTML 5 仕様の重要部分の多くを既に採用し、実装しています。この 5 回連載の記事では、そうした HTML 5 の一部としての新しい技術をいくつか詳細に調べます。これらの機能はモバイル Web アプリケーションの開発に大きな影響を与える可能性があります。この連載の各記事では、HTML 5 の機能を示す、実際に動作するモバイル Web アプリケーションを作成します。これらの機能は、iPhone や Android ベースの機器に見られるような最近のモバイル Web ブラウザーに使われています。


はじめに

この記事では、最新の Web 技術を使用して Web アプリケーションを作成します。ここで紹介するコードの大部分は単なる HTML と JavaScript、そして CSS であり、すべての Web 開発者にとってコアとなる技術が使われています。記事の内容に従うために必要なもののうち、最も重要なものはテストを実行する際に使用するブラウザーです。この記事のコードの大部分は最新のデスクトップ・ブラウザーで実行しますが、いくつか明らかな例外があります。もちろん、モバイル・ブラウザーでもテストする必要があり、そのために iPhone と Android の最新 SDK が必要です。この記事では iPhone SDK 3.1.3 と Android SDK 2.1 を使用しました。またこの記事のサンプルでは、プロキシー・サーバーも使ってブラウザーからリモート・サービスにアクセスしています。このプロキシー・サーバーは単純な Java™ サーブレットですが、PHP、Ruby その他によるプロキシーと容易に置き換えることができます。これらについては「参考文献」のリンクを参照してください。


モバイル機器でのマルチスレッド JavaScript

ほとんどの開発者にとって、マルチスレッド、つまり並行プログラミングは新しいものではありません。最近のほとんどのプログラミング言語では、何らかの形で並行プログラミングをサポートしています。しかし、JavaScript は並行プログラミングをサポートしている言語には含まれません。JavaScript の作成者は、Web ページ上で単純なタスクを実行するように設計された JavaScript のような言語では、並行プログラミングはあまりに問題が多く、不必要であると考えたのです。しかし Web ページが Web アプリケーションへと進化するにつれ、JavaScript によって実行されるタスクの複雑さのレベルは他の言語の場合と同程度になりました。また、並行プログラミングをサポートする他の言語を扱う開発者達は、スレッドやミューテックスなど、並行プリミティブに付随する異常なほどの複雑さに苦労しがちでした。実際最近では、Scala、Clojure、F# など、いくつかの新しい言語が登場しており、それらの言語ではすべて、並行処理を簡単に行えることが特徴になっています。

よく使われる頭字語

  • Ajax: Asynchronous JavaScript + XML
  • API: Application Programming Interface
  • CSS: Cascading stylesheet
  • DOM: Document Object Model
  • HTML: Hypertext Markup Language
  • REST: Representational State Transfer
  • SDK: Software Developer Kit
  • UI: User Interface
  • URL: Uniform Resource Locator
  • W3C: World Wide Web Consortium
  • XML: Extensible Markup Language

Web ワーカー仕様は、単に JavaScript や Web ブラウザーに並行処理を追加するためのものではなく、並行処理をスマートに追加するための仕様であり、この仕様に従うことで、問題を引き起こすツールを使う必要がなくなります。例えばデスクトップ・アプリケーションの開発では、長年マルチスレッドを使ってアプリケーションから I/O リソースにアクセスし、そうした I/O リソースが利用可能になるまで待機する間に UI をフリーズさせないようにしていました。しかしそうしたアプリケーションでは、複数のスレッドによって (UI を含む) 共有リソースが変更される結果、アプリケーションがフリーズしたりクラッシュしたりすることがよくありました。Web ワーカーを使用すると、そうした問題は起こりえません。生成されるスレッドは、メインの UI スレッドと同じリソースにアクセスすることができません。実際、生成されるスレッドの中のコードは、メインの UI スレッドによって実行されるコードと同じファイル上にはありません。

それどころか、その外部ファイルをコンストラクターの一部として提供する必要さえあります (リスト 1)。

このプロセスでは、以下の 3 つを使います。

  1. メイン・スレッドで実行される、Web ページ用の JavaScript (ここではページ・スクリプトと呼びます)。
  2. ワーカー・オブジェクト。これは JavaScript オブジェクトであり、このオブジェクトを使って Web ワーカー機能を実行します。
  3. 新たに生成されるスレッドで実行されるスクリプト。ここではワーカー・スクリプトと呼びます。

では、まずページ・スクリプトを見てみましょう (リスト 1)。

リスト 1. ページ・スクリプトで Web ワーカーを使用する
var worker = new Worker("worker.js");
worker.onmessage = function(message){
    // do stuff
};
worker.postMessage(someDataToDoStuffWith);

リスト 1 を見ると、Web ワーカーを使うための基本的なステップは 3 つあることがわかります。第 1 に、ワーカー・オブジェクトを作成し、新しいスレッドで実行されるスクリプトの URL をそのオブジェクトに渡します。このワーカーが実行するコードはすべてワーカー・スクリプトに含まれている必要があり、このスクリプトの URL がワーカーのコンストラクターに渡されます。このワーカー・スクリプトの URL はブラウザーの同一生成元ポリシーの制約を受け、Web ワーカーを作成するページ・スクリプトをロードしたページをロードしたドメインと同じドメインになければなりません。

次に、onmessage 関数を使ってコールバック・ハンドラー関数を指定します。このコールバック関数はワーカー・スクリプトが実行された後に呼び出されます。message はワーカー・スクリプトから返されるデータであり、このメッセージの処理方法は任意です。このコールバック関数はメイン・スレッドで実行されるため、DOM にアクセスすることができます。一方、ワーカー・スクリプトは別のスレッドで実行されるため、DOM にアクセスすることはできません。そのため、ワーカー・スクリプトからメイン・スレッドにデータを送信する必要があります (メイン・スレッドでは、安全に DOM を変更してアプリケーションの UI を更新することができます)。これは、何も共有しないように設計されている Web ワーカーの重要な特徴です。

リスト 1 の最後の行はワーカーの起動方法を示しており、ワーカーの postMessage 関数を呼び出しています。ここでワーカーにメッセージ (この場合も単なるデータ) を渡しています。もちろん、postMessage は非同期関数であり、この関数を呼び出すと即座に制御が返されます。

今度はワーカー・スクリプトを検証しましょう。リスト 2 のコードはリスト 1worker.js ファイルの内容です。

リスト 2. ワーカー・スクリプト
importScripts("utils.js");
var workerState = {};
onmessage = function(message){
     workerState = message.data;
      // do stuff with the message
    postMessage({responseXml: this.responseText});
}

これを見ると、ワーカー・スクリプトには独自の onmessage 関数があることがわかります。この関数が呼び出されるのは、メイン・スレッドから postMessage が呼び出されたときです。ページ・スクリプトから渡されたデータは message オブジェクトの中で postMessage 関数に渡されます。このデータにアクセスするためには message オブジェクトの data プロパティーを取得します。ワーカー・スクリプトの中でデータの処理を終えたら、postMessage 関数を呼び出し、メイン・スレッドにデータを返送します。メイン・スレッドでも、メイン・スレッドで受信するメッセージのデータ・プロパティーにアクセスすることで、このデータを利用することができます。

ここまでは、単純ながら強力な、Web ワーカーの動作について見てきました。次に、Web ワーカーを使ってモバイル Web アプリケーションを高速化する方法を調べてみましょう。その前に、機器のサポートについて説明する必要があります。つまり、ここで説明しているのはモバイル Web アプリケーションであり、モバイル Web アプリケーションの開発ではブラウザー間の機能の違いに対応することが不可欠なのです。

機器のサポート

Android 2.0 から、Android ブラウザーは HTML 5 の Web ワーカー仕様を完全にサポートしています。この記事の執筆時点で、新しい Android 機器のほとんどには (非常によく使われている Motorola の Droid を含めて) Android 2.1 が使われています。また Web ワーカーは、Maemo オペレーティング・システムを実行する Nokia の機器で使われている Mozilla Fennec ブラウザーや、Windows Mobile 機器でも完全にサポートされています。この中に含まれていない重要なものとして iPhone があります。iPhone OS バージョン 3.1.3 と 3.2 (iPad 上で実行されるバージョンの OS) は、まだ Web ワーカーをサポートしていません。ただし、Safari では既にサポートしています。そのため、iPhone 上で実行される Mobile Safari ブラウザーに Web ワーカーが登場するのも時間の問題でしょう。(特に米国での) iPhone の圧倒的なシェアを考えると、Web ワーカーが存在することを前提とせず、存在を検出できた場合にのみモバイル Web アプリケーションの機能強化に Web ワーカーを使うのが最も適切です。それを念頭に、Web ワーカーを使ってモバイル Web アプリケーションを高速化する方法を調べてみましょう。


ワーカーを利用してパフォーマンスを向上する

スマートフォンのブラウザーでの Web ワーカーのサポートは適切であり、改善されつつあります。そのため、いつモバイル Web アプリケーションにワーカーを使うのか、という疑問が出てきます。答えは簡単で、長い時間を要する処理が必要な場合には、いつでもワーカーを使用することができます。ワーカーの使い方の一部の例として、円周率を 1 万桁まで計算するなど、非常に高負荷の数学計算にワーカーが使われている場合もあります。そうした計算が Web アプリケーションで必要になる可能性は非常に低いと思われ、モバイル Web アプリケーションではもっと低いはずです。しかしリモート・リソースからのデータ取得は非常に一般的であり、この記事の例でもそこに焦点を絞ります。

この例では、Daily Deals (毎日変更される特売品) のリストを eBay から取得します。この特売品リストには、各特売品に関する簡単な情報が含まれています。詳細な情報を入手するためには eBay の Shopping API を使います。ここではこの追加情報を、Web ワーカーを使って先読みします。その間ユーザーは特売品リストを閲覧しながら、気になる商品を 1 つ選択します。こうした eBay データに Web アプリケーションからアクセスするためには、汎用のプロキシーを使ってブラウザーの同一生成元ポリシーを回避する必要があります。このプロキシー用として、簡単な Java サーブレットを使用しました。この記事のコードの中にはこのサーブレットが含まれていますが、ここには示してありません。この記事では、サーブレットのコードよりも、Web ワーカーを扱うコードに焦点を絞りましょう。リスト 3 は、この特売品アプリケーションの基本的な HTML ページを示しています。

リスト 3. 特売品アプリケーションの HTML
<!DOCTYPE HTML>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <meta name = "viewport" content = "width = device-width">
    <title>Worker Deals</title>
    <script type="text/javascript" src="common.js"></script>
  </head>
  <body onload="loadDeals()">
    <h1>Deals</h1>
    <ol id="deals">
    </ol>
    <h2>More Deals</h2>
    <ul id="moreDeals">
    </ul>
  </body>
</html>

見るとわかるように、これは非常に単純な HTML であり、単なるシェルにすぎません。JavaScript を使ってデータを取得し、UI を生成しています。これはモバイル Web アプリケーション用に最適化された設計です。こうすることですべてのコードと静的なマークアップが機器にキャッシュされ、ユーザーはサーバーからのデータを待つだけでよいからです。リスト 3 で、body がロードされると loadDeals 関数を呼び出していることに注意してください。この関数によってアプリケーションの初期データをロードします (リスト 4)。

リスト 4. loadDeals 関数
var deals = [];
var sections = [];
var dealDetails = {};
var dealsUrl = "http://deals.ebay.com/feeds/xml";
function loadDeals(){
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(){
        if (this.readyState == 4 && this.status == 200){
               var i = 0;
               var j = 0;
               var dealsXml = this.responseXML.firstChild;
               var childNode = {};
               for (i=0; i< dealsXml.childNodes.length;i++){
                   childNode = dealsXml.childNodes.item(i);
                   switch(childNode.localName){
                   case 'Item': 
                       deals.push(parseDeal(childNode));
                       break;
                   case "MoreDeals":
                       for (j=0;j<childNode.childNodes.length;j++){
                           var sectionXml= childNode.childNodes.item(j);
                           if (sectionXml && sectionXml.hasChildNodes()){
                               sections.push(parseSection(sectionXml));
                           }
                       }
                       break;    
                   default:
                       break;
                   }
               }
               deals.forEach(function(deal){
                   var entry = createDealUi(deal);
                   $("deals").appendChild(entry);
               });
               loadDetails(deals);
               sections.forEach(function(section){
                   var ui = createSectionUi(section);
                   $("moreDeals").appendChild(ui);
                   loadDetails(section.deals);
               });
        }
    };
    xhr.open("GET", "proxy?url=" + escape(dealsUrl));
    xhr.send(null);
}

リスト 4loadDeals 関数と、このアプリケーションの中で使われるグローバル変数を示しています。ここでは deals という配列と sections という配列を使っています。これらは関連する特売品 (Deals under $10 (10 ドル未満の特売品) など) で構成される追加のグループです。また dealDetails というマップがあり、このマップのキーは (deals データから得られる) Item ID であり、値は eBay の Shopping API から得られる詳細情報です。

最初に、プロキシーに対して Ajax 呼び出しを行います。この呼び出しによって eBay の Daily Deals の REST API を呼び出します。すると特売品リストが XML 文書として得られます。この文書を、Ajax 呼び出しに使われた XMLHttpRequest オブジェクトのonreadystatechange 関数の中で構文解析します。別の 2 つの関数 (parseDealparseSection) を使用して XML ノードを構文解析し、もっと扱いやすい JavaScript オブジェクトに変換します。これらの関数はダウンロード可能なコード・サンプル (「ダウンロード」を参照) の中に含まれていますが、単に XML を構文解析する退屈な関数なので、ここには含めてありません。最後に、この XML を構文解析した後、さらに 2 つの関数 (createDealUicreateSectionUi) を使って DOM を変更します。それが終わると、UI は図 1 のようなものになります。

図 1. モバイル機器での Deals の UI
特売品の例を使用して、モバイル機器での Deals の UI を示すスクリーン・キャプチャー。各特売品には Show Details (詳細を表示) ボタンが表示されています。

リスト 4 に戻り、主要な特売品をロードした後、特売品の各セクションに対して loadDetails 関数を呼び出していることに注意してください。この関数の中で、各特売品の詳細情報を eBay の Shopping API を使ってロードしていますが、このロードを行うのはブラウザーが Web ワーカーをサポートしている場合のみです。リスト 5loadDetails 関数を示しています。

リスト 5. 特売品の詳細情報を先読みする
function loadDetails(items){
    if (!!window.Worker){
        items.forEach(function(item){
            var xmlStr = null;
            if (window.localStorage){
                xmlStr = localStorage.getItem(item.itemId);
            }
            if (xmlStr){
                var itemDetails = parseFromXml(xmlStr);
                dealDetails[itemDetails.id] = itemDetails;
            } else {
                var worker = new Worker("details.js");
                worker.onmessage = function(message){
                    var responseXmlStr =message.data.responseXml;
                    var itemDetails=parseFromXml(responseXmlStr);
                    if (window.localStorage){
                        localStorage.setItem(
                                        itemDetails.id, responseXmlStr);
                    }
                    dealDetails[itemDetails.id] = itemDetails;
                };
                    worker.postMessage(item.itemId);
            }
        });
    }
}

loadDetails の中では、まず windows オブジェクトの Worker 関数があるかどうかをグローバル・スコープで調べています。Worker 関数がなければ、何もしません。Worker 関数がある場合には、まず localStorage を調べ、この特売品の詳細情報の XML がないかどうかを調べます。これはモバイル Web アプリケーションでローカル・キャシュを使用する場合の一般的な方法であり、この連載の第 2 回で詳細に説明しました (「参考文献」のリンクを参照)。

ローカルで XML が見つかった場合には、その XML を parseFromXml 関数で構文解析し、詳細情報を dealDetails オブジェクトに追加します。ローカルで XML が見つからない場合には Web ワーカーを生成し、postMessage を使って特売品の Item ID を Web ワーカーに送信します。ワーカーがそのデータを取得してメイン・スレッドに返送したら、その XML を構文解析して解析結果を dealDetails に追加し、その XML を localStorage に保存します。リスト 6 には、このワーカー・スクリプト、details.js を示しています。

リスト 6. 特売品の詳細情報のためのワーカー・スクリプト
importScripts("common.js");
onmessage = function(message){
    var itemId = message.data;
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(){
        if (this.readyState == 4 && this.status == 200){
            postMessage({responseXml: this.responseText});
        }
    };
    var urlStr = generateUrl(itemId);
    xhr.open("GET", "proxy?url=" + escape(urlStr));
    xhr.send(null);
}

このワーカー・スクリプトは非常に単純です。Ajax を使ってプロキシーを呼び出すと、eBay の Shopping API が呼び出されます。プロキシーから XML を受信すると、JavaScript オブジェクトのリテラルを使ってその XML をメイン・スレッドに返送します。注意する点として、ワーカーの XMLHttpRequest を使用しますが、返されるものはすべてワーカーの responseText プロパティー上にあり、ワーカーの responseXml プロパティー上にあるわけではありません。これは、ワーカー・スクリプトのスコープには JavaScript による DOM パーサーがないからです。generateUrl 関数 (リスト 7) が common.js ファイルに含まれていることに注意してください。リスト 6 では、importScripts を使うことで common.js をインポートしています。

リスト 7. ワーカーがインポートしたスクリプト
function generateUrl(itemId){
    var appId = "YOUR APP ID GOES HERE";
    return "http://open.api.ebay.com/shopping?callname=GetSingleItem&"+
        "responseencoding=XML&appid=" + appId + "&siteid=0&version=665"
            +"&ItemID=" + itemId;
}

これで、(Web ワーカーをサポートするブラウザーの場合に) 特売品の詳細データを追加する方法がわかったので、図 1 に戻り、このデータをアプリケーションの中でどう使うかを調べましょう。各特売品の隣には「Show Details (詳細を表示)」ボタンがあることに注意してください。このボタンをクリックすると、UI が図 2 のように変わります。

図 2. 特売品の詳細を表示する
特売品の詳細を表示した画面のスクリーン・キャプチャー。品目の詳細として、Golla 製の 2 つのポーチ (MP3 プレーヤー、携帯電話、カメラなどを収納) の説明、写真、価格が表示されています。

この UI は showDetails 関数を呼び出すと表示されます。リスト 8 は、この関数を示しています。

リスト 8. showDetails 関数
function showDetails(id){
    var el = $(id);
    if (el.style.display == "block"){
        el.style.display = "none";
    } else {
        el.style.display = "block";
        if (!el.innerHTML){
            var details = dealDetails[id];
            if (details){
                var ui = createDetailUi(details);
                el.appendChild(ui);
            } else {
                var itemId = id;
                var xhr = new XMLHttpRequest();
                xhr.onreadystatechange = function(){
                    if (this.readyState == 4 && 
                                      this.status == 200){
                        var itemDetails = 
                                        parseFromXml(this.responseText);
                        if (window.localStorage){
                            localStorage.setItem(
                                              itemDetails.id, 
                                              this.responseText);
                        }
                        dealDetails[id] = itemDetails;
                        var ui = createDetailUi(itemDetails);
                        el.appendChild(ui);
                    }
                };
                var urlStr = generateUrl(id);
                xhr.open("GET", "proxy?url=" + escape(urlStr));
                xhr.send(null);                        
            }
        }
    }
}

表示対象となる特売品の ID が渡され、その特売品を表示するかどうかを切り替えます。この関数が初めて呼び出されると、表示対象の特売品の詳細が既に dealDetails マップに保存されているかどうかをチェックします。ブラウザーが Web ワーカーをサポートしている場合には、詳細情報は既に dealDetails マップに保存されているため、その特売品に対する UI が作成されて DOM に追加されます。まだ詳細情報がロードされていない場合や、ブラウザーがワーカーをサポートしていない場合には、Ajax 呼び出しを行って詳細情報のデータをロードします。こうすることで、ワーカーがあってもなくてもアプリケーションは適切に動作します。これはつまり、ワーカーがサポートされている場合には既にデータはロードされており、UI は瞬時に応答するということです。ワーカーがない場合にも UI はロードされますが、このロードには数秒間かかります。


まとめ

Web 開発者にとって、Web ワーカーはもの珍しい新技術に思えるかもしれません。しかしこの記事で説明したように、Web ワーカーを利用することで極めて実用的なアプリケーションを作成することができます。特にモバイル Web アプリケーションには Web ワーカーが有効です。Web ワーカーを使うことでデータの先読みや他の事前操作を実行し、より応答性の高い UI を実現することができます。モバイル Web アプリケーションでは、速度の遅いネットワークを介してデータをロードしなければならない場合があるため、Web ワーカーによる応答性の向上は特に有効です。キャッシング戦略とワーカーを組み合わせると、ユーザーはアプリケーションの応答性の良さに驚くはずです。


ダウンロード

内容ファイル名サイズ
Article source codeWorkers.zip8KB

参考文献

学ぶために

製品や技術を入手するために

  • Modernizr プロジェクトは HTML 5 の機能を検出するための包括的なユーティリティーです。
  • Modernizr プロジェクトを訪れ、localStorage、Web ワーカー、applicationCache など、HTML 5 の機能を検出するための包括的なユーティリティーを入手してください。
  • Android Developers の Web サイトを訪れ、Android SDK をダウンロードし、API 資料にアクセスし、また Android の最新ニュースを入手してください。
  • 最新の iPhone SDK を入手し、iPad、iPhone、iPod タッチなどのアプリケーションを開発してください。
  • Android Open Source Project を訪れ、Android モバイル・プラットフォームのオープンソース・コードを入手してください。
  • Google App Engine SDK をダウンロードし、Google を使ったスケーラブルな Web アプリケーションを作成するための Java ツールや Python ツールを入手してください。
  • IBM 製品の評価版をダウンロードするか、あるいは IBM SOA Sandbox のオンライン試用版で、DB2®、Lotus®、Rational®、Tivoli®、WebSphere® などが提供するアプリケーション開発ツールやミドルウェア製品を試してみてください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML, Open source, Web development
ArticleID=498942
ArticleTitle=HTML 5 を使ってモバイル Web アプリケーションを作成する: 第 4 回、Web ワーカーを使ってモバイル Web アプリケーションを高速化する
publish-date=06082010