ネットにHTTPアクセスしてデーターをダウンロードするためには、非同期のAsyncTaskを使い、HttpURLConnectionにて画像を落とします。
API 29
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 の削除
AsyncTask
何も考えずにネットにアクセスしてデーターをダウンロードしようとすると例外が出ます。
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork (StrictMode.java:1114)
非同期でアクセスしないといけません。
これは Android 3.0より前のバージョンではなかったのですが 3.0 以降で、メインスレッドでネットワーク処理を行うとエラーとなるように変わったようです。
非同期処理AsyncTaskの使い方はこちらを参照してください。
また付随してカスタムListenerも実装します。
AsyncTask<Params, Progress, Result>を継承したクラスを作ります。
- Params: doInBackground() バックグラウンド処理の引数
- Progress: 進捗度合いを表示したい時に使うパラメータ
- Result: onPostExecute() 処理終了時に受け取る型
doInBackground(String… params)の
(String… params)は可変長引数といいます
呼出す側では任意の数の String を渡すことが可能
サンプルコード
Listenerを使って作成しました。
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 |
//package your.package.name; import androidx.appcompat.app.AppCompatActivity; import android.graphics.Bitmap; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; public class MainActivity extends AppCompatActivity { private ImageView imageView; private EditText etitText; private DownloadTask task; String param0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); etitText = findViewById(R.id.uri); imageView = findViewById(R.id.result); Button downloadButton = findViewById(R.id.download); downloadButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { param0 = etitText.getText().toString(); if(param0.length() != 0){ // ボタンをタップして非同期処理を開始 task = new DownloadTask(); // Listenerを設定 task.setListener(createListener()); task.execute(param0); } } }); } @Override protected void onDestroy() { task.setListener(null); super.onDestroy(); } private DownloadTask.Listener createListener() { return new DownloadTask.Listener() { @Override public void onSuccess(Bitmap bmp) { imageView.setImageBitmap(bmp); } }; } } |
AsyncTaskを実行するクラスです
DownloadTask.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 |
//package your.package.name; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.util.Log; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; public class DownloadTask extends AsyncTask<String, Void, Bitmap> { private Listener listener; // 非同期処理 @Override protected Bitmap doInBackground(String... params) { return downloadImage(params[0]) ; } // 途中経過をメインスレッドに返す @Override protected void onProgressUpdate(Void... progress) { //working cursor を表示させるようにしてもいいでしょう } // 非同期処理が終了後、結果をメインスレッドに返す @Override protected void onPostExecute(Bitmap bmp) { if (listener != null) { listener.onSuccess(bmp); } } private Bitmap downloadImage(String address) { Bitmap bmp = null; HttpURLConnection urlConnection = null; try { URL url = new URL( address ); // HttpURLConnection インスタンス生成 urlConnection = (HttpURLConnection) url.openConnection(); // タイムアウト設定 urlConnection.setReadTimeout(10000); urlConnection.setConnectTimeout(20000); // リクエストメソッド urlConnection.setRequestMethod("GET"); // リダイレクトを自動で許可しない設定 urlConnection.setInstanceFollowRedirects(false); // ヘッダーの設定(複数設定可能) urlConnection.setRequestProperty("Accept-Language", "jp"); // 接続 urlConnection.connect(); int resp = urlConnection.getResponseCode(); switch (resp){ case HttpURLConnection.HTTP_OK: try(InputStream is = urlConnection.getInputStream()){ bmp = BitmapFactory.decodeStream(is); is.close(); } catch(IOException e){ e.printStackTrace(); } break; case HttpURLConnection.HTTP_UNAUTHORIZED: break; default: break; } } catch (Exception e) { Log.d("debug", "downloadImage error"); e.printStackTrace(); } finally { if (urlConnection != null) { urlConnection.disconnect(); } } return bmp; } void setListener(Listener listener) { this.listener = listener; } interface Listener { void onSuccess(Bitmap bmp); } } |
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 を使って実装してみる