15/3/2014

Desproteger un fichero PDF

Es habitual encontrarse con ficheros PDF protegidos mediante contraseña para evitar que se pueda copiar texto e incluso imprimirlos. Esto ocasiona múltiples molestias a la hora de trabajar con estos tipos de documentos.

En el peor de los casos hasta el acceso de lectura al PDF está restringido. Lo habitual al crear un PDF es protegerlo sólo de modificaciones, impresiones y de copias no autorizadas.

Existen multitud de páginas webs y programas que desprotegen con un par de clics el documento, pero son de dudosa calidad: su código fuente no es abierto y aunque funcionan correctamente, no hay garantía alguna de que no inyecten código malicioso en el fichero con diversos fines. Por eso es importante utilizar programas que ofrezcan un mínimo de seguridad (o bien por ser código abierto / software libre o bien porque detrás hay una compañía seria como Adobe aunque su código sea privativo).

Por estos motivos y, en base a una consulta que me ha sido realizada se ha creado este pequeño tutorial para desproteger aquellos PDFs para que se pueda trabajar con ellos (cuidado con vulnerar los derechos de autor y la distribución del contenido).

  1. Para saber si un PDF está protegido basta con abrirlo con un lector de PDFs cualquiera y éste ya no los indica (para este ejemplo se utiliza Foxit Reader).

    El fichero PDF se encuentra protegido y las opciones de copiar e imprimir se pueden encontrar desactivadas.

  2. Para desproteger el fichero se utilizará el programa PDFarchitect de PDFforge que utiliza una licencia de tipo Freeware (es gratuito y no es libre, pero al menos es de fiar mientras no se termina el desarrollo de GNU Juggler).
  3. Una vez instalado el programa, se abre PDFarchitect y en Archivo > Abrir se selecciona el fichero PDF protegido.
  4. A continuación se puede solicitar una contraseña para desbloquear el PDF. Se deja vacía y se pulsa OK.

    La contraseña se deja en blanco.

  5. Una vez cargue el PFD en Archivo > Guardar como se guarda el fichero con otro nombre, por ejemplo Desprotegido.pdf.
  6. Ahora abriendo este último fichero se puede trabajar perfectamente con él.

    Ahora se puede copiar el texto e imprimir el fichero.



6/3/2014

Deshabilitar la aceleración del ratón en Windows 7

La aceleración del ratón permite que el cursor en la pantalla recorra para un mismo movimiento, más distancia según la rapidez con que se mueva. Deshabilitar esto suele ser útil en caso de jugar a cierto tipos de juegos, como en los FPS para apuntar mucho mejor o, simplemente, por cuestiones de gusto a la hora de trabajar en el equipo utilizando el cursor.

Para deshabilitar en Microsoft Windows 7 la aceleración del ratón (si no se dispone del software controlador del mismo como es el caso del GIGABYTE GM-M6880) se puede hacer sin necesidad de instalar ningún programa adicional. Para ello, en Inicio > Panel de control > Mouse se selecciona la pestaña Opciones de puntero. Una vez allí, deshabilitar la casilla Mejorar precisión del puntero para quitar la aceleración o habilitarla en caso de quererla. Por defecto dicha casilla se encuentra marcada.

"Mejorar la precisión del puntero" es un nombre poco descriptivo y ambiguo. Debería llamarse "habilitar aceleración del puntero" para que no exista confusión entre los usuarios.

En caso de duda, si se desea saber si el ratón posee aceleración o no, se puede comprobar fácilmente observando el comportamiento del cursor en pantalla. Situando el cursor a un lado, se mueve el ratón horizontalmente y despacio unos pocos centímetros y se observa dónde quedó el puntero. A continuación se vuelve a situar el cursor en el lado inicial y se repite el mismo movimiento con la misma distancia pero un poco más rápido. Si el puntero recorrió más distancia es que existe aceleración.



1/3/2014

Recuperación de datos tras formateo accidental de tarjeta microSD

Introducción.

Las tarjetas de memoria microSD están diariamente presentes en nuestras vidas debido principalemente al uso extendido de smartphones y tablets. En ellas se albergan sobre todo datos multimedia, los cuales se pierden habitualmente por formateos accidentales (o no). No suelen ser ficheros de elevada relevancia, pero hay que ser conscientes de que en la mayoría de los casos ante un formateo la mayor parte de la información es recuperable.


Advertencias y consideraciones.

Este manual es una mera guía que puede ser utilizada para recuperar información borrada de una tarjeta multimedia (en este caso una microSD). El proceso a seguir no es único y debe adaptarse a cada caso en concreto, por lo que es aconsejable escribir un comentario en caso de duda o desconocimiento. Gustosamente se le ayudará.

Además, el éxito de la recuperación no está garantizado y depende de muchos factores según cada situación. Finalmente indicar que como en cualquier manual, guía o tutorial de este blog, todas las acciones que realice el usuario serán única y exclusivamente bajo su responsabilidad, quedando exhento de la misma el autor.


Material necesario.

Se necesita disponer de un lector multitarjeta en el portátil o sobremesa. En el caso de no tener uno se puede adquirir uno USB externo o uno interno para integrar en la torre de sobremesa. En este último caso, recomiendo adquirir el de COOLBOX CR600 por ser uno de los más completos del mercado (permite leer hasta DNIe y tarjetas SIM, es compatible con GNU/Linux y en breves se publicará su análisis aquí). Eso sí, ocupa una bahía de 3,5", por lo que puede ser necesario según el modelo de torre adquirir un adaptador.

En el caso de necesitar un lector USB se recomienda cualquier marca conocida, como por ejemplo INNOBO. De todas formas, independientemente de que el lector sea interno o externo, no debería sobrepasar el coste de 11€.

