ViewPager с ImageView выдает java.lang.OutOfMemoryError: размер растрового изображения превышает бюджет виртуальной машины

Я сделал ViewPager для отображения изображений. Когда я продвигаюсь вперед на несколько страниц, я получаю ошибку java.lang.OutOfMemoryError: bitmap size exceeds VM budget.

Есть еще вопросы по этой проблеме, но я не нашел решения (BitMap.Recycle, System.gc() и т.д.). Если у вас есть предложение или решение, пожалуйста, дайте мне знать!

Размер PNG составляет 628 КБ, 478 КБ, 587 КБ, 132 КБ, 139 КБ, 149 КБ, 585 КБ (сбой).

Если у вас есть другое решение (прокручивать изображения как страницы), дайте мне знать!

Мой код:

package nl.ipear.vngmagazine;
import android.content.Context;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Environment;
import android.os.Parcelable;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;

public class ShowMagazine2 extends FragmentActivity {
    private ViewPager myPager;
    private MyPagerAdapter myPagerAdapter;

    private static int NUM_PAGES = 15;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.showmagazine);

        myPager = (ViewPager)findViewById(R.id.viewpager1);
        myPagerAdapter = new MyPagerAdapter();
        myPager.setAdapter(myPagerAdapter);

        return;
    }

    private class MyPagerAdapter extends PagerAdapter{
        @Override
        public int getCount() {
            return NUM_PAGES;
        }

        @Override
        public Object instantiateItem(View collection, int position) {      
            String location = Environment.getExternalStorageDirectory() + "/MYData/" + "2012-02/";

            // Inflate and create the view
            LayoutInflater layoutInflater = (LayoutInflater) collection.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            View view = layoutInflater.inflate(R.layout.magazinepageview, null);

            ImageView imageView = (ImageView) view.findViewById(R.id.magazinePageImage);
            String fileName = String.format("%s%s%02d%s", location, "2012-02_Page_", position + 1, ".png");
            Log.v("PNG", fileName);
            imageView.setImageBitmap(BitmapFactory.decodeFile(fileName));

            ((ViewPager) collection).addView(view,0);

            return view;
        }

        @Override
        public void destroyItem(View collection, int position, Object view) {
            ((ViewPager) collection).removeView((View) view);           
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view==((View)object);
        }

        @Override
        public void finishUpdate(View arg0) {}

        @Override
        public void restoreState(Parcelable arg0, ClassLoader arg1) {}

        @Override
        public Parcelable saveState() {
            return null;
        }

        @Override
        public void startUpdate(View arg0) {}   
    }
}

person Hans    schedule 19.03.2012    source источник
comment
Каков размер (пиксели, а не КБ) растрового изображения? и он крашится на каждом размере? (тоже была эта проблема, но мне нужно немного больше информации)   -  person Bigflow    schedule 19.03.2012
comment
Спасибо за ваш ответ, изображения примерно 1240x1584. Эти изображения созданы из PDF и предназначены для телефонов и планшетов!   -  person Hans    schedule 19.03.2012
comment
Вы пробовали меньшие изображения? например, 300 x 300. Поскольку изображения можно разрезать на более мелкие фрагменты и декодировать эти фрагменты, позже вы можете показать их как полное изображение.   -  person Bigflow    schedule 20.03.2012
comment
@ Ханс, вы смогли решить проблему, пожалуйста, примите ответ, который помог решить проблему, или, если вы решили проблему самостоятельно, поделитесь решением.   -  person suresh cheemalamudi    schedule 25.02.2013
comment
@suresh cheemalamudi Я не решил эту проблему. Единственное «решение», которое я нашел, - это изменить изображение на более низкое разрешение.   -  person Hans    schedule 10.03.2013


Ответы (5)


Может показаться, что ваш ViewPager загружает растровые изображения, но не выпускает их при прокрутке.

Я бы посоветовал вам ограничить количество страниц, доступных по обе стороны от текущей просматриваемой страницы, что позволит системе очистить растровые изображения на других страницах.

Убедитесь, что вы перерабатываете растровые изображения, когда ваша активность/фрагмент уничтожается, чтобы помочь с проблемами OOM.

