Follow Us

 

  • NetworkedBlogs


  • Manejando cambio de orientación con un Progress Dialog

    Posted By On Wednesday, October 5th, 2011 , 1 Comment »

    Puede que tengamos una pantalla, que en algún momento ejecute un cambio o solicitud de manera asíncrona. Para esto debemos mostrarle al usuario algo mientras, ya sea de manera determinada (progreso del trabajo o tarea) o indeterminada (el famoso spinner).

    Cuando hay un cambio de orientación, si la actividad no maneja el cambio de orientación, probablemente sea destruida y recreada. Entonces ¿Qué pasa con el ProgressDialog?.

    Iniciemos con dos reglas que debemos seguir:

    1. Si va a crear el AsyncTask dentro de la actividad como un inner class, asegúrese de que sea estática. Las inner class no estáticas, guardan una referencia de la outer class, en nuestro caso, la Actividad. Esto quiere decir que estamos “filtrando” (leaking) memoria, pues esa referencia de la actividad queda ahi mientras siga vivo el AsyncTask, y esto hay que evitarlo.
    2. Para crear el ProgressDialog, utilice los métodos que tiene disponible la actividad. Me refiero a onCreateDialog, showDialog y dismissDialog. Esto por que a la hora de cambiar la orientación de la pantalla y al destruir y recrear la actividad, Android va a mantener el estado de los dialogs que estaban presentes y se encargará de mostrarlos nuevamente cuando la actividad es recreada.
    Ahora bien, aún siguiendo estas reglas, el principal problema es que la actividad que creó el AsyncTask puede que se destruya por lo que el AsyncTask pierda la referencia (y así debe ser). La solución es simplemente desacoplar la actividad del AsyncTask. Empleando los métodos del ciclo de vida de la activad, es posible “notificar” o “actualizar” el AsyncTask, para que se percate del cambio de actividad y pueda mostrar o ocultar el ProgressDialog.

    El consenso es utilizar el método onRetainNonConfigurationInstance, para “guardar” la referencia del AsynctTask, así, cuando la nueva actividad es creada, ella se dará cuenta si la tarea todavia está en progreso, para poder “acoplarse” a ella.

    public class EjemploProgressDialogActivity extends Activity {
        public static final String TAG = "EXAMPLE_DIALOG";
    
        public static final int PROGRESS_DIALOG = 1;
    
        MyTask task;
    
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            Log.d(TAG, "onCreate");
            Object obj = getLastNonConfigurationInstance();
            if (obj != null && obj instanceof MyTask) {
                Log.d(TAG, "Tarea previa ejecutandose");
                task = (MyTask) obj;
                task.attach(this);
            } else {
                task = new MyTask(this);
                task.execute(10);
                Log.d(TAG, "Nueva tarea creada y ejecutada");
            }
        }
    
        @Override
        protected Dialog onCreateDialog(int id) {
    
            switch (id) {
            case PROGRESS_DIALOG:
                ProgressDialog pd = new ProgressDialog(this);
                pd.setTitle("Trabajando");
                pd.setMessage("Por favor espere...");
                return pd;
    
            default:
                break;
            }
            return super.onCreateDialog(id);
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            Log.d(TAG, "onPause");
        }
    
        @Override
        protected void onRestart() {
            super.onRestart();
            Log.d(TAG, "onRestart");
    
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            Log.d(TAG, "onResume");
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            Log.d(TAG, "onStart");
        }
    
        @Override
        protected void onStop() {
            super.onStop();
            Log.d(TAG, "onStop");
        }
    
        @Override
        public Object onRetainNonConfigurationInstance() {
            // Aqui es donde se hace la magia
            if (task != null) {
                task.deattach();
                return task;
            }
            return super.onRetainNonConfigurationInstance();
        }
    
        private static class MyTask extends AsyncTask {
            WeakReference ctx;
    
            public MyTask(Activity activity) {
                super();
                attach(activity);
            }
    
            @Override
            protected void onPreExecute() {
                Activity activity = ctx.get();
                if (activity != null && !activity.isFinishing()) {
                    Log.d(TAG, "Mostrando Progress Dialog");
                    activity.showDialog(PROGRESS_DIALOG);
                }
            }
    
            @Override
            protected Void doInBackground(Integer... params) {
                int seconds = params[0];
                try {
                    Log.d(TAG, "Tarea va a durar " + seconds + " segundos");
                    Thread.sleep(seconds * 1000);
                    Log.d(TAG, "Tarea Lista");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }
    
            @Override
            protected void onPostExecute(Void result) {
                if (ctx != null && ctx.get() != null) {
                    Activity activity = ctx.get();
                    if (!activity.isFinishing()) {
                        Log.d(TAG, "Ocultando Progress Dialog");
                        activity.dismissDialog(PROGRESS_DIALOG);
                    }
                }
            }
    
            public void attach(Activity activity) {
                this.ctx = new WeakReference(activity);
            }
    
            public void deattach() {
                ctx = null;
            }
    
        }
    }
    

    En la línea 14, pueden ver que en el método onCreate se pregunta si se salvó algún objeto previamente y luego se hacen los chequeos respectivos para asegurarse que el objeto sea el AsyncTask. En este ejemplo en particular, se crea el AsyncTask en el método onCreate, por lo que en el caso de que no exista, se crea.

    En la línea 18 se ejecuta el método attach,para que el AsyncTask tenga la nueva referencia de la Actividad.

    En la línea 77 que es el momento en que la actividad da su último suspiro, el AsyncTask se desacopla y se “salva” la referencia al AsyncTask para que la siguiente actividad (si es el caso) la retome, como vimos en el método onCreate.

    Si ven la implementación del AsyncTask, primero que todo, se guarda la referencia a la actividad dentro de un WeakReference, para así evitar “filtrar” memoria.

    En los métodos onPreExecute y onPostExecute se hacen validaciones para asegurarse de que la referencia de la actividad sea válidad (que exista la referencia y que la actividad no esté en proceso de morirse).

    Si ejecutáramos éste código,  sin mover el dispositivo, esta sería la salida en la bitácora:

    10-04 21:54:46.254: DEBUG/EXAMPLE_DIALOG(6155): onCreate
    10-04 21:54:46.294: DEBUG/EXAMPLE_DIALOG(6155): Mostrando Progress Dialog
    10-04 21:54:46.514: DEBUG/EXAMPLE_DIALOG(6155): Tarea va a durar 10 segundos
    10-04 21:54:46.514: DEBUG/EXAMPLE_DIALOG(6155): Nueva tarea creada y ejecutada
    10-04 21:54:46.514: DEBUG/EXAMPLE_DIALOG(6155): onStart
    10-04 21:54:46.524: DEBUG/EXAMPLE_DIALOG(6155): onResume
    10-04 21:54:56.518: DEBUG/EXAMPLE_DIALOG(6155): Tarea Lista
    10-04 21:54:56.524: DEBUG/EXAMPLE_DIALOG(6155): Ocultando Progress Dialog
    10-04 21:55:12.395: DEBUG/EXAMPLE_DIALOG(6155): onPause
    10-04 21:55:12.554: DEBUG/EXAMPLE_DIALOG(6155): onStop

    Pero si cambiamos la orientación del dispositivo, esto sería el resultado:

    te
    10-04 21:56:08.584: DEBUG/EXAMPLE_DIALOG(6155): Mostrando Progress Dialog
    10-04 21:56:08.754: DEBUG/EXAMPLE_DIALOG(6155): Nueva tarea creada y ejecutada
    10-04 21:56:08.754: DEBUG/EXAMPLE_DIALOG(6155): onStart
    10-04 21:56:08.754: DEBUG/EXAMPLE_DIALOG(6155): Tarea va a durar 10 segundos
    10-04 21:56:08.764: DEBUG/EXAMPLE_DIALOG(6155): onResume
    10-04 21:56:10.424: DEBUG/EXAMPLE_DIALOG(6155): onPause
    10-04 21:56:10.424: DEBUG/EXAMPLE_DIALOG(6155): onStop
    10-04 21:56:10.544: DEBUG/EXAMPLE_DIALOG(6155): onCreate
    10-04 21:56:10.544: DEBUG/EXAMPLE_DIALOG(6155): Tarea previa ejecutandose
    10-04 21:56:10.544: DEBUG/EXAMPLE_DIALOG(6155): onStart
    10-04 21:56:10.704: DEBUG/EXAMPLE_DIALOG(6155): onResume
    10-04 21:56:18.755: DEBUG/EXAMPLE_DIALOG(6155): Tarea Lista
    10-04 21:56:18.755: DEBUG/EXAMPLE_DIALOG(6155): Ocultando Progress Dialog
    10-04 21:56:21.626: DEBUG/EXAMPLE_DIALOG(6155): onPause
    10-04 21:56:21.784: DEBUG/EXAMPLE_DIALOG(6155): onStop

    Imprimí además cuando se ejecutan otros métodos del ciclo de vida, para tener una referencia de cuando ocurre qué.

    fr4gus y Arduino en El Chou TV

    Posted By On Thursday, September 22nd, 2011 , No Comments »

    Muchas gracias a ElChouTV (www.elchoutv.com) por la invitación de ir a hablar de mi nuevo hobby, Arduino, y introducir este tema en el ámbito nacional.


    Video streaming by Ustream

    No se les olvide, el grupo de facebook en http://www.facebook.com/groups/arduinocr

    De Arduino a Android y de vuelta

    Posted By On Tuesday, September 6th, 2011 , No Comments »


    Apesar de mi gran interés en la electrónica, siempre ha sido una area flaca, básicamente por mi dificultad de entender la parte de las Leyes de Ohm y el resto de su teoría. Y es que a mi me gusta aprender haciendo y cuando descubrí Arduino fue como que oyeran mis plegarias.

    No voy a ahondar mucho sobre que es Arduino, básicamente es una plataforma electrónica de código abierto, para crear objetos interactivos al ambiente. Si usted desea aprender más sobre Arduino, le recomiendo que se dé una vuelta por el sitio oficial que es http://www.arduino.cc y en español http://www.arduino.cc/es/.

    Yo me compré este Starter Kit en Sparkfun (amazon / sparkfun), que viene con un Arduino Uno, varios sensores (temperatura, luz, flexibilidad, etc), algunos LEDs, resistencias, mini protoboard y jump wires. Lamentablemente por cuestiones de tiempo lo tuve guardado varios meses.

    Al ver el anuncio en Google IO 2011 sobre el Android Open Accessorie Developer Kit (ADK) y de que podía comunicarse con Android y que además el hardware era compatible con Arduino dije: -¡Ahora sí! pero en ese momento el costo del ADK board era alrededor de los $300. Otro problema es que el ADK solo es compatible con dispositivos 2.3.4+. Como era compatible con Arduino, pensé que debía de haber alguna forma de poder hacer el ADK y efectivamente, ya alguien vió el mismo problema y puso en su blog, como hacer su propio (DIY) ADK board.

    Ronfont en su sitio web, posteó los pasos de como instalar el ADK en un Arduino + USB Host Shield (ver post aquí). No le bastó y 3 días después, publicó un post, donde “hackeó” el protocolo ADB para poder hacer comunicar a un dispositivo Android con la pareja Arduino+USB Host Shield. Esto se convirtió en un proyecto de código abierto llamado MicroBridge que permitiría a dispositivos de 1.5 en adelante, comunicarse con Arduino.

    Ésta no es la única forma de poder comunicar un Arduino con Android. Otra opción basada en USB, es la tarjeta IOIO (en inglés se pronuncia como yo-yo) creada por Sparkfun. SeeedStudio creó su propia version llamada SeedDuino ADK Mainboard. Microship también ofrece el chipKIT. ITeadStudio tiene su version llamada Iteaduino ADK Main board. Todas andan alrededor de los $80 actualmente.

    Además de las opciones USB, hay otra opción de comunicar a Android con Arduino mediante Bluetooth. Primero se ocupa un módulo Bluetooth, y de esos también hay bastantes sabores. De los que yo conozco estan el BlueSmirf y los Bluetooth Shield (slave y slave/master).

    Existe un proyecto de código abierto llamado Amarino que ofrece un API para Android y la librería para Arduino. Ya lo utilicé y es bastante sencillo una vez comprendido las bases.

    Espero que esta información introductoria les sea de utilidad, proximamente postearé más información con ejemplos reales. Pueden revisar mi blog en la sección de Arduino, ahi ya he colocado un par de videos de mi pruebas con Microbrige.

    Referencias:

    1. Google’s Open Accessory Development Kit on Standard Arduino Hardware.
    2. MicroBridge: ADB support for Arduino
    3. Amarino

    Charla sobre el Android Market

    Posted By On Saturday, August 27th, 2011 , No Comments »

    Hoy a las 12pm, estaré dando una charla sobre el Comercialización de Aplicaciones Android en la ULACIT, en un curso sobre Android que imparte mi amigo Julio Marín (sí, el de El ChouTV).

    Aqui les dejo además algunos links de interes (algunos incluidos en la presentación como QR code)

    Implementando RoundedImageView para Android

    Posted By On Thursday, August 18th, 2011 , No Comments »

    Para uno de los proyectos de Possible Worldwide, era necesario mostrar imágenes que se bajaban de un servidor, con las esquinas redondeadas. La primera implementación constaba de bajar la imagen, y “recortar los bordes” antes de guardar la imagen en el almacenamiento del teléfono.

    El método simplemente recortaba la imagen generando un nuevo Bitmap:

        public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, final float roundPx) {
            if (bitmap == null) {
                throw new IllegalArgumentException("Bitmap to round corners can not be null");
            }
            Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);
            Canvas canvas = new Canvas(output);
            final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
            final RectF rectF = new RectF(rect);
            final int color = 0xff424242;
            final Paint paint = new Paint();
            paint.setAntiAlias(true);
            canvas.drawARGB(0, 0, 0, 0);
            paint.setColor(color);
            canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
            paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
            canvas.drawBitmap(bitmap, rect, rect, paint);
            return output;
        }
    

    Pero ciertas imágenes tenia ciertos problemas causando recortes execivos, además de estar alojando dos veces la misma imagen en el heap. Aun cuando se puede mandar a “reciclar” el bitmap viejo, es un gasto innecesario de memoria.

    Luego, investigando un poco más, me encontre esta solución en StackOverflow. Básicamente es crear una clase que herede de ImageView y sobreescribir el método onDraw, y utilizando un clipPath, “ocultar” las esquinas con un rectangulo redondeado.

    public class RoundedImageView extends ImageView {
        public RoundedImageView(Context context) {
            super(context);
        }
        public RoundedImageView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
        public RoundedImageView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
        @Override
        protected void onDraw(Canvas canvas) {
            Path clipPath = new Path();
            int w = this.getWidth();
            int h = this.getHeight();
            clipPath.addRoundRect(new RectF(0, 0, w, h), 10.0f, 10.0f, Path.Direction.CW);
            canvas.clipPath(clipPath);
            super.onDraw(canvas);
        }
    }
    

    Ésta implementación es muy sencilla, pero se podría modificar para que acepte un parámetro desde el Layout (XML) para definir la cantidad de redondeo

    De esta manera, no es necesario procesar la imagen, reduciendo el uso de heap nativo y mostrando la imagen más rapidamente.

    Arduino comunicándose con Android Prueba 2

    Posted By On Monday, August 15th, 2011 , No Comments »

    La segunda prueba de rigor de MicroBridge, era correr el “demo”. El demo es simplemente cargar el sketch demo (desde el IDE, en ejemplos) al Arduino. Esto va a ejecutar el código que abre un puerto una vez que un device ADB se conecte. Por otro lado, se ejecuta en un dispositivo Android la aplicación ServoControl que crea el servidor y cliente por el puerto, para leer las lecturas que envíe Arduino.

    Ya con este ejemplo ejecutandose, me dedicaré a entender el código C del ADB para poder programar mis propios sketchs de arduino y estudiar la aplicación de Android para ver que se me ocurre.

    Arduino comunicándose con Android Prueba 1

    Posted By On Sunday, August 14th, 2011 , 2 Comments »

    Desde el anunció en Google IO de este año sobre la posibilidad de usar Arduino con Android. Mi primera sorpresa fue que los kits oficiales en su momento costaban $300 por lo que he estado leyendo y comprando lo necesario para poder hacer mi propio Android Open Accessory Development Kit. En Internet, me encontre a alguien que lo había hecho, con componentes standard de Arduino y modificando las librerias para hacerlo compatible, por lo que seguí las guías y este es mi primer acercamiento, por dicha, exitoso. El costo del Arduino UNO y el USB Host Shield no supera los $60.

    Fuente:

    Links:

    Softkeyboard aparece al entrar en una ventana

    Posted By On Thursday, July 28th, 2011 , No Comments »

    Me encontré un “error” o “bug” muy particular en Android. Lo pongo entre comillas porque aún no tengo confirmación de que así lo sea.

    El “bug” es que ciertos layouts, pueden provocar que el teclado virtual (o softkeyboard) se active tan pronto como la actividad (o ventana) toma foco (se coloca en la pila de ventananas). Según mis pruebas, (que pueden ver en el post) todo pasa cuando el layout tiene un widget contenedor, llámese un ListView, GridView, Gallery, etc. y debajo de él un campo de texto. Esta combinación, parece provocar, en ciertos devices como el HTC Desire  y el LG GX2, ambos con Android 2.2, la activación del softkeyboad.

    El mes pasado puse una pregunta en el grupo de google para Desarrolladores de Android (ver pregunta aquí), pero lo único que me contestaron fue como “mitigarlo”.

    Dentro de las recomendaciones que me hicieron, fue que creara un repositorio con el código, para que otras personas pudieran probarlo. Pueden bajar el código en Github:

    https://github.com/fr4gus/AndroidSoftKeyboardBug

    Me gustaría saber si alguien más lo puede replicar, con cual o cuales devices y si tiene alguna idea de si es un bug o no.

    -f4

     

    nDroidEs Episodio 4

    Posted By On Wednesday, July 27th, 2011 , 2 Comments »

    El episodio 4 contó con la presencia de Pablo Castro, quien tiene un año trabajando con el markup de la intefaz de Android.

    Prmiero se habló de como se trabaja en equipo en Possible Worldwide para desarrollar aplicaciones para Android.

    Luego Pablo nos comentó, como trabaja los diseños que recibe del área creativa en forma de PSDs para crear los archivos necesarios que ocupa una aplicación en forma de assets, llamense pngs, jpgs, etc. Estos archivos que genera Pablo irán en la carpeta “res” y donde la imágenes irán en las carpetas según su densidad.

    También nos comentó Pablo como es trabajar con las herramientas de Eclipse y de Android para revisar la interfaz hecha.

    Pablo además explicó las generalidades de algunos “layouts”,que son los contenedores para crear la interfaz. Entre ellos explicó los más importantes como son el LinearLayout, RelativeLayout y TableLayout.

    Por último, Pablo explicó como se utiliza el “ninepach” y como se crean en Android.

    Agradecimientos a Pablo por acomañarnos y a Possible Worldwide por hospedarnos este episodio.

    Pablo muy amablemente nos compartió la presentación sobre Android UI para más información.

    nDroidEs Episodio 3

    Posted By On Saturday, July 9th, 2011 , No Comments »

    Version 1.0 (en vivo, por Ustream)

    Version 2.0 (offline)

    En este episodio hablamos de Actividades e Intentos o Intenciones. Además, éste fue el primer episodio de nDroidEs en vivo.  Esperamos poder repetir la experiencia de transmitir en vivo el próximo Martes, a las 9pm(UTC -6).

     
    Page 1 of 512345»