Komprimieren Sie die APK und versuchen Sie, sie am Laufen zu halten


/ PxHere / PD


Die Optimierung des Gewichts der APK ist in den Tagen der Instant App eine nicht triviale, aber sehr relevante Aufgabe. Durch Aktivieren von proguard werden Sie vor unnötigem Code geschützt, wenn Ihre Abhängigkeiten in der Kompilierungsphase ermittelt werden können. Es gibt jedoch mehrere andere Dateitypen in der APK, die von der Assembly ausgeschlossen werden können.


Unter der Anleitung zum Erstellen von Abhängigkeiten - definiert in der Kompilierungsphase, welche Dateien von der Assembly ausgeschlossen werden können und wie dies zu tun ist - werden wir analysieren, wie nicht verwendete Komponenten von der Assembly ausgeschlossen werden, wenn Sie mehrere Anwendungen mit einer gemeinsamen Codebasis haben.


Vor dem Lesen


  • Optimieren Sie das APK für Google - Handbuch, bevor Sie die Tipps aus dem Artikel anwenden. Dieser Artikel richtet sich an Personen, die nicht über genügend Standardoptimierungen verfügen.
  • Mit "proguard" meine ich einen optimierenden Compiler mit Minimierung.
  • Mit Komponente meine ich eine bestimmte Eigenschaft des Produkts aus geschäftlicher Sicht. In unserem Fall ist dies nur eine Sammlung von Dateien in einem bestimmten Paket. Wir haben ein Gradle-Modul für die gesamte Anwendung.

Das Gewicht unserer Google- optimierten APK betrug 4.4 .


Zusätzliche Dateien


Beginnen wir mit einem einfachen. Wenn Sie Kotlin-Reflect nicht verwenden, können Sie Metainformationen zu Kotlin-Klassen aus der Assembly ausschließen. Sie können dies wie folgt tun:
In build.gradle (Module: app)


 android { packagingOptions { exclude("META-INF/*.kotlin_module") exclude("**.kotlin_builtins") exclude("**.kotlin_metadata") } } 

Java Reflection benötigt keine *.kotlin_module , *.kotlin_builtins und *.kotlin_metadata . Es ist sehr einfach festzustellen, welche Reflexion Sie verwenden. Wenn Sie obj::class.<method> schreiben, verwenden Sie die Kotlin-Reflexion, wenn obj::class.java.<method> , dann die Java-Reflexion.


Das Ergebnis der Optimierung für uns: -602,1 kb


Abhängigkeiten


Bibliotheken fügen manchmal Abhängigkeiten für Fälle hinzu, die in Ihrer Anwendung nie vorkommen. Zum Beispiel zieht ktor-client Kotlin-Reflect mit (0,5 MB!).
Ich hatte mit folgenden Fällen zu kämpfen: Ich sammelte die APK mit minifyEnabled = true , warf sie in den Android Studio-Analysator, minifyEnabled = true herunter und suchte nach Paketen, die theoretisch nicht in der Assembly vorhanden sein sollten. Zum Beispiel kotlin.reflect . Nach dem Ausführen der ./gradlew app:dependencies im Projektordner, um nach Abhängigkeiten zu suchen (vergessen Sie nicht, die Länge des Verlaufs im Terminal zu erhöhen. Der Abhängigkeitsbaum kann groß sein!). Aus diesem Baum ist leicht zu verstehen, was sich auf unnötige Abhängigkeiten bezieht, und diese auszuschließen. In build.gradle Ihres Moduls:


 dependencies { implementation("io.ktor:ktor-client-core:$ktorVersion") { exclude(group: "org.jetbrains.kotlin", module: "kotlin-reflect") } implementation("io.ktor:ktor-client-okhttp:$ktorVersion") { exclude(group: "org.jetbrains.kotlin", module: "kotlin-reflect") } } 

Dieser Code entfernt die Abhängigkeit der ktor-client- Bibliothek von kotlin-reflektieren . Wenn Sie etwas anderes ausschließen möchten, ersetzen Sie Ihre Werte.


!!! Verwenden Sie diesen Rat sehr sorgfältig! Stellen Sie vor dem Entfernen von Abhängigkeiten sicher, dass Sie sie nicht benötigen. Wenn Sie dies nicht tun, kann die Anwendung in der Produktion fallen !!!


Das Ergebnis der Optimierung für uns: -500,3 kb


Überprüfen Sie Ihr XML


Leider entfernt proguard keine zusätzlichen XML-Markup-Dateien aus dem Layout-Ordner. Nicht verwendetes XML kann "schwere" Widgets verwenden und Proguard kann sie auch nicht von der Assembly ausschließen! Um dies zu vermeiden, entfernen Sie nicht verwendete Ressourcen mit Refactor -> Remove unused resources...


Überprüfen Sie Ihre di


Wenn Sie wie wir Runtime DI verwenden, prüfen Sie, ob Sie Anbieter für die Abhängigkeiten haben, die Sie nicht verwenden. Proguard kann sie nicht von der Assembly ausschließen, da sie aus Sicht des Compilers nicht unbenutzt sind. Sie verwenden sie beim Erstellen eines Abhängigkeitsdiagramms.


Debug-Abhängigkeiten von Release-Builds ausschließen


Debugging-Tools können unerwartet viel Platz beanspruchen. Zum Beispiel wiegt stetho nach der Komprimierung etwa 0.2 stetho ! In jedem Fall ist es besser, die gesamte Debugging-Infrastruktur vom Release-Build auszuschließen, damit niemand zu viel über Ihre Anwendung erfahren kann, indem Sie sie einfach von Google Play herunterladen.


