android - 如何解决位图大小超出 VM 预算

标签 android android-memory

我设计了一个自定义 ImageView 库,在我的三星 Galaxy S3 上运行起来非常棒,但是当我尝试让它在我的 HTC Wildfire S 上运行时,它会抛出一个 java.lang.OutOfMemoryError:位图大小超出 VM 预算。代码如下,有什么线索吗?

public class ImageViewFlipper extends Activity {

private static final int EXIT = 0;
private static final int SWIPE_MIN_DISTANCE = 120;
private static final int SWIPE_MAX_OFF_PATH = 250;
private static final int SWIPE_THRESHOLD_VELOCITY = 200;
private static final String DIRECTORY = "/sdcard/edu";
private static final String DATA_DIRECTORY = "/sdcard/edu";
private static final String DATA_FILE = "/sdcard/imagelist.dat";
private GestureDetector gestureDetector;
View.OnTouchListener gestureListener;
private Animation slideLeftIn;
private Animation slideLeftOut;
private Animation slideRightIn;
private Animation slideRightOut;
private ViewFlipper viewFlipper;
private int currentView = 0;
List<String> ImageList;
private int currentIndex = 0;
private int maxIndex = 0;

FileOutputStream output = null;
OutputStreamWriter writer = null;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    requestWindowFeature(Window.FEATURE_NO_TITLE);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);

    setContentView(R.layout.main);
    ImageView iv = (ImageView) findViewById(R.id.zero);

    File data_directory = new File(DATA_DIRECTORY);
    if (!data_directory.exists()) {
        if (data_directory.mkdir()) {
            FileUtils savedata = new FileUtils();
            Toast toast = Toast.makeText(ImageViewFlipper.this,
                    "Please wait while we search your SD Card for images...", Toast.LENGTH_SHORT);
            toast.show();
            SystemClock.sleep(100);
            ImageList = FindFiles();
            savedata.saveArray(DATA_FILE, ImageList);

        } else {
            ImageList = FindFiles();
        }

    }
    else {
        File data_file= new File(DATA_FILE);
        if (!data_file.exists()) {
            FileUtils savedata = new FileUtils();


            SystemClock.sleep(100);
            ImageList = FindFiles();
            savedata.saveArray(DATA_FILE, ImageList);
        } else {
            FileUtils readdata = new FileUtils();
            ImageList = readdata.loadArray(DATA_FILE);
        }
    }

    if (ImageList == null) {
        quit();
    }

    SharedPreferences indexPrefs = getSharedPreferences("currentIndex",
            MODE_PRIVATE);
    if (indexPrefs.contains("currentIndex")) {
        currentIndex = indexPrefs.getInt("currentIndex", 0);
    }

    maxIndex = ImageList.size() - 1;

    Log.v("currentIndex", "Index: "+currentIndex);

    viewFlipper = (ViewFlipper) findViewById(R.id.flipper);

    slideLeftIn = AnimationUtils.loadAnimation(this, R.anim.slide_left_in);
    slideLeftOut = AnimationUtils
            .loadAnimation(this, R.anim.slide_left_out);
    slideRightIn = AnimationUtils
            .loadAnimation(this, R.anim.slide_right_in);
    slideRightOut = AnimationUtils.loadAnimation(this,
            R.anim.slide_right_out);

    viewFlipper.setInAnimation(AnimationUtils.loadAnimation(this,
            android.R.anim.fade_in));
    viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(this,
            android.R.anim.fade_out));

    iv.setImageDrawable(Drawable.createFromPath(ImageList
            .get(currentIndex)));
    System.gc();

    gestureDetector = new GestureDetector(new MyGestureDetector());
    gestureListener = new View.OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {
            if (gestureDetector.onTouchEvent(event)) {
                return true;
            }
            return false;
        }
    };

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);

    int NONE = Menu.NONE;
    menu.add(NONE, EXIT, NONE, "Exit");
    return true;
}

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case EXIT:
        quit();
        break;
    }

    return super.onOptionsItemSelected(item);
}

protected void onPause() {
    super.onPause();

    SharedPreferences indexPrefs = getSharedPreferences("currentIndex",
            MODE_PRIVATE);

    SharedPreferences.Editor indexEditor = indexPrefs.edit();
    indexEditor.putInt("currentIndex", currentIndex);
    indexEditor.commit();
}

