2011-11-28 2 views
10

У меня есть приложение, которое отображает довольно много изображений для пользователя, и мы видели множество отчетов об ошибках с исключением OutOfMemoryError.Вращающиеся изображения на Android. Есть ли способ лучше?

То, что мы в настоящее время сделать это:

// Check if image is a landscape image 
if (bmp.getWidth() > bmp.getHeight()) { 
    // Rotate it to show as a landscape 
    Matrix m = image.getImageMatrix(); 
    m.postRotate(90); 
    bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true); 
} 
image.setImageBitmap(bmp); 

Очевидная проблема состоит в том, что мы должны воссоздать растровое изображение с изображением на память и повернуть матрицу, это довольно дорого для памяти.

Мой вопрос прост:

Есть ли лучший способ вращать изображения, не вызывая OutOfMemoryError?

+0

http://www.twintechs.com/2008/06/frame-by-frame-xml-animation-with-google-android/ –

+0

На какой строке выбрасывается исключение – ingsaurabh

+0

@ Dr.nik Итак, вы предлагаете мне поворачивать изображение с анимацией, даже если оно не должно быть анимированным вращением? – Draiken

ответ

0

вы можете попробовать:

image.setImageBitmap(null); 
// Check if image is a landscape image 
if (bmp.getWidth() > bmp.getHeight()) { 
    // Rotate it to show as a landscape 
    Matrix m = image.getImageMatrix(); 
    m.postRotate(90); 
    bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true); 
} 
BitmapDrawable bd = new BitmapDrawable(mContext.getResources(), bmp); 
bmp.recycle(); 
bmp = null; 
setImageDrawable(bd); 
bd = null; 
+0

Как указано в комментарии в вопросе, ошибка происходит в строке «Bitmap.createBitmap». – Draiken

+0

спасибо, я не видел, что – Caner

0

При работе с большим количеством Bitmaps не забудьте вызвать рециркуляцию() на них, как только они не нужны. Этот вызов мгновенно освободит память, связанную с определенным растровым изображением.

В вашем случае, если вам не требуется оригинальное растровое изображение после вращения, а затем переработайте его. Что-то вдоль линий:

Bitmap result = bmp; 

// Check if image is a landscape image 
if (bmp.getWidth() > bmp.getHeight()) { 
    // Rotate it to show as a landscape 
    Matrix m = image.getImageMatrix(); 
    m.postRotate(90); 
    result = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true); 
    // rotating done, original not needed => recycle() 
    bmp.recycle(); 
} 

image.setImageBitmap(result); 
+0

Ницца, не знал об этом. Проблема в том, что мне нужно оригинальное растровое изображение, чтобы повернуть его, и произошла ошибка, прежде чем я смогу освободить его из памяти:/ – Draiken

+0

На самом деле я попробовал переработку, и теперь он просто показывает черный экран вместо растрового изображения. Я предполагаю, что внутри это указатель, и я не могу с ним покончить. – Draiken

+0

Hm. Bitmap.createBitmap() должен создать совершенно новый битмап, поэтому освобождение оригинала должно быть безопасным. На самом деле я очень часто использую этот подход в своем собственном коде, и он работает очень хорошо. Уверены ли вы, что вы перерабатываете правильное растровое изображение и только * после * присваиваете ссылку на повернутое растровое изображение, возвращаемое createBitmap(), в другую ссылку (как в моем примере). Это может вызвать ошибку в createBitmap(), если вы уже использовали всю память. Но если каждый раз, когда вы правильно освобождаете его с помощью recycle(), он должен быть в порядке при следующих вызовах ... – dimsuz

6

2 способа вращения большого изображения:

  1. использования JNI, как on this post.

  2. Использование файла: это очень медленный путь (в зависимости от ввода и устройства, но все еще очень медленный), который сначала помещает декодированное повернутое изображение в диск, а не помещает его в память.

код с помощью файла ниже:

private void rotateCw90Degrees() 
    { 
    Bitmap bitmap=BitmapFactory.decodeResource(getResources(),INPUT_IMAGE_RES_ID); 
    // 12 => 7531 
    // 34 => 8642 
    // 56 => 
    // 78 => 
    final int height=bitmap.getHeight(); 
    final int width=bitmap.getWidth(); 
    try 
    { 
    final DataOutputStream outputStream=new DataOutputStream(new BufferedOutputStream(openFileOutput(ROTATED_IMAGE_FILENAME,Context.MODE_PRIVATE))); 
    for(int x=0;x<width;++x) 
     for(int y=height-1;y>=0;--y) 
     { 
     final int pixel=bitmap.getPixel(x,y); 
     outputStream.writeInt(pixel); 
     } 
    outputStream.flush(); 
    outputStream.close(); 
    bitmap.recycle(); 
    final int newWidth=height; 
    final int newHeight=width; 
    bitmap=Bitmap.createBitmap(newWidth,newHeight,bitmap.getConfig()); 
    final DataInputStream inputStream=new DataInputStream(new BufferedInputStream(openFileInput(ROTATED_IMAGE_FILENAME))); 
    for(int y=0;y<newHeight;++y) 
     for(int x=0;x<newWidth;++x) 
     { 
     final int pixel=inputStream.readInt(); 
     bitmap.setPixel(x,y,pixel); 
     } 
    inputStream.close(); 
    new File(getFilesDir(),ROTATED_IMAGE_FILENAME).delete(); 
    saveBitmapToFile(bitmap); //for checking the output 
    } 
    catch(final IOException e) 
    { 
    e.printStackTrace(); 
    } 
    } 
+0

@android_developer. файл, я думаю, что стоит потратить время, чтобы избежать всех проблем с памятью. –

+0

@PeterFile Я думаю, вы должны сделать это в нескольких резервных копиях от самых быстрых (но наиболее опасных) до медленных (но безопасных): сначала попробуйте использовать обычный способ (используя кучу). Если это не удается (недостаточно памяти), используйте решение JNI. Если это не удается (недостаточно памяти), используйте решение для хранения. Решение для хранения также может потерпеть неудачу (недостаточно памяти), так как созданный файл является декодированным растровым изображением, а не сжатым, но это может произойти редко. –

+0

@androiddeveloper Помогает ли вам сохранить исходный размер и качество изображения? –