Si ya dispone de un lector multitarjeta pero que únicamente lee tarjetas de memoria SD, se puede comprar un adaptador de microSD a SD. Éste adaptador no es más que una falsa tarjeta de plástico con una ranura para insertar la microSD y que pueda ser leída en una ranura SD de tamaño normal y es difícil encontrarlo a la venta separado, ya que habitualmente viene acompañado de una microSD que eleva el precio del conjunto. Por tanto, si no se encuentra por separado, lo mejor es pedirle prestado uno a uno familiar o conocido.

Por último, se necesita un buen programa de recuperación de datos. Un buen programa habitualmente es sinónimo de profesional y no es sencillo de utilizar. Como esta guía está pensada para todos los públicos, se ha elegido EaseUS Data Recovery Wizard como programa de recuperación por su sencillez y facilidad de uso.


Procedimiento.

El primer paso es descargar para Windows (o Mac OS X) el programa. La versión gratuita permite recuperar hasta 2GB de ficheros, lo cual suele ser suficiente. En caso de que no sea así habrá que adquirir la versión de pago o buscar otra alternativa similar. Una vez descargado,ejecutar el instalador para instalar el Free Data Recovery Wizard.

Ahora se procede con la recuperación de datos:
  1. Insertar la tarjeta de memoria en el lector y comprobar que es reconocida por el sistema operativo. No la formatee bajo ningún concepto, ni siquiera si se lo recomienda su SO. Cuantos más formateos tenga más difícil será recuperar la información borrada o perdida accidentalmente. Lo que interesa en este paso es que la tarjeta sea reconocida, no que el SO pueda leerla correctamente o escribir en ella.
  2. Iniciar EaseUS Data RecoveryWizard y seleccionar la opción Complete Recovery ya que es la que permite al programa recuperar información ante un formateo accidental.



  3. Seleccionar los tipos de ficheros que se van a indexar para después recuperar. Es recomendable marcarlos todos... Clicar en Next.



  4. Seleccionar el dispositivo de datos a recuperar. En el ejemplo es la unidad E:


  5. El programa a continuación buscará todos los ficheros que se puedan recuperar.


  6. Una vez indexados todos los ficheros, seleccionar los que interesa recuperar. En el ejemplo, la microSD formateada pertenecía a un usuario de Blackberry que migró a Android y luego formateó sin quererlo, por lo que le interesaban las fotos y la música únicamente. Clic en Recover para comenzar la recuperación.



  7.  A continuación se solicita un directorio para guardar los ficheros. En el ejemplo se utilizó la carpeta RECUPERADOS ubicada en el escritorio. Pulsar en Save.


  8. Finalmente, tras el proceso de recuperación aparece una ventanita con la ruta del directorio sobre el cual se volcaron todos los ficheros recuperados. Haciendo clic sobre el enlace se puede comprobar que todo se ha recuperado correctamente.





28/2/2014

Gestión de la nube: Cloud Management Platforms y ECmanaged

Introducción.

A fecha de hoy, tanto empresas como organizaciones contratan servicios de cloud computing. Esta es una solución que permite ahorrarse inversiones en servidores propios (OPEX en vez de CAPEX), instalaciones para los mismos y mantenimiento, disfrutando de forma inmediata del servicio que se necesite. 

El personal del departamento IT goza de simpatía hacia estos sistemas en un principio por su robustez: La empresa que los ofrece lo hace mediante hardware y otros recursos redundantes, lo que los convierte en sistemas muy robustos. Por tanto, el personal IT de la empresa sólo necesita proveer a su propia organización de conexiones a internet redundantes (normalmente mediante la contratación de una línea secundaria con un ISP distinto al que ya tienen), aparte de otros beneficios secundarios que se obtienen como por ejemplo el pagar en función del uso que se haga de dicho servicio y el evitar un mal aprovechamiento de los recursos.

Desgraciadamente no todo es Jauja, por lo que existen una serie de problemas que forjaron la aparición de los denominados Cloud Management Platforms o CMP, los cuales facilitan enormemente a los profesionales IT las labores de gestión, mantenimiento y monitorización de los sistemas cloud contratados. Una de estas soluciones es ECmanaged, la cual se procederá a probar.


La problemática del cloud.

Las PIMES y start-ups suelen utilizar cloud computing. Ésto les permite crecer minimizando costes e inversión. Por contra, en empresas de tamaño mayor dispondrán de servidores propios en sus instalaciones que hay que gestionar, careciendo en muchos caso de proveedores cloud. Esto es así por el talón de aquiles del concepto de cloud computing: el no disponer de físicamente de los servidores en el CPD.

En caso de fallo, cualquier ingeniero puede acceder físicamente al sistema si el servidor se encuentra en una sede de la empresa, no siendo así en un servicio cloud. En este segundo caso hay que ponerse en contacto con la compañía proveedora, por lo que no se puede solucionar el problema de inmediato.

Partiendo del hecho de no tener los servidores físicamente en el datacenter se derivan otros grandes problemas:
  • Dificultad de gestión: cada servicio cloud ofrece administración poco intuitiva a través de interfaces poco amigables y completamente distintas en función de cada de proveedor y servicio.
  • El rendimiento de cada servicio no es homogéneo y varía en función de cada proveedor.
  • Falta de transparencia al tener sólo acceso a cierta parte del servicio cloud y no a toda la infraestructura del proveedor.
  • Poca capacidad de escalabilidad, ya que depende de la que ofrezca el proveedor en el momento que sea necesaria.
  • Problemas de cobertura en función de la ubicación del proveedor: legislaciones y políticas de protección de datos distintas, mayores latencias accediendo desde otros continentes a dichos servicios y problemas SEO.


La problemática del multicloud.

La problemática del cloud computing reside principalmente en los servicios contratados. Partiendo de la base de que la empresa que contrata servicios en la nube ha hecho los deberes de garantizar el acceso siempre a los mismos a través de conexiones a internet redundantes, la realidad es que no existe un único servicio contratado, teniendo así como objetivo el de paliar en la medida de lo posible los problemas citados en el punto anterior. Es decir; existen habitualmente dos o más servicios contratados a proveedores diferentes o lo que se conoce como multicloud.

La tesitura real en las empresas es la de abarcar distintos servicios cloud, los cuales pueden ser de proveedores distintos.

En el multicloud es importante resaltar el hecho de que no tienen por que ser servicios distintos los contratados. Se puede disponer de dos servicios iguales pero contratados a dos proveedores cloud diferentes para garantizar cierta redundancia y obtener un sistema más robusto frente a fallos. Un buen ejemplo para comprender esta situación es imaginarse una página web que haga uso de una buena BBDD. Para cumplir con la LOPD y con los requisitos de seguridad y disponibilidad se puede contratar un proveedor cloud que sea bueno y fiable, lo que será caro. Para compensar, se pueden contratar dos proveedores web menos fiables pero más baratos y de distintas zonas geográficas, garantizando el disponer de redundancia en caso de fallos y desastres naturales.

Una compañía puede disponer de una web utilizando varios servicios cloud.


Los Cloud Management Platforms.

Como respuesta a los problemas del cloud y del multicloud surgen los sistemas Cloud Management Platforms o CMP. Dichos sistemas homogeinizan las interfaces de los paneles de administración de los distintos proveedores y servicios, de forma que se unifica toda la gestión en un único panel que facilita el mantenimiento de un entorno multicloud.

Los CMP funcionan internamente haciendo uso de las APIs proporcionadas por los servidores cloud para la gestión de los servicios que ofrecen. Por este motivo, cada CMP será compatible con unos u otros proveedores cloud en función de las APIs que utilice.

Los CMP ayudan enormemente a los profesionales IT en su trabajo.

Las funcionalidades habituales que proporcionan los CMP habitualmente son:
  • Encendido, apagado, creación y borrado de servidores cloud.
  • Monitorización de los distintos servicios en tiempo real y de sus recursos (CPU, RAM, disco y red).
  • Despliegue automático de aplicaciones.
  • Automatización de tareas de gestión y mantenimiento, como por ejemplo, actualización de aplicaciones.
  • Autoescalado que lanza servidores cloud adicionales ante picos de demanda, solucionando en parte los problemas de baja escalabilidad que ofrecen muchos proveedores.
Pero... ¿y qué pasa con los servidores físicos de la empresa? Cualquier empresa mediana que disponga de servidores cloud contratados dispone al menos de un servidor propio que sería deseable que también se pudiese gestionar desde el CMP... Pero los CMP no permiten esto. Bueno, uno sí; el de ECmanaged.



El CMP ECmanaged.

El CMP de ECmanaged es el primero en soportar también la gestión de los servidores físicos de la empresa, por lo que no sólo soluciona los problemas habituales del cloud y entornos multicloud, sino que además suple la carencia del resto de CMPs disponibles en el mercado facilitando aún más la difícil labor de gestión al departamento IT. Las funcionalidades que se proporcionan son:
  • Escalar de forma automática una plataforma física con un servicio cloud.
  • Facilitar los procesos de disaster recovery de una plataforma física desplegando una réplica en un servidor cloud.
  • Combinar servidores físicos y cloud. Por ejemplo: El front-end de una web en tres servidores cloud y el back-end tenerlo en los servidores físicos de la empresa.
  • Infraestructura geográficamente distribuida, complementando los servidores físicos disponibles con otros cloud servers en distintas partes del mundo.

ECmanaged utiliza agentes para poder gestionar los servidores que no disponen de su API integrada en la aplicación.

ECmanaged es un CMP SaaS que agiliza y unifica la implementación y la gestión de un entorno mixto de multicloud y de servidores físicos, aumentando enormemente la productividad de los profesionales IT y facilitándoles su trabajo. De este modo, el departamento de informática puede centrarse en las labores que realmente son relevantes para la empresa, evitando perder el tiempo en tareas de poco valor añadido que eran necesarias antes de implantar ECmanaged.

Las APIs que de momento soporta nativamente este CMP son las de Amazon web servicesOpenStack, linode, rackspace, EUCALYPTUS y DigitalOcean. Como se puede observar, son proveedores cloud punteros y el equipo de ECmanaged continúa trabajando para incorporar más APIs de otros proveedores a su CMP.

Otra de las ventajas de ECmanaged es que aporta también funcionalidades avanzadas adicionales frente a CMPs de la competencia:
  • La monitorización no se limita a la que proporciona el proveedor cloud, proporcionando información relativa al rendimiento de las aplicaciones y al estado de los distintos servicios.
  • Posee la capacidad de precisos autoescalados que no sólo tienen en cuenta la capacidad de los servidores sino también el rendimiento de las aplicaciones.
  • Permite automatizar tareas como la actualización del software y el control del ciclo de vida de los equipos. Dichas tareas pueden estar ligadas al resultado de la monitorización o programadas en el tiempo.
  • Puede resolver las incidencias de las aplicaciones de forma automática. La mayoría de ellas se solucionan con un reinicio en tiempos inferiores a los cinco minutos, pudiendo el mecanismo ser personalizado para cada servidor.
Como se puede observar, se trata de una solución muy completa, la cual además ha sido reconocida como la mejor solución del 2013 por el EURO CLOUD SPAIN y por ser, también como empresa, una de las 10 start-ups más interesantes en el 2013 Structure:Europe. A continuación, un vídeo en inglés que explica de forma amena y gráfica la utilidad de este CMP:


Probando ECmanaged.

ECmanaged proporciona una solución web a través de una interfaz muy cuidada. Todos los elementos están bien situados y proporcionan una buena legibilidad al tratar con fondos principalemente de color blanco. A grandes rasgos se posee un menú superior que agrupa las diferentes secciones (inicio, mis plataformas, constructor, mis nubes y la configuración relativa a la cuenta del usuario) que va acompañado de un cuadro de búsqueda, un submenú con las opciones relativas a la sección seleccionada y un área de trabajo bastante grande con concisas descripciones y distintos formatos de ayuda, lo cual se agrede bastante mientras uno se familiariza con la interfaz.


En pocos minutos se puede desplegar un servidor cloud nuevo. Se ha procedido a crear un servidor de ejemplo con el sistema operativo GNU/Linux Fedora.

Se nos presentan múltiples opciones a la hora de configurar nuestra plataforma y de crear servidores. Todo es sencillo y la ayuda presente a lo largo de todo el proceso minimiza bastante la curva de aprendizaje.

Las operaciones realizadas en el back-end se muestran durante el proceso y se detallan en pantalla. Con esto se consigue minimizar la poca transparencia de los procesos y el profesional IT es consciente de lo que el sistema realiza en un momento dado:


Una vez creado el servidor se puede monitorizar o, incluso, asignarle un rol para no tener que configurarlo desde cero. También se puede tener acceso al servidor mediante un consola virtual mediante SSH.

Otra particularidad de ECmanaged es la gestión de plantillas. Una plantilla es una modelización genérica de cada tipo de instancia en la plataforma, compuesta por un sistema operativo, y un software base o rol, como scripts, origenes, o archivos de configuración. Lo bueno es que las plantillas pueden crearse desde cero o bien reutilizar las propias, las que proporciona el equipo de ECmanaged o incluso las de la comunidad.


Otras utilidades no menos interesantes son las relativas a la sección mis nubes, dónde se gestionan todos los servicios contratados con los proveedores cloud y las referentes a nuestra cuenta de usuario, la cual permite desde monetizar todo el consumo y cambiar el plan de contratación con ECmanaged, hasta gestionar el acceso de todos los usuarios del departamento de informática.

El único punto negativo que encontré a este CMP es que no toda la ayuda está traducida al español (por ejemplo, la ayuda para crear una nueva plataforma). De todos modos esto no supone realmente un problema, ya que cualquier profesional IT domina el inglés técnico.

Conclusiones.

Los CMP son realmente útiles y, sin embargo, poco conocidos por parte de los profesionales IT. ECmanaged presenta ante todos estos profesionales una solución eficaz, unificadora y realmente útil y escalable, acompañada toda ella de una generosa documentación. El poder gestionar los servidores propios físicos facilita aún más el mantenimiento, la gestión y la actuación ante desastres.

Los beneficios de adoptar una solución así son muchos: El tiempo y el dinero serán mejor invertidos evitando gastos innecesarios, la disponibilidad de los servicios se verá reforzada, empleados IT con más tiempo a dedicar a tareas de verdadero valor para la empresa que serán menos reacios a trabajar con otros proveedores cloud, mayor poder de integración entre sistemas, monitorización más sencilla, etc...

Lo que se ha visto a lo largo de esta entrada no es más que la punta del iceberg, por lo que las funcionalidades y el potencial ofrecido es mayor del que se resume en este humilde artículo. Por ello lo mejor es informarse en la página www.ecmanaged.com y experimentar con la demo del CMP disponible para todo el que esté interesado. Esta demo se ha utilizado para la redacción de esta entrada y es completamente gratuita, con una duración de 30 días. Dispone de servidores clouds ya configurados para poder trabajar directamente y carece de cualquier limitación. Es decir; la demo es completamente funcional tal y como lo es el producto final de pago.

Las tarifas y planes de contratación mensuales del CMP ECmanaged pueden verse aquí.


Agradecimientos.

En primer lugar agradecer a ECmanaged y a Diana Martínez la propuesta de redactar un artículo relativo a los CMP, dejarme probar su solución y proveerme de material adicional con el que trabajar: todo ello me ha enriquecido profesionalmente.

Así mismo, indicar que salvo las capturas de pantalla de este artículo, los gráficos, diagramas y vídeo presentes son propiedad de ECmanaged.


12/2/2014

Minimizar el uso de memoria RAM en una aplicación desarrollada para Android

Introducción.

La memoria principal (RAM) es un recurso muy preciado en cualquier sistema informático. Como habitualmente se tiene experiencia desarrollando aplicaciones para sistemas PC y similares, éstos perdonan en gran medida a los programadores el mal aprovechamiento de la memoria por disponer de bastante cantidad de ella (además de la generosa partición swap que ofrece el sistema operativo).

Obviamente el problema se acentúa en el desarrollo de dispositivos móviles como es, en este caso, para Android. El desarrollador ingenuo por inexperiencia que trabaje en una app (no de gestión) comenzará a notar como merma el rendimiento, poco a poco, hasta que su aplicación finaliza de forma inesperada por la falta de RAM. También puede darse el caso en el que la aplicación funciona correctamente en una máquina virtual AVD (Android Virtual Device) no siendo así en un dispositivo real conectado al equipo de desarrollo durante el período de depuración.


La gestión de memoria de Android.

