アプリケーションで扱う画像サイズ(OutOfMemoryError対策)について

1,901 views
Skip to first unread message

MIYAKOSI Sigeaki

unread,
Jun 30, 2011, 7:35:36 PM6/30/11
to android-g...@googlegroups.com
宮腰と申します。
アプリケーションで扱う画像のサイズについて質問があります。

状況:
現在、実解像度854x480、dipでは569x320の端末でアプリケーションを開発しております。
この際に背景やImageボタンの素材に使う画像についてなのですが、
たとえば背景画像ですと、569x320のものを使いますと、拡大されますので当然のことながら画像が荒くなります。
854x480のものを使いますと綺麗にはなるのですが、さらに大きい画像を使うとより綺麗になる感じがします。
感覚的には、実端末の1.5倍(1281x720)程度の素材までは画質が向上し、
それ以上はそんなに変化がないように見えます。
そのため、基本的に実端末の1.5倍(dip指定の縦横3倍)の画像素材を用いる方向で
開発を進めております。

しかしながら、この状態でカメラで撮影した写真画像や、インターネットサーバからダウンロードした
多数の画像(200kb x 30個以上)を扱おうとすると、高頻度で OutOfMemoryError
(bitmap size exceeds VM budget)が発生してしまいます。

ここで過去の投稿にありますように、(ImageView)view.setImageDrawable(null); を積極的に行っていけば
OutOfMemoryError が発生しにくくなるようです。
また使用する画像素材のサイズを小さいものに変えただけでも、OutOfMemoryError が発生しにくくなりました。
そのため、大きい画像素材が OutOfMemoryError の一因になっていると考察しております。

そこで今回のアプリでは複数のActivityを使用しますので、改善の方向性としては
onResume()で画像素材を読み込み、onPause()で画像の解放を行うように改修することで
表示されていない画面の画像素材についてはGC対象となることから、
OutOfMemoryError が発生しなくなるのでは、と考えております。

質問事項:
・綺麗な画像を表示するために、dip指定の縦横3倍の素材を使うという方法は適切でしょうか。
・OutOfMemoryError に対する対応策として、上記は妥当でしょうか。
 またよりよい方法がありそうでしょうか。

(なお、インターネットサーバからダウンロードした多数の画像を扱う部分については、
 サムネ情報を扱うように変更するなどの設計変更を検討しております)

皆様のお知恵を拝借したいと思っております。よろしくお願いいたします。
--
宮腰 茂明(MIYAKOSI Sigeaki)
miyakosi...@gmail.com
http://sites.google.com/site/miyakosis/

kobadroid

unread,
Jun 30, 2011, 10:39:50 PM6/30/11
to android-g...@googlegroups.com
宮腰様

KobadroIDと申します。

>・綺麗な画像を表示するために、dip指定の縦横3倍の素材を使うという方法は適切でしょうか。

適切かどうかは判断しかねますが、私も同じことをやっています。

標準のImageボタンではなく独自のフレームワークの中でやってい
ますので、参考にならないかもしれませんが、ボタンに貼り付ける
画像について、想定する画面上のサイズの1.2~5倍程度にして
います。理由は同じですね。

メモリ不足には私も悩みましたが、結局一番効果あったのは
画像サイズの縮小でした。特に背景のように大きなビットマップは
最も削りました。
BitmapConfigを調整してメモリサイズを縮小する等もそれなりに
効果がありました。

画像のファイルサイズではなく、実際のBitmapConfigの設定で
各画像のメモリサイズを見積もってみてはいかがですか。

各画像のメモリサイズと必要性を対比して、優先順位が低いも
のは削る or サイズ縮小などの検討ができるかもしれません。
(既に実施済みでしたら恐縮です)

--
KobadroID Web
http://kobadroid.web.fc2.com/

2011年7月1日8:35 MIYAKOSI Sigeaki <miyakosi...@gmail.com>:

--
このメールは Google グループのグループ「日本Androidの会」の登録者に送られています。
このグループに投稿するには、android-g...@googlegroups.com にメールを送信してください。
このグループから退会するには、android-group-j...@googlegroups.com にメールを送信してください。
詳細については、http://groups.google.com/group/android-group-japan?hl=ja からこのグループにアクセスしてください。

