We have developed an android library (aar) using Java and it depends on android support library v4. In fact, we have extended ViewPager (android.support.v4.view.ViewPager) class and consumed it in our library’s GUI.
We have applied proguard on the library and it works fine with our test apps but we have a customer that has developed its android app in native C++ and they are going to integrate our component into their app and there is an issue on build.
They receive com.android.dex.DexIndexOverflowException on build which is a sign of having more than almost 64K methods that are allowed in a single dex.
They asked us to use fewer or smaller dependencies as they have to include our component dependencies into their build setting and one of their suggestions is that we should extract ViewPager out of android support source and put it in our component source code.
Now the questions are
If we extract ViewPager out of android source and add it to our
library source code then will it reduce the amount of methods to
prevent mentioned exception? Is it a good practice to do that?
What is the best way to resolve this issue?
Thanks
If we extract ViewPager out of android source and add it to our library source code then will it reduce the amount of methods to prevent mentioned exception?
Probably not, at least for release builds. Your customer should have configured ProGuard, which will identify and remove unnecessary code pulled in via libraries.
Is it a good practice to do that?
No. Your customer should know better than that.
What is the best way to resolve this issue?
Mostly, it is not your problem. It is your customer's problem. Your customer is blaming you, but there is no evidence in your question that your library is a significant source of the customer's DEX method references.
Your customer should be configuring ProGuard, and your customer should use the APK Analyzer in Android Studio 2.2+ to see where their method references come from.
You, in your library, could:
Try to use more focused dependencies than support-v4. ViewPager itself is in the support-core-ui artifact. However, if you are using FragmentPagerAdapter or FragmentStatePagerAdapter, in addition you will need either support-fragment (if you are using the v4 edition of those classes) or support-v13 (if you are using the v13 edition of those classes).
Use the APK Analyzer on some demo project that you create that uses your library, so you can see how many DEX method references that your library uses, to see if you are really a significant source of such references.
Suggest ProGuard settings for your customer that will keep required classes of yours, to help them use ProGuard effectively with their app.
You can set jumboMode in your build.gradlefile like this:
dexOptions {
jumboMode true
}
This option will allow you to have 32it wide string references in your .dex file. Thus you can have 2^32 referenced strings in your project.
Hope it helps.
Available options are :
Yes, you can extract ViewPager from support library. ViewPager import some files from support library that are also required to be extracted. This will reduce method count with significant difference.
You can also use exclude parameter in dependencies in build.gradle file.
compile ('com.android.support:recyclerview-v7:+') {
exclude module: 'support-v4'
}
Related
I have a modularized application architecture with 3 module layers.
A core library module at the bottom and an application library module and my application on top of it. Each builds it's own native shared library. But not only the java code but also the C++ code in them also depends on each other.
So my "application.so" must link to "library.so" and "core.so" and on the other hand "library.so" must link "core.so" just like you expect from any layered architecture.
I can see in the file system that the shared so libraries all end up in the same build directory but i can't find a way to make them link each other. "find_library" is not going to work. And just naming them in "target_link_libraries" is not going to work either.
EDIT: With
buildFeatures {
prefabPublishing true
}
prefab {
infosqueezer {
headers "src/main/cpp/include"
}
}
i was able to generate an aar. But i'm not able to include it into the the other module. Both debug and release aar's are generated in the "library/build/output/aar/" directory. The modules are in the same project so i just need to reference by file somehow.
I'm using
implementation project(path: ':library')
but it does not pickup the so file. Also the AAR does not contain any "libraryConfig.cmake" or "library-config.cmake" that the find_package command would need to find the link library.
Also the whole prefab process seems to be terrible. I just want to split the in project source code to reduce compile time and dependencies. Prefab is adding so much and surely a good idea to distribute indpendent libraries but overkill for internal libraries.
All i need is just a way to reference the generated "library.so" file in another module.
I know it's Android but do they really make everything so terrible complicated?
There is no supported way to depend on native libraries from other modules in the same project with externalNativeBuild (at least in part because it will lead to worse build performance, see below).
As described your project should have a single gradle module for its native code if you want the best build performance (and want to be on a supported usage model).
If you really want these to be separate, the only way to do this that works with AGP is for them to be separate projects. I don't think you should do that.
Also the whole prefab process seems to be terrible.
This isn't what it's meant for. Screw drivers make terrible hammers.
I just want to split the in project source code to reduce compile time and dependencies.
Splitting gradle projects into more modules usually makes compile times worse, not better. I can't think of any reason that it would improve build performance for native projects, and it can easily make it much slower since invoking CMake is not cheap and splitting modules up multiplies that cost.
I've been working on an Android app project. I'm using quite a few libraries (because why redo work that someone else has done to make other people's life easier?).
My question is: what are the costs of importing libraries in a project? (I'm talking about the implementation XXX.YYY:v2.0.0 type of line added in the build.gradle dependencies list.)
Just as an example (though please provide a more encompassing answer): when compiling and publishing my application, does it take all of the libraries' classes and methods and put them in my application, thus making it much heavier than it would need to be?
Each library dependency requires an additional download while you compile your app. So these will increase the amount of time required to compile.
The code for each library is included in your final APK so they will increase the size.
For Every Library a download is necessary In order to built your app.
e.g If you want Libraries regarding to Firebase then You download the Library by adding the Firebase Project to your App. In build.gradle File you see the dependencies after you add them to Your Project App.
An unreleased android library I am working on has a third party networking library in it - OkHttp in this case.
Projects that use this library as a dependency also are now able to create objects with that networking library.
Can I limit or disable access to the networking library contained within my library?
You could make the dependency transitive however if your code hits the missing code inside their app it will fail ClassNotFound or MethodNotFound
dependencies {
compile('com.squareup.okhttp3:okhttp:3.2.0') {
transitive = false
}
}
Short of that once the code is packaged with your lib it's available to anyone who wants to use it from your lib.
This still won't solve the problem as you would like but you could use proguard to rename the okhttp classes. Users of your lib could still call the okhttp classes but they would be renamed by proguard to something like a,b,c,...
What you want to do is shade the dependency. Here's a description from a blog post about shading dependencies in Gradle:
To Shade a library is to take the contents files of said library, put
them in your own jar, and change their package.This is different from
packaging which is simply shipping the libraries files in side your
own jar without relocating them to a different package. The term
fatjar is commonly used to refer to jars that have the application as
well as its dependencies packaged or shaded into them.
It's easy to do for smaller libraries. I could image it might be difficult for a large library like OkHttp. You can do it manually by simply copying the files into your project and changing the package. There are also some scripts that will do it automatically. They usually use Jar Jar Links.
Normally be default you don't have the dependencies like that:
compile rootProject.ext.okhttp
compiled in your jar only your sources are. So OkHttp classes will not be in your lib.
I have exactly the same case. I use gradle to build and upload to maven.
You can check here
So if your intention is to have the exact dept version in the package and to be hidden you just need to include it in you project as a module and to change some things like the package of OkHttp to avoid conflicts and also the access to currenly public okhttp members. OkHttp is using Okio so you may want to privatize it too.
Note that this kind of shadowing + hiding functionality of the shadowed class can be useful for framework dependencies(ensuring all depts in runtime available) but it is increasing the size of your libs and will not be the best option for apps using your lib as they anyway ensure packaging required depts in the apk.
I received some legacy code of app (not developed by me, but by some other team, with no documentation), which has almost 20+ dependencies, in build.gradle.
Now, I wanted to clean up unused Libraries/dependencies, by removing them from build.gradle
I searched on Google and came across this project for resource shrinking. But it seems to be used for removal of resources that are unused, at build time, in the packaged app and this also removes resources from libraries you are depending on if they are not actually needed by your application.
Also, I use ProGuard, for obfuscation and shrinking in conjunction with shrinkResources true in build.gradle
My intention is to remove unused Libraries/dependencies from build.gradle itself, without breaking app functionality.
Is there a way or tool which shows which library is safe to remove without breaking the app functionality?
By 20+ dependencies you don't need any tooling and can do a manual check.
I would proceed like this:
Comment out all dependencies and check what fails (see below)
Uncomment the dependency that causes the failure
Repeat
This way you might also notice dependencies that are seldom used or can be replaced with standard libraries or other libraries that you use in the project.
Here are the things that will indicate you that a dependency is required (in the order of slowing down the feedback loop):
compilation errors
unit test errors
integration / system / end-to-end / device test errors (whatever you use and call them)
application functionality at runtime
application performance at runtime
Runtime dependencies can be especially tricky. For example, your code might not depend on a library, but this library provides a runtime implementation for some other library you depend on. Removing such a dependency will only be visible at runtime as missing functionality or performance issues.
Instead of commenting out all dependencies I would go the other way around - comment out one dependency at a time and see what breaks. This way you would also get a grasp of use-cases of all dependencies because the IDE will point you to the place where code broke. If nothing breaks after commenting out a dependency you'll know that it's not used. Another thing you could potentially do is analyze an unobfuscated release .apk where all unused dependencies will be missing but package structure will be preserved.
If you mean that finding unused library or import, you can easily see with "Ctrl + alt + shift + i" and type "unused import"
You can see now all unused imports.
Finding libraries and resources used in an Android app comes up in several contexts.
For the apps published in Google Play, AppBrain maintains reverse lookups, from the library to the more popular apps that use it. For example, apps using a newish 2D game library Godot.
Apktool will decode the APK directly.
The author instead wants to find (unused) resources, starting from the source code and the build process. Gabriele Mariotti above links to the question, whose accepted answer provides detailed information on use of minifyEnabled and shrinkResources in Gradle configuration.
Review Shrinking Android app and ProGuard vs R8.
For the moment I am developing small Android projects to practice with the Android prorgramming. However, once on the market, I would like to obfuscate / optimise the APK thanks to ProGuard. But this tool renames classes to obfuscate the code, so:
Is it safe to use tools like Android Query to write the code?
If it is not safe, what are some framework examples that can be used safely with Pro Guard?
What could be a solution to the problem? Or should I write everything using the good old Android style and forget about a "write less, do more" approach?
How do I identify the tools that are ProGuard-safe from the ones that are not?
I assume you want to use third party libraries (jar files). You could use a 3 step approach:
If the third party jar explicitly supports Android, it will have a proguard configuration. Usually this is a snippet that you merge into your proguard-project.txt.
If there is no such explicit support, you may still try to use the jar, obfuscate and test your app. If errors occur, gradually exclude classes from obfuscation until it works. A common problem is that libraries use reflection to instantiate classes and call methods which breaks after obfuscation.
Exclude the whole library from obfuscation. This will work in any case and proguard will not touch the library at all. (The Android toolchain will still repackage the contents of the jar into your apk which might cause problems.) This will also produce the least obfuscated result and should really be your last resort.
In any case, obfuscation is not a switch that you simply toggle. You'll need to get familiar with proguard config files which involves a learning curve.