protected void onResume() {
    super.onResume();
    SharedPreferences indexPrefs = getSharedPreferences("currentIndex", MODE_PRIVATE);
    if (indexPrefs.contains("currentIndex")) {
        currentIndex = indexPrefs.getInt("currentIndex", 0);
    }   
}

private List<String> FindFiles() {
    final List<String> tFileList = new ArrayList<String>();
    Resources resources = getResources();
    // array of valid image file extensions
    String[] imageTypes = resources.getStringArray(R.array.image);
    FilenameFilter[] filter = new FilenameFilter[imageTypes.length];

    int i = 0;
    for (final String type : imageTypes) {
        filter[i] = new FilenameFilter() {
            public boolean accept(File dir, String name) {
                return name.endsWith("." + type);
            }
        };
        i++;
    }

    FileUtils fileUtils = new FileUtils();
    File[] allMatchingFiles = fileUtils.listFilesAsArray(
            new File(DIRECTORY), filter, -1);
    for (File f : allMatchingFiles) {
        tFileList.add(f.getAbsolutePath());
    }
    return tFileList;
}

public class FileUtils {

    public void saveArray(String filename, List<String> output_field) {
         try {
            FileOutputStream fos = new FileOutputStream(filename);
            GZIPOutputStream gzos = new GZIPOutputStream(fos);
            ObjectOutputStream out = new ObjectOutputStream(gzos);
            out.writeObject(output_field);
            out.flush();
            out.close();
         }
         catch (IOException e) {
             e.getStackTrace(); 
         }
      }

      @SuppressWarnings("unchecked")
    public List<String> loadArray(String filename) {
          try {
            FileInputStream fis = new FileInputStream(filename);
            GZIPInputStream gzis = new GZIPInputStream(fis);
            ObjectInputStream in = new ObjectInputStream(gzis);
            List<String> read_field = (List<String>)in.readObject();
            in.close();
            return read_field;
          }
          catch (Exception e) {
              e.getStackTrace();
          }
          return null;
      }

    public File[] listFilesAsArray(File directory, FilenameFilter[] filter,
            int recurse) {
        Collection<File> files = listFiles(directory, filter, recurse);

        File[] arr = new File[files.size()];
        return files.toArray(arr);
    }

    public Collection<File> listFiles(File directory,
            FilenameFilter[] filter, int recurse) {

        Vector<File> files = new Vector<File>();

        File[] entries = directory.listFiles();

        if (entries != null) {
            for (File entry : entries) {
                for (FilenameFilter filefilter : filter) {
                    if (filter == null
                            || filefilter
                                    .accept(directory, entry.getName())) {
                        files.add(entry);
                        Log.v("ImageViewFlipper", "Added: "
                                + entry.getName());
                    }
                }
                if ((recurse <= -1) || (recurse > 0 && entry.isDirectory())) {
                    recurse--;
                    files.addAll(listFiles(entry, filter, recurse));
                    recurse++;
                }
            }
        }
        return files;
    }
}

