【Android】Next.jsでローカルWebview時のエラー対応

フロントエンド技術

Next.jsを用いてビルドしたHTML/CSS/JavaScriptをAndroid上のローカルWebviewで動作させる際に多数のエラーに遭遇しました。オフショアメンバーと解決した方法を紹介します。

はじめに

プロジェクト背景

とあるAndroid携帯アプリのPOC(概念実証)で、API経由のデータ取得はAndroid側、表示ロジックはWebview側でチームを分けて開発しました。Webviewは、静的ファイル設置(ローカルWebviewと呼ばれます)で、ウェブサーバーはないという条件で、技術選定を行いました。

当初はWebview用の静的ファイルの作成の為にjQueryかReactを考えていましたが、オフショアメンバーの強いお勉強意欲・モチベーションによりNext.jsを選択しました。

Webviewとは何なのか?

WebView(ウェブビュー)とは、AndroidやiOSアプリ内にWebサイトを表示できるようにする技術の事です。「アプリ内ブラウザ」と呼ばれることがあります。

Webviewファイルの格納

Next.jsで構築したWebサイトをビルドしすると、distディレクトリにHTML/CSS/JavaScriptが生成されます。

今回は、Android端末でPOCを行うので、Webviewアセットの格納先は、Android Studio/プロジェクト配下の /app/src/main/assets です。

Android側では、以下の様に指定する事で、assetsディレクトリ内のファイルが指定することができます。

webView.loadUrl("file:///android_asset/index.html");

相対パスのファイル参照エラーの対応

Webサーバーがない状態で相対パスでのファイル参照(file://)やnpm run devによるローカルホストで確認すると、プロジェクトフォルダまでの絶対ファイルパスが繋がらずエラーが発生しました。これを解決するために、ビルド時に相対パスのファイル参照エラーに対応するためのprefix設定suffix設定を行いました。リンク先は英語ですが、弊社のオフショア開発では、こうして課題、URL、議論がSlack上で飛び交い、チーム一丸となって対応していきます。

また、AndroidのローカルWebviewに置く際には、ローカルの開発環境用の絶対パスで出力されたソースを/android_asset配下に置いたかのようにパスを置換しました。そうすることで、Android側はfile:///android_asset配下を参照できます。

なお、先述のprefix設定で予め/android_assetに出力するのもいいと思いますが、ローカルホストで開発やブラウザ確認した後に、Android Studio配下にソースをコピーしてWebview確認をしていたので、後述するShellで置換を行いました。

Android側で “_” 始まりのフォルダ名が非許可の対応

Next.jsのビルド時に生成されるdistディレクトリ配下のライブラリパスは”_next”となります。しかし。Android側で_始まりのフォルダが非許可となっていました。この問題を解決するために、“_next”を”next”に変更しました。

ビルド専用のシェル準備

Next.jsのソースを更新してWebviewに反映する度に、上述の「相対パスのファイル参照エラーの対応」と「Android側で_始まりのフォルダが非許可の対応」を毎回対応する必要があります。面倒ですので、ビルド専用のシェルを準備しました。

シェルの処理概要としては、以下となります。
1. Next.jsのビルド(npm run build)
2.「相対パスのファイル参照エラーの対応」
3.「Android側で_始まりのフォルダが非許可」
4. ビルドファイルをAndroidのWebviewアセット格納先へコピー

<Link>タグによるCORSエラーの対応

コンソールログでページ間の移動時にCORSエラーが発生しました。

Access to fetch at "xxxx" from origin null has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes

Next.jsの<Link>タグは、次の画面のパーツを予め取得しようとしますが、これが原因でエラーが発生していました。この問題を解決するために、<a>タグに変更しました。

難読化によるメソッド名の変化

APIでのデータ取得はAndroid側で行い、Webview側のJavaScriptメソッドを呼んでもらう処理がありました。Android⇔Webview間でメソッドを呼び合う方法については、こちらの記事が詳しいので、説明を割愛します。Android側のエンジニアさんには、「〜のメソッド名で呼んで下さい」と依頼していましたが、メソッド名が見つからないエラーが発生してしまいました。

なんと、Webpackのビルド時にされると、メソッド名が変化していたのです。
「難読化(英語で obfuscation)/名前修飾(英語で mangling) されてしまった」とオフショアのエンジニアに伝え、試行錯誤します。

これを防ぐために、terser-webpack-pluginというプラグインを使って、next.config.js上にWebpackで難読化しないメソッド名を指定し、解決しました。

const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
  webpack(config, { dev, isServer }) {
    if (!dev && !isServer) {
      config.optimization.minimizer = [
        new TerserPlugin({
          terserOptions: {
            mangle: {
              reserved: ['mangleExcepted'], // mangleから除外する関数名を指定
            },
          },
        }),
      ];
    }

    return config;
  },
};

グローバルオブジェクトとしてのメソッド宣言

メソッド名が難読化により変更されないように設定しましたが、今度はそのメソッドがNext.js内部に定義されており、Webview側のJavaScriptメソッドからはアクセスできずエラーが発生しました。

(self.webpackChunk_N_E = self.webpackChunk_N_E || []).push([[458], {
3494: function(e, t, r) {
// 上のNext.jsの定義中に、難読化を免れた下のメソッド「mangleExcepted」が定義されている
, mangleExcepted = async function() {

この問題を解決するために、JavaScript側のメソッドをwindowオブジェクトに紐づけ、グローバルオブジェクトとして宣言し、Androidからも見えるようにしました。

windowオブジェクトはDOMドキュメントを含むウィンドウを表し、ドキュメントへのアクセスや操作に使用できます。windowオブジェクトはブラウザ内のグローバルオブジェクトであるため、そのプロパティやメソッドはどこからでもアクセスできます。

以下のように、mangleExcepted をwindowオブジェクトのメソッドとして定義しました。

window.mangleExcepted = async function() {
    // ここにコードを書く 
};

このようにメソッドを定義する事で、Androidからは以下のようにアクセスできるようになります。

webView.loadUrl("javascript:window.mangleExcepted('" + result + "')");

関数をグローバルに公開することで、名前の衝突を引き起こす可能性がありますので、ユニークな関数名にしました。

まとめ

以上が、Next.jsをビルドしで出力したHTML/CSS/JavaScriptをAndroid上のローカルWebviewで動かす為に解決したエラーでした。

Next.jsのアプリケーションをAndroid上で動かす際に、何かの参考になれば幸いです。



オフショア開発ならCRAID!

オフショア開発とは、システム開発業務などを海外の開発会社や海外子会社に委託することです。

CRAIDは東証プライム上場のフリービット株式会社の子会社です。CRAIDのオフショア開発拠点「フルスピードテクノロジーズ」は、当初は月間3000億ものリクエスト処理にも対応できる自社システム開発を行うためのオフショア開発部門として始まりました。各グループ会社の開発やクライアント様の受託開発やラボ型開発も多く手掛けております。

CRAIDやオフショア開発に関して、お気軽にお問い合わせください。

この記事を書いた人

米国の大学を卒業。インド財閥系IT企業でブリッジSEとして3年間勤務後、
半導体メーカーの社内SEとして、6年間システムの海外展開や運用を担当。
子供の英語教育を念頭にフィリピン移住を目指し、2022年CRAIDに入社。
現在セブ島でオフショア開発子会社の開発組織マネージャーとして勤務中。

Yoshimuraをフォローする
フロントエンド技術
CRAID オフショア開発ブログ
タイトルとURLをコピーしました