Redimension Bitmap con SoftReference evitando OutOfMemoryError

Blog >Lista Soluciones >  Evitar java.lang.OutOfMemoryError

Redimensiona Bitmap usando SoftReference <Bitmap> para evitar java.lang.OutOfMemoryError


Hola desarrolladores, hoy voy a poneros una clase, la cual a mi me ha costado bastante sacar y varios quebraderos de cabeza, pero al fin lo conseguí! Esta clase la uso para poner una imagen bmp en un softreference, y este en un layout. El problema principal vino porque al hacerlo a pelo con el bmp acaba dando la excepcion outofmemory ya que mi aplicacion primero capta las medidas de la pantalla del telefono android y despues redimensiona las imagenes en funcion de estas medidas:
codigo error:
04-23 06:20:21.521: E/AndroidRuntime(1920): FATAL EXCEPTION: main
04-23 06:20:21.521: E/AndroidRuntime(1920): java.lang.OutOfMemoryError
04-23 06:20:21.521: E/AndroidRuntime(1920):  at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:503)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:356)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:379)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:409)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at com.example.boxingclock.Img.resizeImage(Img.java:23)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at com.example.boxingclock.MainActivity.gestionTema2(MainActivity.java:301)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at com.example.boxingclock.MainActivity.onCreate(MainActivity.java:177)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at android.app.Activity.performCreate(Activity.java:5133)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2175)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at android.app.ActivityThread.access$600(ActivityThread.java:141)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at android.os.Handler.dispatchMessage(Handler.java:99)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at android.os.Looper.loop(Looper.java:137)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at android.app.ActivityThread.main(ActivityThread.java:5103)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at java.lang.reflect.Method.invokeNative(Native Method)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at java.lang.reflect.Method.invoke(Method.java:525)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
04-23 06:20:21.521: E/AndroidRuntime(1920):  at dalvik.system.NativeStart.main(Native Method)

Asi que como el gestor de basura en cuanto a las imagenes de android tiene fallos nativos y en definitiva es una basura segun he leido en distintos foros, la mejor opcion para librarnos de esta odiosa excepcion, es cargar las imagenes en referencias,y cuando a Android le haga falta memoria, sera de donde primero tire:
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;

public class Img
{
 //Objeto softReference: Cuando Android necesita memoria lo primero
 static  SoftReferenceBitmp;
  public SoftReference resizeImage(Context ctx, int resId,int reqWidth, int reqHeight) {
   // PRIMERO PROGRAMAMOS inJustDecodeBounds=true PARA MODIFICAR MEDIDAS
   final BitmapFactory.Options options = new BitmapFactory.Options();
   options.inJustDecodeBounds = true;
   //REFERENCIAMOS A LA IMAGEN
   BitmapFactory.decodeResource(ctx.getResources(),resId);
   // CALCULAMOS DIMENSIONES
   options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
   // Decode bitmap with inSampleSize set
   options.inJustDecodeBounds = false;
   
   //INSERTO IMAGEN EN REFERENCIA
   Bitmp=new SoftReference(BitmapFactory.decodeResource(ctx.getResources(),resId));
   //reciclamos memoria
   BitmapFactory.decodeResource(ctx.getResources(),resId).recycle();
   return Bitmp;
}  

  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;
  }
  }

Ahora para hacer otra referencia en el onCreate de nuestra activity y poder usar la referencia creada en esta clase se usa:
SoftReference img1;
Drawable imagenfondo;

   protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        //QUITO ORIENTACION ORIZONTAL
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);


 //BACKGROUND,EN MI XML PONGO ID AL LAYOUT DONDE QUIERA HACER LA REDIMENSION
  LinearLayout layout1 =(LinearLayout)findViewById(R.id.fondomoderno);
 //CAPTURO DIMENSIONES DE LA PANTALLA
 Display display = getWindowManager().getDefaultDisplay();
     //int alto =150;
     int alto = display.getWidth();
     int ancho = display.getHeight();

 Img redimension=new Img(); 

 //COJO EL ID DE LA IMAGEN
  int resourceId = this.getResources().getIdentifier("fondomodernobox", "drawable", this.getPackageName());
 //AHORA LA REFERENCIA SOFTREFERENCE QUE ME DA LA CLASE IMG
  img1=new SoftReference(redimension.resizeImage(this, resourceId, (int)ancho, (int)alto).get());

   //PARA PASAR A DRAWABLE
   if(img1.get()!=null){
    imagenfondo=new BitmapDrawable(img1.get());
   }
   layout1.setBackground(imagenfondo);

}

AVISO IMPORTANTE: La excepción java.lang.OutOfMemoryError aun usando SoftReference y siendo la imagen muy grande va a seguir apareciendo, mientras que si son imágenes pequeñas, se corrige a la perfección. Espero que haya sido de ayuda el aporte. Salu2!!


Podeis descargar el código fuente de thebestandroide con sólo compartir en facebook,twitter,linkedin o suscribirte a nuestro canal RSS más abajo. 



Compartir Compartir Compartir Compartir




0 comentarios:

Publicar un comentario