サーバーにデーターを送信するために非同期で HttpURLConnection のPOSTを使って送信できます。
Android 9.0
HttpURLConnection
WebサーバとWebクライアント間でデータ送受信を行うために用いられるプロトコル(規約)をHTTPと言います。データの送受信にはGETとPOSTのメソッドがあり用途により使い分けます。
POST & GET
基本的には、両方とも送受信が可能です。
例えばGETでは画像データが欲しいというリクエストを送信して、その画像データを受け取る。POSTではデータベースの情報をサーバーに保存するために大量のデータを送るなど。以下は両者のザックリとした相違点
- GET
- サーバから情報を取得するケースに使うことが多い
- データを送信できるのはテキストのみで少量しか送れない
- URLに付加して送信するので簡単
- POST
- サーバへ情報を登録するケース
- バイナリーデータの送信
- GETでのデータ送信限度を超えてしまう場合
- 送信データをBODY部分に含めて送信できるので制限はない
今回はこのPOSTのメソッドを使って、サーバーへデータ送信するケースを考えてみます。
Android6.0からApacheのHTTP Clientが基本的に使えなくなりました。Apache HTTP Client Removal
AsyncTask
何も考えずにネットにアクセスしてデーターをダウンロードすると例外がでます。
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1114)
非同期でアクセスしないといけません。
これは android 3.0より前のバージョンではなかったのですがAndroid 3.0 以降で、メインスレッドでネットワーク処理を行うとエラーとなるように変わったようです。
基本的な考え方は非同期処理AsyncTaskの使い方にあります。
AsyncTask<Params, Progress, Result>を継承したクラスを作ります。このジェネリックの意味はそれぞれ
- Params: doInBackground() バックグラウンド処理の引数
- Progress: 進捗度合いを表示したい時に使うパラメータ
- Result: onPostExecute() 処理終了時に受け取る型
doInBackground(String… params)の
(String… params)は可変長引数といいます
呼出す側では任意の数の String を渡すことが可能
phpで受け取る
サーバー側で例えばパスワードのような文字列を受け取りhtmlを生成してそのデータをブラウザで表示させてみたいと思います。
phpファイルで’word’というキーワードで受け取った文字列をそれを含めたhtmlファイルを生成させるコードを作りサーバーに置きます。
pass_check.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?php // バッファリング開始 ob_start(); ?> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> </head> <body> <br><br> <br><br> <br><br> <font size="6"> Word = <?php echo $_POST['word']; ?> </font> <br><br><br><br> </body> </html> <?php // 同階層の pass_check.html にphp実行結果を出力 file_put_contents( 'pass_check.html', ob_get_contents() ); // 出力用バッファをクリアしてオフ ob_end_clean(); ?> |
POST DATAでEditTextで入力した文字列を送信し、BROWSERボタンでChromeなどに飛ばして生成されたhtmlを開いてパスワード文字列を確認する流れで作って見ます。
またviewの参照によるリークを避けるためListenerを使っています。
非同期処理AsyncTaskの使い方
加えて、非同期での作業終了をカスタムlistenerを使って取得しています。
カスタム Listener を interface を使って実装してみる
サンプルコード
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
import android.content.Intent; import android.net.Uri; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private UploadTask task; private TextView textView; // wordを入れる private EditText editText; // phpがPOSTで受け取ったwordを入れて作成するHTMLページ(適宜合わせてください) String url = "https://hoge-hoge.com/pass_check.html"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); editText = findViewById(R.id.uriname); Button post = findViewById(R.id.post); // ボタンをタップして非同期処理を開始 post.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String param0 = editText.getText().toString(); if(param0.length() != 0){ task = new UploadTask(); task.setListener(createListener()); task.execute(param0); } } }); // ブラウザを起動する Button browser = findViewById(R.id.browser); browser.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // phpで作成されたhtmlファイルへアクセス Uri uri = Uri.parse(url); Intent intent = new Intent(Intent.ACTION_VIEW,uri); startActivity(intent); // text clear textView.setText(""); } }); textView = findViewById(R.id.text_view); } @Override protected void onDestroy() { task.setListener(null); super.onDestroy(); } private UploadTask.Listener createListener() { return new UploadTask.Listener() { @Override public void onSuccess(String result) { textView.setText(result); } }; } } |
AsyncTaskを継承したクラスでPOSTを実行するためのものを作成します。
UploadTask.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
import android.os.AsyncTask; import android.util.Log; import java.io.IOException; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; public class UploadTask extends AsyncTask<String, Void, String> { private Listener listener; // 非同期処理 @Override protected String doInBackground(String... params) { // 使用するサーバーのURLに合わせる String urlSt = "https://hoge-hoge.com/token_check.php"; HttpURLConnection httpConn = null; String result = null; String word = "word="+params[0]; try { // URL設定 URL url = new URL(urlSt); // HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); // request POST httpConn.setRequestMethod("POST"); // no Redirects httpConn.setInstanceFollowRedirects(false); // データを書き込む httpConn.setDoOutput(true); // 時間制限 httpConn.setReadTimeout(10000); httpConn.setConnectTimeout(20000); // 接続 httpConn.connect(); // POSTデータ送信処理 OutputStream outStream = null; try { outStream = httpConn.getOutputStream(); outStream.write( word.getBytes("UTF-8")); outStream.flush(); Log.d("debug","flush"); } catch (IOException e) { // POST送信エラー e.printStackTrace(); result="POST送信エラー"; } finally { if (outStream != null) { outStream.close(); } } final int status = httpConn.getResponseCode(); if (status == HttpURLConnection.HTTP_OK) { // レスポンスを受け取る処理等 result="HTTP_OK"; } else{ result="status="+String.valueOf(status); } } catch (IOException e) { e.printStackTrace(); } finally { if (httpConn != null) { httpConn.disconnect(); } } return result; } // 非同期処理が終了後、結果をメインスレッドに返す @Override protected void onPostExecute(String result) { super.onPostExecute(result); if (listener != null) { listener.onSuccess(result); } } void setListener(Listener listener) { this.listener = listener; } interface Listener { void onSuccess(String result); } } |
レイアウトファイル
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#fd9" tools:context=".MainActivity"> <TextView android:id="@+id/word" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginEnd="10dp" android:text="@string/word" android:textSize="24sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="@+id/guideline" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.15" /> <EditText android:id="@+id/uriname" android:layout_width="0dp" android:layout_height="wrap_content" android:autofillHints="@string/hint" android:background="#fff" android:hint="@string/hint" android:inputType="textUri" android:textSize="24sp" app:layout_constraintStart_toStartOf="@+id/guideline" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.15" /> <Button android:id="@+id/post" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="30dp" android:layout_marginEnd="10dp" android:text="@string/post" app:layout_constraintTop_toTopOf="parent" app:layout_constraintRight_toLeftOf="@+id/browser" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintVertical_bias="0.3" /> <Button android:id="@+id/browser" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginEnd="30dp" android:layout_marginStart="10dp" android:text="@string/browser" app:layout_constraintLeft_toRightOf="@+id/post" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintVertical_bias="0.3" /> <TextView android:id="@+id/text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="24sp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintVertical_bias="0.4" /> <android.support.constraint.Guideline android:id="@+id/guideline" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintGuide_begin="100dp" /> </android.support.constraint.ConstraintLayout> |
リソース
strings.xml
1 2 3 4 5 6 7 |
<resources> <string name="app_name">TestHttpURLConnectionPOST</string> <string name="word">word: </string> <string name="hint">any words</string> <string name="post">post data</string> <string name="browser">browser</string> </resources> |
マニフェストに INTERNET のパーミッションを入れるのを忘れずに
AndroidManifest.xml
1 2 3 4 5 6 7 8 9 10 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.testhttpurlconnectionpost"> <uses-permission android:name="android.permission.INTERNET" /> <application .... </manifest> |
サンプル動画
サーバーにphpファイルを置いて試してみました。
レンタルサーバーを契約していれば、https://hoge-hoge.com/… 以下を変更してFTPなどでphpファイルを上げてテストできるでしょう。
関連ページ:
- HttpURLConnection POST でデータを送信する
- HttpURLConnection GET で画像をダウンロードする
- 非同期処理AsyncTaskの使い方
- カスタム Listener を interface を使って実装してみる
Reference:
HttpURLConnection | Android Developers