Meilleure pratique: étendre ou remplacer une classe de projet de bibliothèque Android

Nous utilisons un projet de bibliothèque Android pour partager des classes de base et des ressources à travers différentes versions (cibles) de notre application Android. Les projets Android pour chaque cible spécifique font référence au projet de bibliothèque de base (dans les coulisses, Eclipse crée et référence un fichier jar à partir du projet de bibliothèque référencé).

Remplacer les ressources telles que les images et les mises en page XML est facile. Fichiers de ressources placés dans le projet cible, tels que l'icône de l'application ou une mise en page XML, remplacez automatiquement les ressources de la bibliothèque principale avec le même nom lorsque l'application est construite. Cependant, une classe doit parfois être remplacée pour activer le comportement spécifique à la cible. Par exemple, L'écran Préférences cible Amazon ne peut pas contenir de lien vers la page de L'application Google Play, ce qui nécessite une modification des préférences du projet Amazon.classe d'activité xml et préférences.

L'objectif est de réduire la quantité de code dupliqué parmi les projets cibles tout en supprimant autant de cibles spécifiques code de la bibliothèque de base que possible. Nous avons mis au point quelques approches pour implémenter une logique spécifique à différentes cibles:

  1. écrivez les fonctions spécifiques à la cible dans les classes de bibliothèque de base et utilisez les blocs if/switch pour sélectionner le comportement en fonction de L'UGS du produit. Cette approche n'est pas très modulaire et gonfle la base de code de la bibliothèque principale.
  2. étendez la classe de base particulière dans un projet cible et remplacez les fonctions de classe de base (Core) si nécessaire. Ensuite, Gardez une référence à l'objet de classe de base dans la bibliothèque de base et l'instancier avec un objet de classe étendue (à partir de Comment remplacer une classe dans un projet de bibliothèque Android?)

Existe-t-il d'autres stratégies pour remplacer ou étendre une classe de projet de bibliothèque Android? Quelles sont les meilleures pratiques pour partager et étendre les classes communes entre les cibles D'applications Android?

32
demandé sur Community 2012-03-31 03:32:48

7 réponses

Le projet de Bibliothèque est référencé comme une dépendance de projet brute (mécanisme basé sur les sources), pas comme une dépendance jar compilée (mécanisme de bibliothèque basé sur le code compilé).

@yorkw ce N'est pas vrai pour les dernières versions du Plugin ADT pour Eclipse http://developer.android.com/sdk/eclipse-adt.html

À partir de la version 17 journal des modifications

Nouvelles fonctionnalités de construction Ajout d'une fonctionnalité pour configurer automatiquement les dépendances JAR. Tout .fichiers jar dans les / libs dossier sont ajoutés à la configuration de construction (similaire à la façon dont le système de construction Ant fonctionne). Également, .les fichiers jar nécessaires aux projets de bibliothèque sont également automatiquement ajoutés aux projets qui dépendent de ces projets de bibliothèque. (plus d'infos)

Plus d'infos http://tools.android.com/recent/dealingwithdependenciesinandroidprojects

Avant cela, mettre à jour l'écrasement de l'activité du projet de bibliothèque était facile, il suffit d'exclure la classe. Maintenant, la Bibliothèque est incluse en tant que fichier jar, et il n'y a aucun moyen d'exclure le fichier de classe de la dépendance jar.

Modifier:

Ma solution pour surcharger / étendre L'activité de la bibliothèque jar:

J'ai créé une classe util 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);
}
}

Afin d'écraser la classe "SampleActivity" d'une bibliothèque dans le projet qui dépend de cette bibliothèque, créez une nouvelle classe avec le nom SampleActivityExtended dans le projet dans le même paquet et ajoutez la nouvelle activité à votre AndroidManifest.XML.

IMPORTANT: Toutes les intentions le référencement des activités écrasées doit être créé via la classe util de la manière suivante:

Intent intent = ActivityUtil.createIntent(MainActivity.this, SampleActivity.class);
...
startActivity(intent);
17
répondu Borislav Gizdov 2014-06-06 18:55:52

Dans les coulisses, Eclipse crée et référence un fichier jar à partir du projet de bibliothèque référencé.

