Workaround für doppelte Klassen in Gradle Aromen und Main

Das Problem:

Ich stelle mir vor, dass mein Problem ziemlich häufig ist. Ich habe eine ziemlich große Gradle-Code-Basis, aus der ich maßgeschneiderte Versionen mit Produkt-Aromen produzieren. Diese Produkt-Aromen erfordern oft eine benutzerdefinierte Version von einer oder mehreren Klassen von src\main\java .

Ich habe die gradle dokumentation gelesen und habe auch auf die folgenden fragen gestoßen:
Verwenden von Build-Aromen – Strukturieren von Quellordnern und build.gradle korrekt
Erstellen Sie Aromen für verschiedene Version der gleichen Klasse

  • Wie man mehrere Array-Werte in einer Custom-Adapter-Klasse für die benutzerdefinierte Listenansicht übergibt?
  • Wie man die Größe der Registerkarten in TabLayout erhöht
  • Wie man Dolch in einem Android-Bibliotheksprojekt verwendet
  • Kann nicht auf SD-Karte schreiben - canWrite gibt false zurück
  • Wie man Schwerkraftfahnen in einer benutzerdefinierten Android-Ansicht überprüft?
  • Android: Entfernen Sie alle vorherigen Aktivitäten aus dem Back Stack
  • Ich verstehe, warum Sie nicht die gleiche Klasse in src\main\java und auch in Ihren Aromen definieren können, aber die Lösung der Verschiebung der Klasse von src\main\java in Ihr Produkt Geschmack hat einen ziemlich großen Nachteil. Wenn du eine Klasse von src\main\java in deinen letzten Geschmack ziehst, um sie anzupassen, musst du auch eine Kopie der ursprünglichen, nicht benutzten Version dieser Klasse in jeden anderen vorherigen Produktgeschmack verschieben oder sie werden nicht mehr bauen.

    Sie müssen nur einmal ein oder zwei verschiedene Klassen von den Originalen zwicken (und dann müssen diese Klassen zu den Geschmacksverzeichnissen neu zu verteilen), aber im Laufe der Zeit wird die Anzahl der bearbeiteten Klassen gebaut und die Nummer bleibt in src\main\java wird jedes Mal abnehmen, wenn du das tun musst. Irgendwann werden die meisten Klassen in den Aromen sein (obwohl die Mehrheit Kopien der Originale sein wird) und src\main\java wird fast leer sein, Art der Besiegung der Zweck der gesamten Gradle Build-Struktur.
    Darüber hinaus müssen Sie einen "Standard" Geschmack, dass Sie klonen können jedes Mal, wenn Sie einen neuen Geschmack zu starten, so dass Sie wissen, dass Sie beginnen mit allen Klassen wie pro Ihre ursprüngliche Code-Basis.

    Meine anfängliche Umgehung:

    Verwenden Sie Felder in der BuildConfig, um festzulegen, ob benutzerdefinierte Klasse verwendet werden soll oder nicht:

     buildConfigField 'boolean', 'CUSTOM_ACTIVITY_X', 'true' 

    Sie können dann Code wie verwenden:

     final Intent intent = new Intent(); ... if (BuildConfig.CUSTOM_ACTIVITY_X) { intent.setClass(ThisActivity.this, CustomActivityX.class); } else { intent.setClass(ThisActivity.this, DefaultActivityX.class); } startActivity(intent); 

    Jeder Geschmack braucht noch eine Kopie CustomActivityX, aber es kann nur ein Dummy leere Klasse in Aromen, wo Sie wissen, dass es nicht verwendet werden. Dies bedeutet, dass Ihre Standardversionen der Klassen immer in src\main\java beibehalten werden.

    Eine verbesserte Umgehung:

    Beim Versuch, die Notwendigkeit für einen Dummy CustomActivityX in jedem anderen Geschmack loszuwerden, habe ich bei der Verwendung von Class.forName() .
    Beispielsweise:

     final Class activityX; if (BuildConfig.CUSTOM_ACTIVITY_X) { try { activityX = Class.forName("CustomActivityX"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } else { activityX = DefaultActivityX.class; } final Intent intent = new Intent(); ... intent.setClass(ThisActivity.this, activityX); startActivity(intent); 

    Allerdings ist dies offensichtlich in "activityX möglicherweise nicht initialisiert worden", wenn Sie versuchen, es zu benutzen, wegen der try/catch Block.

    Wie kann man das überwinden?

  • Wie bekomme ich Android Device Status (Offline oder Online) mit GCM?
  • Android SDK Manager Extras
  • Gedächtnis für "kurze" Felder in Dalvik?
  • Android-Kompass-Rauschalgorithmus
  • Emulator für Galaxie s3
  • Kann keine Debug- und Release-Version auf demselben Gerät installieren
  • 3 Solutions collect form web for “Workaround für doppelte Klassen in Gradle Aromen und Main”

    Also gibt es zwei Fragen hier 1) die Codierung Bug in Ihrem wichtigsten Workaround 2) das breitere Problem, das Sie versuchen zu lösen.

    Ich kann mehr mit der ersten Ausgabe helfen als die zweite. Alles was Sie tun müssen, ist die Initialisierung Ihrer Variablen. Sie fragten: "Wie kann man das überwinden?" Ich glaube, das wird der Trick tun:

     Class activityX = null; //remove 'final' and initialize this to null, then null-check it later if (BuildConfig.CUSTOM_ACTIVITY_X) { try { activityX = Class.forName("CustomActivityX"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } else { activityX = DefaultActivityX.class; } final Intent intent = new Intent(); ... if(activityX != null) { intent.setClass(ThisActivity.this, activityX); startActivity(intent); } 

    Nun, in Bezug auf die breitere Frage, die Sie lösen, ist es ziemlich klar, dass der Code oben hat einige Code-Gerüche, die signalisieren, dass es vielleicht einen besseren Weg. Ich habe Aromaspezifische Klassen verwendet, ohne sie auf alle anderen Aromen kopieren zu müssen. In diesen Fällen haben andere Aromen keinen Code ausgeführt, der sich auf diese Klassen stützte. Zum Beispiel stellen Sie sich eine "bezahlte" Version vor, in der die "freie" Version einfach niemals einige der Klassen für bezahlte Benutzer lädt.

    Also ich denke, das Problem entsteht nur, wenn alle Aromen versuchen, die Klasse in Frage zu laden. Es ist schwer, eine Alternative vorzuschlagen, ohne Ihre gesamte Codebasis zu verstehen. Allerdings würde ich vorschlagen, dass Sie versuchen, Vererbung, abstrakte Klassen oder Schnittstellen, um Ihr Problem zu lösen.

    Das erste, was ich untersuchen würde: Ist die Aktivität wirklich die kleinste Einheit von Code / Verhalten, die Sie überschreiben müssen? Ich vermute, es ist nicht Vielleicht kannst du eine BaseActivity erstellen, die alle Boilerplate-Code hat und dann den geschmacksspezifischen Code in die exakten Komponenten, die es erfordern, isoliert.

    Zum Beispiel ist ein häufiges Muster, das ich verwendet habe, um diese Art von Ding über XML-Dateien zu behandeln. Auf diese Weise können Aktivitäten und Fragmente immer das gleiche Verhalten über Aromen haben, aber sie laden unterschiedliche Layouts. Diese Layouts enthalten benutzerdefinierte View-Komponenten (die sich von einer gemeinsamen Schnittstelle oder einer abstrakten übergeordneten Klasse erstrecken, die die Aktivität erlaubt, diese Schnittstelle zu codieren). Jetzt werden Aromen, die keine gewissen Klassen brauchen, niemals versuchen, sie zu laden, weil sie nicht in dem Layout existieren, das von diesem bestimmten Geschmack geladen wird.

    Wie auch immer, ich hoffe das hilft in irgendeiner Weise. Es ist sehr schwierig, Ihr breiteres Problem anzugehen, ohne die Nuancen Ihrer Codebasis zu verstehen. Mein Rat wäre, die Dinge grundsätzlich zu überdenken und zu versuchen, den Klassenlader davon abzuhalten, jemals Ihre geschmacksspezifischen Klassen zu laden.

    Ich glaube, dass die Erweiterung Ihrer Aktivitäten (und andere Klassen) eine sauberere Lösung ist.

    Zum Beispiel, wenn Sie eine HelpActivity im Hauptgeschmack haben, machen Sie das eine abstrakte Klasse, und im Geschmack erstellen Sie eine FlavorHelpActivity, die HelpActivity erweitert. Hier rufst du super an und füge all die Dinge hinzu, die für diesen Geschmack einzigartig sind.

    Sie müssen das Manifest in jedem Geschmack aktualisieren, um auf den korrekten Geschmacksnamen der Aktivität (FlavorHelpActivity) zu verweisen, und auch Ihre Menüpunkte müssen korrekt angeben, also müssen Sie in den erweiterten Aktivitäten das onOptionsItemSelected überschreiben.

    Ich werde diese Lösung ausprobieren, also vielleicht später kann ich sagen, ob es Nachteile gibt.

    – Aktualisierung –

    Ich habe meinen vorgeschlagenen Ansatz versucht und es ist nicht ideal:

    • Programmierung in der Tätigkeit, die andere Tätigkeit verlängert, ist ein bisschen komisch. Die Hälfte Ihrer Logik ist nicht in der Klasse, die du siehst.
    • Sie haben ganz etwas Overhead auf immer alles richtig in der Manifest und wie die Aktivitäten sind verbunden mit Intents.

    Es ist eine gute Art zu arbeiten, aber ich werde wahrscheinlich wieder auf die Situation zurückgehen, wie von der Person beschrieben, die die Frage gestellt hat.

    Keine Notwendigkeit zu harten Code Aktivitätsnamen.

    Fügen Sie Absichtsfilter für die jeweiligen Aktivitäten hinzu, die nach Geschmack geladen werden sollen.

    Geschmack A: ActivityA.java

    Geschmack B: ActivityB.java

    Fall: Main (Common): BaseActivity mit Schaltfläche. Auf Knopf klicken für Geschmack A sollte zu ActivityA navigieren und für Geschmack B sollte zu ActivityB navigieren.

    Manifest für Geschmack A:

     <activity android:name="com.abc.ActivityA" android:screenOrientation="portrait" android:theme="@style/AppTheme.NoActionBar"> <intent-filter> <action android:name="com.abc.openDetailsActivity" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> 

    Manifest für Aroma B:

     <activity android:name="com.abc.ActivityB" android:screenOrientation="portrait" android:theme="@style/AppTheme.NoActionBar"> <intent-filter> <action android:name="com.abc.openDetailsActivity" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> 

    Beide sollten die gleiche Handlung im Manifest haben.

    Jetzt von BaseActivity Aufruf:

     Intent i = new Intent(); i.setAction("com.abc.openDetailsActivity"); startActivity(i); 

    Wenn Build-Variante A ist, wird ActivityA von Flavour A öffnen und wenn Build-Variante B ist, wird ActivityB von Flavour B öffnen

    Das Android ist ein Google Android Fan-Website, Alles ├╝ber Android Phones, Android Wear, Android Dev und Android Spiele Apps und so weiter.