ネットワーク上から保存した JPG 画像の画質を比較 & Android の DownloadManager を使用して罠にハマってみた件
第一部 〜ネットワーク上から保存した JPG 画像の画質を比較〜
概要
Android アプリ開発中に、ネットワーク上から保存した JPG 画像の画質に微妙な差があることに気付いたので、同一の JPG 画像をダウンロードしてサイズの比較を行ってみます。
手順
Google+ に検証用の JPG 画像を添付して投稿
投稿した JPG 画像を自作の Android アプリから URL を指定してダウンロード
端末内に保存された JPG 画像のサイズを確認します
Google+ に投稿する写真(元画像)
値 | |
---|---|
サイズ | 172,109 バイト(176KB) |
大きさ | 458 × 458 |
検証では大きさの 458 x 458 が変わることはありませんでしたが、サイズの値は変化しました。
画像の取得方法
java.net.URL
を用いて画像を取得します(「Android Studio : インターネット上の画像を取得して、Bitmap か Drawable で ImageView に表示する」を参考にしました)。
検証パターン
取得した画像を InputStream
と Bitmap
の状態で保存してみます。
※ AndroidManifest.xml
に <uses-permission android:name="android.permission.INTERNET" />
と
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
を記述するのをお忘れなく
InputStream
の状態で保存する
「画像の保存→表示」を参考にしました。
値 | |
---|---|
サイズ | 172,207 バイト(176KB) |
大きさ | 458 × 458 |
見た感じはオリジナル画像と変わらない気がします。しかしながらサイズが微増しています
Bitmap
の状態から保存する
「Androidで非同期で画像をサーバからダウンロードしてSDカードに保存する」と「Bitmap画像をsdに保存」を参考にしました。両方とも同じ結果になりました。
値 | |
---|---|
サイズ | 186,378 バイト(188KB) |
大きさ | 458 × 458 |
JPG 画像の白色部分にガサガサが見えるようになりました。そして 176 KB → 188 KB とオリジナル画像よりサイズが大きくなっています
InputStream
が生データなのに対して Bitmap
は一度加工しているため画質に差が生じています。
Android の Google+ アプリでダウンロードする
値 | |
---|---|
サイズ | 172,109 バイト(176KB) |
大きさ | 458 × 458 |
オリジナル画像と同じサイズの画像を保存することができました。一体どのようにして保存しているのでしょうか(?)
※ 現在の Google+ アプリからは画像を直接保存する機能が無くなっているようでした。画像詳細画面 >「シェア」ボタンをタップ >「フォトにアップロード」を選択すれば Google フォト に画像を保存することができました
第二部 〜DownloadManager との邂逅〜
この記事を公開した後
@KanaSakaguchi さんから「DownloadManager を使用すればオリジナルと同様のサイズで画像を保存できるのではないか」という旨のコメントを頂きました。
DownloadManager とは?
ネットワーク上にある .jpg
.png
などの画像ファイルや .mp4
などの動画ファイル、音声ファイルなどを保存することができます。
DownloadManager を使用してみる
「JavaのDownloadManagerを使ってファイルをダウンロード」を参考にしました。
値 | |
---|---|
サイズ | 172,109 バイト(176KB) |
大きさ | 458 × 458 |
Android 端末に保存された画像を Mac に転送してサイズを確認してみたところ、オリジナルと同じサイズで保存できていることが分かりました
DownloadManager に書き換えてアプリをリリースしてみた結果
バグが3件見つかりました
1件目の不具合
Crashlytics にクラッシュレポートが送られていました。
Fatal Exception: java.lang.IllegalArgumentException
Unknown URL content://downloads/my_downloads
エラー文で検索したところ StackOverflow の「How to Enable Android Download Manager」に辿り着きました。
設定 > [アプリケーション] > すべて(システムのアプリを表示) > ダウンロードマネージャー が無効になっている状態で DownloadManager.enqueue()
するとアプリが落ちるので対策が必要になります。
2件目の不具合
Crashlytics にレポートが届いていました。
Fatal Exception: java.lang.IllegalStateException
Unable to create directory:
N-05D (Android 4.0.4) などのプライマリストレージが SD カードになっているスマホからプライマリストレージの SD カードを取り除いた状態にして setDestinationInExternalPublicDir (String dirType, String subPath) を実行するとアプリが落ちます。
Nexus 5 や Nexus 7 (2013) などの新機種はプライマリストレージが内蔵ストレージになっており SD カードのように取り外しができないため上記エラーは発生しないと思われます(Crashlytics のレポートが送信されてきたのは Android 4.4.4 以下の古い端末でしたので)。
※ 参考にしたサイト Android におけるストレージまとめ
3件目の不具合
Google Play ストアのレビューやカスタマーサポートに「画像が保存できない」というクレームが届くようになりました。端末は N-04E (Android 4.1.2) で発生するとのこと。
原因を調べていると 設定 > [アプリ] > すべて(システムのアプリを表示) > ダウンロードマネージャー のデータ使用量が増えている状態では Response Headers に ETag の付いていない画像を保存できなくなるということが分かりました(※ ネットワーク上の画像の Response Headers に ETag が付いているかは Google Chrome Developer Tools を使用して調べました)。
N-04E 端末内の標準ブラウザも DownloadManager を使用しているため同様の現象が再現します。
対策としては How to Enable Android Download Manager を参考に DownloadManager での保存に失敗した場合にダウンロードマネージャーの設定に誘導して「データを消去」ボタンを押してもらうようにするか、自社サーバーのファイルのみダウンロードするアプリの場合は Response Headers に ETag を設定するというのが考えられます。
この不具合は Android 4.1 系周辺バージョンのみで発生している気がするので、下位バージョンをサポートしていないアプリでは特に対策を考える必要は無いかも知れません。
それでも ETag 無しの画像を保存したい場合は
Jake Wharton 様の Picasso 2 OkHttp 3 Downloader と OkHttp を使用すれば画質劣化ゼロでネットワーク上の画像を保存することができました。
public class ImageLoadHelper {
private static OkHttp3Downloader downloader;
private synchronized static OkHttp3Downloader getOkHttp3DownloaderInstance() {
if (downloader == null) {
downloader = new OkHttp3Downloader(new OkHttpClient());
}
return downloader;
}
/**
* 画像データの InputStream を返す
*
* @param url
* @return
* @throws IOException
*/
public static InputStream getInputStream(String url) throws IOException {
Downloader.Response response = getOkHttp3DownloaderInstance().load(Uri.parse(url), 1);
return response.getInputStream();
}
}
手順
RxJava の非同期処理中に上記の getInputStream()
を実施
取得した画像の InputStream
を端末内に保存する
番外編 〜Android の Google Chrome アプリで画像保存してみる〜
値 | |
---|---|
サイズ | 172,109 バイト(176KB) |
大きさ | 458 × 458 |
Android 端末に保存された画像を Mac に転送してサイズを確認したところ、オリジナルと同じサイズの画像を保存できていることが分かりました。
設定からダウンロードマネージャーを無効にした後 Google Chrome から画像を保存すると「失敗しました」と Snackbar 表示されるのでダウンロードマネージャーが使用されていることが分かります。
他にも Google Play や Google Drive, Slack アプリもダウンロードマネージャーを使用していることが確認できました。
Author And Source
この問題について(ネットワーク上から保存した JPG 画像の画質を比較 & Android の DownloadManager を使用して罠にハマってみた件), 我々は、より多くの情報をここで見つけました https://qiita.com/84d010m08/items/541c67b1e9b8deb4051f著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .