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




Android: Conseguir vibración del dispositivo

Blog >Lista Soluciones > Vibración terminal


Vibración del dispositivo Android

Hola amigos, hoy toca mostraros como se consigue un efecto de vibración durante un tiempo determinado,que podemos prolongar o disminuir a antojo:
Para gestionar la vibración de tu dispositivo en tu app, primero hay que pedir permiso en el androidmanifiest.xml:

 
    

    

    
        
            
                
                
            
        
    
 


Seguidamente, cuando queramos hacer que vibre nuestro dispositivo movil, insertar en Java:
Vibrator v = (Vibrator) getSystemService(VIBRATOR_SERVICE);
v.vibrate(3000);

Pues ya está compañeros, los 3000 se miden en milisegundos, si quereis más tiempo o menos, calcular ese campo, la relación en milisegundos con los segundos es 1s=1000ms,  fácil y sencillo, Salu2!


Android: Tareas en segundo plano ( AsyncTask)

Blog >Lista Soluciones > AsyncTask : lanzar segundo hilo



Lanzar un segundo hilo

Todos los componentes de una aplicación Android, tanto las actividades, los servicios o los broadcast receivers que se ejecutan en el mismo hilo de ejecución, el hilo principal. Cualquier operación larga o costosa que realicemos en este hilo va a bloquear la ejecución del resto de componentes de la aplicación, produciendo al usuario un efecto evidente de lentitud o bloqueo. 

Y como no queremos que nos ocurra, vamos a usar hilos segundarios para ejecutar las operaciones tan "costosas" :

Creamos un nuevo proyecto y colocamos un botón y un ProgressBar en nuestra actividad :





y en el onCreate() de nuestra actividad principal, les hacemos referencia :

        b1=(Button) findViewById(R.id.button1);
        progress= (ProgressBar) findViewById(R.id.progressBar1);
  progress.setMax(100);
        progress.setProgress(0);
        
        b1.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View arg0) {
    // TODO Auto-generated method stub
    
   }
         
        });


Ahora, haremos una función que nos haga esperar un segundo cuando hagamos click en el botón, que se llamará desde esta otra que irá dentro del onClick de nuestro botón :

public void onClick(View arg0) {
 reiterarHilo(); 
}

public void reiterarHilo(){
      for(int x=1; x<=10; x++) {
       hilounseg();
             progress.incrementProgressBy(10);
         }
    }

public void hilounseg(){
    try {
        Thread.sleep(1000);
    } catch(InterruptedException e) {}
}

Ejecutamos la aplicación para observar el resultado, de momento el efecto no es el deseado, se ha quedado colgada y a los 10 segundos se ha llenado, esto es porque esta corriendo en el hilo principal.Ahora vemos el método thread, que internamente tiene el run() :

new Thread(new Runnable() {
    public void run() {
        //COMIENZO DE OTRO HILO
    }
}).start();

El código entero :

public class MainActivity extends Activity {

 private Button b1;
 private ProgressBar progress;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        b1=(Button) findViewById(R.id.button1);
        progress= (ProgressBar) findViewById(R.id.progressBar1);
        //LIMITES
  progress.setMax(100);
        progress.setProgress(0);    
        b1.setOnClickListener(new OnClickListener(){
   @Override
   public void onClick(View arg0) {
    new Thread(new Runnable() {
        public void run() {
           //COMIENZO HILO EN SEGUNDO PLANO
         reiterarHilo();
        }
        public void post(){
         
        }
    }).start(); 
   }
        });
        
    }
    public void reiterarHilo(){
      for(int x=1; x<=10; x++) {
       hilounseg();
             progress.incrementProgressBy(10);
         }
    }
    
    public void hilounseg(){
        try {
         //ESTA ES LA LINEA QUE NOS HACE ESPERAR UN SEGUNDO
            Thread.sleep(1000);
        } catch(InterruptedException e) {}
    }
    
}



Ahora sí podemos ver el progreso de nuestro progressBar. La creación de otro hilo nos ha permitido mantener el hilo principal libre para que nuestra interfaz de usuario se actualize durante la ejecución de la tarea en segundo plano. 





AsyncTask

Ahora vamos a ver AsynkTask, esta clase es menos engorroso y más práctico.Si quereis ver más información, ya que yo me enfoco sólo en el ámbito práctico del código y quereis curiosear podeis meteros aquí.

