web-dev-qa-db-fra.com

Android - plusieurs versions personnalisées de la même application

Quel est le meilleur moyen de déployer plusieurs versions personnalisées d'une application Android? 

Actuellement, j'ai un script pour échanger le dossier de ressources afin d'obtenir une version personnalisée de mon application. Cela fonctionne très bien, mais toutes les versions personnalisées ont toujours le même nom de package dans le fichier AndroidManifest.xml. Par conséquent, il n'est pas possible d'installer deux versions personnalisées de l'application en même temps.

Ceci est une solution à ce problème, mais cela doit être fait à la main

Pouvez-vous penser à une solution plus simple, ou à la manière dont cela pourrait être intégré à un script?

(d'ailleurs: ce n'est pas pour une application porno/spam/quelle que soit l'application, pas même une application payante)

46
Ulrich Scheller

Le concept "de bibliothèque" intégré d'Android n'était peut-être pas complètement cuit au moment de la publication d'origine, mais il pourrait s'agir de la méthode préférée à partir de 2011. Suivez ces étapes pour créer une fourmi:

En partant d'une application fonctionnelle (appelons-le répertoire "myOrigApp", package com.foo.myapp), ajoutez simplement cette ligne à "default.properties" pour en faire une bibliothèque:

Android.library=true

Créez maintenant une nouvelle application dans un répertoire frère de la façon que vous préférez (appelons-le répertoire "frère", package com.foo.myVariant). En utilisant Intellij Idea, par exemple, créez un projet "à partir de zéro" avec le répertoire "frères" et il créera tous les fichiers/répertoires dont vous auriez normalement besoin.

Dans ce nouveau répertoire frère, éditez "default.properties" pour ajouter la dépendance:

Android.library.reference.1=../myOrigApp

Copiez le manifeste à partir du répertoire d'origine:

cd sibling
cp ../myOrigApp/AndroidManifest.xml  ../myOrigApp/local.properties ../myOrigApp/build.properties  .

Modifiez le fichier Manifest copié pour changer le nom de votre package en votre nouvelle variante, "com.foo.myVarient"; c'est le seul changement.

Si vous exécutez simplement les scripts ant build, vous avez peut-être terminé. (Je devais juste mettre en place des clés de signature.)

Si vous souhaitez configurer un IDE comme Idea pour que le projet de bibliothèque dépende du projet variant, procédez comme suit pour ajouter un projet de bibliothèque au projet variant (en supposant qu'un projet ait déjà été configuré pour les deux projets). ):

  • Ouvrez le projet d'origine, affichez les paramètres du projet, sélectionnez votre facette, cochez l'option "Est un projet de bibliothèque" et enregistrez.
  • Ouvrez le projet variant, ouvrez Paramètres du projet, sélectionnez Modules.
  • Ajouter un module
  • Sélectionnez “Importer le module existant”
  • Accédez au répertoire d'origine (myOrigApp) et sélectionnez son fichier .iml (fichier source du projet IntelliJ).
  • Cliquez sur "Terminer". (Le projet de bibliothèque est ajouté en tant que module dans le projet de variante.)
  • Dans la liste des modules, cliquez sur le projet Variant pour le sélectionner.
  • Sur le côté droit, sélectionnez l'onglet "Dépendances".
  • Cliquez sur "Ajouter…"
  • Choisissez "Dépendance du module…" (une liste contenant le nom du module/bibliothèque que vous avez précédemment ajouté au projet doit apparaître - peut-être la seule entrée de la liste). 
  • Sélectionnez le projet de bibliothèque que vous avez ajouté et appuyez sur OK. (Il sera ajouté à la liste des dépendances de votre projet.)
  • Appuyez sur OK pour terminer la configuration du projet. (Vous devriez voir 2 modules, avec les ressources et les classes de la bibliothèque disponibles et reconnues dans le projet Variant.)
23
larham1

Vous décidément souhaitez utiliser Gradle savors qui est fourni nativement, voire encouragé, sur Android Studio.

Cela semble expliquer très bien toutes les bases. Je viens de terminer la conversion en Gradle aujourd'hui, et cela fonctionne très bien. Icônes d'applications personnalisées, noms et chaînes, etc.

Comme l'explique le site Web, une partie du but de cette conception était de la rendre plus dynamique et de permettre plus facilement la création de plusieurs fichiers APK avec essentiellement le même code, ce qui semble similaire à ce que vous faites.

Je n'ai probablement pas expliqué le mieux possible, mais ce site Web fait un très bon travail.

6
craned

Ce que j'ai fait pour quelque chose de similaire, c'est simplement utiliser une tâche antlib, puis parcourir tous les fichiers Java et XML pour remplacer mon ancienne chaîne de package par la nouvelle chaîne de package. Peu importait que les fichiers ne soient pas dans le bon chemin src selon le paquet. Faire juste un remplacement de regex pour tous les fichiers était suffisant pour que cela fonctionne ...

Par exemple pour le remplacer dans tous vos fichiers Java sous le répertoire src:

 <replaceregexp flags="g" byline="false">
    <regexp pattern="old.package.string" /> 
    <substitution expression="new.package.string" />
    <fileset dir="src" includes="**/*.Java" /> 
 </replaceregexp>
6
Prashast

La solution liée à ne doit pas être faite à la main. N'oubliez pas qu'il n'est pas nécessaire que l'attribut package de l'élément <manifest> se trouve à l'emplacement du code, tant que vous épelez les classes pleinement qualifiées ailleurs dans le manifeste (par exemple, activity Android:name="com.commonsware.Android.MyActivity" plutôt que activity Android:name=".MyActivity"). Écris ton changement de manifeste et utilise Ant pour créer un nouvel APK. Autant que je sache, cela devrait fonctionner.

4
CommonsWare

Soutenir plusieurs partenaires Préparer config.xml

Projet de construction pour partenaire différent 

<!--partner.dir, pkg.name, ver.code, ver.name are input from command line when execute 'ant' -->

<!-- set global properties for this build -->
<property name="build.bin" location="bin"/>
<property name="build.gen" location="gen"/>
<property name="src" location="src"/>
<property name="res" location="res"/>

<target name="preparefiles" description="Prepare files for different partner" >
    <delete dir="${build.bin}" />
    <delete dir="${build.gen}" />

    <copy todir="${res}" overwrite="true" />
        <fileset dir="${partner.dir}/res" /> 
    </copy>

    <!-- change the import in all Java source files -->
    <replaceregexp file="AndroidManifest.xml"
        match='Android.versionCode="(.*)"'
        replace='Android.versionCode="${ver.code}"'
        byline="false">

    <replaceregexp file="AndroidManifest.xml"
        match='Android.versionName="(.*)"'
        replace='Android.versionName="${ver.name}"'
        byline="false">

    <replaceregexp file="AndroidManifest.xml"
        match='package="(.*)"'
        replace='package="${pkg.name}"'
        byline="false">

    <!-- change the package name in AndroidManifest -->
    <replaceregexp flags="g" byline="false">
        <regexp pattern="import(.*)com.myproject.com.R;" /> 
        <substitution expression="import com.${pkg.name}.R;" />
        <fileset dir="${src}" includes="**/*.Java" /> 
    </replaceregexp>

    <replaceregexp flags="g" byline="false">
        <regexp pattern="(package com.myproject.com;)" /> 
        <substitution expression="\1&#10;import com.${pkg.name}.R;" />
        <fileset dir="${src}" includes="**/*.Java" /> 
    </replaceregexp>
</target>

Préparer les fichiers $ Ant -f config.xml -Dpartner.dir = "xxx" -Dpkg.name = "xxx" -Dver.code = "xxx" -Dver.name = "xxx" preparefiles

Créez le fichier build.xml Build $ Ant debug Ou $ Ant.

2
figofuture

Je suis avec le plugin maven-Android pour y parvenir. Spécifiez un fichier AndroidManifest.xml pour l'objectif des sources générées et un autre fichier AndroidManifest.xml pour l'objectif de l'apk final. Ainsi, le projet de code source conserve le nom du package de code source lors de la génération de la classe R et de la phase de construction, tandis que le nom du package de manifeste adapté au marché se trouve dans le deuxième fichier AndroidManifest.xml, inclus dans le fichier apk final.

1
Fredrik Jonson

Je pense que le meilleur moyen est de créer un nouveau projet et de copier le contenu. steps, - créer un nouveau projet Android sans classe - créer un package (le nom du package doit correspondre à celui du fichier manifeste), ou simplement copier le nom du package dans le dossier 'gen' - copier les fichiers Java - copier les dossiers pouvant être dessinés - copier les fichiers de mise en page - copier tout autre fichier utilisé dans votre projet - copier les données du fichier manifeste

cela a été plus simple pour moi pour la tâche

0
M. Usman Khan

Je me suis retrouvé avec un script qui corrige les sources; corriger la source semble risqué, mais en présence de contrôle de version, le risque est acceptable.

J'ai donc créé une version, validé la source, l'autre version, commis la source, et en regardant diffs, j'ai écrit un script de correction en Python.

Je ne suis pas sûr que ce soit la meilleure solution. (Et le code manque quelques os.path.joins)

Le coeur du script est la fonction suivante:

# In the file 'fname',
# find the text matching "before oldtext after" (all occurrences) and
# replace 'oldtext' with 'newtext' (all occurrences).
# If 'mandatory' is true, raise an exception if no replacements were made.
def fileReplace(fname,before,newtext,after,mandatory=True):
    with open(fname, 'r+') as f:
    read_data = f.read()
    pattern = r"("+re.escape(before)+r")\w+("+re.escape(after)+r")"
    replacement = r"\1"+newtext+r"\2"
    new_data,replacements_made = re.subn(pattern,replacement,read_data,flags=re.MULTILINE)
    if replacements_made and really:
        f.seek(0)
        f.truncate()
        f.write(new_data)
        if verbose:
            print "patching ",fname," (",replacements_made," occurrence", "s" if 1!=replacements_made else "",")"
    Elif replacements_made:
        print fname,":"
        print new_data
    Elif mandatory:
        raise Exception("cannot patch the file: "+fname)

Et vous pouvez trouver le suivant d'utilisation:

# Change the application resource package name everywhere in the src/ tree.
# Yes, this changes the Java files. We hope that if something goes wrong,
# the version control will save us.
def patchResourcePackageNameInSrc(pname):
    for root, dirs, files in os.walk('src'):
    if '.svn' in dirs:
        dirs.remove('.svn')
    for fname in files:
        fileReplace(os.path.join(root,fname),"import com.xyz.",pname,".R;",mandatory=False)

Il existe également une fonction qui copie les ressources de x-assets-cfgname dans assets (il m’était plus tôt qu’il était plus pratique d’avoir un sous-répertoire dans assets).

def copyAssets(vname,force=False):
    assets_source = "x-assets-"+vname+"/xxx"
    assets_target = "assets/xxx"
    if not os.path.exists(assets_source):
        raise Exception("Invalid variant name: "+vname+" (the assets directory "+assets_source+" does not exist)")
    if os.path.exists(assets_target+"/.svn"):
        raise Exception("The assets directory must not be under version control! "+assets_target+"/.svn exists!")
    if os.path.exists(assets_target):
        shutil.rmtree(assets_target)
    shutil.copytree(assets_source, assets_target, ignore=shutil.ignore_patterns('.svn'))

Eh bien, vous avez l'idée. Maintenant, vous pouvez écrire votre propre script.

0