2017-05-02 5 views
-2

ビットマップを読み込むときに、OutOfMemoryErrorを持たずに割り当てることができる多くのメモリを把握する必要があります。私はファイルから大きなイメージをロードすることに決めました。 URLは次のとおりです。https://upload.wikimedia.org/wikipedia/commons/3/3d/LARGE_elevation.jpgAndroidで本当に大きな画像を読み込むときのメモリ割り当て

イメージサイズは14 488 498バイト(14,5 MBのディスク)です。そして、ここに私のコードです:

public class MainActivity extends AppCompatActivity implements View.OnClickListener { 

    private static final String LOG_TAG = "LargeBitmapLargeBitmap"; 

    private ActivityManager mActivityManager; 
    private ImageView mImageView; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); 
     mImageView = (ImageView) findViewById(R.id.image); 
     findViewById(R.id.get_mem_info).setOnClickListener(this); 
     findViewById(R.id.load_bitmap).setOnClickListener(this); 

    } 

    @Override 
    public void onClick(final View v) { 
     switch (v.getId()) { 
      case R.id.get_mem_info: 
       Runtime rt = Runtime.getRuntime(); 
       Log.v(LOG_TAG, "maxMemory: " + rt.maxMemory()); 
       Log.v(LOG_TAG, "freeMemory: " + rt.freeMemory()); 
       Log.v(LOG_TAG, "totalMemory: " + rt.totalMemory()); 
       Log.v(LOG_TAG, "mActivityManager.getMemoryClass(): " + mActivityManager.getMemoryClass()); 
       break; 
      case R.id.load_bitmap: 
       new ImageLoadingTask(mImageView).execute(); 
       break; 
     } 
    } 

    // I don't handle the screen rotation here since it's a test app 
    private static class ImageLoadingTask extends AsyncTask<Void, Void, Bitmap> { 

     private ImageView mImageView; 

     ImageLoadingTask(ImageView imageView) { 
      mImageView = imageView; 
     } 

     @Override 
     protected Bitmap doInBackground(final Void... params) { 
      String path = Environment.getExternalStorageDirectory().getAbsolutePath() 
        + File.separator 
        + "LARGE_elevation.jpg"; 
      Bitmap bitmap = null; 
      try { 
       bitmap = BitmapFactory.decodeFile(path); 
      } catch (OutOfMemoryError error) { 
       Log.e(LOG_TAG, "Failed to load the large bitmap", error); 
      } 
      return bitmap; 
     } 

     @Override 
     protected void onPostExecute(final Bitmap bitmap) { 
      super.onPostExecute(bitmap); 
      mImageView.setImageBitmap(bitmap); 
     } 
    } 
} 

私は上記のコードをコンパイルし、私のネクサス5にそれを実行した後、私はget_mem_infoボタンを押すと、次の出力を得た:

05-02 12:07:35.872 28053-28053/com.sample V/LargeBitmapLargeBitmap: maxMemory: 201326592 
05-02 12:07:35.872 28053-28053/com.sample V/LargeBitmapLargeBitmap: freeMemory: 4991056 
05-02 12:07:35.872 28053-28053/com.sample V/LargeBitmapLargeBitmap: totalMemory: 20733248 
05-02 12:07:35.873 28053-28053/com.sample V/LargeBitmapLargeBitmap: mActivityManager.getMemoryClass(): 192 

そのヒープを意味しています私のアプリケーションで利用できるメモリは192 MBです。私はload_bitmapボタンを押したときには、イメージがロードされていなかった、と私はOutOfMemoryErrorを得た:

05-02 12:07:38.712 28053-29533/com.sample E/LargeBitmapLargeBitmap: Failed to load the large bitmap 
                    java.lang.OutOfMemoryError: Failed to allocate a 233280012 byte allocation with 10567360 free bytes and 176MB until OOM 
                     at dalvik.system.VMRuntime.newNonMovableArray(Native Method) 
                     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method) 
                     at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:635) 
                     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:611) 
                     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:391) 
                     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:417) 
                     at com.sample.MainActivity$ImageLoadingTask.doInBackground(MainActivity.java:70) 
                     at com.sample.MainActivity$ImageLoadingTask.doInBackground(MainActivity.java:55) 

なぜそれが起こったのか?なぜシステムは233280012バイト(約220MB)を割り当てる必要がありましたか? 「10567360のフリー・バイトとOOMまでの176MB」の部分も私にとっては奇妙に見えます。なぜこれらの数字が表示されますか?

+1

あなたはこれを読んでいます。https://developer.android.com/topic/performance/graphics/load-bitmap.html –

+0

@MuhammadBabar、はい。しかし、実際に私の質問は、それらの奇妙な数字についてです –

答えて

2

まず第一に、物事、
14.5メガバイトはJPEGではなく、あなたの実際の画像のサイズの大きさです。
同様に、PNGなどの画像を再保存しようと、あなたはそのサイズが10の要因によって増加するでしょう、それも150メガバイトに達する可能性がある。すなわち、あなたが心に留めておく必要があります

一つのことは、これらの画像はであるということですをJPEGまたはPNGに圧縮しました。
しかし、これらの画像がimageviewなどに読み込まれると、画像の各ビットが解凍され、RAMのメモリを占有します。

はだからあなたの画像の実際のサイズは、基本的には図4(A-R-G-B)を乗じ、その解像度

EDITは - どのようにしてこれらの画像を処理するために?
まず、画像用のAsyncTaskを作成する代わりに、Glide(またはPicasso)を使用して画像を読み込みます。

グライドには、というメソッドがあり、のダウンロード中に画像のサイズを変更します。
理想的には、幅と高さはImageView(または任意のビュー)の正確な寸法にする必要があります。これにより、画像のピクセル化が防止され、メモリの消費量が節約されます。 (画像のアスペクト比を保持するために常に小さな計算を行うことができます)

+1

まず、対応するPNGのサイズについてあなたは正しかったです。 116 965 828バイト(ディスク上に117 MB)です。私はJPEGをPNGファイルとして保存しました。実際には10倍です。第2に、読み込まれた画像のサイズが高さにその幅を掛けて4を乗じたものとして計算された場合、結果は10800 * 5400 * 4 = 233 280 000バイトになります。それは私のログで見たものです。233280012バイトの割り当てに失敗しました。わからない、それは偶然かもしれない –

-2

Glideを使用してください。 Googleが承認したライブラリ。これは自動的にすべてのメモリ管理と画像読み込み機能を処理します。

+0

ほんの少しの説明のため。グライドはメモリの問題を自動的に処理しません。グライドを少し誤用すると、OOMの大きな問題が発生する可能性があります。あなたは、グライドや他のイメージが適切に機能するようにアプリケーションをデザインしなければなりません。また、Glideビルダーも効率的に使用する必要があります。 –

+0

有用な情報ありがとうございます。 –

関連する問題