We included this AndroidPdfViewer library to support viewing of PDF reports in the app. It lead to massive increase in APK size from 4.7Mb to 20.1Mb .
Is there a way to reduce this size. Let me know where and what to tinker around to help or solve this.
I am familiar with proguard and have it configure for my app with reasonable success.
Why resulting apk is so big?
As stated in the documentation by barteksc/AndroidPdfViewer
Android PdfViewer depends on PdfiumAndroid, which is set of native
libraries (almost 16 MB) for many architectures. Apk must contain all
this libraries to run on every device available on market.
Fortunately, Google Play allows us to upload multiple apks, e.g. one
per every architecture. There is good article on automatically
splitting your application into multiple apks, available here.
Most important section is Improving multiple APKs creation and
versionCode handling with APK Splits, but whole article is worth
reading. You only need to do this in your application, no need for
forking PdfiumAndroid or so.
API: https://github.com/barteksc/AndroidPdfViewer
You have to generate Multiple APKs for different devices, by doing this you can reduce the size of apk up to 10 MB less than previous. Add below code to build.gradle (Module:App)
android{
.....
splits {
abi {
enable true
reset()
include 'x86_64', 'x86', 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'mips'
universalApk false
}
}
.....
}
ext.versionCodes = ['armeabi': 1, 'armeabi-v7a': 2, 'arm64-v8a': 3, mips: 4, 'x86': 5, 'x86_64': 6]
import com.android.build.OutputFile
// For each APK output variant, override versionCode with a combination of
// ABI APK value * 1000 + defaultConfig.versionCode
android.applicationVariants.all { variant ->
// assign different version code for each output
variant.outputs.each { output ->
output.versionCodeOverride =
project.ext.versionCodes.get(output.getFilter(OutputFile.ABI)) * 1000 + android.defaultConfig.versionCode
}
}
Whatch video here https://youtu.be/bP05Vpp49hs for detail explanation on how to do it.
If anyone is looking for a lightweight pdf viewer for your app, then try this one
Use this library
It helped me to reduce the app size from 27MB to 10MB because of the barteksc-androidpdfviewer requires more space
I also faced the same problem, but this is very easy as of now to reduce the apk sizes by using android app bundles.
Firstly in build.gradle(Module: app) add these lines inside android { } brackets.
bundle {
abi {
enableSplit = true
}
}
This will handle apk splits on the basis of architecture.
Now, also remember to add this line in proguard-rules.pro
-keep class com.shockwave.**
If you will not add this line in proguard-rules.pro then your app will crash in release version.
Now, after this go to Build and then select, Generate Signed Bundle/apk. From here, generate a signed bundle. Bundles are generated in very same way, as the apk's are generated.
Then, upload your bundle(.aab file) on Google Play Console and you will see that every device gets different apks depending upon their architecture.
This is the most easiest way to reduce the app size.Multiple apk concept is more complicated.
So,I suggest you to use this way.
Related
How to fix the warning below? Are there any alternatives to 'auto'?
Warning:DSL element 'ProductFlavor.resConfigs' has a value 'auto' which is obsolete and has not been replaced. It will be removed at the end of 2018
android {
...
flavorDimensions "device", "paid", "market"
productFlavors {
phone {
// Phone config version for the application
resConfigs ("auto")
dimension "device"
matchingFallbacks = ['mobile']
}
...
}
...
}
This is the error after updating to Android Studio 3.1:
Based on the official advise here the best thing to do is removing the tag entirely if you support all the languages or supply an array with the language's code your app supports like:
resConfigs "en", "de", "it", "fr" // etc. etc.
More info:
This is one of the resources optimization proposed by the official documentation here so i decided to test this flag with those FirebaseUI dependencies in a sample project
implementation "com.firebaseui:firebase-ui-auth:$firebase_ui_version"
implementation "com.firebaseui:firebase-ui-database:$firebase_ui_version"
implementation "com.firebaseui:firebase-ui-storage:$firebase_ui_version"
creating the debug APK with both the options and those are the results:
Using resConfigs "auto" the debug APK was: 3,793 KB
Using resConfigs "en" (so 1 language only) the debug APK was: 3,294 KB
This means that with all the string resources for all the languages of those dependencies I got only ~500KB of size increase. That's something you could reason about, you definitely should make a test with the dependencies you use and see if the size increase is negligible or not and consequently decide to provide the list of supported languages or remove the resConfigs option.
PS: If you are using Android FirebaseUI this was one of the suggested optimizations, I've created an issue here about the thing and this has been solved immediately by the awesome guy called SUPERCILEX
auto is no longer supported because it created a number of issues with multi-module projects. Instead, you should specify the locale that your app supports. Android plugin 3.1.0 and higher ignore the auto argument, and Gradle packages all string resources your app and its dependencies provide.
com.android.tools.build:gradle:3.2.0-alpha08 BaseFlavor.java
* <p><b>Note:</b> <code>auto</code> is no longer supported because it created a number of
* issues with multi-module projects. Instead, you should specify the locale that your app
* supports, as shown in the sample above. Android plugin 3.1.0 and higher ignore the <code>auto
* </code> argument, and Gradle packages all string resources your app and its dependencies
* provide.
I am successfully generating signed APKs in Android studio, splitting them by ABI and assigning a different versionCode for each, by adding the following code to my build.gradle file:
// Map for the version code that gives each ABI a value.
ext.abiCodes = ["armeabi-v7a":1, "arm64-v8a":2, "x86":3, "x86_64":4]
import com.android.build.OutputFile
// For each APK output variant, override versionCode with a combination of
// ext.abiCodes + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
android.applicationVariants.all { variant ->
// Assigns a different version code for each output APK
// other than the universal APK.
variant.outputs.each { output ->
// Stores the value of ext.abiCodes that is associated with the ABI for this variant.
def baseAbiVersionCode =
// Determines the ABI for this variant and returns the mapped value.
project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))
// Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
// the following code does not override the version code for universal APKs.
// However, because we want universal APKs to have the lowest version code,
// this outcome is desirable.
if (baseAbiVersionCode != null) {
// Assigns the new version code to versionCodeOverride, which changes the version code
// for only the output APK, not for the variant itself. Skipping this step simply
// causes Gradle to use the value of variant.versionCode for the APK.
output.versionCodeOverride =
baseAbiVersionCode + variant.versionCode
}
}
}
Now, I want to use ProGuard (minifyEnabled true) to obfuscate my code. As stated in the official android documentation, it is important to keep the mapping.txt files for each APK you release in order to decrypt a crash report's obfuscated stack trace received via the Google Play Developer Console. But when I generate the APKs split by ABI, I only find one mapping.txt file in the <module-name>/build/outputs/mapping/release/ directory.
My question: Could someone please confirm that this single mapping.txt file will allow me to decode obfuscated stack traces for the 4 APKs which were split by ABI? If not, how can I generate the 4 different mapping files?
I tried generating the different mapping files based on a snippet I found in this post, essentially trying to copy and rename the mapping.txt files as they are created during the multi APK generation process but I still only get one single mapping file:
applicationVariants.all { variant ->
if (variant.getBuildType().isMinifyEnabled()) {
variant.assemble.doLast {
copy {
from variant.mappingFile
into "${rootDir}/proguardTools"
rename { String fileName ->
"mapping-${variant.name}.txt"
}
}
}
}
}
I am very new to gradle and I find its syntax quite confusing. Any help would be very much appreciated.
I ended up testing the stack trace deobfuscation with Firebase Crash Reporting (i.e. without having to deploy a crash test version of my app to the google play store) and I can confirm that the one mapping.txt file created when generating the signed APKs in Android Studio does correctly deobfuscate stack traces for crashes occurring on APKs corresponding to different ABI types.
Im facing this problem which seems im not able to solve. Here is scenario:
Im building apk which uses gradle dependency and this dependency is architecture specific so for apk for x86 i need different dependency and for arm different as well.
I solved it with product flavors:
productFlavors {
dev { ... }
develx86 { ... }
production { ... }
productionx86 { ... }
}
So then i defined dependency like this:
develCompile 'dependency_for_arm'
develx86Compile 'dependency_for_x86'
This works good. But recently i had to add to my application an usage of renderscript. I did it in this way:
renderscriptTargetApi 22
renderscriptSupportModeEnabled true
And after this when i uploaded apk on Google Play it says it's apk is suitable with arm, x86. I don't know how this is possible. As you can think it will crash on device with different CPU (if i generated apk for arm and user will execute it on x86 app will crash).
So i decited to use ABI splits:
splits {
abi {
enable true
reset()
include 'armeabi', 'x86'
universalApk false
}
}
//Ensures architecture specific APKs have a higher version code
//(otherwise an x86 build would end up using the arm build, which x86 devices can run)
ext.versionCodes = [armeabi:0, x86:1]
import com.android.build.OutputFile
android.applicationVariants.all { variant ->
// assign different version code for each output
variant.outputs.each { output ->
int abiVersionCode = project.ext.versionCodes.get(output.getFilter(OutputFile.ABI)) ?: 0
output.versionCodeOverride = android.defaultConfig.versionCode + abiVersionCode
}
But now when i see generated apk files, my dependency which is flavor-specific is not included into apk and apk will crash when i open section which uses API from this dependency.
Do someone know how to solve this issue? Or someone know why Google Play says that apk is for both architectures when i included renderscript? (Without it it works properly but i need renderscript).
Thank you for your time. I will appreciate any help.
If you look inside your APK, under lib folder, you should see that renderscript support mode added libs for other architectures than the one you're supporting.
You can keep your earlier configuration with ABI-specific flavors.
But in order to ensure that no libs for other architectures are included, try adding abiFilters to your flavors:
productFlavors {
dev { ... ndk.abiFilters 'armeabi-v7a' }
develx86 { ... ndk.abiFilters 'x86' }
production { ... ndk.abiFilters 'armeabi-v7a' }
productionx86 { ... ndk.abiFilters 'x86' }
}
Sorry I cannot comment inline yet.
What's in the apk, especially in res/raw/ and lib/?
Also, are you using gradle-plugin 2.1.0? (since you are using renderscriptTargetApi 22), have you tried Build-Tools 23.0.3?
I would like to offer my Android 4+ app both in the Play Store and in the Amazon Market. I found several questions here on SO dealing with the question how to integrate both stores in the same APK. This seems to be quite difficult since there is not reliable why to check wether the app was download from store A or B, but if it was loaded from B all links to the store, reviews, etc. have to point back to B, etc...
Thus both stores in the same APK is not what I am looking for. I would like to create two different APKs, one for each store. Additional bonus: The APKs will be smaller since each will only contain the libs it really needs. How can this be done?
In Xcode/iOS I would simply create two different targets, each with its own set of libs and configurations. How can this be done in Eclipse? There is only one AppManifest and the "File/Export/Export Android Application" option always uses the same configuration to create the same APK.
What can I do to create two different app version from the same Eclipse project?
not the answer you're looking for but it's the reality.
Eclipse + ADT is not a very flexible way of building apps and to do what you're asking for you'll need a fairly big amount of ANT scripting (I saw it before in a banking app that build different .apks for each of it's brands). But it's probably even more complex than building everything into one apk.
On the other hand, if you're willing to migrate your project to AndroidStudio + Gradle, that is a way more flexible approach to software building and they have an "easy to use" concept of flavours, here is a snippet of the build.gradle of the app I work:
productFlavors {
phone {
resConfigs "xhdpi", "xxhdpi", "xxxhdpi", "nodpi"
versionCode 100000 + project.ver.versionCode
}
phone_low_end {
resConfigs "ldpi", "mdpi", "hdpi", "nodpi"
versionCode 200000 + project.ver.versionCode
}
tablet {
versionCode 300000 + project.ver.versionCode
}
unified {
versionCode project.ver.versionCode
}
}
sourceSets{
unified{
res {
srcDir 'src/tablet/res'
}
assets{
srcDirs 'src/phone/assets', 'src/tablet/assets'
}
}
}
and with that fairly small configuration the project is being built with different versionCode, with different assets. For your project you could easily integrate like this:
sourceSets{
googleplay{
src {
srcDir 'src/googleplay/java/'
}
}
amazon{
src {
srcDir 'src/amazon/java/'
}
}
}
Does Amazon app store support multiple APK uploading like Android market?
The Amazon Appstore does support multiple APKs for the one app. This is confirmed in the Amazon documentation. It is possible to upload multiple APKs for different platforms and capabilities including screen size and density, OpenGL compression format, architecture or API version.
Multiple APK support in the Amazon Appstore works the same as the multiple APK support in the Play Store, where the app android:versionCode must be unique for each uploaded APK.
You can use the gradle build script to automate the generation of unique versionCode for the multiple APKs. Here is an example that generates different APKs based on device architecture:
splits {
abi {
enable true
reset()
include 'x86', 'armeabi', 'armeabi-v7a'
universalApk true
}
}
project.ext.versionCodes = ['armeabi': 1, 'armeabi-v7a': 2, 'arm64-v8a': 3, 'mips': 5, 'mips64': 6, 'x86': 8, 'x86_64': 9]
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
output.versionCodeOverride =
project.ext.versionCodes.get(output.getFilter(
com.android.build.OutputFile.ABI), 0) * 10000000 + android.defaultConfig.versionCode
}
}
You can visit developer.amazon.com. Create an account there and your are free to upload android apps. They also give you an option to choose when do you want the app to be available for the users. Unlike android market, the app will not be live immediately. It will undergo a review process and only after approval it will become live. You cannot edit the apk details once it is sent for review.
This feature is unsupported by Amazon Market
http://www.amazonappstoredev.com/2011/02/when-to-create-multiple-binaries-for-your-app.html