anatoo.jp twitter

HTML5によるハイブリッドアプリ開発に関する雑記

HTML5でiPhoneやAndroid向けのハイブリッドアプリを作るのが最近の流行りみたいです。ハイブリッドアプリとは、外面は普通のアプリとしてAppStoreやGoogle play marketでインストールできるものの、その中身や一部がHTML5で記述されているアプリです。

最近の有名な例だと、Cookpadã‚„LinkedIn、はてなスペース、少し前にネイティブに移行してしまいましたがfacebookのモバイルアプリもHTML5を使って記述されていました。GREE界隈で言われているらしいガワネイティブっていう言葉もハイブリッドアプリを指します。ちょっとググってみると、2016年には企業向けのアプリの50%がハイブリッドアプリになるという予測もあります。

ハイブリッドアプリの何がいいかというと、Objective-CとかJavaとかがわからなくてもウェブ系技術者であればAndroidやiPhone向けアプリが書けちゃうので、今までウェブでやってきた技術者のスキルをそのまま使えるというところがよく言われます。また、マルチプラットフォーム対応する際には、全てネイティブで書くと同じ内容のアプリをJavaとObjective-Cで開発しなければいけませんが、HTML5であればワンソースでAndroidとiOS両方のプラットフォームに対応できるという事もよく言われます。世の中ウェブ系技術者はいっぱいいますが、スマートフォンという比較的歴史の浅いプラットフォームの開発者は余る程いるわけではないということなんだろうと思います。

ただ、HTML5でモバイルアプリを開発する際のノウハウというものは、結構細々とした話が多いせいかそれほど共有されていません。おいおいHTML5だから普通のウェブ技術者も普通に開発できるからそんなノウハウとか必要ないんじゃないの、と思うかもしれません。が、同じHTML5でもPCとくらべてスペックの低いAndroid端末やiOS端末できちんと動くようにしなければいけなかったり、モバイルアプリ特有の事情があったりするわけです。あとネイティブとHTML5を橋渡しするのはどうするか、とかも。ハイブリッドアプリも銀の弾丸では無く、色んなハマりどころがあります。よくあるのは、HTML5で作ってみたけど重すぎて使い物にならなかったり、一応動くけどUI的に大変ヘボいアプリができてしまう、またはfacebookが直面したようなHTML5で作ったアプリが落ちるけどWebViewの中身がブラックボックスなのでなんで落ちるのかよくわからない、というところです。

とりあえずハイブリッドアプリ開発の時に必要となる細々としたノウハウなどを雑多にまとめて書いておきます。

