I am using the following splits code in my gradle to reduce APK size:
splits {
abi {
// Enable ABI split
enable true
// Clear list of ABIs
reset()
// Specify each architecture currently supported by the Video SDK
include "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
// Specify that we do not want an additional universal SDK
universalApk false
}
}
When I run the app, the APK is generated fine, with reduced size and runs on Emulator.
But when I try to build APK file from Build > Build bundles/apks like
I get this error:
Execution failed for task ':app:packageAbcDebug'.
> A failure occurred while executing com.android.build.gradle.tasks.PackageAndroidArtifact$IncrementalSplitterRunnable
> Could not find EOCD in '....apk'
Possible solution:
- Declare repository providing the artifact, see the documentation at https://docs.gradle.org/current/userguide/declaring_repositories.html
I only wanted to exclude "x86" architectures, to reduce the APK size and need to send the APK to my client. How do I fix this?
I was running into a similar issue during my build process, though I wasn't enabling split. Take it for what you will.
After digging through the source kit for PackageAndroidArtifact and other sources in Android, I discovered "EOCD" means "End Of Central Directory". That is, it's related to the end marker of the zip file that gets built when building your output. (The link to the comments in Android's source kit.)
In my own case, I discovered that even though I'm asking Android Studio to do a complete clean of my build directory, it's leaving the app-debug.apk build artifact file. (For reasons, I'm trying to package the debug version of our APK for internal testing.) And somehow that file got corrupted, which completely confuses the build process.
The resolution was to manually delete the apk file prior to running the build process. (In my case, it was found in my build's app/debug directory, next to the app/build directory.)
G'figure...
May be its late but here is the solution with reason for it to work.
Since we are using splits to create apks for each architecture build system needs a different name for each apk being generated.
Best solution is to provide a dynamic way of generating apk names.
Just go to app level build.gradle file
Add rules for release/debug build variants in buildTypes block inside android block like this
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
applicationVariants.all { variant ->
variant.outputs.all { output ->
project.ext { appName = 'YourApkName' }
outputFileName = "${appName}-${output.getFilter(OutputFile.ABI)}-${variant.name}-${variant.versionName}.apk"
}
}
}
}
Explaination :
Here the apk name is appended by the ABI name that helps build system identify the apks for each architectures.
You can also use Android Size Analyze to identify sizes and reduces the apk size.
In order to understand which files are actually taking up more space in the application, use the Android Size Analyzer plugin inside Android Studio. For installing the plugin
Select File > Settings (or on Mac, Android Studio > Preferences.)
Select the Plugins section in the left panel.
Click the Marketplace tab.
Search for the “Android Size Analyzer” plugin.
Click the Install button for the analyzer plugin.
Restart the IDE after installing the plugin. Now, to analyze the application, go to Analyze > Analyze App Size from the menu bar.
Related
I am trying to release an Android app for the first time on play.google.com using an app bundle. The app is implemented in Android Studio using Flutter SDK and the bundle is generated using the following command:
flutter build appbundle
When I upload the bundle to Google Play Console, I get the following warning:
This App Bundle contains native code, and you've not uploaded debug symbols.
We recommend you upload a symbol file to make your crashes and ANRs easier to
analyze and debug. This App Bundle contains native code, and you've not uploaded
debug symbols. We recommend you upload a symbol file to make your crashes and
ANRs easier to analyze and debug.
I observe lots of people raised similar issues, but I'mnot able to figure out the exact reason for this issue and how to solve this.
Update:
By modifying root_app_dir/android/app/build.gradel as follows
buildTypes {
release {
signingConfig signingConfigs.release
ndk {
debugSymbolLevel 'FULL'
}
}
}
Started giving me the following error:
* What went wrong:
Execution failed for task ':app:extractReleaseNativeDebugMetadata'.
> NDK is not installed
it is duplicate here go for the answer that says "If talking about Flutter..."
however you don't have to worry about it too much it is just warning and I've uploaded multiple flutter apps with same warning and it works just fine.
After some trials and errors, I finally managed to get the Flutter build command to include the debug symbols files.
In android\local.properties make sure you add the path to the NDK. Here's mine (for Windows):
sdk.dir=C:\\Users\\Roslan\\AppData\\Local\\Android\\sdk
ndk.dir=C:\\Users\\Roslan\\AppData\\Local\\Android\\sdk\\ndk\\25.1.8937393
In android\app\build.gradle have the following:
buildTypes {
release {
signingConfig signingConfigs.release
ndk {
debugSymbolLevel 'FULL'
}
}
}
In my app I'm using "de.mrmaffen:vlc-android-sdk:2.0.6" library and it's taking much storage but I can't find a smaller alternative.
So I decided as CommonsWare mentioned to use splits or product flavors but I can't get it to work.
I tried to follow the docs with no luck.
Any help appreciated.
Building the LibVLC Android SDK yourself
If you are using "de.mrmaffen:vlc-android-sdk:2.0.6", buid the LibVLC Android SDK yourself
As explained here, afterwards simply run this Gradle command: ./gradlew buildLibVlc
Building a specific version of the LibVLC Android SDK
cd vlc-android // if this folder doesn't exist yet, simply run ./gradlew cloneVlcAndroid
git tag // to list all release versions
git checkout {tag-name} // to checkout the git repo at the given tag
cd ..
./gradlew buildLibVlc // build it
Make sure you followed the Android compile instructions in terms of dependencies and check this:
git clone repo.
open up a command prompt in this repo. "cd" or change directory into
it.
git clone .
change directory into the vlc-android directory.
git tag
git checkout <tag_version>. In this case, 2.1.2.
cd ..
comment out both the compile/build of the build.gradle script that
you don't need.
./gradlew buildLibVlc
you should have a successful build with both the Java sources and
shared object (*.so) files in the jniLibs folder.
create a libs folder right next to the jniLibs folder if you
updated the gradle version.
Get it via Maven CentralJCenter
Just add this dependency to your project and you're good to go.
dependencies {
implementation 'de.mrmaffen:libvlc-android:2.1.12#aar'
}
I'm using this version. Remember to add JCenter to your project's build.gradle file:
allprojects {
repositories {
jcenter()
}
}
VLC Android SDK via JCenter supports the next ABIs: armeabi-v7a, arm64-v8a, x86 and x86_64.
You can filter specific ABIs in your app's build.gradle file (x86_64 and arm64-v8a are excluded):
android {
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
It's possible to check included files in mrmaffen's repository and via recent Android Studio versions:
Configure multiple APKs for ABIs
Add an abi block inside
your splits block. In your abi block, provide a list of desired ABIs. Source
android {
...
splits {
// Configures multiple APKs based on ABI.
abi {
// Enables building multiple APKs per ABI.
enable true
// By default all ABIs are included, so use reset() and include to specify that we only
// want APKs for x86 and armeabi-v7a.
// Resets the list of ABIs that Gradle should create APKs for to none.
reset()
// Specifies a list of ABIs that Gradle should create APKs for.
include "x86", "armeabi-v7a"
// Specifies that we do not want to also generate a universal APK that includes all ABIs.
universalApk false
}
}
}
Build multiple APKs
Click Build > Build APK to build all APKs for the currently selected
module in the Project pane.
Gradle creates the APKs for each ABI
into the project's build/outputs/apk/ directory.
Configure Build Variants
This page builds on the Configure Your Build Overview to show you how you can configure build variants to create different versions of your app.
Combine abi filters and build variants
Exclude abi from apk
android {
productFlavors {
x86 {
ndk {
abiFilter "x86"
}
}
arm {
ndk {
abiFilters "armeabi-v7a", "armeabi"
}
}
}
}
Multi-APK through ABI and density splits
How to reduce the number of APKs with ABI splits
Here’s
a code snippet that you can use to set version codes for ABI splits.
Give the x86_64 and x86 higher version numbers than ARM, as many x86
devices can run ARM code through an emulation layer, although with
lower performance.
If you don’t wish to manage too many APKs, target the most popular
ones (usually ARM and maybe x86) with a split APK and serve a
universal APK to everyone else.
It’s vital that you publish the universal APK on the Play Store with a lower
version number than all other ABI-specific packages.
If you need more flexibility for your Multi-APK setup, check out
Multi-APK through product
flavors.
Recommended Medium post to choose supported ABIs
In this
post
we will understand what is ABI, problems with the ABI split, and
alternative way to avoid those problems... For instance, Android supports the following ABIs :
mips, mips64, X86, X86–64, arm64-v8a, armeabi, armeabi-v7a
So you have to take a call on supporting 64-bit libraries based on
size vs performance criteria but mips, mips-64, and armeabi should be
removed without any hesitation.
Solve UnsatisfiedLinkError Problem on some devices
As explained here, 64-bit processors generate and check arm64 folder to load native libraries.
If your project does not have arm64 folder. Here is the solution:
build.gradle
defaultConfig {
...
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
You need to add this filters(abiFilters) to your app module’s
build.gradle files. So when your device try to run your app, it will
check gradle file and understands that it should not generate any
folder and use existing native library resources. Boom. Almost solved.
But still there is one more thing. Add this line to your gradle.properties to use deprecated Ndk.
gradle.properties
android.useDeprecatedNdk=true
You can use app bundle so you don't need to handle multiple apks. Google Play will handle it for you automatically. Plus this approach can provide additional benefits. For more details: https://developer.android.com/guide/app-bundle
I have a project with three different build types: debug, beta, and release. My test package is always created for debug builds, but QA uses the beta build and we want QA to run these tests on their vast array of devices.
I'm trying to create a testing apk for QA that is signed by the same key as the beta build. Looking through the Android-Gradle documentation, I don't see anything telling me that I can't do this, but I don't see anyway to configure this. Is there anyway I can configure which keystore is used when assembling a test apk? Or is there a way to create an unsigned test apk?
You can now point this to a different target, I don't know when this happened, but from the docs:
Currently only one Build Type is tested. By default it is the debug
Build Type, but this can be reconfigured with:
android {
...
testBuildType "staging"
}
This is an incomplete answer to your question in that it documents what you can't do, but the connectedAndroidTest task, which is what runs the androidTest tests in your project, is hardcoded to run against the debug build type, and I don't see a way to point it at a different build type.
Taking the advice from Is there a way to list task dependencies in Gradle? and examining the task dependency tree, if you run:
./gradlew tasks --all
you get this in your output:
Verification tasks
------------------
app:check - Runs all checks. [app:lint]
app:connectedAndroidTest - Installs and runs the tests for Build 'debug' on connected devices. [app:assembleDebug, app:assembleDebugTest]
app:connectedCheck - Runs all device checks on currently connected devices. [app:connectedAndroidTest]
app:deviceCheck - Runs all device checks using Device Providers and Test Servers.
The documentation for the connectedAndroidTest task claims it runs tests against debug, and the task dependencies (which you see with the -all flag) confirm that the task depends on assembleDebug.
Adding additional build types and flavors doesn't seem to affect the dependency on the built-in debug type.
It's possible that with greater Gradle-fu than mine, you could rewire the tasks to make the tests depend on a different build type, but doing this is likely to be fragile since it's bound to depend on things that aren't supported API in the Android Gradle plugin.
To answer your question most directly, though, if all you want is to run tests against a build with a different certificate, you could change the signing config on your debug build to use the beta certificate:
android {
signingConfigs {
beta {
keyAlias 'key'
keyPassword 'password'
storeFile file('/path/to/beta_keystore.jks')
storePassword 'password'
}
}
buildTypes {
debug {
signingConfig signingConfigs.beta
}
beta {
signingConfig signingConfigs.beta
}
}
}
I tested it and I am able to run androidTest targets against debug builds that use a custom keystore in this way. However, I doubt this solves your problem, because I suspect you want to run your tests against the beta build, not a debug build with the beta certificate.
To add a testing source set for your build variant, follow these steps:
In the Project window on the left, click the drop-down menu and
select the Project view.
Within the appropriate module folder,
right-click the src folder and click New > Directory.
For the directory name, enter "androidTestVariantName." For example,
if you have a build variant called "MyFlavor" then the directory name
shoulbe "androidTestMyFlavor." Then click OK.
Right-click on the new directory and click New > Directory. Enter
"java" as the directory name, and then click OK.
Now you can add tests to this new source set by following the steps above to add a new test. When you reach the Choose Destination Directory dialog, select the new variant test source set.
The instrumented tests in src/androidTest/ source set are shared by all build variants. When building a test APK for the "MyFlavor" variant of your app, Gradle combines both the src/androidTest/ and src/androidTestMyFlavor/ source sets.
Another way is to put following line your in default config.
Currently only one Build Type is tested. By default it is the debug Build Type, but this can be reconfigured with:
android {
...
testBuildType "staging"
}
When building my android project, I have added the following to the build.gradle file to enable proguard:
buildTypes {
release {
runProguard true
proguardFile 'proguard-project.txt'
proguardFile '../common/proguard-shared.txt'
proguardFile getDefaultProguardFile('proguard-android.txt')
}
}
Everything builds okay BUT when I disassemble the resulting dex file, it turns out that both the obfuscated and non-obfuscated files are there.
For example, both common.Base64 and common.a exist, the first is non-obfuscated, while the second is.
Not sure its related, but the project itself has a non-typical structure.
This is a result of us having a large android code base with more than 40 android apps.
We are trying to create a gradle based build flow side-by-side of existing eclipse based build.
If all goes well, we intend to change the file structure to be more native gradle, and start using flavours and build-types to have-away with many of the libraries we created to accommodate for the lack of flavours and such.
Project E above relies on a chain of libraries like that:
E -> D -> C -> B -> A
e.g. The E project depends on the library D which depends on library C ... all the way up to A.
After looking into this, I found out that this is a problem if you first build without proguard enabled and then build it with it enabled. This is due to the incremental mode of dex.
You can do a clean build after enabling proguard and it'll fix this.
Edit: I previously indicated that you can disable incremental mode in dex, but it turns out that actually doesn't help!
In my android app, i want to test some features with proguard on.
I don't need to really "debug" it, but i want proguard to run when i hit run in eclipse. I don't want to export the binary every time (so, in release mode) and save as apk and get it to the device to test.
Is there any way to run proguard in this way?
Update:
It seems like this is possible if you are not using Eclipse; as question title does not include Eclipse, there are multiple correct answers to this question.
If you want to make the whole build process easier for you, you should switch over to gradle and Android Studio IDE.
Then you could easily add the following to your build.gradle file to run ProGuard:
android {
buildTypes {
release {
}
debug {
minifyEnabled true
proguardFile 'proguard-android.txt'
zipAlignEnabled true
}
}
}
This will run ProGuard on your debug build, configured with the file "proguard-android.txt", which should be put at your project's root folder. And in addition your apk is being zip aligned (Just remove "zipAlignEnabled true", if you don't want that to happen). If you want to do the same for your release build, just add those three lines under "release".
Slightly off-topic: Stuff like adding dependencies, signing your apk or adding other custom tasks to your build process is also way more uncomplicated with gradle. In addition you'll be able to not only build your apk via Android Studio IDE, but also via a simple command on the command line (e.g. ./gradlew assembleDebug). So if you are working on a team, the setup process for new members is just one "./gradlew assembleDebug" away. Without the need for any IDE configuration at all. Importing your project including all dependencies is as simple as a one-click process
EDIT:
As of Gradle Android Build Tools version 0.14.0 the property names have changed (http://tools.android.com/tech-docs/new-build-system):
BuildType.runProguard -> minifyEnabled
BuildType.zipAlign -> zipAlignEnabled
I've updated the above code.
Old Answer :
http://developer.android.com/tools/help/proguard.html
ProGuard runs only when you build your application in release mode, so you do not have to deal with obfuscated code when you build your application in debug mode.
When you build your application in release mode, either by running ant release or by using the Export Wizard in Eclipse, the build system automatically checks to see if the proguard.config property is set. If it is, ProGuard automatically processes the application's bytecode before packaging everything into an .apk file. Building in debug mode does not invoke ProGuard, because it makes debugging more cumbersome.
Update: 13-3-2016
It is possible with the new gradle build system. You need to set minifyEnabled to true in your build.gradle file. Generally you have pro-guard running in release mode. There are other options available like shrinking resources. You can find some useful info # http://tools.android.com/tech-docs/new-build-system
Also do have a look #
http://developer.android.com/tools/building/configuring-gradle.html
android {
...
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
Regarding custom Ant builds (and based on Victor's answer), adding the following to my build.xml file works for me:
<target name="-debug-obfuscation-check">
<!-- enable proguard even in debug mode -->
<property name="proguard.enabled" value="true"/>
<!-- Secondary dx input (jar files) is empty since all the jar files will be in the obfuscated jar -->
<path id="out.dex.jar.input.ref" />
</target>
Notice that I had to override (actually pre-set) the out.dex.jar.input.ref; otherwise, the later running of dx will attempt to merge non-disjoint jars and throw the DexException: Multiple dex files define Xxx.
It is possible if you build with Ant. See Android custom build using Ant on how to build your project with ant. Then, simply override in the project's build.xml the target "-debug-obfuscation-check" and set proguard.enabled to true:
<target name="-debug-obfuscation-check">
<!-- proguard is never enabled in debug mode -->
<property name="proguard.enabled" value="true"/>
</target>
With Android Studio you can use -dontobfuscate option in your Proguard rules file and debugger will work fine. I'm not sure if it works with Eclipe as well.
If you are using AGP (Android Gradle Plugin) 7.2.0 or newer, beware that we have a bug open without a solution so far. Workaround as of now is to downgrade AGP to 7.1.3 so you can obfuscate your debug APK.
https://issuetracker.google.com/issues/242214899?pli=1