Android utiliza la máquina virtual Java conocida como Dalvik y su forma de gestionar la memoria es un tanto peculiar. A grandes rasgos, existen unas diferencias sustanciales, en cuanto a la gestión de memoria en Android con Dalvik respecto a las JVM tradicionales bajo Windows o GNU/Linux. Las diferencias a tener en cuenta a la hora de desarrollar son:
  • Se carece de unidad swap y la memoria virtual sólo sirve para el código de la aplicación. Es decir; los objetos de nuestro programa se encuentran en memoria y no son escritos a la memoria secundaria en ningún momento. Conforme se liberan las referencias de objetos, el recolector de basura (GC) los va destruyendo y la memoria disponible va aumentando.
  • Android comparte la memoria principal entre todos los procesos. El proceso de la app se crea a partir del denominado proceso Zygote una vez que el SO cargó todo el marco de trabajo.
  • Los datos estáticos de la app son mapeados de la memoria a un fichero para ser compartidos con otros procesos o para poder ser recuperados posteriormente. Entre este tipo de datos se encuentran el código Dalvik y el código de la propia aplicación.
  • Android comparte las mismas zonas de memoria entre varios procesos para ciertos menesteres, como por ejemplo, hacer uso de los cursores. Si una app utiliza cursores y hace uso de algún ContentProvider, los cursores estarán en una zona de memoria compartida por el ContentProvider y nuestra app.
  • La pila o heap asociada a cada proceso tiene asignado un tamaño de memoria, el cual puede incrementarse sólo hasta el límite establecido para la aplicación. Este límite varía en función del tamaño de memoria del dispositivo y tiene como principal función garantizar la multi-tarea. Si dicho límite se excede se lanza un OutOfMemoryError.
  • El GC está mejorado para liberar más memoria y hacerlo más rápido a partir de Android 2.3. Si la app no libera los recursos usados el GC no sirve de nada. Suena a perogrullada, pero siempre que se pueda es mejor no reservar memoria que utilizarla (a pesar de que después se libere). Además a mayor memoria usada, más posibilidades hay de que la acción del recolector sobre el proceso de la app se vuelva más agresiva provocando pequeños retardos o lags que perjudiquen la experiencia del usuario.
  • La caché de procesos en ejecución de Android es de tipo LRU por lo que en caso de poca memoria se finalizará con los procesos que menos se estén usando. Aún así, se pueden seleccionar procesos que estén abarcando mucha cantidad de RAM aunque se hayan usado con mayor frecuencia, ya que al eliminar un proceso grande se libera mayor cantidad de memoria. Moraleja: El ahorro de memoria ayuda a mantener nuestros procesos en ejecución.


Optimización del uso de memoria.

Teniendo en mente la optimización durante todo el proceso de desarrollo, las optimizaciones que pueden realizarse a todos los niveles pueden clasificarse en dos tipos: genéricas y específicas. Con este tipo de clasificación no se pretende sentar cátedra, tan sólo ofrecer un mínimo de estructuración para poder discernir cuales de ellas se aplican a cualquier desarrollo (genéricas) y cuales son específicas de un desarrollo para la plataforma Android (específicas).



OPTIMIZACIONES GENÉRICAS.
Aquí se presentan optimizaciones para cualquier desarrollo global, siendo características de las etapas de análisis y diseño principalmente. Las relativas a implementación suelen ser extensibles a la mayoría de lenguajes de programación, concretamente Java. Por norma general estas optimizaciones ahorran memoria pero de forma poco significativa en la mayoría de los casos ya que puede haber excepciones.


Refactorizar el código.

Es difícil escribir a la primera código conciso, de gran calidad y que haga sólo lo que tiene que hacer y nada más. El objetivo es escribir el mínimo código que proporcione la mayor funcionalidad y robustez. Un código muy pequeño y concentrado (típicos idioms de C/C++) van en contra de la legibilidad del programa y dificulta su comprensión por parte de otros programadores. Por tanto se buscará el código mínimo funcional, robusto y de calidad sin deteriorar su comprensión para facilitar el mantenimiento y posibles ampliaciones.


Utilizar bucles for-each siempre que sea posible.
Este tipo de bucle proporciona un mayor rendimiento en recorridos secuenciales y consume menos memoria que un bucle for normal que trabaje explícitamente con una variable a modo de contador. Funciona con vectores y con clases que implementen la interfaz Iterable. Fue incorporado al lenguaje Java a partir de la versión 1.5 y posee la siguiente sintaxis:
for ( Tipo variable : coleciónDeElementosTipo ) {
    // Hacer algo.
}


Utilizar tipos de datos enteros en vez de decimales.
Por muy bueno que sea el hardware hoy en día, no es lo mismo trabajar con números enteros que con decimales. Por motivos de velocidad, y en menor medida de memoria, hay varios escenarios en los que se puede trabajar con tipos básicos int y long en vez de con float y double.

Un típico ejemplo en el que se puede utilizar un float en vez de un long es la clase Monedero. En ella se tiene el atributo privado dinero. Si se declara como long y en ella se almacena el dinero usando como única unidad monetaria los céntimos, no se pierde significado semántico del problema y se puede calcular con suma facilidad la parte equivalente a la entera (euros).


Utilizar vectores en vez de listas.
Siempre que sea factible, utilizar vectores o clases que los implementen (tipo ArrayList) frente a las listas basadas en punteros como LinkedList. Por norma general, también es mejor siempre utilizar tipos de datos básicos antes que clases. Por ejemplo, utilizar el tipo int frente a la clase Integer.


