たった2ファイルで,HTML+JS製のネイティブAndroidアプリを作る手順 (動作するサンプルコード付き。WebViewの活用方法)
重要なお知らせ:
この記事で公開した情報は,AndroidのMVCフレームワーク「Android-MVC」の機能の一部として取り込まれました。
より正確な設計情報や,動作可能な全ソースコードを閲覧したい場合,「Android-MVC」の公式ページより技術情報を参照してください。
AndroidのMVCフレームワーク - 「Android-MVC」
http://code.google.com/p/android-mvc-...
HTMLとJavaScriptを使って,Androidのネイティブ・アプリを作成し,実機上で動かす。
画面の裏側では,Javaのコード(Activityなど)が動いている。
特別なツールは,何も必要ない。
Androidの,標準の開発環境さえあればよい。
サンプルコードと動作手順
まず、アクティビティ。
WebviewTestActivity.java
package com.example; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.webkit.WebView; /** * HTMLとJavaScriptでアプリの画面周りを実装するサンプル。 * @author id:language_and_engineering * */ public class WebviewTestActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); WebView wv = new WebView(this); // WebView内でJavaScriptを有効化 wv.getSettings().setJavaScriptEnabled(true); // WebView内のJavaScriptから,Javaのオブジェクトを参照可能にする wv.addJavascriptInterface(this, "droid"); // WebView内でズーム機能を有効にする wv.getSettings().setBuiltInZoomControls(true); setContentView(wv); // WebView内に,アプリが保持するHTMLを表示 wv.loadUrl("file:///android_asset/hoge.html"); } // 任意の文字列をログ出力するメソッド public void x( String s ) { Log.d("WebViewTest", s); } // 文字列を返すメソッド public String y() { return "fuga"; } }
次にassetsフォルダ内に,hoge.htmlを設置:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>WebViewのテスト</title> </head> <body> <h1>WebViewのテスト</h1> <h2>JavaScriptからJava側のメソッドを呼び出す(1)</h2> 入力欄:<input type="text" size="20" id="text1" ><br> <input type="button" value="上記の文字列をログに出力する" onclick="f1()" > <script> function f1() { var text1 = document.getElementById("text1"); var str = text1.value; // Java側のメソッドを呼び出して,値を渡す droid.x( str ); } </script> <h2>JavaScriptからJava側のメソッドを呼び出す(2)</h2> <input type="button" value="Java側からStringを受け取って表示" onclick="f2()" ><br> <span id="text2">ここに表示されます。</span> <script> function f2() { // Java側のメソッドを呼び出して,返り値を表示 var str = droid.y(); var text2 = document.getElementById("text2"); text2.innerHTML = str; // ※alertは利用不可能 } </script> <!-- スタイルシートによるレイアウト・デザインも利用可能 --> <style> body { background-color : #FFE4B5; padding : 1em; /* CSS中では長さの単位にdipを使えない */ } </style> </body> </html>
これだけ。
「HTML製のAndroidアプリ」が完成する。
EclipseからAndroidアプリケーションとして実行すると,
画面上には2つのボタンがあり,いずれもJava側のコードとうまく連携して動作してくれる。
レイアウトXMLとか,イベントリスナをコーディングする手間が大幅に削減されたのを実感できるだろう。
たった2ファイルのコーディングだけで済ますためのポイント:
- ActivityからWebViewをsetContentViewする。
- assets内のhtmlでJSを利用する。
- レイアウトXML,マニフェストXMLはいじらない。
参考:
AndroidとJavaScriptを連携させる方法
http://www.adamrocker.com/blog/172/ja...
- WebViewのWebkit経由でJSからAndroidのGPSデータを取得するサンプルコード
- JSから呼び出せるのはpublicのメソッドだけで,privateメソッドを呼び出すとエラーになるので注意
WebViewに拡大/縮小機能をつける方法
http://android49.blog.fc2.com/blog-en...
- setBuiltInZoomControlsで,マルチタッチのピンチイン・ピンチアウト操作が可能に
JavaScript alert not working in Android WebView
http://stackoverflow.com/questions/52...
- window.alertはWebView中では動作しないで無視されるので,WebChromeClient に置き換えるか,またはJava側でダイアログを表示するようなメソッドをコールする
How to specify image size in webview css using dip?
http://stackoverflow.com/questions/66...
- CSS中では,レイアウトXMLのようにdipという単位を使う事ができない。マルチデバイスの解像度に対応するためには,emを使って工夫したりする。
ところで実は,HTMLはファイルでなくてもOK。
ファイルを準備せずに,Java側でただの文字列としてHTMLを構築して済ませてしまう事もできる。
その場合,この要件は「たった1ファイルで」実現できる事になる。
WebViewからローカル画像を参照する
http://bugcloud.com/?p=1239
- プログラム内でhtml文字列を生成して、WebView#loadData(html, “text/html”, “UTF-8″)
- loadDataWithBaseURLを使う
Referencing drawables in HTML in Android’s WebView
http://remotedroid.net/blog/2011/01/1...
- HTMLをassetsフォルダ内に配置した場合,HTML側からdrawableな画像リソース等を直接参照することはできない。icon画像を表示したい場合は一工夫必要
また,loadUrlで表示したいHTMLファイルは,assetsフォルダ直下に置かなくてもよい。
assetsの中にサブフォルダを作って階層化し,ローカルファイルを整理できる。
Displaying Android asset files in a WebView?
http://stackoverflow.com/questions/53...
- wv.loadUrl("file:///android_asset/html_no_copy/test.html"); // Works
また,UI層でJavaを使わないという事は,つまりリソースクラス(R.java)も参照できない事になる。
それだと,多言語対応やローカリゼーションで不便かも?
と思うかもしれないが,そこは別の手段で対処できる。
Android他言語機能2.1の留意点
http://kokuzuki.blogspot.jp/2011/06/a...
- if ( Locale.JAPAN.equals(Locale.getDefault() ) でloadUrl対象を分岐させる
このように,WebViewは非常に便利。
しかし念のため,注意事項を。
このサンプルでは,利用するファイル数ができるだけ少なくて済むように…という方針でコーディングした。
しかし実際には,Activityとかcontextなどの「何でも出来てしまうオブジェクト」をJS側に渡すという行為は,セキュリティ的には非常に危険である。
JS側に渡すための小さなオブジェクトを独自に作成し,必要最小限の機能だけを呼び出せるようにしておくべきだ。
セキュリティ面の情報は後述する。
モバイルアプリ開発用のフレームワークの内面
「HTML5でネイティブアプリを生成」,
というモバイル用フレームワークが,巷で盛んに流行っているが,その仕組みはどうなっているのか。
どうしてそんな事が可能なのか。
Androidの場合,本記事で紹介したWebViewがうまく使われている。
フレームワーク側では,JavaScriptから参照できるようなJavaオブジェクトを準備する。
HTML内では,そのオブジェクトを経由してAndroid SDKのAPIを利用可能になる。
要は,HTML「5」である必然性は低いのだ。
単純に,Webページが動く。という話。
「5」だけが格別,特殊な存在ということではない。そこは誤解しやすい。
WebViewにJavascriptでAndroidのAPIをたたけるインタフェースを追加する
http://d.hatena.ne.jp/ttshrk/20110511...
- PhoneGap(android版)やjsWaffleなどのWebViewベースのフレームワークで端末のAPIを呼び出すために行っている方法
- Titaniumもandroid版はまだWebViewがメイン(2011年5月)
「HTML5によるAndroidアプリ開発入門」献本いただきました
http://mzsm.me/2012/01/18/html5-andro...
- Titanium Mobileは,Java/Objective-Cの代わりにJavaScriptを記述言語として利用し,AndroidやiPhone用のアプリを作る
- PhoneGapを「ブリッジフレームワーク」として利用する場合は,HTML5やCSS3を含めたWeb制作の技術で「Webアプリ」を作成。そのWebアプリをフレームワークでラッピング。そうしてネイティブアプリを作成
HTML製のモバイルアプリを作るツールとして,幾つか例を挙げてみる。
(1)Titanium
Titatium Mobile の場合は,JavaScript側では「Ti」というオブジェクトを利用し,Java側のTitanium APIを呼び出す。
そしてJava側では,「Ti」の実体は,あらゆるAndroid SDKを呼び出せるようプロキシメソッドが満載になった便利オブジェクトである。
Titanium mobile 開発で最低限理解が必要な JavaScript のこと
http://astronaughts.net/titanium-mobi...
- 主にJSONでUIを処理する
- Ti. は Titanium. の略。Ti. には通信、位置情報、加速度センサ等の操作ができる処理が含まれている
(2)PhoneGap
PhoneGapフレームワークは,現在ではAdobe社に買収されてオープンソースになり,Apache Cordovaとも呼ばれている。
PhoneGapについて
http://d.hatena.ne.jp/takeR/20120120/...
- HTML5をベースにしたハイブリットアプリケーションフレームワーク
- Titaniumよりも移植性が高い
AdobeがPhoneGapブランドでCordovaを提供
http://www.infoq.com/jp/news/2012/03/...
- Adobeは、2011年11月に Nitobi SoftwareとそのPhoneGap製品を買収
- Adobeは PhoneGapブランドでCordovaディストリビューションを提供し続ける
このツールも,HTMLファイル内からJavaScript経由で,Android側のAPIにアクセスできる。
アクセス可能なJavaコードを自前で自由に準備したい場合は,PhoneGap Plugin機構を利用する。
Androidネイティブコードの連携
http://d.hatena.ne.jp/takeR/20120125/...
- PhoneGapはJavaScriptからAndroidの端末固有の機能を呼び出す仕組みを提供する
- 他の処理と自由に連携させたい場合はPhoneGapPluginを使用する
Android版PhoneGapプラグインの紹介
http://dev.classmethod.jp/smartphone/...
- Java側で音声認識が可能なプラグインを準備し,JavaScriptから呼び出しているサンプル
PhoneGap APIを拡張する@iPhone
http://lab.dwango.jp/articles/extendi...
- JS側のPhoneGap.exec()関数内部でgap://<Class>.<command>/[<arguments>][?<dictionary>]という形式のURIに変換し、Objective-C側へのブリッジを実現
・・・
このように,HTMLでAndroidアプリを開発するフレームワークを利用すると,
ある程度のアプリであれば,Javaのコードを書かずに完成する。
JavaScriptのスクリプティングだけで実現してしまう事が可能だ。
こういった方式で開発されたモバイルアプリの事を,ネイティブアプリ・Webアプリに対して,ハイブリッドアプリと呼ぶ。
JSは動的言語なので,プログラミングが柔軟でスピーディ。
ただ,アプリの実行スピードは通常の場合よりも遅くなる。
セキュリティ面
このように,Androidアプリのビュー部分にHTML+JavaScriptを利用する方法には,メリットもデメリットもある。
モバイルを知らなくてもWebデザイナがプロジェクトに参画できるので,既存のWeb制作のスキル・人材を使いまわせる。
「Web制作の便利さを,モバイル開発の世界に一気に持ち込む」事ができる。
これは良い面だ。
だが同時に,「Webが持つ危険を,モバイルの世界に一気に持ち込む」事にもなる。
そのリスクを忘れてはならない。
Android WebViewのセキュリティリスクと攻撃の実際(2012年6月)
http://dl.dropbox.com/u/439702/slide/...
- スライド形式で詳しく解説 ※forwardボタンで次の画面へ進む
WebView での JavaScript の使用
http://www.techdoctranslator.com/andr...
注意: JavaScript にバインドされたオブジェクトはそれが生成されたスレッドではない別スレッドで実行します。警戒: addJavascriptInterface() を使うと JavaScript で Android アプリケーションを制御できるようになります。
これはとても便利ではありますがセキュリティ的に問題があり危険です。WebView の HTML が信頼できないと ( 例えば、HTML の一部または全部が提供元が不明な人物や行為により提供されているもの ) 、攻撃者によりクライアント側のコードや、もしかすると攻撃者が選別したコードを実行するような HTML を埋め込むことができてしまいます。
WebView に表示させる HTML と JavaScript のすべてが記述されていない場合は、そういった用途で addJavascriptInterface() を使用すべきではありません。
また、WebView の内で自分が作成したウェブページ以外にユーザがアクセスできるようにすべきではありません ( その代わり、ユーザにはデフォルトのブラウザアプリケーションで外部のリンクを開かせるようにします。ユーザのウェブブラウザは、デフォルトですべての URL リンクを開くようになっているのでいいのですが、以下のセクションで説明する際のページのナビゲーションのハンドリングの場合のみ注意が必要です ) 。
そういうわけで,使いどころ+使い方に要注意。