web-dev-qa-db-de.com

Android - mehrere benutzerdefinierte Versionen derselben App

Was ist der beste Weg, um mehrere angepasste Versionen einer Android-Anwendung bereitzustellen? 

Derzeit habe ich ein Skript, um den Ressourcenordner auszutauschen, um eine angepasste Version meiner App zu erhalten. Es funktioniert großartig, aber alle benutzerdefinierten Versionen haben in AndroidManifest.xml immer noch den gleichen Paketnamen. Daher ist es nicht möglich, zwei angepasste Versionen der App gleichzeitig zu installieren.

Dies ist eine Lösung für dieses Problem, aber das muss von Hand gemacht werden

Fällt Ihnen eine einfachere Lösung ein oder wie könnte diese in ein Skript integriert werden?

(Übrigens: Es ist nicht für eine Porno/Spam/welche App auch nicht bezahlt, sondern auch für eine bezahlte).

46
Ulrich Scheller

Das integrierte Android-Bibliothekskonzept wurde zum Zeitpunkt des ursprünglichen Beitrags möglicherweise nicht vollständig gebacken, es könnte jedoch die bevorzugte Methode ab 2011 sein. Befolgen Sie diese Schritte für einen Ant-Build:

Wenn Sie von einer funktionierenden App aus starten (nennen Sie es das Verzeichnis "myOrigApp", Paket com.foo.myapp), fügen Sie diese Zeile einfach zu "default.properties" hinzu, um daraus eine Bibliothek zu erstellen:

Android.library=true

Erstellen Sie nun in beliebiger Weise eine neue App in einem Geschwister-Verzeichnis (nennen wir das Verzeichnis "Geschwister", Paket com.foo.myVariant). Erstellen Sie zum Beispiel mit Intellij Idea ein Projekt "von Grund auf" mit dem Verzeichnis "Geschwister", und es werden alle Dateien/Verzeichnisse erstellt, die Sie normalerweise benötigen.

Bearbeiten Sie in diesem neuen, gleichgeordneten Verzeichnis "default.properties", um die Abhängigkeit hinzuzufügen:

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

Kopieren Sie das Manifest aus dem ursprünglichen Verzeichnis:

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

Bearbeiten Sie die kopierte Manifest-Datei, um den Paketnamen in Ihre neue Variante zu ändern: "com.foo.myVarient"; das ist die einzige Änderung.

Wenn Sie nur die Ant-Build-Skripts ausführen, sind Sie möglicherweise fertig. (Ich musste nur Signaturschlüssel einrichten.)

Wenn Sie ein IDE like Idea einrichten möchten, um das Bibliotheksprojekt als von dem Variantenprojekt abhängig zu definieren, führen Sie die folgenden Schritte aus, um dem Variantenprojekt ein Bibliotheksprojekt hinzuzufügen (vorausgesetzt, Sie haben bereits ein Projekt eingerichtet.) für beide):

  • Öffnen Sie das Originalprojekt, rufen Sie die Projekteinstellungen auf, wählen Sie Ihre Facette aus und markieren Sie "Ist Bibliotheksprojekt", und speichern Sie.
  • Öffnen Sie das Variantenprojekt, rufen Sie die Projekteinstellungen auf, und wählen Sie Module aus
  • Fügen Sie ein Modul hinzu
  • Wählen Sie "Vorhandenes Modul importieren" aus.
  • Navigieren Sie zum Originalverzeichnis (myOrigApp) und wählen Sie die .iml-Datei (IntelliJ-Projektquelldatei) aus.
  • Klicken Sie auf "Fertig stellen". (Das Bibliotheksprojekt wird als Modul innerhalb des Variantenprojekts hinzugefügt.)
  • Klicken Sie in der Modulliste auf das Variant-Projekt, um es auszuwählen.
  • Wählen Sie auf der rechten Seite die Registerkarte "Abhängigkeiten" aus.
  • Klicken Sie auf "Hinzufügen".
  • Wählen Sie "Modulabhängigkeit ..." (Es sollte eine Liste angezeigt werden, die den Namen des Moduls/der Bibliothek enthält, das Sie zuvor zum Projekt hinzugefügt haben - möglicherweise der einzige Eintrag in der Liste). 
  • Wählen Sie das Bibliotheksprojekt aus, das Sie hinzugefügt haben, und drücken Sie OK. (Es wird der Liste der Abhängigkeiten Ihres Projekts hinzugefügt.)
  • Klicken Sie auf OK, um die Konfiguration des Projekts abzuschließen. (Sie sollten 2 Module anzeigen, wobei die Ressourcen und Klassen der Bibliothek verfügbar sind und im Variant-Projekt erkannt werden.)
