ネットにHTTPアクセスしてデーターをダウンロードするためには、非同期での処理が必要です。以前はAsyncTaskを使っていましたが非推奨となり、代わりにjava.util.concurrent.Executorsを使います。
2021.2.1
HttpURLConnection
WebサーバとWebクライアント間でデータ送受信を行うために用いられるプロトコル(規約)をHTTPと言います。データの送受信にはGETとPOSTのメソッドがあり用途により使い分けます。
非同期など面倒な処理が必要なのですが、実はPicassoといライブラリーを使うと画像のダウンロードは簡単にできてしまいます。
(ただし、サードパーティのライブラリーはメンテナンスが多少不安ではあります)
POST & GET
基本的には、両方とも送受信が可能
例えばGETでは画像が欲しいというリクエストを送信して画像データを受信する。POSTではデータベースの情報をサーバーに保存するために大量のデータを送るなど。以下は両者のザックリとした相違点
- GET
- サーバから情報を取得するケースに有効
- データを送信できるのはテキストのみで少量しか送れない
- https://www.google.co.jp/search?q=http+get&oq=http+get&aqs….のようにURLに付加して送信する
- POST
- サーバへ情報を登録するケース
- バイナリーデータの送信
- GETでのデータ送信限度を超えてしまう場合
- 送信データをBODY部分に含めて送信できるので制限はない
今回はこのGETのメソッドを使って、サーバーから画像を受け取るケースを考えてみます。
Android6.0からApacheのHTTP Clientが基本的に使えなくなりました。
Apache HTTP Client の削除
Executors
何も考えずにネットにアクセスしてデーターをダウンロードしようとすると例外が出ます。
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork (StrictMode.java:1114)
非同期アクセスしないといけません。AsynTaskは非推奨になりました、代案としてはjava.util.concurrent
が推奨されているので
java.util.concurrent.Executor
java.util.concurrent.ExecutorService
単一のワーカー・スレッドだけを設定する
newSingleThreadExecutor()
を使っていきま
サンプルコード
URLをEditTextで入力して、ボタンをタップ
目的の画像を取得して表示するという流れです
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 |
//package your.package.name; import androidx.appcompat.app.AppCompatActivity; import androidx.core.os.HandlerCompat; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.concurrent.Executors; public class MainActivity extends AppCompatActivity { private EditText etitText; private ImageView imageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); etitText = findViewById(R.id.uri); imageView = findViewById(R.id.img); Button downloadButton = findViewById(R.id.download); downloadButton.setOnClickListener(v -> { String stringUrl = etitText.getText().toString(); dowloadImage(stringUrl); }); } private void dowloadImage(String urlSt){ // Singleの別スレッドを立ち上げる Executors.newSingleThreadExecutor().execute(() -> { try { URL url = new URL(urlSt); HttpURLConnection urlCon = (HttpURLConnection) url.openConnection(); // タイムアウト設定 urlCon.setReadTimeout(10000); urlCon.setConnectTimeout(20000); // リクエストメソッド urlCon.setRequestMethod("GET"); // リダイレクトを自動で許可しない設定 urlCon.setInstanceFollowRedirects(false); InputStream is = urlCon.getInputStream(); Bitmap bmp = BitmapFactory.decodeStream(is); // 別スレッド内での処理を管理し実行する HandlerCompat.createAsync(getMainLooper()).post(() -> // Mainスレッドに渡す imageView.setImageBitmap(bmp) ); } catch (IOException e) { e.printStackTrace(); } }); } } |
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 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="#ffaa88" tools:context=".MainActivity"> <EditText android:id="@+id/uri" android:inputType="textUri" android:layout_width="match_parent" android:layout_height="wrap_content" android:autofillHints="@string/hint" android:hint="@string/hint" android:layout_gravity="center_horizontal" /> <Button android:id="@+id/download" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="@string/download" /> <ImageView android:id="@+id/result" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:gravity="center" android:contentDescription="@string/description"/> </LinearLayout> |
リソースです
strings.xml
1 2 3 4 5 6 |
<resources> <string name="app_name">YourAppName</string> <string name="hint">input a image url</string> <string name="download">download</string> <string name="description">image</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" ... <uses-permission android:name="android.permission.INTERNET" /> <application ... </application> </manifest> |
サンプル動画
サンプル画像をサーバーに置いてダウンロードさせてみます。
お好みの画像が無ければこれでも落としてみてください
https://developer.android.com/images/home/android-p-clear-bg-with-shadow.png?hl=JA
エラーというかこのようなログで画像を落とせない場合はSSL通信ではないからです
1 |
java.io.IOException: Cleartext HTTP traffic to xxx.com not permitted |
ネットワーク セキュリティ構成にあるcleartextTrafficPermitted=”false がデフォルトで設定されるため暗号化されていないHTTP通信に失敗します。
SSL通信(https://)以外を除外する設定
AndroidManifest.xmlにnetworkSecurityConfigを設定しないといけないようです。
関連ページ:
- HttpURLConnection POST でデータを送信する
- HttpURLConnection GET で画像をダウンロードする
- 非同期処理AsyncTaskの使い方
- カスタム Listener を interface を使って実装してみる