Ce n'est pas tout à fait exact. Le projet de Bibliothèque est référencé en tant que dépendance de projet brut (mécanisme basé sur les sources), pas en tant que dépendance jar compilée (mécanisme de bibliothèque basé sur le code compilé). Actuellement, Android SDK ne prend pas en charge l'exportation d'un projet de bibliothèque vers un fichier JAR autonome. Le projet de bibliothèque doit toujours être compilé / construit indirectement, en référençant la bibliothèque dans l'application dépendante et la construction de cette application. Lorsque le projet dépendant de la construction, la source compilée et les ressources brutes qui doivent être filtrées / fusionnées à partir du projet de bibliothèque sont copiées et correctement incluses dans le fichier Apk final. Notez que L'équipe Android avait commencé à réorganiser l'ensemble de la conception du projet de bibliothèque (déplacez-le du mécanisme basé sur ource au mécanisme de bibliothèque basé sur le code compilé) depuis r14, comme mentionné dans ce billet de blog précédent .

Quels sont certains des meilleurs pratiques pour le partage et l'extension des classes communes entre les cibles D'applications Android?

La solution donnée par Android est projet de bibliothèque .
La solution donnée par Java est Héritage et Polymorphisme.
Venez ensemble, la meilleure pratique IMO est la deuxième option que vous avez mentionnée dans la question:

2.Étendez la classe de base particulière dans un projet cible et remplacez les fonctions de classe de base (Core) si nécessaire. Ensuite, Gardez une référence à la objet de classe de base dans la bibliothèque de base et l'instancier avec un objet de classe étendue (à partir du projet de bibliothèque Android-Comment écraser une classe?)

D'après mon expérience personnelle, j'utilise toujours le projet de bibliothèque Android (parfois avec un projet Java régulier, pour implémenter / construire common-lib.jar qui ne contient que POJO) gère le code commun, par exemple la Superactivité ou le SuperService,et étend / implémente les classes/interfaces appropriées dans le projet dépendant pour le polymorphisme.

4
répondu yorkw 2012-04-05 11:08:35

Solution basée sur la solution de L'empoisonneur et la solution 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;
    }
}

Les avantages de ceci est que

  1. La classe étendue peut être dans le package du projet, pas dans le package de la bibliothèque. Merci à Turbo pour cette partie.

  2. En prenant un String comme argument au lieu d'un objet Class, cette méthode peut être utilisée même avec ProGuard. getName() est l'endroit où le problème est avec ProGuard, car cela retournera quelque chose comme "a" au lieu du nom de L'original classe. Donc, dans la solution d'origine au lieu de chercher ClassExtended, il cherchera aextended à la place, quelque chose qui n'existe pas.

2
répondu IvonLiu 2014-01-08 04:01:07

Qu'en est-il de l'utilisation d'une approche callback ici? (D'accord, le rappel est un peu trompeur mais je n'ai actuellement aucun autre mot pour cela:

Vous pouvez déclarer une interface dans chaque activité qui devrait / peut être étendue par l'utilisateur. Cette interface aura des méthodes comme List<Preference> getPreferences(Activity activity) (Passez tous les paramètres dont vous avez besoin ici, j'utiliserais un Activity ou au moins un Context pour être à l'épreuve du futur).

Cette approche pourrait vous donner ce que vous voulez quand je l'ai bien compris. Alors que je n'ai pas fait cela avant et je ne sais pas comment les autres personnes gèrent cela, je voudrais essayer et voir si cela fonctionne.

1
répondu Luminger 2012-04-02 06:16:21

Pourriez-vous, s'il vous plait, clarifier ce qui est différent dans Kindle et Android régulier? Je pense qu'ils sont les mêmes. Ce dont vous avez besoin est différentes ressources pour Kindle et d'autres appareils. Ensuite, utilisez la ressource appropriée. Pour exemple, j'utilise 2 liens pour stocker:

<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>

Et utiliser appStore pour tous les Aucun produit Amazone et appStore_amazon pour Kindle.

Comment déterminer où Êtes - vous en cours d'exécution-ce serait une autre question qui a été répondue ici plusieurs fois.

1
répondu Alex 2012-04-08 21:04:14

J'ai été inspiré par la réponse de PoinsoneR pour créer une classe utilitaire pour faire la même chose pour les Fragments - remplacer un fragment dans une bibliothèque android. Les étapes sont similaires à sa réponse, donc je n'entrerai pas dans les détails, mais voici la classe:

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;
    }
}

Utilisation:

FragmentUtilities.getFragment(MySpecialFragment.class)
1
répondu DiscDev 2013-03-04 21:48:08

Vous pouvez également utiliser une fabrique D'activités si vous devez fournir des activitys étendus pour des variantes de construction différentes et que votre bibliothèque gère seule la fabrique abstraite. Cela peut être défini dans votre fichier d'Application build variants.

1
répondu Dori 2013-11-12 12:18:23