Pregunta Práctica recomendada: ampliar o anular una clase de proyecto de biblioteca de Android


Estamos usando un Proyecto de biblioteca de Android para compartir clases principales y recursos en diferentes compilaciones (objetivos) de nuestra aplicación Android. Los proyectos de Android para cada objetivo específico hacer referencia al proyecto de la biblioteca central (detrás de escena, Eclipse crea y hace referencia a un jar del proyecto de biblioteca referenciado).

Sobrescribir 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 central 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 del objetivo. Por ejemplo, la pantalla de preferencias de Amazon no puede contener un enlace a la página de la aplicación Google Play, lo que requiere un cambio en las preferencias del proyecto Amazon.xml y la clase de actividad de preferencias.

El objetivo es reducir la cantidad de código duplicado entre los proyectos de destino al mismo tiempo que se elimina tanto código específico de destino de la biblioteca Core como sea posible. Hemos llegado a un par de enfoques para implementar lógica específica para diferentes objetivos:

  1. Escriba las funciones específicas del objetivo dentro de las clases de la biblioteca Core y use if / switch blocks para seleccionar el comportamiento basado en el SKU del producto. Este enfoque no es muy modular e hincha la base de código de la biblioteca central.
  2. Extienda la clase Core particular en un proyecto objetivo y anule las funciones de clase base (Core) según sea necesario. Luego mantenga una referencia al objeto de clase base en la biblioteca Core y ejemplínelo con un objeto de clase extendido (desde ¿Cómo anular 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?


32
2018-03-30 23:32


origen


Respuestas:


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

@yorkw esto no es cierto para las últimas versiones de ADT Plugin para Eclipse http://developer.android.com/sdk/eclipse-adt.html

De la versión 17 Registro de cambios

Nuevas funciones de compilación   Se agregó una característica para configurar automáticamente las dependencias de JAR. Cualquier archivo .jar en la carpeta / libs se agrega a la configuración de compilación (similar a cómo funciona el sistema Ant build). Además, los archivos .jar necesarios para los proyectos de la biblioteca también se agregan automáticamente a los proyectos que dependen de esos proyectos de la 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 Activity from Library era fácil, solo excluía la clase. Ahora la biblioteca se incluye como archivo jar y no hay forma de excluir el archivo de clase de la dependencia jar.

EDITAR:

Mi solución para sobrescribir / ampliar la actividad desde el contenedor de la biblioteca:

Creé una clase simple de utilidades:

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 la 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 las 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);

17
2018-04-12 10:01



detrás de escena, Eclipse crea y hace referencia a un jar del proyecto de biblioteca referenciado.

Esto no es del todo exacto. El proyecto de biblioteca se referencia como una dependencia de proyecto sin procesar (mecanismo basado en fuente), no como una dependencia de jar compilada (mecanismo de biblioteca basado en código compilado). Actualmente, Android SDK no admite exportar un proyecto de biblioteca a un archivo JAR independiente. El proyecto de la biblioteca siempre debe ser compilado / construido indirectamente, haciendo referencia a la biblioteca en la aplicación dependiente y creando esa aplicación. Cuando el proyecto dependiente de la compilación, la fuente compilada y los recursos sin procesar que necesitan filtrarse / fusionarse del proyecto de la Biblioteca se copian y se incluyen correctamente en el archivo apk final. Tenga en cuenta que el equipo de Android había comenzado a modernizar todo el diseño del Proyecto de la Biblioteca (moverlo del mecanismo basado en Microsoft a un mecanismo de biblioteca basado en código compilado) desde r14, como se menciona en esta publicación 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 Proyecto de biblioteca.
La solución dada por Java es Herencia y Polimorfismo.
Juntos, la mejor práctica de la OMI es la segunda opción que mencionaste en la pregunta:

2. Extienda la clase Core particular en un proyecto objetivo y anule las funciones de clase base (Core) según sea necesario. Luego, mantenga una referencia al objeto de la clase base en la biblioteca Core y ejemplínelo con un objeto de clase extendido (desde el proyecto de la biblioteca Android: ¿cómo sobrescribir una clase?)

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


4
2018-04-03 04:37



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. Tomando un String como un argumento en lugar de una Class objeto, este método se puede usar incluso con ProGuard. getName() es donde está el problema con ProGuard, ya que devolverá algo así como "a" en lugar del nombre de la clase original. Entonces, en la solución original, en lugar de buscar ClassExtended, buscará un Extendido en su lugar, algo que no existe.


2
2018-01-08 03:38



¿Qué tal usar una callbackacercarse aquí? (De acuerdo, la devolución de llamada es un poco engañosa, pero actualmente no tengo otra palabra para eso:

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

Este enfoque podría darle lo que desea cuando lo he entendido correctamente. Si bien no he hecho esto antes y no sé cómo otras personas manejan esto, lo probaría y vería si funciona.


1
2018-04-02 06:16



¿Podrías, por favor, aclarar qué es diferente en Kindle y en Android normal? Creo que son los mismos. Lo que necesita son diferentes recursos para Kindle y otros dispositivos. Luego usa 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 use appStore para todos los productos Amazone y appStore_amazon para Kindle.

Cómo determinar dónde se encuentra en tiempo de ejecución: esa sería otra pregunta que fue respondida aquí muchas veces.


1
2018-04-08 21:04



Me inspiré en la respuesta de PoinsoneR para crear una clase de utilidad para hacer lo mismo para Fragmentos: anular un fragmento en una biblioteca de 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)

1
2018-03-04 21:48



También puede usar una fábrica de actividades si necesita proporcionar actividades extendidas para diferentes variantes de compilación y hacer que su biblioteca se ocupe solo de la fábrica abstracta. Esto se puede establecer en su archivo de aplicación de variantes de compilación.


1
2017-11-12 12:18