2009-08-03 10 views
45

¿Cuál es la mejor manera de implementar varias versiones personalizadas de una aplicación de Android?Android: varias versiones personalizadas de la misma aplicación

Actualmente tengo un script para intercambiar la carpeta de recursos para obtener una versión personalizada de mi aplicación. Funciona muy bien, pero todas las versiones personalizadas todavía tienen el mismo nombre de paquete en AndroidManifest.xml. Por lo tanto, no es posible instalar dos versiones personalizadas de la aplicación al mismo tiempo.

This is one solution for this problem, but that has to be done by hand

¿Puede pensar en una solución más fácil, o cómo esto podría ser incorporado en un skript?

(por cierto: no se trata de una película porno/spam/lo que sea la aplicación, ni siquiera una paga uno)

+0

He respondido una pregunta similar aquí, quizás también pueda ayudarlo: http://stackoverflow.com/questions/16840127/how-to-install-the-same-app-twice-without-interference/21332803 # 21332803 –

+2

La respuesta aceptada es un poco anticuada. Hay uno publicado por craned que probablemente sea la forma correcta de hacerlo. – Darthg8r

+0

Usted ** definitivamente ** quiere usar ['Gradle' sabores] (http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Product-flavors) que viene de forma nativa, incluso se recomienda en Android Studio. – craned

Respuesta

23

Tal vez la incorporada en el Android concepto de "biblioteca" se cuece no totalmente en el momento de la publicación original, pero puede ser el método preferido a partir del 2011. Siga estos pasos para una compilación de ant:

Comenzando desde una aplicación en funcionamiento (llamémoslo directorio "myOrigApp", paquete com.foo.myapp), simplemente agregue esta línea a "default".propiedades" para que sea una biblioteca:

android.library=true 

A continuación, cree una nueva aplicación en el directorio hermano en la forma que prefiera (vamos a llamarlo directorio 'hermano', com.foo.myVariant paquete) El uso de IntelliJ IDEA, por ejemplo, cree un proyecto 'desde cero' con el directorio 'hermano' y creará todos los archivos/directorios que normalmente necesitaría.

En ese nuevo directorio hermano edite "default.properties" para agregar la dependencia:

android.library.reference.1=../myOrigApp 

Copie el Manifiesto del original dir:

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

Editar que copiar el archivo de manifiesto para cambiar el nombre del paquete a su nueva variante, "com.foo.myVarient"; ese es el único cambio.

Si acaba de ejecutar los scripts de compilación de ant, es posible que haya terminado. (Solo tuve que configurar las claves de firma.)

Si desea configurar un IDE como Idea para tener el proyecto de biblioteca como dependiente de la variante del proyecto, siga estos pasos para agregar un proyecto de biblioteca al proyecto de variante (asume que ya tiene un proyecto establecido para ambos):

  • Abrir el proyecto original, que aparezca Configuración del proyecto, seleccionar su faceta y marcar "es el proyecto de biblioteca" y guardar.
  • abra el proyecto de variante, hacer que aparezca Configuración del proyecto, seleccione Módulos
  • Añadir un módulo
  • Seleccione “Importar módulo existente”
  • Vaya al directorio original (myOrigApp) y seleccione el archivo de .iml (proyecto de IntelliJ archivo fuente)
  • Haga clic en "Finalizar". (El proyecto de la biblioteca se agrega como un módulo dentro del proyecto de variante.)
  • En la lista de módulos, haga clic sobre el proyecto Variant para seleccionarlo.
  • En el lado derecho, seleccione la pestaña "Dependencias".
  • Haga clic en "Agregar ..."
  • Elija "Módulo de dependencia ..." (Debe aparecer una lista que incluya el nombre del módulo/biblioteca que agregó previamente al proyecto, quizás la única entrada en la lista).
  • Seleccione el proyecto de biblioteca que agregó y presione OK. (Se agregará a la lista de dependencias de su proyecto.)
  • Presione OK para finalizar la configuración del proyecto. (Debería ver 2 módulos, con los recursos y las clases de la biblioteca disponibles y reconocidos en el proyecto Variant.)
+0

crédito a http://soledadpenades.com/2011/06/17/android-library-projects-with-intellij-idea/ para los pasos de la Idea – larham1

+0

Creo que esta solución no existía en ese momento, pero es Definitivamente mejor que ir a través de archivos de origen con expresiones regulares. He estado usando la solución de Prashast antes y funcionó bien. Pero para proyectos nuevos, estoy a favor del proyecto de la biblioteca. –

+0

Me gusta más esta solución que alterar docenas de archivos fuente (¡una pesadilla de control de fuente!). –

4

El ligada a la solución no tiene que ser hecho a mano. Tenga en cuenta que el atributo package en el elemento <manifest> no tiene que estar donde reside el código, siempre que especifique las clases completas en el manifiesto (por ejemplo, activity android:name="com.commonsware.android.MyActivity" en lugar de activity android:name=".MyActivity"). Guia el cambio de manifiesto y utiliza Ant para compilar una nueva APK. AFAIK, eso debería funcionar.

