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上で動かす際に、何かの参考になれば幸いです。