El código y las clases también ocupan.
El código de una clase en Java ocupa aproximadamente y como mínimo, unos 500 bytes. Una instancia de dicha clase ocupa como mínimo, 12 bytes. En memoria va a haber código e instancias, cuanto menos ocupemos de cada una de ellas mejor. ¡Ojo, que aquí no se trata de minimizar el número de clases e instancias a lo bruto! Lo mejor es conseguir el mejor diseño, el más limpio y reutilizable, de forma que nos asegure que no hay instrucciones o funcionalidades de más (el software debe hacer lo que tiene que hacer, ni más, ni menos).


No olvidar marcar las constantes como static final.
Acompañar al modificador final con static ahorra memoria, aunque sólo con los tipos de datos básicos o de tipo String. Esto es así porque el compilador evita que se invoque al método de inicialización la primera vez que se utilice la clase a la que pertenezca la constante, utilizando directamente y en su lugar el valor de la misma.
 

Evitar el uso de objetos innecesarios.
Otro factor a tener en cuenta y que según todos ya lo hacemos. Pero no es cierto, ya que sucede en mayor o menor medida. Por citar algunos ejemplos tenemos:
  • El típico método que devuelve parte de un String mayor y que crea un objeto intermedio temporal a retornar del mismo tipo, cuando directamente se puede devolver una parte como un "substring" ahorrándose la copia intermedia.
  • Utilizar vectores para casi todo: Un vector ocupa menos espacio en memoria que, por ejemplo, un ArrayList. Si se necesita una matriz con dos filas o crear un objeto de tipo clave-valor compensa crear dos vectores que conjuntamente provean la misma funcionalidad.

Usar métodos estáticos.
Si en un método de una clase no se accede a ningún dato miembro es altamente aconsejable acompañarlo del modificador static. Según la documentación oficial de Android se consigue acelerar la invocación a dicho un método entre un 15-20% aparte de considerlo una buena práctica: se está indicando implícitamente de cara al exterior que ese método no altera el estado de dicho objeto.


Abstracciones las justas.
Se considera buena práctica crear clases abstractas para categorizarlo todo. Está muy bien, pero son clases con código que ocuparán espacio en memoria. ¿Realmente son necesarias? Pues depende... Hay que buscar el equilibrio entre un buen diseño sin excederse en categorizar. Por ejemplo, si vuestra aplicación va de animales y se tiene como superclases abstractas Animal, Mamífero, Paquidermo, Reptil, etc... y como hijas de estas las clases Perro, Elefante, Iguana... y la categorización no es un factor relevante para nuestra aplicación (da igual conocer de qué familia son), sería mejor crear una interfaz Animal dónde se especifique el comportamiento que tengan que implementar todos los animales. De esta forma tendríamos una clase en vez de más de cuatro como al principio.


No reservar memoria que después no puedas liberar.
Parece fácil, suena a perogrullada... Pero seguro que en los proyectos habitualmente existen flujos sin cerrar y lecturas a buffers enormes cuyos contenidos en memoria podrían ser más eficientes (leídos del disco en bloques) o pocas invocaciones en los destructores de un programa en C++ que haga uso de memoria dinámica (por citar un ejemplo no Java).


Minimizar el uso de librerías externas.
Si de una librería sólo queremos un par de utilidades, ¿realmente es necesario importarla toda? ¿se pueden importar únicamente aquellos componentes que necesitamos? Si la respuesta a esta última pregunta es no, habrá que barajar la posibilidad de utilizar otra librería más ligera como alternativa. Si dicha alternativa no existe, habrá que implementarla.


Divide y vencerás.
Dividir la aplicación en varios procesos minimiza el uso general de memoria siempre que se haga de forma correcta, cuando se debe, con buena comunicación entre ellos y deteniendo temporalmente (o no) la ejecución de los que proceda. De no ser así se tendrán procesos que, sin apenas utilidad, sólo se ejecutarán para consumir recursos.


Escalar las imágenes.
Una imagen más pequeña siempre va a ocupar memor tamaño que una grande. Calcular el tamaño que se desea mostrar (redimensionando la imagen si procede) para tener en memoria la imagen que menos ocupe.


OPTIMIZACIONES ESPECÍFICAS.
Estas son las optimizaciones exclusivas para un desarrollo Android, estando la mayoría recogidas en su documentación para desarrolladores. También pueden ser generalizadas y aplicadas a otras plataformas de dispositivos móviles. Su impacto en la utilización de RAM suele ser mayor al de las optimizaciones genéricas.


Usar procesos de forma económica.
Al comenzar un proceso background sin interacción con el usuario se reserva memoria para él que ningún otro proceso va a poder usar, reduciéndose la multi-tarea. Es importante que el proceso retarde su ejecución hasta que tenga que realizar su trabajo y finalice lo antes posible. Android proporciona el proceso IntentService, el cual nos facilita cumplir la norma anterior: recibe un Intent, trabaja con él y en cuanto termine su labor, finaliza su ejecución.


Liberar memoria cuando la GUI esté oculta.
Cuando el usuario cambia de una aplicación a otra, la primera pasa a estar "oculta". En ese momento se invoca el método onTrimMemory() de la actividad que se estuvo ejecutando. En este método se pueden liberar los recursos que la actividad estuviera usando, consiguiendo de este modo transiciones más fluidas entre apps. No confundir onTrimMemory() con el método onStop() de Activity, ya que son dos cosas diferentes.

El método onTrimMemory() puede ser utilizado también para conocer el consumo de memoria que realiza el proceso de la aplicación y fue incorporado a partir de la versión 14 de la Android API. Para asegurar la retrocompatibilidad igual hay que trabajar con onLowMemory().