t.shinsay

unread,
Jul 2, 2011, 12:03:28 AM7/2/11
to 日本Androidの会
tshinsayです。

・画面解像度より大きな画像を用意することについて
基本的にピクセル等倍である場合、意図した見た目≒綺麗な見た目になるはずです。
補間してボケた画像が綺麗と錯覚してるか、あるいはdip計算時に微妙に座標が狂っているように思えます。
前者はともかく、後者はpx指定すれば解決すると思います。

もしアンドロイド特有の事情がありましたらすいません…

・OutOfMemoryについて
普通、オリジナルの画像が全部一度に必要になることは無いはずです。画像ビューアを例にとりますと、
一枚絵を表示している間は他の画像は表示されていませんし、一覧表示する場合、縮小されているわけなので、
オリジナルの解像度は必要ありません。

よって少メモリ環境での根本的な解決策としては、オンデマンドで読み込み、不要になった画像は
不要になり次第GCではなく、自分で開放することです。

それでも足りない場合、画像サイズを縮小するか(ファイスサイズではなく幅高さという意味のサイズです)、
ピクセルフォーマットを変えます。
この手段は画像が劣化しますので、機能要件と照らし合わせる必要があると思います。

MIYAKOSI Sigeaki

unread,
Jul 2, 2011, 6:52:34 AM7/2/11
to android-g...@googlegroups.com
宮腰です。
KobadroID様、tshinsay様ご回答をありがとうございました。

KobadroID様


> メモリ不足には私も悩みましたが、結局一番効果あったのは
> 画像サイズの縮小でした。特に背景のように大きなビットマップは
> 最も削りました。
> BitmapConfigを調整してメモリサイズを縮小する等もそれなりに
> 効果がありました。

サイズと仰っているのは、ピクセル数のことかと思います。
たしかに高解像度で準備した方がよい素材を吟味し、
それ以外は低解像度にする方針で今後進めてみます。
また、もしかして素材画像の色数を減らす(詳しくないのですがPNG-8等?を使う)のも
効果があるかもしれない、と試したいところです。

BitmapConfigについては、BitmapFactory.decodeFile()等で作成したbitmapに対して、
Bitmap bitmapNew = bitmap.copy(Bitmap.Config.RGB_565, true);
bitmap.recycle();
とするのかな、と考えております。
(createするタイミングではRGB_565は指定できない、ですよね?)

オブジェクトが実際に占有しているメモリ量が実測できれば
調査の参考になると思うのですが、調べ方が分からないため、
手探りでトライアルしている状況です。
DDMSのHeapタブなどの情報を見てもよくわからない
(Bitmapで使用しているメモリ情報が載ってこないように見える?)のですが、
メモリサイズを実測方法など、どなたかご存知ないでしょうか?

tshinsay様


> ・画面解像度より大きな画像を用意することについて
> 基本的にピクセル等倍である場合、意図した見た目≒綺麗な見た目になるはずです。
> 補間してボケた画像が綺麗と錯覚してるか、あるいはdip計算時に微妙に座標が狂っているように思えます。
> 前者はともかく、後者はpx指定すれば解決すると思います。

もしかしたらdip指定だと必ず拡大等の処理が入ってしまい、
画質の低下を招いているのかもしれません。
px指定というのは、気がつきませんでした。
確かに現状では解像度ごとにlayoutファイルを作成しなければいけない画面も
多数ありますので、どの道別々にlayout作成するのであればpx指定もアリな気がします。
試してみます。

> よって少メモリ環境での根本的な解決策としては、オンデマンドで読み込み、不要になった画像は
> 不要になり次第GCではなく、自分で開放することです。

そうですね、一覧画面で画像に解像度が必要ないのは仰るとおりですので、
一覧画面用にサムネ画像を作成するなどの処理を今後入れていこうと考えております。

なお画像のメモリ解放の具体的な方法について、
Bitmapに対しては recycle() を呼ぶと思うのですが、
Drawableに対しては解放するメソッドがちょっと分かりませんでした。
どのように解放するか、ご教示いただけないでしょうか?

