Práctica recomendada: Ampliación o anulación de una clase de proyecto de biblioteca de Android

Estamos utilizando un proyecto de biblioteca de Android para compartir clases y recursos básicos en diferentes compilaciones (objetivos) de nuestra aplicación de Android. Los proyectos de Android para cada objetivo específico hacen referencia al proyecto de biblioteca principal (detrás de escenas, Eclipse crea y hace referencia a un tarro del proyecto de biblioteca referenciado).

La sustitución de recursos como imágenes y diseños XML es fácil. Los archivos de recursos colocados en el proyecto de destino, como el icono de la aplicación o un diseño XML, anulan automáticamente los recursos de la biblioteca principal con el mismo nombre cuando se crea la aplicación. Sin embargo, a veces una clase debe ser anulada para habilitar el comportamiento específico de destino. Por ejemplo, la pantalla de preferencias de destino de Amazon no puede contener un enlace a la página de la aplicación Google Play, que requiere un cambio en las preferencias del proyecto Amazon.xml y las preferencias de la clase de actividad.

El objetivo es reducir la cantidad de código duplicado entre los proyectos de destino mientras se elimina tanto código específico de destino de la biblioteca central como sea posible. Hemos ideado un par de enfoques para implementar la lógica específica de los diferentes objetivos:

  1. Escriba las funciones específicas de destino en las clases de la biblioteca principal y use los bloques if / switch para seleccionar el comportamiento basado en SKU del producto. Este enfoque no es muy modular y hincha la base de código de la biblioteca Core.
  2. Amplíe la clase Core específica en un proyecto de destino y anule las funciones de clase base (Core) según sea necesario. A continuación, mantenga una referencia al objeto de clase base en la biblioteca de núcleo e instántela con un objeto de clase extendido (de cómo reemplazar una clase dentro de un proyecto de biblioteca de Android? )

¿Existen otras estrategias para anular o ampliar una clase de proyecto de biblioteca de Android? ¿Cuáles son algunas de las mejores prácticas para compartir y extender clases comunes entre los objetivos de aplicaciones de Android?

El proyecto de biblioteca se hace referencia como una dependencia de proyecto sin procesar (mecanismo basado en la fuente), no como una dependencia de jar compilada (mecanismo de biblioteca basado en código compilado).

@yorkw esto no es cierto para las versiones más recientes de ADT Plugin for Eclipse http://developer.android.com/sdk/eclipse-adt.html

Desde la versión 17 Registro de cambios

Funciones de nueva construcción Función añadida para configurar automáticamente las dependencias de JAR. Todos los archivos .jar de la carpeta / libs se agregan a la configuración de compilación (similar a cómo funciona el sistema de construcción Ant). Además, los archivos .jar que necesitan los proyectos de biblioteca también se agregan automáticamente a proyectos que dependen de esos proyectos de biblioteca. (más información)

Más información http://tools.android.com/recent/dealingwithdependenciesinandroidprojects

Antes de eso, la sobrescritura de la actualización del proyecto Actividad de la Biblioteca era fácil, solo excluye la clase. Ahora la biblioteca se incluye como archivo jar, y no hay manera de excluir el archivo de clase de la dependencia de jar.

EDITAR:

Mi solución para sobreescargar / extender Actividad de biblioteca jar:

He creado una clase de utilidad simple:

public class ActivityUtil { private static Class getActivityClass(Class clazz) { // Check for extended activity String extClassName = clazz.getName() + "Extended"; try { Class extClass = Class.forName(extClassName); return extClass; } catch (ClassNotFoundException e) { e.printStackTrace(); // Extended class is not found return base return clazz; } } public static Intent createIntent(Context context, Class clazz) { Class activityClass = getActivityClass(clazz); return new Intent(context, activityClass); } } 

Para sobrescribir la clase "SampleActivity" de una biblioteca, es un proyecto que depende de esa biblioteca, cree una nueva clase con el nombre SampleActivityExtended en el proyecto en el mismo paquete y agregue la nueva actividad a su AndroidManifest.xml.

IMPORTANTE: todas las intenciones que hacen referencia a actividades sobrescritas deben crearse a través de la clase util de la siguiente manera:

 Intent intent = ActivityUtil.createIntent(MainActivity.this, SampleActivity.class); ... startActivity(intent); 

Detrás de las escenas, Eclipse crea y hace referencia a un tarro del proyecto de biblioteca referenciado.

Esto no es muy preciso. El proyecto de biblioteca se hace referencia como una dependencia de proyecto sin procesar (mecanismo basado en la fuente), no como una dependencia de jar compilada (mecanismo de biblioteca basado en código compilado). Actualmente, SDK de Android no admite la exportación de un proyecto de biblioteca a un archivo JAR autónomo. El proyecto de biblioteca siempre debe ser compilado / construido indirectamente, haciendo referencia a la biblioteca en la aplicación dependiente y construyendo esa aplicación. Cuando el proyecto dependiente de la construcción, el origen compilado y los recursos en bruto que necesitan ser filtrados / fusionados desde el proyecto de biblioteca se copian y se incluyen correctamente en el archivo apk final. Tenga en cuenta que el equipo de Android había comenzado a renovar todo el diseño del proyecto de biblioteca (muévalo del mecanismo basado en ource al mecanismo de biblioteca basado en código compilado) desde r14, como se mencionó en esta entrada anterior del blog .

¿Cuáles son algunas de las mejores prácticas para compartir y extender clases comunes entre los objetivos de aplicaciones de Android?

La solución dada por Android es Library Project .
La solución dada por Java es Herencia y Polimorfismo .
Vengan juntos, la mejor práctica IMO es la segunda opción que usted mencionó en la pregunta:

2.Extender la clase principal en un proyecto de destino y anular las funciones de clase base (núcleo) según sea necesario. A continuación, mantenga una referencia al objeto de clase base en la biblioteca Core e instántela con un objeto de clase extendido (desde el proyecto de biblioteca de Android – ¿Cómo sobreescribir una clase?)

Desde mi experiencia personal, siempre uso Android Library Project (A veces con Regular Java Project, para implementar / construir common-lib.jar que solo contiene POJO) administrar código común, por ejemplo SuperActivity o SuperService, y extiende / implementa clases / interfaces apropiadas En el proyecto dependiente de Polimorfismo.

Solución basada en la solución de PoisoneR y la solución de Turbo.