Sie können verschiedene Versionen derselben Dateien zum Debuggen und Freigeben erstellen. Erstellen Sie dazu im Ordner src neben main die Ordner debug und release . Jetzt können Sie eine initStetho Funktion, die Stetho initialisiert, in die src/debug/java/your/pkg/Stetho.kt und eine initStetho Funktion initStetho , die in der src/debug/java/your/pkg/Stetho.kt src/release/java/your/pkg/Stetho.kt nichts src/release/java/your/pkg/Stetho.kt .


Stellen Sie für alle Fälle sicher, dass diese Abhängigkeit nur in Debug-Builds enthalten ist. Sie können dies tun, indem Sie die implementation durch debugImplementation in build.gradle . Meistens eliminiert proguard unnötige Dateien auch ohne diesen Schritt, aber nicht immer. Die Antwort auf die Frage "Warum?" unten im Text des Artikels .


Plattformen


Manchmal werden auf derselben Codebasis mehrere verschiedene Versionen einer Anwendung ausgegeben. Dies können unterschiedliche Versionen für verschiedene Länder oder Regionen oder, wie in unserem Fall, für verschiedene Kunden sein. Im Folgenden finden Sie Tipps zum Auslagern der Plattform.



/ PxHere / PD


Unsere Erfahrung


Wir entwickeln einen E-SHOP Mobile App Designer. Wir haben mehrere Dutzend Kunden und jeder hat seine eigenen Komponenten. Einige Komponenten werden von allen Kunden verwendet, andere sind nur ein Teil. Unsere Aufgabe ist es, nur die Komponenten in die Kundenbaugruppe aufzunehmen, die er benötigt.


Flaggenausnahme


Für jeden Kunden erstellen wir einen eigenen productFlavor . Dies ist praktisch, da es einfach ist, verschiedene Ressourcen für verschiedene Clients zu erstellen. Die IDE bietet eine grafische Oberfläche zum Wechseln zwischen Geschmacksrichtungen und Caches funktionieren gut. Sie können auch für jeden Client eine eigene BuildConfig.java generieren. Die Feldwerte dieser Klasse sind in der Kompilierungsphase bekannt. Das brauchen wir! Erstellen Sie für jede Komponente ein Feld vom Typ boolean .


 android { productFlavors { client1 { buildConfigField("boolean", "IS_CATALOG_ENABLED", "true") } client2 { buildConfigField("boolean", "IS_CATALOG_ENABLED", "false") } } } 

Dies ist eine vereinfachte Version der Konfiguration. Die Gegenwart ist aufgrund der Integration in unser CI komplex.


Es ist jetzt bekannt, ob die Komponente in der Kompilierungsphase aktiv ist, und Proguard kann sie von der Assembly ausschließen!


Wieder XML


Jetzt bekommt das Problem mit nicht verwendeten XML-Layouts eine neue Dimension! Sie können das Markup einer Komponente nicht einfach übernehmen und entfernen, nur weil einige Kunden es nicht benötigen.


In unserer XML-Anwendung einer der selten verwendeten Komponenten haben wir ein Widget verwendet, das auf die Bilderkennungsbibliothek firebase.ml.vision . Es wiegt ungefähr 0,2 MB, was sehr viel ist. Es wurde beschlossen, dieses Widget mit Code hinzuzufügen, anstatt es im Markup zu deklarieren. Danach konnte proguard die vision für Kunden, die sie nicht benötigen, von der Versammlung ausschließen.


Das Ergebnis der Optimierung für uns: -222,3 kb für die durchschnittliche APK


@Keep


Es gibt zwei Möglichkeiten, proguard mitzuteilen, dass Ihre Klasse nicht minimiert werden kann: Schreiben Sie eine Regel in die Datei proguard-rules.pro oder @Keep Annotation @Keep . In der play-services-vision Bibliothek befindet sich diese Anmerkung in der Stammklasse. Daher hingen 0,2 MB selbst in Clientanwendungen, die keine Bilderkennung benötigen, tot.


Ich habe keinen einfachen und sicheren Weg gefunden, um diese Anmerkung zu entfernen. Wenn Sie wissen wie - schreiben Sie bitte in die Kommentare.


Glücklicherweise verwendet die Bibliothek firebase.ml.vision , eine neuere Version von play-services-vision , diese Anmerkung nicht, und wir haben das Problem gelöst, indem wir sie aufgerufen haben.


Und wieder DI


Zu guter Letzt. DI für nicht angeschlossene Komponenten. Hier ist alles einfach: Für jede Komponente verwenden wir unseren eigenen Container und verbinden die allgemeinen Abhängigkeiten über ein separates Modul.


Das Optimierungsergebnis für uns: -20,1 kb für die durchschnittliche APK


Schlussfolgerungen


  • Das Gewicht der durchschnittlichen APK ist von 4.4 auf 3.1 und das Minimum auf 2.5 gesunken!
  • Der Anwendungscode wurde nicht beschädigt, sondern verbessert. DI ist jetzt einfacher zu bearbeiten

Alle im Artikel vorgestellten Optimierungen sind "niedrig hängende Früchte". Sie sind recht einfach zu implementieren und erhalten schnell das Ergebnis. Bis zu -43% für eine bereits optimierte APK in unserem Fall. Ich hoffe, ich habe Ihre Zeit gespart, indem ich alles an einem Ort aufgelistet habe.


Danke an alle!

Source: https://habr.com/ru/post/de452524/


All Articles