よろしくお願いいたします。


2011年7月2日13:03 t.shinsay <t.sh...@gmail.com>:

> --
> このメールは Google グループのグループ「日本Androidの会」の登録者に送られています。
> このグループに投稿するには、android-g...@googlegroups.com にメールを送信してください。
> このグループから退会するには、android-group-j...@googlegroups.com にメールを送信してください。
> 詳細については、http://groups.google.com/group/android-group-japan?hl=ja からこのグループにアクセスしてください。
>
>

--

t.shinsay

unread,
Jul 2, 2011, 8:55:02 PM7/2/11
to 日本Androidの会
tshinsayです。

ソースを読んでわけでは無いので詳しくは分かりませんが、ドキュメントを見る限り
Bitmap等の実体をラップするだけのクラスで、内部情報的にはジオメトリ系の情報とBitmap等実体
への参照から構成されると考えられます。よって開放メソッドは無いと思われます。
例えばBitmapDrawableの場合でしたら、中のBitmapを開放してあげると目的は達せられる
と思います。

エラーメッセージを見る限り、Bitmapの場合のみ特別な領域を確保しているように見えますので、
Bitmapさえ適切に開放すれば、後はGCに任せて大丈夫でしょう。

MIYAKOSI Sigeaki

unread,
Jul 3, 2011, 10:51:47 AM7/3/11
to android-g...@googlegroups.com
宮腰です。

tshinsay様、ふたたびご回答ありがとうございました。

画像系のメモリはddmsで見られるヒープの外に確保される?みたいで、
解放漏れの調査が大変です。解析する良いツールがあるといいんですけれども・・・


2011年7月3日9:55 t.shinsay <t.sh...@gmail.com>:

kobadroid

unread,
Jul 3, 2011, 11:24:44 AM7/3/11
to android-g...@googlegroups.com
宮腰さん

インラインで失礼します。

>サイズと仰っているのは、ピクセル数のことかと思います。
そうです。

>たしかに高解像度で準備した方がよい素材を吟味し、
> それ以外は低解像度にする方針で今後進めてみます。
それが無難かと思います。

t.shinsayさんが別途ご指摘されているように、一度に使う
ビットマップを減らし、なるべくrecycleするという点もご考慮
されたほうがよいと思います。

ただ、私の開発作業時に発生した問題として、recycleを
それなりの速度で繰り返すとoutofmemoryが発生するなど
副次的な問題が発生したことがあります。

そもそもネイティブヒープは完全に制御できないわけですか
ら、あまりrecycleも過信できないなぁと個人的には思っています。
完全に制御するならネイティブで開発するしかないんじゃない
でしょうか。