 public static Class<?> getExtendedClass(Context context, String clsName) { // Check for extended activity String pkgName = context.getPackageName(); Logger.log("pkgName", pkgName); String extClassName = pkgName + "." + clsName + "Extended"; Logger.log("extClassName", extClassName); try { Class<?> extClass = Class.forName(extClassName); return extClass; } catch (ClassNotFoundException e) { e.printStackTrace(); // Extended class is not found return base return null; } } 

Los beneficios de esto es que

  1. La clase extendida puede estar en el paquete del proyecto, no en el paquete de la biblioteca. Gracias a Turbo por esta parte.

  2. Al tomar un String como argumento en lugar de un objeto Class , este método puede utilizarse incluso con ProGuard. getName() es donde está el problema con ProGuard, ya que devolverá algo como "a" en lugar del nombre de la clase original. Así que en la solución original en lugar de buscar ClassExtended buscará aExtended en su lugar, algo que no existe.

¿Qué sobre el uso de un acercamiento de la callback aquí? (Bueno, la devolución de llamada es un poco engañosa, pero actualmente no tengo otra palabra para ello:

Usted podría declarar una interfaz en cada actividad que debe / puede ser ampliada por el usuario. Esta interfaz tendrá métodos como List<Preference> getPreferences(Activity activity) (pase los parámetros que necesites aquí, usaría una Activity o al menos un Context para ser futuro).

Este enfoque podría darle lo que quiera cuando lo haya entendido correctamente. Aunque no he hecho esto antes y no sé cómo otras personas manejar esto me gustaría darle una oportunidad y ver si funciona.

¿Podría, por favor, aclarar qué es diferente en Kindle y Android normal? Creo que son iguales. Lo que necesita son diferentes recursos para el Kindle y otros dispositivos. Luego use el recurso apropiado. Por ejemplo, uso 2 enlaces para almacenar:

 <string name="appStore">&lt;a href=http://market.android.com/details?id=com.puzzle.jigsaw>Android Market&lt;/a> or &lt;a href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw>Amazon Appstore&lt;/a> &lt;br>http://market.android.com/details?id=com.puzzle.jigsaw &lt;br>href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw</string> <string name="appStore_amazon">&lt;a href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw>Amazon Appstore&lt;/a> &lt;br>href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw</string> 

Y el appStore del uso para todos ningunos producto de Amazone y appStore_amazon para el Kindle.

Cómo determinar dónde estás en tiempo de ejecución – que sería otra pregunta que se respondió aquí muchas veces.

Me inspiré en la respuesta de PoinsoneR para crear una clase Utility para hacer lo mismo con Fragments – anular un fragmento en una biblioteca android. Los pasos son similares a su respuesta así que no entraré en gran detalle, pero aquí está la clase:

 package com.mysweetapp.utilities; import android.support.v4.app.Fragment; public class FragmentUtilities { private static Class getFragmentClass(Class clazz) { // Check for extended fragment String extClassName = clazz.getName() + "Extended"; try { Class extClass = Class.forName(extClassName); return extClass; } catch (ClassNotFoundException e) { e.printStackTrace(); // Extended class is not found return base return clazz; } } public static Fragment getFragment(Class clazz) { Class fragmentClass = getFragmentClass(clazz); Fragment toRet = null; try { toRet = (Fragment)fragmentClass.newInstance(); return toRet; } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } return toRet; } } 

Uso:

 FragmentUtilities.getFragment(MySpecialFragment.class) 

También puede utilizar una fábrica de actividades si necesita proporcionar actividades extendidas para diferentes variantes de construcción y hacer que su biblioteca trabaje solo con la fábrica abstracta. Esto se puede configurar en el archivo de aplicación de las variantes de construcción.

  • El temporizador Android llega tarde cuando la pantalla del teléfono está bloqueada
  • Android: ¿qué elegir para los valores de la petición?
  • ScheduledExecuterService.scheduleAtFixedRate crea varios grupos de hilos - Android
  • Pestañas en la parte superior e inferior de la pantalla
  • ¿Cómo usar una clase de contrato en android?
  • No se puede acceder a Internet a través de WiFi desde un servicio de fondo
  • IR transmite clase ConsumerIrManager después de Android 4.4 no funciona
  • Obtener longitud desconocidaHttpInputStream al obtener InputStream de HttpURLConnection en android
  • ¿Cómo detener pero no congelar la ejecución de Android?
  • Reemplazar cadenas en una cadena larga:
  • ¿Hay un equivalente de MethodHandle en Android?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.