class MyGestureDetector extends SimpleOnGestureListener {
    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
            float velocityY) {
        try {
            if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
                return false;
            // right to left swipe
            if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE
                    && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                viewFlipper.setInAnimation(slideLeftIn);
                viewFlipper.setOutAnimation(slideLeftOut);

                if (currentIndex == maxIndex) {
                    currentIndex = 0;
                } else {
                    currentIndex = currentIndex + 1;
                }
                if (currentView == 0) {
                    currentView = 1;
                    ImageView iv = (ImageView) findViewById(R.id.one);
                    iv.setImageDrawable(Drawable.createFromPath(ImageList
                            .get(currentIndex)));
                    System.gc();
                } else if (currentView == 1) {
                    currentView = 2;
                    ImageView iv = (ImageView) findViewById(R.id.two);
                    iv.setImageDrawable(Drawable.createFromPath(ImageList
                            .get(currentIndex)));
                    System.gc();
                } else {
                    currentView = 0;
                    ImageView iv = (ImageView) findViewById(R.id.zero);
                    iv.setImageDrawable(Drawable.createFromPath(ImageList
                            .get(currentIndex)));
                    System.gc();
                }
                Log.v("ImageViewFlipper", "Current View: " + currentView);
                viewFlipper.showNext();
            } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE
                    && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                viewFlipper.setInAnimation(slideRightIn);
                viewFlipper.setOutAnimation(slideRightOut);
                if (currentIndex == 0) {
                    currentIndex = maxIndex;
                } else {
                    currentIndex = currentIndex - 1;
                }
                if (currentView == 0) {
                    currentView = 2;
                    ImageView iv = (ImageView) findViewById(R.id.two);
                    iv.setImageDrawable(Drawable.createFromPath(ImageList.get(currentIndex)));
                } else if (currentView == 2) {
                    currentView = 1;
                    ImageView iv = (ImageView) findViewById(R.id.one);
                    iv.setImageDrawable(Drawable.createFromPath(ImageList.get(currentIndex)));
                } else {
                    currentView = 0;
                    ImageView iv = (ImageView) findViewById(R.id.zero);
                    iv.setImageDrawable(Drawable.createFromPath(ImageList
                            .get(currentIndex)));
                }
                Log.v("ImageViewFlipper", "Current View: " + currentView);
                viewFlipper.showPrevious();
            }
        } catch (Exception e) {
            // nothing
        }
        return false;
    }
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (gestureDetector.onTouchEvent(event))
        return true;
    else
        return false;
}

public void quit() {
    SharedPreferences indexPrefs = getSharedPreferences("currentIndex",MODE_PRIVATE);

    SharedPreferences.Editor indexEditor = indexPrefs.edit();
    indexEditor.putInt("currentIndex", 0);
    indexEditor.commit(); 
    File settings = new File(DATA_FILE);
    settings.delete();
    finish();
    int pid = android.os.Process.myPid();
    android.os.Process.killProcess(pid);
    System.exit(0);
}


@Override 
public void onDetachedFromWindow() {
    try {
      super.onDetachedFromWindow();
    } catch (IllegalArgumentException e) {

    }
  } 
@Override
public void onConfigurationChanged(Configuration newConfig) {
  super.onConfigurationChanged(newConfig);




}


 @Override
    protected void onDestroy() {
    super.onDestroy();
    System.gc();
    }