+7

Esto destruye el archivo R autogenerado. Por lo tanto, debe pasar más o menos por todos sus archivos fuente y cambiar la importación del archivo R (punto 8). –

+0

Ah. Buen punto. Aún scriptable, solo más doloroso. – CommonsWare

+0

Hemos resuelto el problema de R. import old.package.string. *; import new.package.string. *; Esto solo importa R para ambas compilaciones. – Fedor

6

Lo que hice por algo similar a esto es simplemente usar una tarea antlib y luego revisar todos los archivos java y xml para reemplazar mi cadena de paquete anterior por la nueva cadena de paquete. No importaba si los archivos no estaban en las rutas src correctas de acuerdo con el paquete. Sólo haciendo una expresión regular reemplazar todos los archivos era suficiente para mí para conseguir este trabajo ...

Por ejemplo, para sustituirlo en todos los archivos Java en el directorio src:

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

Esto resolvió mi problema, gracias. Ahora estoy usando dos replaceregexp. Una es exactamente como la tuya y otra para reemplazar el nombre del paquete en AndroidManifest.xml. –

+0

No funciona para mí, porque todos los archivos fuente todavía están en una estructura de directorio que coincide con el nombre del paquete anterior. Como solucionaste esto? – Matthias

+0

Para mí, la ubicación del archivo fuente no importaba. Siempre que tengan la información correcta del paquete. Sin embargo, abrirlo en Eclipse puede no funcionar. Lo había usado solo con la construcción Ant y el uso del archivo apk generado. – Prashast

1

estoy using the maven-android-plugin lograr esto. Especifique un AndroidManifest.xml para el objetivo de orígenes generados y otro AndroidManifest.xml para el objetivo final del apk. De esta forma, el proyecto del código fuente conserva el nombre real del paquete del código fuente durante la generación de la clase R y la fase de compilación, mientras que el nombre del paquete manifiesto del mercado se encuentra en el segundo AndroidManifest.xml que se incluye en el archivo apk final.

2

soporte a múltiples socios Preparar config.xml proyecto de construcción

para diferentes socios

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

preparar archivos $ ant -f config.xml -Dpartner.dir = "xxx "-Dpkg.name =" xxx "-Dver.code =" xxx "-Dver.name =" xxx "preparefiles

Crear build.xml Construir $ depuración hormiga o liberación de hormigas $

+0

esto es bueno si solo trabajas con hormigas. si trabaja en eclipse, después de ejecutar la expresión regular puede funcionar, pero si cambia un recurso, se volverá a estancar. – codeScriber

+0

¡El archivo tenía algunos problemas menores pero en general una buena idea! Copié esto y lo modifiqué un tanto para usarlo en un proyecto de cliente. ¡Gracias! – BrianPlummer

0

Terminé con un script que corrige las fuentes; parchar la fuente suena arriesgado, pero en presencia de control de versión el riesgo es aceptable.

Así que hice una versión, comprometí la fuente, hice la otra versión, comprometí la fuente, y mirando diffs escribí una secuencia de comandos en Python.

No estoy seguro de si es la mejor solución. (Y el código pierde algo de os.path.uniones)

El corazón de la secuencia de comandos es la siguiente función:

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

y usted puede encontrar el siguiente de uso:

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

También hay una función que copia los activos de x-assets-cfgname a assets (anteriormente resultó que para mí es más conveniente tener un subdirectorio en 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')) 

Bueno, ya te has planteado la idea. Ahora puedes escribir tu propia secuencia de comandos.

0

Creo que la mejor manera es crear un nuevo proyecto y copiar las cosas. pasos, - cree un nuevo proyecto de Android sin una clase - cree el paquete (el nombre del paquete debe corresponder al del archivo de manifiesto) o simplemente copie el nombre del paquete en la carpeta 'gen' - copie los archivos java - copiar las carpetas dibujable - copiar los archivos de diseño - copiar cualquier otro archivo (s) que se utiliza en ur proyecto - copiar los datos del archivo de manifiesto

esto ha sido más fácil para mí para la tarea

5

Usted definitivamente quiero usar Gradle flavors que viene de forma nativa, incluso animado, en A ndroid Studio.

Parece que explica todos los conceptos básicos muy bien. Acabo de terminar de convertirme al Gradle hoy, y funciona muy bien. Iconos de aplicaciones personalizadas, nombres y cadenas, etc.

Como explica el sitio web, parte de la finalidad de este diseño era hacerlo más dinámico y permitir que se crearan múltiples APK con el mismo código, lo que suena similar a lo que estás haciendo.

Probablemente no lo haya explicado mejor, pero ese sitio web hace un buen trabajo.

+0

Este es el camino a seguir ahora. +1 –

Cuestiones relacionadas