La forma más básica de usar la clase AsyncTask consiste en crear una nueva clase que extienda de ella y sobrescribir varios de sus métodos :

  • onPreExecute() : Se ejecutará antes del código principal de nuestra tarea. 
  • doInBackground() : Contendrá el código principal de nuestra tarea. 
  • onProgressUpdate() : Se ejecutará cada vez que llamemos al método publicProgrees() desde el método doInBackground(). 
  • onPostExecute() : Se ejecutará al finalizar la tarea o tras la finalización de doInBackground()
  • onCancelled() : Se ejecutará cuando se cancele la ejecución de la tarea antes de su finalización normal. 


AsyncTask nos permite un uso fácil del hilo, permitiendonos realizar procesos en background y publicandolos posteriormente sin necesidad de usar handlers. Son tareas asíncronas : Estas tareas asíncronas se definen con tres tipos, llamados Params, Progress y Result, y cuatro métodos, onPreExecute, doInBackground, onProgressUpdate y onPostExecute. Por ejemplo, para cargar los elementos en un ListView :

private class ListViewTask extends AsyncTask>{
    Context context;
    ProgressDialog pDialog;
 //CONSTRUCTOR
    public CargarListView(Context context){
        this.context = context;
    }
 
    @Override
    protected void onPreExecute() {
        // TODO Auto-generated method stub
        super.onPreExecute();
 
        pDialog = new ProgressDialog(context);
        pDialog.setMessage("Cargando");
        pDialog.setCancelable(true);
        pDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        pDialog.show();
    }
 
    @Override
    protected ArrayAdapter<String> doInBackground(Void... arg0) {
        // TODO Auto-generated method stub
 
        try{
            Thread.sleep(2000);
        }catch(Exception ex){
            ex.printStackTrace();
        }
 
        ArrayAdapter adaptador = new ArrayAdapter(context, android.R.layout.simple_list_item_1, sistemas);
        return adaptador;
    }
 
    @Override
    protected void onPostExecute(ArrayAdapter result) {
        // TODO Auto-generated method stub
        super.onPostExecute(result);
        list.setAdapter(result);
        pDialog.dismiss();
    }
}


Para llamarla...
new CargarListViewTask(activity).execute();



O por ejemplo, para cargar una foto a traves de Internet por medio de un enlace :

class DownloadImageTask extends AsyncTask {
  ImageView bmImage;

  public DownloadImageTask(ImageView bmImage) {
   this.bmImage = bmImage;
  }

  @Override
  protected Bitmap doInBackground(String... urls) {
   String urldisplay = urls[0];
   Bitmap mIcon11 = null;
   try {
    InputStream in = new java.net.URL(urldisplay).openStream();
    mIcon11 = BitmapFactory.decodeStream(in);
   } catch (Exception e) {
    Log.e("Error", e.getMessage());
    e.printStackTrace();
   }
   return mIcon11;
  }

  @Override
  protected void onPostExecute(Bitmap result) {
   bmImage.setImageBitmap(result);
  }
 }
}


Para llamarla...
new DownloadImageTask(R.id.image).execute("http//:miImagenColgada.jpg");




Compartir Compartir Compartir Compartir


Android: Crear una animacion mediante xml y Java

Blog >Lista Soluciones > Animacion frames


Crear animación mediante frames en xml

Hola programadores, hoy vengo a mostraros como se crea una animacion, lo implementaremos de tal forma que se ejecute cuando se inicia la actividad: Tenemos estas imágenes :




Y queremos este efecto,¿Cómo se hace?


Bien... vamos a ello; la tecnica que vamos a usar, es haciendo un nuevo documento XML en la carpeta drawable que llamaremos alarma.xml



 
   
   
   
   



Y en nuestra activity, en la parte gráfica pondremos una imagen, ajustandola como mejor veamos, pero a la derecha en la propiedad src:@drawable/alarma (en mi caso el archivo xml se llama intro)




Ahora para que se inicie cuando comienze la actividad, procedemos a insertar este código JAVA:


public class SplashScreenActivity extends Activity {

    private ImageView imagen;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Hide title bar
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        
        imagen=(ImageView)findViewById(R.id.introduccion);
        final AnimationDrawable myAnimationDrawable=
                 (AnimationDrawable)imagen.getDrawable();
            imagen.post(
                 new Runnable(){
                   @Override
                   public void run() {
                    myAnimationDrawable.start();
                   }
                 });
    }   



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




Nuevo software para Developers: Android Studio

Muy buenas, aquí os dejo el último software de google para desarrolladores android, personalmente cuando acabe mi app actual me migrare a trabajar en este entorno... Os dejo el link de descarga y una muy buena referencia desde esta excelente pagina : www.elandroidelibre.com aquí.
Link de descarga aquí.

Notification Manager Activity new instance and resume activity

Blog >Lista Soluciones > Notification Manager