编辑(日志猫):

     01-05 15:56:38.049: I/Process(573): Sending signal. PID: 573 SIG: 9
     01-05 15:59:38.389: D/dalvikvm(637): GC_EXTERNAL_ALLOC freed 64K, 52% free    2613K/5379K, external 1625K/2137K, paused 49ms
      01-05 15:59:48.739: D/dalvikvm(637): GC_EXTERNAL_ALLOC freed 9K, 52% free 2634K/5379K, external 3139K/3903K, paused 53ms
      01-05 15:59:49.070: D/szipinf(637): Initializing inflate state
      01-05 16:00:02.489: V/currentIndex(637): Index: 1
      01-05 16:00:02.791: D/dalvikvm(637): GC_EXTERNAL_ALLOC freed 57K, 50% free 2748K/5447K, external 5580K/5793K, paused 36ms
      01-05 16:00:03.180: D/dalvikvm(637): GC_EXPLICIT freed 48K, 51% free 2700K/5447K, external 8692K/10740K, paused 38ms
      01-05 16:00:04.670: D/dalvikvm(637): GC_EXTERNAL_ALLOC freed 8K, 50% free 2737K/5447K, external 8692K/10740K, paused 34ms
      01-05 16:00:05.069: D/dalvikvm(637): GC_EXPLICIT freed 32K, 51% free 2705K/5447K, external 13317K/15365K, paused 54ms
      01-05 16:00:05.069: V/ImageViewFlipper(637): Current View: 1
      01-05 16:00:05.170: D/dalvikvm(637): GC_EXTERNAL_ALLOC freed <1K, 51% free 2705K/5447K, external 14817K/15365K, paused 33ms
       01-05 16:00:19.109: D/dalvikvm(637): GC_EXTERNAL_ALLOC freed 5K, 50% free 2734K/5447K, external 14817K/16865K, paused 41ms
      01-05 16:00:19.139: E/dalvikvm-heap(637): 4736340-byte external allocation too large for this process.
       01-05 16:00:19.249: E/GraphicsJNI(637): VM won't let us allocate 4736340 bytes
     01-05 16:00:19.249: D/dalvikvm(637): GC_FOR_MALLOC freed 0K, 50% free 2734K/5447K, external 14817K/16865K, paused 36ms
     01-05 16:00:19.259: D/skia(637): --- decoder->decode returned false
       01-05 16:00:19.259: D/AndroidRuntime(637): Shutting down VM
      01-05 16:00:19.259: W/dalvikvm(637): threadid=1: thread exiting with uncaught exception (group=0x40015560)
     01-05 16:00:19.270: E/AndroidRuntime(637): FATAL EXCEPTION: main
      01-05 16:00:19.270: E/AndroidRuntime(637): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
      01-05 16:00:19.270: E/AndroidRuntime(637):    at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
     01-05 16:00:19.270: E/AndroidRuntime(637):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:470)
     01-05 16:00:19.270: E/AndroidRuntime(637):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:284)
     01-05 16:00:19.270: E/AndroidRuntime(637):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:309)
     01-05 16:00:19.270: E/AndroidRuntime(637):     at android.graphics.drawable.Drawable.createFromPath(Drawable.java:800)
     01-05 16:00:19.270: E/AndroidRuntime(637):     at com.edubridge.ImageViewFlipper$MyGestureDetector.onFling(ImageViewFlipper.java:307)
     01-05 16:00:19.270: E/AndroidRuntime(637):     at android.view.GestureDetector.onTouchEvent(GestureDetector.java:568)
     01-05 16:00:19.270: E/AndroidRuntime(637):     at com.edubridge.ImageViewFlipper.onTouchEvent(ImageViewFlipper.java:354)
     01-05 16:00:19.270: E/AndroidRuntime(637):     at android.app.Activity.dispatchTouchEvent(Activity.java:2099)
     01-05 16:00:19.270: E/AndroidRuntime(637):     at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1675)
     01-05 16:00:19.270: E/AndroidRuntime(637):     at android.view.ViewRoot.deliverPointerEvent(ViewRoot.java:2194)
     01-05 16:00:19.270: E/AndroidRuntime(637):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1878)
     01-05 16:00:19.270: E/AndroidRuntime(637):     at android.os.Handler.dispatchMessage(Handler.java:99)
      01-05 16:00:19.270: E/AndroidRuntime(637):    at android.os.Looper.loop(Looper.java:123)
      01-05 16:00:19.270: E/AndroidRuntime(637):    at android.app.ActivityThread.main(ActivityThread.java:3683)
      01-05 16:00:19.270: E/AndroidRuntime(637):    at java.lang.reflect.Method.invokeNative(Native Method)
      01-05 16:00:19.270: E/AndroidRuntime(637):    at java.lang.reflect.Method.invoke(Method.java:507)
      01-05 16:00:19.270: E/AndroidRuntime(637):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
      01-05 16:00:19.270: E/AndroidRuntime(637):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
      01-05 16:00:19.270: E/AndroidRuntime(637):    at dalvik.system.NativeStart.main(Native Method)
      01-05 16:01:27.619: I/Process(637): Sending signal. PID: 637 SIG: 9

最佳答案

您需要缩小图像以消耗更少的内存,查看this回答以查看如何以正确的方式缩小它,以及当您需要知道图像的宽度和高度而不将其加载到内存时使用此代码,

BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inputStream, null, bitmapOptions);
int imageWidth = bitmapOptions.outWidth;
int imageHeight = bitmapOptions.outHeight;
inputStream.close();

关于android - 如何解决位图大小超出 VM 预算,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14170611/

相关文章:

android - Android Studio 的内存监视器显示什么样的内存?

java - 如何在android recyclerview上管理太多项目的内存

一段时间后 Android 通知消失

java - 如何修复 Android 中的 "Value &lt;!DOCTYPE of type java.lang.String cannot be converted to JSONObject"错误?

java - PreferenceFragment 布局值

不同版本之间的Android应用服务内存使用差异

android - 如何处理内存不足异常?

android - SharedElement 和自定义 EnterTransition 导致内存泄漏

java - 更新后只下载文件?

android - 如何正确停止前台服务?