person Mimminito    schedule 19.03.2012
comment
Как я могу ограничить количество страниц? На данный момент загружено три изображения. - person Hans; 19.03.2012
comment
Должна быть утечка памяти или ошибка в Android, потому что я освобождаю память в destroyItem. Я действительно думаю, что мне нужно использовать другой подход, потому что я не могу исправить ошибку. - person Hans; 19.03.2012
comment
@Hans Привет, даже у меня такая же проблема .. Как ты наконец решил ее? - person Y M; 25.02.2015

Попробуйте уменьшить растровое изображение, это действительно решает проблему, вот как вы это делаете.

BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile( filename, options );
        options.inJustDecodeBounds = false;
        options.inSampleSize = 2; 

        bitmap = BitmapFactory.decodeFile( filename, options );
        if ( bitmap != null && exact ) {
            bitmap = Bitmap.createScaledBitmap( bitmap, width, height, false );
        }

Отрегулируйте размер выборки так, как вы хотите, например. 4, 8 и т. д.

person Arif Nadeem    schedule 19.03.2012
comment
Когда я уменьшаю изображения, это будет работать, но пользователи должны иметь возможность увеличивать масштаб. Будет ли это работать, когда я уменьшаю изображение? - person Hans; 19.03.2012
comment
попробуйте сами, меняя размер выборки, все будет хорошо, если вас устраивает качество изображения.. - person Arif Nadeem; 19.03.2012
comment
Я надеюсь, что можно повторно загрузить изображения, когда пользователь увеличивает масштаб. - person Hans; 19.03.2012

Да, следуйте за Мимминито. Я советую 2 страницы, так как рендеринг одинаково быстр для вперед и назад для 3 страниц.

Теперь, если вам нужны изображения без задержек и изображения в Интернете. Убедитесь, что вы загрузили его и поместили внутрь internalStorageDevice, а затем повторно используете его, если он существует.

мой главный ответ, возможно, неверен для проблемы утечки памяти. Этот новый ответ является вероятной причиной утечки памяти из-за высоты и ширины изображения.

private byte[] resizeImage( byte[] input ) {

    if ( input == null ) {
        return null;
    }

    Bitmap bitmapOrg = BitmapFactory.decodeByteArray(input, 0, input.length);

    if ( bitmapOrg == null ) {
        return null;
    }

    int height = bitmapOrg.getHeight();
    int width = bitmapOrg.getWidth();
    int newHeight = 250;

    float scaleHeight = ((float) newHeight) / height;

    // creates matrix for the manipulation
    Matrix matrix = new Matrix();
    // resize the bit map
    matrix.postScale(scaleHeight, scaleHeight);

    // recreate the new Bitmap
    Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0,
            width, height, matrix, true);

    bitmapOrg.recycle();

    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    resizedBitmap.compress(CompressFormat.PNG, 0 /*ignored for PNG*/, bos);            

    resizedBitmap.recycle();

    return bos.toByteArray();            
}       

Этот верхний код предназначен для изменения размера растрового изображения. «newHeight» — это новая высота, заданная растровому изображению.

Теперь, если это не сработает, я теперь на 100% уверен, что OP должен перегружать ViewPager большим количеством представлений, которые может обрабатывать память Android. Решение для этого состоит в том, чтобы использовать ViewFlipper и использовать ответ, который я указал выше.

person sdfwer    schedule 19.03.2012
comment
Изображения хранятся на внешнем хранилище, даже если для setOffscreenPageLimit установлено значение 1, выдается ошибка памяти!? - person Hans; 19.03.2012
comment
Хорошо, я думаю, что масштабирование может быть неправильным, если оно слишком велико для своего размера. Я опубликую код в своем ответе, проверив это только на одной странице. Затем установите его в onflicklistener, чтобы всякий раз, когда он щелкал, он отображал новое изображение на второй странице. - person sdfwer; 19.03.2012

Нашел ответ здесь: Android:java.lang. OutOfMemoryError: не удалось выделить 23970828 байтов с 2097152 свободными байтами и 2 МБ до OOM

Просто добавьте android:hardwareAccelerated="false" и android:largeHeap="true" в тег вашего приложения внутри AndroidManifest:

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        android:hardwareAccelerated="false"
        android:largeHeap="true">
person dianakarenms    schedule 09.12.2016

вызов System.gc() в oncreate()

person ro1    schedule 05.05.2014
comment
Не делайте этого. Пожалуйста, никогда не пытайтесь управлять сборщиком мусора вручную. Скорее всего, вы получите худшие результаты, чем если бы вы этого не сделали. - person Alexey Malev; 05.05.2014