Androidのwebviewを使って開発サーバーにajaxでアクセスして、サーバーからもらったデータを画面に表示させるアプリを作ることにした。
が、ローカルでは動いているのにjs側から下記のようなエラーが出ていてアクセスできない。
D/MyApplication★: {"readyState":0,"status":0,"statusText":"error"} -- From line 40 of
結論からいうと、js、ajaxの設定きちんとしていてもhttpでのアクセスを許可していないと上記のエラーが出て通信が切断されるようだ。
参考
jsを許可してajax通信するまでのコード
AndroidManifest.xml
src\main\AndroidManifest.xml
インターネットアクセスのパーミッションの設定は追加してある、ないとネットアクセスできないらしい。
<uses-permission android:name="android.permission.INTERNET"/>
MainActivity.java
myWebViewという名前のWebViewを作って表示させている、jsの有効化、consoleでのログをAndroidStudio側への吐き出しも設定。
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = getIntent(); myWebView = (WebView) findViewById(R.id.webView); myWebView.setWebViewClient(new WebViewClient()); // assetsフォルダ内のindex.htmlファイルを指定 myWebView.loadUrl("file:///android_asset/index.html"); // WebView内のJavaScriptの実行を許可 myWebView.getSettings().setJavaScriptEnabled(true); myWebView.getSettings().setDomStorageEnabled(true); // jsのconsole.logの中身をAndroid側のコンソール(Runタブ)に書き出す設定(見つけやすくするために★マークを入れた) myWebView.setWebChromeClient(new WebChromeClient() { public boolean onConsoleMessage(ConsoleMessage cm) { Log.d("MyApplication★", cm.message() + " -- From line " + cm.lineNumber() + " of " + cm.sourceId()); return true; } }); }
ajax
サーバーは前回作ったvagrantのLAN内公開サーバーなのでそのURLを使っている。
src\main\assets\script.js
jQuery(function($){ //アクセスするURL const URL = "http://192.168.0.111/api.php"; let menuList = null; $.ajax({ url: URL, type: 'get', cache: false, dataType:'json', data: null }).done(function(res) { // 通信成功時の処理 }).fail(function(xhr) { // xhrの中身はJavaScript のオブジェクトなので、JSON文字列に変換して中身が見えるようにする console.log(JSON.stringify(xhr)); alert("E001:通信時にエラーが発生しました"); }).always(function(xhr, msg) { //通信完了時の処理 }); });
サーバー側php(api.php)
jsonでステータスとメッセージ返すだけの内容になっている。
<?php header("Access-Control-Allow-Origin: *"); echo json_encode(["status"=>"OK","message"=>"hoge"]); die();
試したこと
ajaxを同期通信にすると動いたという記事を見つけたので「async: false」としてみる。
$.ajax({ url: URL, //アクセスするURL type: 'post', cache: false, dataType:'json', data: jsonObj, async: false, //非同期通信をオフ timeout: 1000 //タイムアウト1秒 }).done(function(res) {
これをやると下記エラーが出る。
Androidはメインスレッドでの同期通信は非推奨らしいので、やらないほうがいいようだ。
D/MyApplication: Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/. -- From line 2 of file:///android_asset/lib/jquery-3.5.1.min.js D/MyApplication: {"readyState":0,"status":0,"statusText":"NetworkError: Failed to execute 'send' on 'XMLHttpRequest': Failed to load 'http://192.168.0.111/api.php'."} -- From line 41 of file:///android_asset/script.js
jsは動いているし、応答の速さ的にサーバーに行く前にAndroid内で切断されているみたいだったので、webview内直接apiのURLに飛ばすことにした
jQuery(function($){ location.href = "http://192.168.0.111/api.php"; });
するとAndroid本体の方で下記のエラーが出る。
ウェブページへのアクセス不可 ウェブページ(http://192.168.0.111/api.php)は次の理由で読み込めませんでした: net::ERR_CLEARTEXT_NOT_PERMITTED
これはhttpへのアクセスだから起きる問題らしい。
暗号化されていない通信(httpsでない通信)はデフォルトで拒否されるようだ。
下記のように修正すると、httpsでもアクセスできるらしいので変更してみる。
が、変わらずエラーが出る。(この方法APIレベルによって効かないみたい、今回はAPIレベル30だった)
src\main\AndroidManifest.xml
<application android:usesCleartextTraffic="true">
解決
「network_security_config.xml」にドメインレベル(IPそのまま書いてるけどドメインでも大丈夫)で個別許可を書くことにする。
(ファイルがない場合は作成する)
src\main\res\xml\network_security_config.xml
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">192.168.0.111</domain> </domain-config> </network-security-config>
無事動いた…。
おわり。