2011年12月11日日曜日

WebViewからJavaScriptをコールしたり



AndroidとかiPhoneのアプリ開発はWebViewを使ったWebアプリが増えていく感じがあるが、
WebViewだけだと読み込みが遅いとかレスポンスが悪いとかまだまだNativeで作った方がユーザとしてはストレスが少ないものが作れるような気がする。
HTML5でアプリがWebアプリになったとしてもなかなかレスポンスの部分は解決が難しいのではないかな?というのが個人的な印象。
そんなときにはNativeとWebViewの組み合わせでアプリを作ろうとする場合があると思う。
両者を橋渡しすることになるのはJavaScriptになりそうだけど、それぞれのプラットフォームでコールの仕方とか違うみたいなので少し調べてみた。

AndroidとiPhoneを比べると
NativeからJSを呼び出すのはiPhoneのが整っていて
JSからNativeを呼び出すのはAndroidの方が整っている印象。


Androidの場合は、
・NativeからJSの呼び出し
mWeb.loadUrl("javascript:コールしたいJSの関数");
のようにWebViewのインスタンスからmethodをコールして
JSを呼び出すような感じになっている。
ただ、この関数戻り値がvoidのなので結果が分からない。。。
結果を知ろうと思ったら、関数コールの引数にcallbackを指定するような文字列を
入れるなどして結果を取得しなければならない。サンプルではconsole.logをフックして結果を表示しているけど、ほめられた方法ではない。


・JSからNativeの呼び出し
mWeb.addJavascriptInterface(new NativeClass(), "Android");
のようにするとNativeClassが"Android"がJS内における名前空間にように扱えるので
Android.callNative("arg");
のようにJS側からコールスことが出来る。callNativeはJavaのNativeClassのmethodになっている。
この関数は戻り値をとることが出来るので同期でコールすることが出来そうだ。


iPhoneの場合は、
・NativeからJSの呼び出し
[_webView stringByEvaluatingJavaScriptFromString:@"IPHONE.CallJSfromNative('hoge')"];
のように、WebViewのメソッドをコールスことでJSを呼び出すことが出来る。
ここでは、IPHONE.CallJSFromNativeがJS側で宣言されている関数になる。
stringByEvaluatingJavaScriptFromStringは戻り値があるので同期でコールすることが出来そうだ。


・JSからNativeの呼び出し
iPhoneの場合はJSからNativeをコールする方法はきちんと用意されていないようで、少し調べた限りでは、いろんな人がそれぞれ工夫しながら実現しているような印象だ。
具体的には独自のスキームを利用してコンテンツをJSからロードしたときにshouldStartLoadWithRequestがコールされるので、独自のスキームをフックして、処理を行うような感じにするのが一般的のようだ。
- (BOOL) webView:(UIWebView *)webView
                shouldStartLoadWithRequest:(NSURLRequest *)request
                navigationType:(UIWebViewNavigationType)navigationType {
    
    NSString *requestString = [[request URL] absoluteString];
    if (![requestString hasPrefix:@"callobjc:"]) return YES;
    
    NSArray *components = [requestString componentsSeparatedByString:@":"];
    NSString *function = [components objectAtIndex:1];
    [self performSelector:NSSelectorFromString(function)];   
    return NO;
}
上のような感じで、関数名を取り出した後にself performSelector:NSSelectorFromString()で内部に宣言した関数をコールしている。
サンプルをここにおいておいた。
WebがわはDjandoで作っているが、アプリではなく静的なファイルを戻すだけなので、
必要なのは、それぞれのディレクトリ内にあるindex.htmlとsite_templeteの中にあるJSのファイルだけだ。AndroidについてはeclipseにPydevさえ入れてあればwebサーバとかたてなくても試せるはず。


少し調べてみた感じだと、すべてのフレームワークに共通のIFを用意するのはなかなか難しそうで、用意するとしたら全部非同期になりそうだ。なぜなら戻り値がなかったりするから。
https://github.com/callback/callback-ios.git
にPhoneGapのソースがあって、これは結構面白かった。
JS側からNativeをコールするときにはPhoneGap.exec()を利用する。
内部では、gap:のスキームをloadするとWebViewのコールバックが呼ばれて、その中でJS側でキューイングされている文字列を取得して、Native側の関数をキューイングされている分だけすべて関数をコールする感じになっている。
独自にJSのNativeの連携部分を作るよりもこういったコードを流用する方がよいかもしれない。
./PhoneGapLib/javascripts/core/phonegap.js.base
は拡張子が微妙だけどJS部分だろうと思う。
./PhoneGapLib/Classes/PhoneGapDelegate.m
の中にJSからのコールバック部分があったりした。


実際にWebViewとNativeの組み合わせのアプリ作ってみて感じたけど、やっぱりWebだと連打はつらいなー。というのが現状のようだ。
それと思いのほかWebViewとNativeの組み合わせでやり取りしても、画面表示のレスポンスは結構よかった。


1 件のコメント:

  1. とても魅力的な記事でした!!
    また遊びに来ます!!
    ありがとうございます。。

    返信削除