サーバーにデーターを送信するためには、HTTP のPOSTを使って送信できます。
非同期に以前はAsyncTaskなどを使っていましたが、Volleyを使うと簡単に実装できます。
2021.2.1
HTTP
WebサーバとWebクライアント間でデータ送受信を行うために用いられるプロトコル(規約)をHTTPと言います。データの送受信にはGETとPOSTのメソッドがあり用途により使い分けます。
POST & GET
基本的には、両方とも送受信が可能です。
例えばGETでは画像データが欲しいというリクエストを送信して、その画像データを受け取る。POSTではデータベースの情報をサーバーに保存するために大量のデータを送るなど。以下は両者のザックリとした相違点
- GET
- サーバから情報を取得するケースに使うことが多い
- データを送信できるのはテキストのみで少量しか送れない
- URLに付加して送信するので簡単
- POST
- サーバへ情報を登録するケース
- バイナリーデータの送信
- GETでのデータ送信限度を超えてしまう場合
- 送信データをBODY部分に含めて送信できるので制限はない
今回はこのPOSTのメソッドを使って、サーバーへデータ送信するケースを考えてみます
- Android 6からApacheのHTTP Clientが基本的に使えなくなりました。
Apache HTTP Client Removal - Android 11から非同期のAsyncTaskが非推奨となりました
Volley
何も考えずにネットにアクセスしてデーターをダウンロードすると例外がでます。
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1114)
非同期でアクセスしないといけません。
これは android 3.0より前のバージョンではなかったのですがAndroid 3.0 以降で、メインスレッドでネットワーク処理を行うとエラーとなるように変わったようです。
非同期は以前はAsyncTaskを使っていましたが、Android 11から非推奨となりました。
代案としてはExecutorsやHandlerを使用する等がありますが、Volleyを使うとシンプルです。
Volleyは非同期機能が提供され、ネットワーク操作を容易化、高速化する HTTP ライブラリで次のような利点があります。
- ネットワーク リクエストの自動スケジューリング。
- 複数のネットワークの並行接続。
- 標準の HTTP キャッシュ コヒーレンシを備えた透過的ディスク / メモリ レスポンス キャッシュ。
- リクエストの優先順位付けのサポート。
- Cancellation request API。単一のリクエストのキャンセル、ブロックの設定、キャンセル リクエストの範囲の設定ができます。
- 容易なカスタマイズ(再試行、バックオフなど)。
- 強力な順序付けにより、ネットワークから非同期に取得されるデータを UI に正しく簡単に入力できます。
- デバッグツールとトレースツール。
Ref: Volleyの概要
phpで受け取る
例として、AndroidからHTTP POSTで送信しサーバー側の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(); ?> |
Android側では build.gradle ファイルに以下の依存関係を追加します。
(バージョンは適宜あわせます)
build.gradle(Module)
1 2 3 4 |
dependencies { ... implementation 'com.android.volley:volley:1.2.1' } |
AndroidManifest.xml にインターネットのパーミッションを追加
1 |
<uses-permission android:name="android.permission.INTERNET" /> |
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 |
//package your.project.name; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import com.android.volley.Request; import com.android.volley.RequestQueue; import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.toolbox.StringRequest; import com.android.volley.toolbox.Volley; import java.util.HashMap; import java.util.Map; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // RequestQueuenoのインスタンスを生成 RequestQueue queue = Volley.newRequestQueue(this); //url String url = "https://hoge-hoge.com/pass_check.php"; // Request a string response from the provided URL. StringRequest postRequest = new StringRequest(Request.Method.POST,url, new Response.Listener<String>() { @Override public void onResponse(String s) { // } }, new Response.ErrorListener(){ @Override public void onErrorResponse(VolleyError error){ //error } }){ @Override protected Map<String,String> getParams(){ Map<String,String> params = new HashMap<>(); params.put("word","test"); return params; } }; queue.add(postRequest); } } |
これでテストすると
Word = test
と結果がブラウザに表示されました。
サンプルコード
EditTextで入力した文字列をPOST送信し、BROWSERボタンでChromeなどに飛ばして生成されたhtmlを開いてパスワード文字列を確認する流れで作って見ます。
また、上記のコードはlambdaで簡略化できます
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 |
//package your.project.name; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import com.android.volley.Request; import com.android.volley.RequestQueue; import com.android.volley.toolbox.StringRequest; import com.android.volley.toolbox.Volley; import java.util.HashMap; import java.util.Map; public class MainActivity extends AppCompatActivity { private final String urlSt = "https://hoge-hoge.com/pass_check.php"; // phpがPOSTで受け取ったwordを入れて作成するHTMLページ(適宜合わせてください) private final String urlHtml = "https://hoge-hoge.com/pass_check.html"; private EditText editText; private TextView textView; private String str; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); editText = findViewById(R.id.uriname); textView = findViewById(R.id.word); Button button = findViewById(R.id.post); button.setOnClickListener(v -> { str = editText.getText().toString(); httpPost(urlSt, str); }); // ブラウザを起動する Button browser = findViewById(R.id.browser); browser.setOnClickListener(v -> { // phpで作成されたhtmlファイルへアクセス Uri uri = Uri.parse(urlHtml); Intent intent = new Intent(Intent.ACTION_VIEW,uri); startActivity(intent); }); } private void httpPost(String url, String strValue) { // Instantiate the RequestQueue. RequestQueue queue = Volley.newRequestQueue(this); StringRequest stringRequest = new StringRequest(Request.Method.POST,url, this::onResponse, error -> textView.setText(R.string.strerror)){ @Override protected Map<String,String> getParams(){ Map<String,String> params = new HashMap<>(); params.put("word", strValue); return params; } }; queue.add(stringRequest); } private void onResponse(String response) { textView.setText(response); } } |
レイアウトファイル
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"?> <androidx.constraintlayout.widget.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="#fdc" 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" /> <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintGuide_begin="100dp" /> </androidx.constraintlayout.widget.ConstraintLayout> |
リソース
strings.xml
1 2 3 4 5 6 7 8 |
<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> <string name="strerror">"Error: That didn't work!"</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ファイルを上げてテストできるでしょう。
関連ページ:
- HTTP POST でデータを送信する
- HttpURLConnection GET で画像をダウンロードする
- 非同期処理AsyncTaskの使い方
- カスタム Listener を interface を使って実装してみる
Reference:
Volley の概要 | Android デベロッパー
シンプルなリクエストを送信する | Android デベロッパー
RequestQueue をセットアップする | Android デベロッパー