Javascript Interfaceとは?
スマホアプリ内にウェブページを埋め込むwebViewを使う際、
フロントエンドのコードがスマホアプリのネイティブコードを叩く手段が提供されている。
それがJavascriptInterface
使い方導入
kotlin実装
class MyJavascriptInterfaces {
@JavascriptInterface
fun hello(){
Log.i("webViewApp", "Hello, World")
}
}
というJavascript Interfaceを搭載したクラスを作り
binding.webView.apply {
settings.javaScriptEnabled = true
addJavascriptInterface(MyJavascriptInterfaces(), "MyJavascriptInterface")
loadUrl("https://www.google.co.jp/")
}
webViewのaddJavascriptInterface関数を使用して登録するだけ。簡単。
フロントエンドから叩く方法
MyJavascriptInterface.hello()
で叩ける。
もしもリリース済みのアプリで
- JavascriptInterfaceを導入しようとしている場合
- JavascriptInterfaceの関数を追加しようとしている場合
は、過去のアプリ上でエラーが発生しないよう
MyJavascriptInterface?.hello?.()
とすると安全である。
実際に動かしてみる
webViewアプリを起動して
Chrome上で chrome://inspect/#devices
を開いてください
このように現在起動しているwebView画面の一覧が出て来ます。
スマホでGoogle Chrome等のブラウザを開いているとそれも一覧に出て来ます。
inspectを押すと見慣れた物が出て来ます。
先ほどのJavascriptInterface呼び出し関数をconsole上で実行すると
エラーが発生せず
LogCatを確認すると Hello, World
が正常に出力されています。
取り扱える値
誰かドキュメント探して教えてください(土下座)!!!探しても見つからなかったので実際に動かして試しました。
結論から言うと、引数と戻り値ともに
- Boolean
- Int
- Double
- String
- String?
が取り扱える。
引数限定として
- StringArray
も渡せる。
@JavascriptInterface
fun getInt(v: Int): Int {
return v+2
}
@JavascriptInterface
fun getBoolean(v: Boolean): Boolean {
return !v
}
@JavascriptInterface
fun getDouble(v: Double): Double {
return v+1
}
@JavascriptInterface
fun getString(v: String): String {
return "hello~ $v"
}
@JavascriptInterface
fun getOptionalString(v: String?): String? {
return v?.let{"hello~ $it"}
}
@JavascriptInterface
fun getStringArray(v: Array<String>) {
v.forEach {
element -> Log.i("WebViewApp", element)
}
}
応用例
Stringが扱えるので、jsonを一回stringに変換すれば相互にjsonの受け渡しが可能です。
@JavascriptInterface
fun getJsonString(v: String): String {
val json: JSONObject = JSONObject(v)
val name: String = json.get("name") as String
val age: Int = json.get("age") as Int
json.put("status", "alive")
return json.toString()
}
let character = {"name": "taro", "age":25}
// jsonをstringに変換すると共にJavascriptInterfaceで値の受け渡し
let returnString = MyJavascriptInterface.getJsonString(JSON.stringify(character))
// stringをjsonに変換
let json = JSON.parse(returnString)
注意点
このJavascriptInterfaceは UIスレッド上
で実行されます。その為できるだけ重たい処理をさせる事は避けた方が無難です。
もしも重たい処理をしたい場合は明確に別スレッドやコルーチンを使用して非同期で処理をするのがオススメです。
@JavascriptInterface
fun heavyTask1(): String {
Thread.sleep(10000)
return "DONE!"
}
@JavascriptInterface
fun heavyTask2(callback: String) {
CoroutineScope(Dispatchers.IO).launch {
Thread.sleep(10000)
val returnValue = "DONE!"
withContext(Dispatchers.Main){
binding.webView.evaluateJavascript("javascript:$callback('$returnValue')", null)
}
}
}
function handleResult(result) {
console.log(result);
}
HeavyJavascriptInterfaces.heavyTask2("handleResult")
heavyTask1では10秒の間、UI処理が中断されてしまいユーザー体験が悪くなります。
一方でheavyTask2では処理自体は別スレッドで実行し、evaluateJavascriptでcallBack関数を呼んでいます。
サンプルレポジトリ