Invocar getters y setters desde fuera de la interfaz.
En Android, invocar a un método virtual (con ligadura dinámica de por medio) es sumamente costoso. Por eso, como buena práctica de la POO se debe acceder desde fuera del objeto a sus propiedades a través de estos métodos, pero por rendimiento y menor consumo de memoria, desde las clases se accede siempre a los atributos directamente, sin invocarlos a través de dichos métodos.


Medir dinámicamente el tamaño del heap.
Android proporciona la posibilidad de que nuestra aplicación pueda conocer el tamaño disponible de la pila asignada a la misma mediante una llamada a getMemoryClass(). Como esta puede variar en función de cada dispositivo y sus especificaciones, se puede conocer en tiempo de ejecución de cuanto espacio libre se dispone y poder actuar en consecuencia antes de conseguir un OutOfMemoryError.


Utilizar contenedores de datos optimizados.
Android proporciona alternativas diversas al HashMap. Una de ellas es SparceArray que proporciona un mayor rendimiento y eficiencia ya que requiere de una menor cantidad de memoria.


Utilizar ProGuard y Zipalign.
La primera utilidad no sólo permite ofuscar el código para evitar la ingeniería inversa: refactoriza clases, atributos, nombres... hasta el punto que elimina código redundante y empequeñece el código resultante, con lo que al ejecutarse el programa va a requerir menor cantidad de memoria principal. Puedes conocer más acerca de ProGuard en Android Developers.

Zipalign se utiliza antes de desplegar nuestra aplicación tras firmarla con nuestro certificado. Actúa sobre el fichero .apk final, alineando sus bits y haciendo que ocupe algo menos. Este paso es obligatorio si quieres subir tu app a Google Play, ya que esta no acepta aplicaciones sin pasar por dicho programa.


Supervisión del uso de memoria de una aplicación.

La optimización del uso de memoria debe formar parte de todo el proceso de desarrollo de la aplicación Android, desde el análisis y diseño hasta las pruebas. Ahora que se cononcen bastantes pautas para optimizar la memoria, se debe proceder a la supervisión de la misma durante el desarrollo para poder ser conscientes de la cantidad de memoria ahorrada tras los cambios efectuados así como para detectar los factores que enfatizan su mayor consumo. Y no sólo eso, ya que todo lo anterior no es garantía de éxito y podemos encontrarnos con bugs que desbordan la memoria.

Existen varios métodos para supervisar la memoria: interpretar los mensajes del log, visualizar las actualizaciones de la pila, realizar un seguimiento de la asignación de memoria y utilizar la herramienta MAT que proporciona el IDE Eclipse.


INTERPRETACIÓN DE LOGS.
Al correr una aplicación en el AVD o en un dispositivo conectado al equipo de desarrollo (con el modo de depuración USB activado), desde Eclipse se nos muestra una pestaña con el LogCat. En dicho log se muestra toda la información relativa que proporciona Android durante la depuración de una app, entre ella qué pasa cuando el recolector de basura entra en acción. Los distintos tipos de mensaje que proporciona el GC son:
  • GC_CONCURRENT: indica que una nueva estancia concurrente del recolector de basura ha iniciado su ejecución para liberar memoria de la aplicación ya que la pila se encuentra casi completa al máximo.
  • GC_FOR_MALLOC/GC_FOR_ALLOC: El recolector se ha lanzado cuando tu aplicación intenta que se le asigne más memoria y ya posee la pila completa. El SO puede detener la aplicación. Existen dos posibles mensajes porque el primero fue renombrado al segundo, según con que versión de la API se trabaje (aunque significan lo mismo).
  • GC_HPROF_DUMP_HEAP: El recolector de basura es lanzado para crear un volcado de la pila a un fichero HPROF, respondiendo así a la petición del desarrollador.
  • GC_EXPLICIT: El GC es invocado mediante una llamada explícita en el código de la aplicación (su uso es poco recomendable).
  • GC_EXTERNAL_ALLOC: El GC es invocado para limpiar una zona externa de memoria a la de la aplicación (está en deshuso y sólo funciona con la API 10 o anteriores).
  • GC_BEFORE_OOM: Lo peor de lo peor sucede al leer esto. El GC es iniciado porque no queda memoria disponible en la pila y al intertar desbordarla la app al asignar más memoria se intenta liberar la máxima posible. Si la memoria liberada es inferior a la nueva que se intenta asignar se produce el fatídico OutOfMemoryError.
Junto con el tipo de mensaje, en la salida del LogCat, se encuentra información adicional como la cantidad de memoria liberada por el recolector de basura, el estado actual de la pila, el estado de la asignación de memoria externa (API 10 o inferior) y el tiempo en que se detuvo la pila antes y después del GC. Por ejemplo, esta es la salida de una aplicación que se está desarrollando:

Captura del LogCat bajo Eclipse en un proyecto real.
Hay tres líneas en este log que son clave:
D/dalvikvm(3346): GC_CONCURRENT freed 6560K, 39% free 29237K/47331K, paused 2ms+29ms
D/dalvikvm(3346): GC_FOR_ALLOC freed 6632K, 40% free 28845K/47331K, paused 99ms
D/dalvikvm(3346): GC_BEFORE_OOM freed 9K, 40% free 28836K/47331K, paused 73ms

Todas tienen el mismo formato. En la primera (no sale en la captura de pantalla) se indica que la máquina virtual Dalvik (la de Android) ha iniciado el recolector para la aplicación ya que la pila de la misma está bastante llena. Se indica que se liberaron 6560K de memoria, que ahora se dispone de un 39% de espacio libre en la pila (29237K es lo que ocupan los objetos y 47331K el tamaño total de la pila) y finalmente los tiempos de espera: 2ms estuvo sin ejecutarse la el proceso de la aplicación esperando que el GC actuase y, tras su limpieza, se tardaron 29ms más en continuar con su ejecución. Las unidades son K, supongo que Kibibytes pero no puedo asegurarlo.