Hola programadores, os traigo un problemilla que me ha llevado unos días, ya que debido a la falta de info en la red, me ha traido varios quebraderos de cabeza. Expongo aquí una función que he comentado bastante para que se entienda, al llamar a esta función.

Crea una notificación en la persiana de notificaciones del teléfono Android, el código puede abrir una nueva instancia de la Activity o abrir la antigua en el método onPause()


oOo JAVA oOo
//FUNCION DE CREAR NOTIFICACION
   protected void LanzarNotificacion(){
//EL ID DE LA NOTIFICACION
        int notificationID = 1;
        //SE  CREA UN INTENT EN EL QUE SE GUARDA EL ID DE LA NOTIFICACION, SE LLAMA ASI MISMA
        Intent i = new Intent(this, MainActivity.class);
        i.putExtra("notificationID", notificationID);
        //EL PENDINGINTENT ES UN INTENT ADAPTABLE PARA QUE SE REALICE LA NOTIFICACION
        //MIENTRAS LA APLICACION PASA AL CICLO DE VIDA ONPAUSE()
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, i, 0);
        NotificationManager nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
         
        //DETALLES
        CharSequence ticker ="Cronómetro Asalto Boxeo Activado";
        CharSequence contentTitle = "Cronómetro Asalto Boxeo";
        CharSequence contentText = "Cronómetro activado, sonará hasta que cierre la aplicación.";
        Notification noti = new NotificationCompat.Builder(this)
                                .setContentIntent(pendingIntent)
                                .setTicker(ticker)
                                .setContentTitle(contentTitle)
                                .setContentText(contentText)
                                .setSmallIcon(R.drawable.guante)
                                .addAction(R.drawable.guante,minuteroNotif+"  "+ticker, pendingIntent)
                                .setVibrate(new long[] {100, 250, 100, 500})
                                .build();
        
        //pa/ra que cuando se pulse desaparezca
        noti.flags= Notification.FLAG_AUTO_CANCEL;

        nm.notify(notificationID, noti);
        hayNotificacion=true;
    }


Y el resultado:





El problema de raíz, le tenía en que al pulsar sobre la notificación, me abría mi app pero en una nueva instancia, en vez de la anterior, que estaba en el ciclo de vida onPause()
Para que vuelva a abrir la instancia, en vez de una nueva:  En el Android Manifiest.xml añadir:

 oOo XML oOo
 
        
            /////////////////////////////////
            
                
                
            
        



Crea una notificación en la persiana de notificaciones del teléfono Android, que vuelve a primera instancia que se abrio de la Activity

Y en la función que lanza la actividad:
 oOo JAVA oOo
//FUNCION DE CREAR NOTIFICACION
   protected void LanzarNotificacion(){
//EL ID DE LA NOTIFICACION
        int notificationID = 1;
        //SE  CREA UN INTENT EN EL QUE SE GUARDA EL ID DE LA NOTIFICACION, SE LLAMA ASI MISMA
        Intent i = new Intent(this, MainActivity.class);
        //////////////////////////////////////////////////////////////////////////////////
        //AQUI ESTA LA LINEA PARA QUE SE LLAME A LA INSTANCIA DE LA ACTIVIDAD QUE ESTABA ONPAUSE();
        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        //////////////////////////////////////////////////////////////////////////////////
        i.putExtra("notificationID", notificationID);
        //EL PENDINGINTENT ES UN INTENT ADAPTABLE PARA QUE SE REALICE LA NOTIFICACION
        //MIENTRAS LA APLICACION PASA AL CICLO DE VIDA ONPAUSE()
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, i, 0);
        NotificationManager nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
         
        //DETALLES
        CharSequence ticker ="Cronómetro Asalto Boxeo Activado";
        CharSequence contentTitle = "Cronómetro Asalto Boxeo";
        CharSequence contentText = "Cronómetro activado, sonará hasta que cierre la aplicación.";
        Notification noti = new NotificationCompat.Builder(this)
                                .setContentIntent(pendingIntent)
                                .setTicker(ticker)
                                .setContentTitle(contentTitle)
                                .setContentText(contentText)
                                .setSmallIcon(R.drawable.guante)
                                .addAction(R.drawable.guante,minuteroNotif+"  "+ticker, pendingIntent)
                                .setVibrate(new long[] {100, 250, 100, 500})
                                .build();
        
        //pa/ra que cuando se pulse desaparezca
        noti.flags= Notification.FLAG_AUTO_CANCEL;

        nm.notify(notificationID, noti);
        hayNotificacion=true;
    }



Bye Bye!! Haber si a más de uno le echa un cable!! salu2!!!

oOo mAkOnE oOo



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