Every unconsidered usage of external library results in Gradle error about reaching 65k methods. So I'm wondering what is the best solution to prevent this, and I would like not consider using Proguard custom settings.
What comes to my mind (of course if we are using open source libraries) is just downloading code and putting it to our project manually. Then we can delete unused classes and methods. It looks like we are playing Proguard role and it's time consuming.
Question
But what is the difference between using Gradle to fetch jars and
putting code manually? Or is it any difference in performance?
I will be thankful for any best practices in creating project with many libraries.
Note: I would like to stop thinking for now about Proguard custom settings, because it often produces warnings, where omitting them looks a bit weird for me by e.g. -dontwarn. Also I'm a bit afraid of using MultiDex support, which I thougt is not recommended.
One thing you can do in gradle is to get very specific about what code modules you're including.
For example:
compile 'com.google.android.gms:play-services:8.4.0'
will include a lot more stuff than just...
compile 'com.google.android.gms:play-services-location:8.4.0'
Generally it's best to let gradle manage the external dependencies. The biggest reason is that you can rest assured that you're getting a snapshot of the code tied to a specific version. If you just manually import code, it's very easy to lose track of which version of the code you're working with. A big risk to modifying (or selectively including) code from a 3rd party is when the 3rd party updates their code, perhaps to fix a major bug caused by a new version of Android, and they included some changes that botch your manual tweaks. Now you have more a complex problem on your hands than if you had just included it as a gradle dependency.
Related
Background
For the past few weeks, I've been on a mad quest to build a native Android client for an AWIPS2-EDEX server based on the open-source code for their Eclipse-framework-based Java client.
The specific repo of interest: https://github.com/unidata/awips2-core (and its dependencies, https://github.com/unidata/awips2-core-foss).
The Problem
I finally managed to get enough of awips2-core to build in Android Studio as a Gradle-based module (see my github fork at https://github.com/jskubick/awips2core-android ). After I finished celebrating my victory at getting it to build, I set out to actually make use of it in an actual Android app... then watched the build fail because awips2-core has dependencies on JAXB, which has been exhaustively documented elsewhere as being fundamentally incompatible with Android(*).
I'm now attempting to eliminate the module's dependencies on JAXB, but it got me thinking... how many MORE unexploded mines are out there waiting in the code for me to trip over... things that compile fine as "straight Java", but will blow up the moment something Android-specific references them?
Is there any tool that can scrape through library .jar files, and identify any dependencies within them that can't be satisfied by Android?
Update: my attempted project to use awips2-core directly with Android is dead. Upon further investigation, the implications of awips2-core's extensive use of Apache Camel sank in, and I realized that attempting to use awips2-core directly by Android in its present form is hopeless. As I understand it, Camel dynamically builds and compiles its own classes at runtime... which is completely impossible under Android, but allowed by "regular" Java.
This doesn't mean I've given up on my goal of writing an Android awips2-EDEX client, but it does look like my goal of reusing the existing code in awips2-core to write an Android client for EDEX is a lost cause & I'm going to have to write my own Android EDEX client from scratch.
(*) By "fundamentally incompatible", I mean that there's no known way to build it in a way that allows it to work with unmodified JAXB-using generic Java code built for Android. This is due to at least two problems:
Namespace problems. Think: BouncyCastle-vs-SpongyCastle. Apparently, Android hijacked SOME of JAXB's namespace for its own purposes, and Dalvik/ART's classloader won't allow you to override it with your own code using the same namespace.
Limits to Dalvik/ART's introspection capabilities. Basically, "real" Java has runtime introspection capabilities related to annotations that Dalvik/ART not-really-Java doesn't support... and some functionality with JAXB depends upon it.
From what I've read, various individuals over the past ~7 years have found partial work-arounds for 1 or 2, but the collective one-two punch of both has ultimately led to everyone who's tried to find a solution to either throw in the towel and write off the problem as intractable & hopeless, or hack it just enough to make it work for whatever they originally needed it for & call it a day.
I'm in development of an Android library module.
My library : https://github.com/sangeethnandakumar/TestTube
Rather than re-inventing the wheel, I'm depended on some of the other libraries to make my work done. After including 6 libraries inside my library namespace, My library reached around 1.8 MB.
My library depends only on specific features of other libraries but the complete library is importing and massed up right now.
Is there a way to reduce my library size even after including all of 6? My library is already optimized.
After including 6 libraries inside my library namespace, My library reached around 1.8 MB.
Your library is not 1.8 MB. It is possible that your library and its 8 dependencies combined (with transitive dependencies) is 1.8 MB. After all, one of those 8 dependencies is appcompat-v7, and that's over 1 MB on its own, the last time I looked.
My library is already optimized.
No, it is not. You are depending on both Volley and OkHttp. These do the same thing. Get rid of one. Since you are also using Picasso, I recommend getting rid of Volley.
Is there a way to reduce my library size even after including all of 6?
Get rid of dependencies that you do not need (e.g., Volley).
If consumers of your library might not need all of the functionality of your library, split your library into pieces. For example, if some consumers of your library might want your networking code but not your UI code, you might create testtube-core and testtube-ui or something. testtube-core would jettison things that are pure UI (e.g., appcompat-v7), so for consumers that only need testtube-core functionality, they get a smaller library. testtube-ui would depend upon testtube-core, so consumers using testtube-ui would get all the dependencies, and your testtube-ui code can call testtube-core code to do what needs to be done.
Beyond that, you will have to rely on minification at the app level (e.g., ProGuard) to get rid of things that are unnecessary. Bear in mind that app developers might use your library and need things from your dependencies themselves. appcompat-v7 is a great example of this: just because you might need only a slice of appcompat-v7 does not mean that the app only needs that same slice.
This is why the comment advising you to clone the code and get rid of pieces is not very wise. At best, that might work for obscure libraries that app developers using your library might not need (e.g., whatever gun0912.ted:tedpermission is). But creating your own hacked copy of appcompat-v7 or gson or something will make the size issue worse, if the app also is using those libraries for other purposes.
I am using the AppIntro library,and integrated it via build.gradle as normally suggested.
But since the library classes are locked this way and I want to be able to edit every class manually I have the following questions.
1. What is the go to way of including a library such as AppIntro in a way
that I will be able to edit every class.
2. Since I want to minimize the final size of my Android App, does different integration of librarys have an impact here or does the magic during build process exclude stuff that is not needed ?
I am simply not surf if downloading the complete github .zip is necessary or even the right way to do it, since it also includes a sample app and other unnecessary stuff.
What is the go to way of including a library such as AppIntro in a way that I will be able to edit every class.
Grab the sources and add to your project as separate module.
Since I want to minimize the final size of my Android App, does different integration of libraries have an impact here or does the
magic during build process exclude stuff that is not needed
ProGuard shall strip all the library code (but also your own code too :) that are not referenced (that's why it's important to sometimes tell it not do to that if you i.e. need code that rely on reflection to work.
As any Android developer should know, there is a 65k method limit for your apk (because the VM has just 16 bits for method handling). It should be quite hard to reach by your own, but it's easy as soon as you start to add some libraries.
Since last year you can get rid of this by enabling MultiDex on Android 5.0 and above (and adding a support library for prior Android versions). Even when this is possible, it's always better to reduce the methods number and get a smaller api (and the performance should be better, shouldn't it?).
At certain point the Android guys realized the size of their Google Play Services library was unaffordable (20k methods) and they took the great decision of split it up in different modules, so you can add simply the parts you need like (with grade):
compile ('com.google.android.gms:play-services-analytics:8.1.0')
compile('com.google.android.gms:play-services-appindexing:8.1.0')
Do you know if is it possible to do something like that with Guava library? It's around 15K methods, so it will be really helpful. I would like to use just a small part of Guava, so I don't need/want to include the other. I've been looking in the documentation and googling, but nothing found.
compile group: 'com.google.guava', name: 'guava', version: '18.0'
Some guy asked something similar two years ago, probably for a Java project, but didn't exist smaller guava parts then. Downloading part of guava-libraries
The only thing that comes to my mind is to copy just the needed Guava classes instead of loading the library, but I think is an awful solution.
Don't you think any big library should use a module splitting system like they did with Play Services?
EDIT: Besides, the multidex task takes too long (one minute and a half when before was less than 15 seconds), so each time I want to launch the app I have to wait. So, even when using Proguard is a great solution for a production release, it's not suitable for development as it takes longer than multidexing.
There is ongoing work to improve Guava on Android, but there are no plans to split it into smaller modules. As mentioned, ProGuard lets you exclude the parts of Guava you don't use from your final build.
I have multiple Android applications, and I've created a common Android library project, and a common Java library project (The Android library project compiles the java one). These libraries are filled with common components that I use for all my Android apps.
I'm using gradle as my build system.
I'm using git for versioning.
What would be the best way to link everything together? Keep in mind things are still being added / changed in the library, and I need a way to propagate changes to all the Android apps. Copy / Paste wouldn't be a great option. I've tried a few things, and they aren't working out very well, so I'd love some input.
EDIT: It's probably also worth mentioning that multiple people are working on these projects. It's not just me.
The current version of Android Studio has a limitation that all of its modules must be under the project's root directory in the filesystem, and this limitation hampers a lot of people in your situation, because frequently they want those common libraries to live someplace else. It seems like this is the case for you as well.
We're in the process of lifting this limitation in Android Studio, and soon you'll be able to have modules outside the project root. I think this might be the best solution for you -- you can pull your common libraries from wherever makes sense in source control, put them wherever makes sense in your filesystem, and link them up into whatever projects need them. However, this isn't available yet, but will show up in v0.5.0, which will hopefully go out this week. I haven't personally tested it in our dev builds and can't vouch for how well it works, but at any rate it should be coming along soon.
Some developers have worked around the limitations by adding script to their settings.gradle files to set different module root directories. They say it works, but I find it a little scary because the IDE just isn't expecting things to work that way, and I don't know for sure if there are problems with it.
If you read other answers to this question on Stack Overflow, they're written before this feature was implemented and will have different advice. If you don't want to wait for 0.5.0 or there are problems in it that prevent you from using it, you can follow that general advice, which is to have your common code compile to libraries that you publish to a Maven repository (which can be local to your machine or common to the developers in your group), and pick up those libraries with Maven-style dependency statements in the projects that need them. This has the disadvantage that you'll need to open up separate projects to edit the code in those libraries, along with a more complex build process, but it will work.