La siguiente ejecución del GC se remite a una de tipo GC_FOR_ALLOC, en la cual la aplicación necesita que se le asigne más memoria pues no llega con la que hay libre en la pila. Se libera una poca pero no es suficiente. Al reclamar la aplicación más memoria con la pila llena, se lanza de nuevo el GC con un tipo de llamada GC_BEFORE_OOM, el cual consigue liberar un 40% de la pila pero no es suficiente: A continuación se produce un OutOfMemoryError, pues la aplicación requería más espacio nuevo del que ha sido liberado.

Tras seguir las trazas de depuración, justo se da con la posible causa del desbordamiento de pila de la app: un ByteArrayOutputStream ocupó excesiva memoria al devolver un vector de bytes.


Llegados a este punto se podría pensar en ampliar explícitamente el tamaño del heap para la aplicación. Aunque es posible, es totalmente desaconsejable hacerlo, ya que el tamaño seguiría realmente variando en función del dispositivo (Android se encarga de hacerlo a partir del tamaño anteriormente preestablecido) y tan sólo se conseguirían más ejecuciones del recolector de basura lo que ralentizaría la ejecución de los procesos asociados y una disminución del rendimiento.

A partir de los mensajes de log es posible deducir hasta qué punto está bien implementada (o no) una aplicación a pesar del hecho de que aparentemente funcione correctamente. Por ejemplo: la mayoría de mensajes pueden ser de tipo GC_CONCURRENT y el heap, por tanto, nunca llega a llenarse. La aplicación finaliza su ejecución correctamente pero ha estado trabajando al límite, pues la gestión que hace del uso de memoria está mal optimizada. Si la aplicación corriera sobre un dispositivo con menos memoria RAM disponible, o en el mismo dispositivo con más procesos en ejecución, seguramente se recibirían mensajes de tipo GC_FOR_ALLOC pudiendo llegar al punto de un GC_BEFORE_OOM o, lo que es peor, a que nuestra aplicación finalice su ejecución por petición del SO. Por tanto, todo se resume en la máxima de "ser eficientes con la memoria maximiza la posibilidad de supervivencia de la ejecución de la aplicación en cualquier entorno multi-tarea Android".

Como consejo adicional, indicar que es sumamente útil crear un filtro en el LogCat para depurar asuntos de memoria relacionados con el recolector de basura. De este modo, seleccionando sólo el filtro tendríamos todas las notificaciones del GC sin tener que buscarlas a mano o con Ctrl+f. Para crear el filtro, en la pestaña LogCat de Eclipse, pulsar sobre el botón cruz verde Add a new logcat filter y rellenar el formulario.

Crear un filtro de LogCat para visualizar cómodamente las salidas del GC agiliza la depuración.


SUPERVISAR LA MEMORIA DESDE DDMS.
La perspectiva DDMS (Dalvik Debug Monitor Server) en Eclipse ofrece dos pestañas de valiosa utilidad: Heap y Allocation Tracker.

En la primera de ellas, se nos muestra en tiempo real el estado de la pila durante la ejecución de la aplicación como cuánta esta libre y ocupada, porcentajes de uso, tipos de elementos en memoria, etc... Incluso se proporciona el botón Cause GC el cual realiza una invocación explícita sobre el recolector de basura, algo similar a invocar la función gc() desde el código.

Para que la monitorización del heap funcione es necesario que en la pestaña Devices se despliegue el menú del dispositivo de prueba escogido, luego seleccionar el proceso a monitorizar y pulsar sobre el botón verde superior Update Heap. Importante resaltar que la información sólo se mostrará en pantalla durante la ejecución del proceso, desapareciendo cuando éste finalice.

Monitorización de la pila de un proceso mediante la perspectiva DDMS en Eclipse.

La segunda pestaña hace referencia al seguimiento de las asignaciones de memoria en la pila. De este modo se facilita la labor de localizar qué objetos son los problemáticos a la hora de ocupar memoria para poder optimizar y actuar en consecuencia. En la pestaña Allocation Tracker > Start Tracking (con el proceso de la aplicación seleccionado en la sección Devices) y pulsar en Get Allocations para conseguir las asignaciones de memoria en un momento dado. Al igual que con la monitorización de la pila, una vez finalice la ejecución del proceso se borrará toda la información de la pantalla.

Monitorización de las asignaciones de memoria mediante DDMS.

En caso de que estas dos monitorizaciones no fuesen suficientes, siempre se puede realizar un volcado de la pila a un fichero HPROF, el cual contendría toda la información de la pila en el momento del volcado así como su contenido desglosado junto a todo tipos de datos para una depuración más exhaustiva. Para visualizar el contenido del fichero HPROF es necesario el uso de Eclipse MAT.


Conclusiones.

La optimización del uso que hace una aplicación de la memoria principal de un sistema puede considerarse una disciplina aparte. Son requisitos indispensables una elevada experiencia y conocimientos a todos los niveles, tanto de APIs, lenguajes de programación, sistema operativos y hardware.

En aplicaciones de escritorio o pequeñas apps de gestión para dispositivos móviles, las optimizaciones pueden no ser necesarias. Aún así, al final todo desarrollador acabará enfrentándose a una optimización seria de memoria antes o después. Para cuando eso suceda, espero que este humilde artículo les sirva de guía y les ayude en la difícil labor de optimización.


Referencias web: