Android Studioでアプリ開発中・・・
こんなエラーに遭遇しました。
1 |
java.lang.RuntimeException: java.lang.Throwable: A WebView method was called on thread 'JavaBridge'. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 1) {9d6ce5c} called on Looper (JavaBridge, tid 2202) {2449f37}, FYI main Looper is Looper |
初めて遭遇するよくわからないエラーです。
このエラーの解決策についてまとめます。
問題は@JavascriptInterfaceの関数内で起きた
開発には主にWebViewを使ってます。
そこでこういうコードを書いてました。
- JS側からJavaメソッド呼び出したい
- MainActivityにそのメソッドを作成
- @JavascriptInterfaceをつける
要するにWebView側のJavaScriptからMainActivityのメソッドを呼び出したかったわけです。呼び出し方はActivity側のメソッドに @JavascriptInterface をつけるだけです。
具体的にはこういうコードですね。
▼ 実際のコード(のイメージ)
1 2 3 4 5 6 7 8 9 10 11 |
@android.webkit.JavascriptInterface public void hogehoge(String hoge) { /// DB操作とか色々書いてある... /// WebView側でJSを実行(エラー) String script = "(function(){" + " fugafuga();" + "})();"; webView.evaluateJavascript(script, null); } |
これを実行したところ冒頭のエラーに
どうやら @JavascriptInterface がついたメソッドはメインスレッドとは別で実行されるらしく、その中でWebView側のメソッドを呼び出すと「スレッドが違うじゃん!」という事情でエラーが出るみたいです。
解決策はrunOnUiThreadで実行するだけだった
この問題の解決策は簡単。
単にUIスレッド内で実行すればいいだけです。
そのために runOnUiThread が用意されてます。
▼ 公式での説明
Runs the specified action on the UI thread. If the current thread is the UI thread, then the action is executed immediately. If the current thread is not the UI thread, the action is posted to the event queue of the UI thread.
引用元 : https://developer.android.com/reference/android/app/Activity#runOnUiThread(java.lang.Runnable)
▼ この説明の簡単な意訳
UIスレッドの中で特定のアクションを実行します。もし現在のスレッドがUIスレッドならアクションは即時に実行されます。もし現在スレッドがUIスレッドじゃないなら、そのアクションはUIスレッドのイベントキューに登録して後で実行されます。
こういうメソッドがありました。
単純にこれ使えばいいだけですね。
実際に試したコートがこういうのです。
▼ こういうコードに変更
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@android.webkit.JavascriptInterface public void hogehoge(String hoge) { /// DB操作とか色々書いてある... runOnUiThread(new Runnable() { @Override public void run() { /// WebView側でJSを実行(エラー) String script = "(function(){" + " fugafuga();" + "})();"; webView.evaluateJavascript(script, null); } }); } |
このように runOnUiThread(new Runnable(){...}) のようにWebView側の処理をUIスレッドに移すだけです。このテクニックは通信処理とかでもよく使うことが多いかもしれません。
実際にこのコードを試したらJSコード実行もうまくいきました。これで Activity ⇔ WebView の間で相互通信できるようになります。
UIスレッド外からWebView操作は気を付けよう
簡単にまとめるとこうなります。
- @JavascriptInterfaceは別スレッド実行
- だからUI操作的なコードはエラーになる
- 単純にrunOnUiThreadから呼び出せばOK
この点はWebViewに限らず気を付けたいですね。
UIスレッドなのか、そうでないのか・・・
それをコードを書くときに意識するのが大事です。
間違いなどはコメント欄から。ではまた (@^^)/~~~