51歳からのプログラミング 備忘 AndroidからLaravelにHttpURLConnection接続


androidからローカルのLaravelファイルにHttpURLConnection()を使ってアクセスすると

D/NetworkSecurityConfig: No Network Security Config specified, using platform default
W/System.err: java.io.IOException: Cleartext HTTP traffic to 192.168.xxx.xxx not permitted

とエラーを賜った!

192.168.xxx.xxxには、
<network security Config>の指定がない!
cleartextTrafficPermittedがfalseになってるよ!
ってことでした。

Api29以降は、デフォルトでcleartextTrafficPermittedはfalseにしてあって、Https接続でないとエラーになる仕様。

<network_security_config.xml>を作成して、192.168.xxx.xxxに対して、cleartextTrafficPermittedをtrueにして、AndroidManifest.xmlにnetworkSecurityConfigを設定してあげましょう!

■network_security_config.xmlの設定

192.168.xxx.xxxのcleartextTrafficPermittedをtrueにするよ!

androidプロジェクトのapp\resの下に、xmlフォルダを作成し、その中にnetwork_security_config.xmlファイルを作成しましょう!

app/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.xxx.xxx</domain>
    </domain-config>
</network-security-config>

API29以降は上記コードのcleartextTrafficPermittedがデフォでfalseになってます。これを上記のコードのようにtrueに直してあげて、Http接続でもOkにしてあげましょう。
<domain-config>は必要なだけ追記しましょう^^!

ここはドメインですから、`http://www.@@@.@@@'の@@@.@@@を指定だね!

■AndroidManifest.xmlの設定

<network_security_config.xml>の設定をAndroidManifest.xmlに記述しよう!

app/manifests/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
  <manifest ... >
    <application android:networkSecurityConfig="@xml/network_security_config"
            ...>
            ...
    </application>
 </manifest>

■Laravelサイトに接続するコード

LaravelはLocalhostではなく、IPアドレスで接続します。
webサイトですが、タグが全く書かれていないとnullが返ってくる。例えばファイルにhelloだけ書いてる場合など。read.readLine()は

public class MainActivity extends AppCompatActivity {
    TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // オブジェクトを取得するよ!
        Button button1  = findViewById(R.id.button1);
        Button button2  = findViewById(R.id.button2);
               textView = findViewById(R.id.textView);

        // ボタン1はローカルにあるLaravelサイトに接続
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String site = "http://192.168.xxx.xxx:8000";
                new httpConnectionAsync().execute(site);
            }
        });

        // ボタン2はグーグル様に!
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String site = "https:www.google.co.jp";
                new httpConnectionAsync().execute(site);
            }
        });
    }
    // 非同期通信でHttpURLConnection()で接続する!
    private class httpConnectionAsync extends AsyncTask<String,Void,String> {
        StringBuffer buffer;
        @Override
        protected String doInBackground(String... string) {
            String site   =     string[0];
                   buffer = new StringBuffer();
            try {
                HttpURLConnection connection = (HttpURLConnection) new URL(site).openConnection();
                BufferedReader    reader     = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                while(reader.readLine() != null){
                    buffer.append(reader.readLine());
                }
                reader.close();
                connection.disconnect();
            } catch (IOException e) {
                e.printStackTrace();
            }

            return buffer.toString();
        }
        @Override
        protected void onPostExecute(String stringBuffer){
            textView.setText(stringBuffer);
        }
    }
}

■LaravelからAndroidにCookieを渡してみる

ちょっとだけ データ形式

Laravelには

$request = json_decode($request,true);

っていう文字列を連想配列に変換する便利なメソッドが使えるので、androidとlaravelでデータをやり取りする場合にはjsonが使えるよな形式で!

ちなみにandroidでは、

senddata.put("key1","value1");
senddata.put("key2","value2");
などとして
HttpURLConnectionで
connection.addRequestProperty("senddata",String.balueoOf(senddata);

で送信すればJson形式の文字列で送れる

ではCookie!

AsyncTaskを使います!

Laravelでsetcookieを設定すると、%エンコードされる
なので、 setrawcookieを使ってcookieを送る

Laravel Route

Route

Route::get('/sample',function(){setrawcookie("param","laravel");});


Android

MyAsyncTask
public class MyAsyncTask extends AsyncTask<Void, Void, Map<String,List<String>>> {

    @Override
    protected Map<String,List<String>> doInBackground(Void... aVoid) {
        try {
            HttpURLConnection connection    = (HttpURLConnection) new URL("https://www...").openConnection();
            connection.connect();
            Map<String,List<String>> header = connection.getHeaderFields();
            connection.disconnect();

        } catch (Exception e) {}

        return header;
    }

    @Override
    protected void onPostExecute(Map<String,List<String>> m) {
        super.onPostExecute(m);
        String param = m.get("Set-Cookie").get(0);
        Log.d("------>", param);
    }
}

LaravelでHeaderをVarDumpするとAndroidで正しく取れない

Controller
public function sample(Request $request){
   var_dump($_SERVER);
} 

これだとAndroid側では$_SERVERのキー値のみ取得できて、値は取得できない。
(var_dump($_SERVER['DOCUMENT_ROOT']が取得できない)
なぜか、先にvar_dump()するとandroid側でValueのみ取得できる。
なぜ?

Controller
public function sample(Request $request){
   var_dump("");
   var_dump($_SERVER);

これだとvalue値を表示してる。
var_dump($_SERVER['DOCUMENT_ROOT']でルートは取得できる。
うーむ・・・

超時間かかった。辿り着くまで3日。でも一歩前進。いつも通り亀の歩みだね!