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>
無事動いた…。
おわり。