23
larham1

Sie auf jeden Fall möchten verwendenGradle- - das kommt nativ, sogar auf Android Studio ermutigt.

Es scheint alle Grundlagen wirklich gut zu erklären. Ich habe gerade die Konvertierung in Gradlebeendet, und es funktioniert großartig. Benutzerdefinierte App-Symbole, Namen und Zeichenfolgen usw.

Wie die Website erklärt, bestand ein Teil des Zweckes hinter diesem Design darin, es dynamischer zu gestalten und das Erstellen mehrerer APKs mit im Wesentlichen demselben Code zu ermöglichen, was ähnlich klingt, wie Sie es tun.

Ich habe es wahrscheinlich nicht am besten erklärt, aber diese Website macht einen guten Job.

6
craned

Was ich für etwas Ähnliches getan habe, ist, einfach eine antlib-Aufgabe zu verwenden und dann alle Java- und XML-Dateien durchzugehen, um meine alte Paketzeichenfolge durch die neue Paketzeichenfolge zu ersetzen. Es spielte keine Rolle, ob die Dateien nicht entsprechend den Paketen in den korrekten SRC-Pfaden waren. Nur einen Regex für alle Dateien zu ersetzen, reichte mir aus, um dies zum Laufen zu bringen ...

Um es beispielsweise in allen Ihren Java-Dateien im Verzeichnis src zu ersetzen:

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

Die verlinkte Lösung muss nicht von Hand gemacht werden. Beachten Sie, dass das package-Attribut im <manifest>-Element nicht an dem Ort sein muss, an dem sich der Code befindet, solange Sie die vollqualifizierten Klassen an anderer Stelle im Manifest ausschreiben (z. B. activity Android:name="com.commonsware.Android.MyActivity" und nicht activity Android:name=".MyActivity"). Skriptieren Sie Ihre Manifeständerung und verwenden Sie Ant, um eine neue APK zu erstellen. AFAIK, das sollte funktionieren.

4
CommonsWare

Mehrere Partner unterstützen Config.xml vorbereiten

Projekt für verschiedene Partner erstellen 

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

Dateien vorbereiten $ Ant -f config.xml -Dpartner.dir = "xxx" -Dpkg.name = "xxx" -Dver.code = "xxx" -Dver.name = "xxx "Prepefiles

Erstellen Sie build.xml Build $ Ant-Debug Oder $ Ant-Release

2
figofuture

Ich bin benutze das Maven-Android-Plugin , um dies zu erreichen. Geben Sie eine AndroidManifest.xml für das generierte Quellziel und eine weitere AndroidManifest.xml für das endgültige APK-Ziel an. Auf diese Weise behält das Quellcode-Projekt den tatsächlichen Quellcode-Paketnamen während der Generierung der R-Klasse und der Build-Phase bei, während der marktangepasste Manifest-Paketname in der zweiten AndroidManifest.xml-Datei enthalten ist, die in der endgültigen apk-Datei enthalten ist.

1
Fredrik Jonson

Ich denke, der beste Weg ist, ein neues Projekt zu erstellen und das Zeug zu kopieren. Schritte, - Neues Android-Projekt ohne Klasse erstellen - Paket erstellen (Paketname sollte dem in der Manifestdatei entsprechen) oder einfach den Paketnamen in den Ordner 'gen' kopieren - Kopieren Sie die Java-Dateien - Kopieren Sie die Zeichenordner - Kopieren Sie die Layoutdateien - Kopieren Sie alle anderen in Ihrem Projekt verwendeten Dateien. .] - Daten der Manifestdatei kopieren

das war für mich für die aufgabe einfacher

0
M. Usman Khan

Ich endete mit einem Skript, das die Quellen korrigiert. Das Patchen der Quelle klingt riskant, aber bei Versionskontrolle ist das Risiko akzeptabel.

Also habe ich eine Version erstellt, die Quelle festgeschrieben, die andere Version erstellt, die Quelle festgeschrieben und die Unterschiede herausgesehen, schrieb ein Patching-Skript in Python.

Ich bin mir nicht sicher, ob es die beste Lösung ist. (Und der Code vermisst einige os.path.joins)

Das Kernstück des Skripts ist folgende Funktion:

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

Und Sie können die folgende Verwendung finden:

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

Es gibt auch eine Funktion, die Assets von x-assets-cfgname nach assets kopiert (früher stellte sich heraus, dass es für mich praktischer ist, ein Unterverzeichnis in assets zu haben).

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

Nun, du verstehst die Idee. Jetzt können Sie Ihr eigenes Skript schreiben.