以下、箇条書き。

  • ハイブリッドアプリの作り方
  • HTML5からネイティブの機能を呼び出したいときはPhoneGapなどを使える
    • PhoneGapに実装されていない機能でも、プラグインを書くことで実装できる
  • セキュリティ
    • ハイブリッドアプリ内ではJSからネイティブの機能を呼び出せるので、XSSに気をつけるのはもちろん、iframe内で信頼出来ないHTMLを読み込む際にも気をつける
    • PhoneGapだとホワイトリスト式で読み込むHTMLを制限するなど色々対策されている
    • 自前でHTML5とネイティブを通信する仕組みを作って脆弱性生むよりも素直にPhoneGap使ったほうがいい
  • モバイル端末で表示しているHTMLにウェブインスペクタを利用するには
    • iOSならsafariのリモートインスペクタを使う
    • それ以外はweinre使う
      • 自分の場合、社内にweinreサーバ立ててすぐに使えるようにしてる
  • 何もかもHTML5で書こうとするとだいたい破綻する
    • android2.3ã‚„iOS4だと,CSSのposition: fixedがまともに使えないので、上部に固定されたツールバーが作れない
    • パフォーマンス上HTML5だと無理っぽい部分が出てくる
    • ネイティブですでに用意されているUI部品があるのにHTML5でイミテーションを作る無意味さ
    • 開発効率を上げるためにハイブリッドで、という場合になんでもHTML5でやろうとするとかえって効率悪くなったりへぼいアプリが出来上がったりして本末転倒になる
    • 自分の場合、画面のツールバーやそのボタンなどは、HTML5で書かずにネイティブで書いてる(中身のコンテンツの部分だけHTML5で書いている)
    • あと画面遷移時のスライドアニメーションなどもネイティブのコードで書いてる
    • パフォーマンスや原理的にHTML5では難しい画面の場合、PhoneGapプラグイン書いてその画面だけネイティブにするのが吉
  • ボタンなどにclickイベントは使わない
    • 試してみるとわかるけどびっくりするほど遅延が発生する
    • tappableのようなタップイベントをうまく扱えるライブラリを使う
  • jQueryじゃなくてzepto.js使いましょう
  • アニメーションにはCSSアニメーション使いましょう
    • JavaScriptのみで直接アニメーションするとクソ重い
    • JavaScriptからアニメーション操作するときは domElement.style.webkitAnimation みたいなアニメーション関連のスタイルのプロパティを書き換えて使う
    • アニメーションライブラリを使う場合は内部でCSSアニメーション使っているかどうか確認しておく
    • 仕方なくJavaScriptだけでアニメーションする場合でも、setTimeoutã‚„setIntervalではなく、できればrequestAnimationFrame使う
    • iOSだと、webkitTransformにtranslateXã‚„translateを使うとチラつくので、translate3dの方を使う。
    • アニメーションの描画にはGPUを有効にして早くする
      • GPUを有効にしたい要素にはこの呪文: -webkit-transform-style: preserve-3d;
      • androidでGPUが効いてるかどうかを確認するには、開発者向けオプションで「GPUオーバードローを有効にする」が付いている端末で見る
      • iOSの場合は、シミュレータで確認できる。コマンドラインでこれ実行 => CA_COLOR_OPAQUE=1 open /Applications/Xcode.app/Contents/Applications/iPhone\ Simulator.app
  • パフォーマンスが必要なときは、DOMをいじらない
    • 見た目を変えるときに、class属性を変更するやり方はよくあるが、アニメーション中の処理などのパフォーマンスが必要なときにやると重い。子要素を追加したりするのも同様
    • DOMそのものをいじるのではなく、DOM要素のstyleプロパティをJavaScriptから直接弄ったほうが断然軽い
  • viewport使いましょう
    • viewportはモバイル用のHTML5に特有の概念
    • 端末の幅に合わせて画面を自動的に拡大縮小みたいなことができる
    • androidだとviewportのwidth設定が効かないので、viewport.jsのようなviewportの動作をエミュレーションするライブラリを使う
  • リストのような要素を沢山表示する画面でのイベント処理には、イベントが発生するDOMElement一個一個にaddEventListenerすると重くなる
    • document.bodyにひとつだけイベントハンドラをつけてevent.targetでどのDOM要素でのイベントかを見て処理する
  • android4系だと、WebViewがassetsからリソースを512回以上読み込むとSIGSEGVで必ず落ちるバグがある
    • HTMLだけではなくJSã‚„CSS、画像なども回数に含む。同じリソースを何度も読み込んでも発生する。リソースの読み込みがある画面遷移を繰り返すと確実に落ちる
    • WebView内部のNDKで書かれた部分のバグなのでどうしようもない
    • HTMLなどはassetsじゃなくてファイルシステムから読み込むようにする
    • アプリの初回起動時などにassetからファイルシステムに移すとよい
  • 画面の傾きをハンドリングする
    • 画面が傾いた時には、iOSだとorientationChangeイベントが発火する
    • android2.2以前だとorientationChangeイベントが発火されず、代わりにresizeイベントが発火する
    • なのでandroidではresizeイベントからwindowの縦横のアスペクト比を見て判断する
  • touch関連イベントの扱い
    • iOSã‚„Androidでは指のタッチを扱うイベントとして、touchstart、touchmove、touchend、touchcancelというmouseイベントとは別の扱いのイベントが発火される
    • touchstartイベントが発火された要素で、touchendイベントが発火されるとは限らない
    • touchendの捕捉はその個別のDOM要素ではなくページ全体でやる
    • それとは別の話で、androidだとtouchstartした後touchendイベントがしばしば発火されない
  • iOSで動かすハイブリッドアプリのほうがはるかに速い
    • androidとiOS両対応する場合は、まずandroidに合わせる
    • 最初にiOS用のコードを書いて、その後にandroidに対応しようとするとandroidで重すぎて死ぬ