>また、もしかして素材画像の色数を減らす(詳しくないのです
素材画像よりも、最終的にvm上でどのフォーマットで扱うかによります。

bitmap.configのマニュアルを見ると以下があるようです。
RGB565=1画素あたり16ビット
ARGB8888=1画素あたり32ビット
ARGB4444=1画素あたり16ビット (でもこれ使うとcanvas回りで
変なことが起きますね、経験則ですが・・・)
alpha8はよくわかりません。
androidのbitmapクラスでは1ピクセル を8ビットで表現するモード
はないように見えます。

>オブジェクトが実際に占有しているメモリ量が実測できれば
>調査の参考になると思うのですが、調べ方が分からないため、
単純に、ファイルの画素数x上記のピクセルあたりのビット使用数で
そのビットマップのメモリへの消費「インパクト」は測れると思います。
dalvikおよびネイティブヒープ上で、「厳密に何バイト使っているか」
までも(調べる方法はあるのでしょうが)調べる必要はないでしょう。

例えば200x300でARGB8888ならば
200x300x32でそのビットマップのサイズは測れるでしょう。
重要なのは、pngファイルのファイルサイズで比較しても
意味はないということです。


>Bitmap bitmapNew = bitmap.copy(Bitmap.Config.RGB_565, true);
bitmap.recycle();
とするのかな、と考えております。
(createするタイミングではRGB_565は指定できない、ですよね?)
そうですね、私もまさにそんな感じでやっています。
まじめにやるなら自前で減色ロジックを組めばいいと思います。
その場合はsetpixelsあたりを参照してください。


--
KobadroID Web
http://kobadroid.web.fc2.com/
 

2011年7月2日19:52 MIYAKOSI Sigeaki <miyakosi...@gmail.com>:

たろサ

unread,
Jul 3, 2011, 12:01:50 PM7/3/11
to android-g...@googlegroups.com
 宮腰さん、こんにちは。

 下記のサイトは参考になりませんでしょうか。
 d.hatena.ne.jp/hidecheck/20110625/1309024778
 d.hatena.ne.jp/hidecheck/20110626/1309100335

 後、bitmap.recycle();は、gcしてもいいよという命令だと思いますので、こ
の後、bitmapを開放した後、System.gc()として、明示的にgcを走らせてはダメ
でしょうか。

 失礼しました。

(2011/07/03 23:51), MIYAKOSI Sigeaki wrote:
> 宮腰です。
>
> tshinsay様、ふたたびご回答ありがとうございました。
>
> 画像系のメモリはddmsで見られるヒープの外に確保される?みたいで、
> 解放漏れの調査が大変です。解析する良いツールがあるといいんですけれども・・・
>
>
> 2011年7月3日9:55 t.shinsay<t.sh...@gmail.com>:
>> tshinsayです。
>>
>> ソースを読んでわけでは無いので詳しくは分かりませんが、ドキュメントを見る限り
>> Bitmap等の実体をラップするだけのクラスで、内部情報的にはジオメトリ系の情報とBitmap等実体
>> への参照から構成されると考えられます。よって開放メソッドは無いと思われます。
>> 例えばBitmapDrawableの場合でしたら、中のBitmapを開放してあげると目的は達せられる
>> と思います。
>>
>> エラーメッセージを見る限り、Bitmapの場合のみ特別な領域を確保しているように見えますので、
>> Bitmapさえ適切に開放すれば、後はGCに任せて大丈夫でしょう。
>>
>> --
>> このメールは Google グループのグループ「日本Androidの会」の登録者に送られています。
>> このグループに投稿するには、android-g...@googlegroups.com にメールを送信してください。
>> このグループから退会するには、android-group-j...@googlegroups.com にメールを送信してください。
>> 詳細については、http://groups.google.com/group/android-group-japan?hl=ja からこのグループにアクセスしてください。
>>
>>
>
>
>

--
山本三七男 (Minao Yamamoto) ---------------- ハンドル:たろサ -----
E-Mail: taro...@gmail.com

MIYAKOSI Sigeaki

unread,
Jul 5, 2011, 1:22:21 AM7/5/11
to android-g...@googlegroups.com
宮腰です。

kobadroid様、たろサ様、コメントありがとうございます。

素材画像の解像度についてその後試したところ、ScaleDensity 1.5の端末において
実液晶の解像度と同じdip指定である1.5倍のものがもっともバランスがいいように見えますので、
今後1.5倍のもので作成していく方針を考えております。
(2倍以上の画像は、気持ちシャープに見える気がしますが、縮小アルゴリズムが
 たまたま綺麗に見える方向に働いただけかもしれません)

これはtshinsay様よりご指摘いただいた内容の通りです。
前に2倍や3倍の画像の方が綺麗に見えた気がしたのはちょっと謎です。
使われている色合いや文字の有無とかでも、すこし変わってくるのかもしれません。

recycleをそれなりの速度で繰り返すとoutofmemoryが発生するというのは、
以下は私の推測ではありますが、
GCが走るタイミングとの関係で、連続した大きな空き領域がない状態で
比較的大きいメモリを要求してoutofmemoryになるのでは、と考えています。
ただ下記サイトによると「オブジェクトのアロケートに失敗したときもGC が
実行される」とありますので、ちょっと私の仮説とは合いません。
http://www.adamrocker.com/blog/246/overview-of-the-dalviks-gc.html

なおたろサさんのご提示いただいたサイトは大変参考になりました。
ありがとうございました。

2011年7月4日1:01 たろサ <taro...@gmail.com>:

--

Reply all
Reply to author
Forward
0 new messages