[オリジナル]連載1-高効率表示画像に関するAndroidの問題を深く議論する-大きなビットマップの効率的なロード方法


詳細は以下の公式サイトのアドレスを参照してください.http://developer.android.com/training/building-graphics.html
Androidアプリケーションを始めたばかりの時は、画像を表示するのはとても簡単なことだと思いました.シミュレータの中でよく動いています.本物のマシンに置くと、java.lang.OutfMemoryErr:bitmap size exceedget.などの異常がよくあります.その後、公式サイトの詳しい紹介を見て、写真についての説明が少なくないことが分かりました.
勉強の心得を皆さんと共有してみましょう.
  
なぜ写真を表示するのが苦手な問題がありますか?
携帯電話は800万画素の画像(現在主流の携帯電話の画素はほぼ800万画素以上)を表示し、約32 MBのメモリを使用する必要があります.これはちょうどAndroidシステムが各アプリケーションに割り当てられた最大メモリです.このような図を携帯アプリで直接開くと、メモリオーバーによりプログラムが終了してしまうことがほとんどです.このような状況は多くの人が経験したことがあると信じています.
Galaxy Nexusを例にとると、バックカメラの画素数は500万画素で、その解像度は2592×1936画素である.ビットマップ設定ではARGB_を使用します.8888(Android 2.3およびより高いバージョンでは、この値はデフォルト値である)では、ロードマップは約19 MBのメモリ(2592*1936*4 bytes)を占有するので、プログラムはすぐにAndroidが各アプリケーションの最大メモリに割り当てられ、プログラムが崩壊する原因となる.
アプリケーションが500万以上のピクセルの画像を表示したいとは限りませんが、もしプログラムの設計が間違っていたら、同じように画像を表示する時に、プログラムが崩壊する問題があります.例えば、写真関連のアプリケーションでは、多くの画像を表示する必要がありますので、ListView、GridView、またはViewer Pagerを使用します.表示されている画像を処理しないと、表示画像が多すぎてプログラムがクラッシュします.
  
表示画像によるメモリオーバーの問題をどう解決しますか?
この問題を解決するには、以下の5点から始める必要があります.
1.大きなビットマップを効率的にロードする方法.(どのように大きなビットマップを復号しますか?各アプリケーションが許可する最大メモリを超えないようにします.)http://yhz61010.iteye.com/blog/1848337
2.どのように非UIスレッドでビットマップを処理しますか?(どのようにAyncTaskを使ってバックグラウンドスレッドでビットマップを処理し、併発問題を処理するか)http://yhz61010.iteye.com/blog/1848811
3.ビットマップをキャッシュする方法.(どのようにしてメモリキャッシュとディスクキャッシュを作成して複数のビットマップをスムーズに表示するか)http://yhz61010.iteye.com/blog/1849645
4.ビットマップメモリの管理方法.(どのように異なるAndroidバージョンに対してビットマップメモリを管理しますか?)http://yhz61010.iteye.com/blog/1850232
5.UIにビットマップを表示する方法.(どのようにViewer PagerとGridViewで複数の画像を表示するか)http://yhz61010.iteye.com/blog/1852927
これから説明します.
  
大きなビットマップを効率的にロードするにはどうすればいいですか?
1.位置サイズとタイプを取得する
ビットマップをBitmap Factoryで復号する場合はBitmap Factory.Optionsを使用します.OptionsのinJust DecodeBoundsをtrueに設定した場合、ビットマップにメモリを割り当てることは避けられますが、BitmapFactory.decodeXの戻り結果はnullですが、Optionsにはout Width、out Heht、out MimeTypeの値が設定されます.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
上記のコードにより、ビットマップにメモリを割り当てることなく、ビットマップの幅、高さ、ビットマップタイプを取得することができます.その後、画像を表示する際には、取得した情報で画像を処理して表示する必要があるかどうかを判断し、メモリオーバーフローを避けることができます.
2.縮小された画像をメモリに読み込む
プログラム中に128 x 96サイズのサムネイルを表示するためだけに、元のサイズの1024 x 768の画像をメモリにロードすると、大変お得になりません.したがって、表示する画像を等分して縮小して表示する必要があります.
オプティクス.inSampleSizeを設定することにより、縮小されたピクチャが生成されます.例えば、Ooptions.insampleSize=4の場合、元のサイズが2048 x 1536のビットマップでは、新しいビットマップサイズは約512 x 384である.この新しいビットマップをメモリにロードするには0.75 MBしか必要ではないが、原図では約12 MBのメモリを占有する必要がある.
具体的なプログラムは以下の通りです.
まず、inJust DecodeBoundsをtrueに設定し、ビットマップ情報を取得し、新たなinSampleSize値を設定し、最後にinJust DecodeBoundsをfalseに設定して、新たに生成したビットマップをメモリにロードします.
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}
上記の方法で使用されるcacaculateInSampleSize方法の実現は以下の通りである.
public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
    }

    return inSampleSize;
}
なお、上記方法で返したinSampleSizeの値は、2のn乗が望ましい.(詳しくはhttp://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize)
以上のように、上記のような方法があれば、メモリオーバーを心配することなく、任意のサイズの画像をプログラムにロードすることができます.例えば、元の画像を100 x 100ピクセルのサムネイルとして表示します.
mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));