We are building an app that imports, via Artifactory, a collection of libraries that are then referenced from the main app.
We are in the process of localizing our app. We can localize our libraries and verify it works by directly using the libraries in a test app. When the libraries are pushed to our artifactory, we can then import, via gradle, the libraries for use in our app.
We can verify that making code changes or even changes to English strings in the libraries all work, and when pulled into the main app, any such changes are reflected correctly. However, while we have localized our libraries (into Spanish), when pulling the localized libraries into the main, the Spanish strings are not used. Instead, it uses the default (English) strings. We have looked at the libraries on our artifactory, downloaded the AARs and verified that the snapshots do in fact contain the localized strings but for reasons we can't figure out when imported into the main app, it does not use them.
We're confused why this is the case - wether we are doing something wrong or if there is a bug in gradle or Android.
Does anyone have any insight? I apologise for the vagueness but the issue is rather esoteric so I'm not sure what code if any might be relevant to solving the issue. Feel free to ask for further clarification.
UPDATE:
We decided to import the .AAR directly, by placing it in a libs/ directory and referencing it in our Gradle build. It now works fine, the library in question is correctly localized.
So it would seem the issue comes from when the snapshot is downloaded from the repository.
The app is looking at the wrong version of the library: the previously released version rather than the latest SNAPSHOT.
Full disclosure: James and I work together.
Related
When an APK is built with AndroidX libraries, I get a lot of small text files like META-INF/androidx.core_core.version. What is the purpose of these files? How are they used by androidx?
I was recently curious as well, so I browsed the source code a bit.
The first attempt for version information injection was here; it was implemented by adding a meta-data tag into AndroidManifest.xml.
Then came a pair (1, 2) of commits which added a task (into Gradle plugin, that is shared between many/all? androidx libraries), whose sole purpose is to generate the *.version files. This approach replaced the earlier meta-data tag injection, where the reason is (from commit message):
Previously support library tracked its usage by adding meta-data tags
to each library, however that caused issues for certain libraries as
there is a high cost associated with services and meta-data in package
manager.
To work around this issue move to storing version information in java
resource inside the jar META-INF/group_name.version.
However, that doesn't answer the question - as none of these commits mention the reasoning behind storing the version information in androidx libraries.
I've also tried to search for *.version usages in the androidx codebase, but unfortunately couldn't find anything interesting.
Eventually, the author of *.version generation task was kind enough to shed some light on their purpose:
The main purpose for them is for us to be able to track which versions
of androidx libraries are being used by the apps in the play store or
when someone submits a failing repro APK. We also recently started
using in compose inspection tool on studio side to detect what
features should be available given the library version.
Some of my apps use publicly available libraries. I feel comfortable downloading such libraries as jar files, given that the code of a jar stored locally is "safe" with me.
Sometimes, however, the library is only available to be added as a dependency in the module's build.gradle, as below:
implementation 'com.darth.vader.lib.filechooser:filechooser:1.1.0'
This worries me because I have zero control over that code, and no clue if/when it changes.
Can somebody enlighten me on the pros & cons of the 2 approaches? And, on how I can "save" such "dependency" code?
Depending on where the filechooser:1.1.0 comes from, you do know when it changes (never).
Once the version is published in an immutable repository like maven-central or jcenter you can expect the version to remain immutable.
I have a mature app that needs to have an SDK brought in that wraps the camera and makes it do some spiffy processing while it's running. The SDK has come to me in the form of some aar files but my app still lives in Eclipse. Because of my massive, steaming pile of a branding structure and deadlines for this integration the uncertain timeline required to fully migrate to Android Studio will not work (for now) so I'm going for converting the aars and using them in my app via Eclipse.
The problem I'm having is that I need to kick off the activity in the library but even though I fixed up all my build time reference problems, when running the app once I get to the point that is supposed to kick off the activity I get this error.
I've read through and double checked dozens of how-tos explaining how to consume the aar files and I think I've followed every step including:
Unzip the aar files and dress them up as individual library projects, including the file structure with the resources, the manifest, and the .jar
Make the project that needs the libraries add them as such
Add the .jars contained in the library projects to the build path (this step was not listed in most articles, and wasn't necessary for the project to build, but nevertheless it did not help my problem)
Declare the activity in your AndroidManifest.xml that the library brings in and declares in its AndroidManifest.xml
As I've said, everything looks good at build time, so I'm not sure what else to check. Because I'm reading that Gradle and Android Studio mashes manifests together really well, I have a hunch that it's something I'm supposed to regulate between the manifests but I don't know what it could be if that is it. I've wondered if I'm declaring the 3rd party's activity properly, but I'm not sure how to test it because the only way I can think to test it is to provide gibberish for the namespace but even then there are no complaints. I've also tried dissecting the .apk to look at the .dex file but I could not decipher anything useful.
My guess is that the library may not be building properly in eclipse - even before it's being added as a dependency to the application project.
Try looking at out/classes/* and making sure you have a .class file for the activity in question. I think the .class should actually be in the library as well as end up in the application project's out/ dir also.
If there are any native files (x.so) (as I would imagine there might be for spiffy camera stuff), you can look for the x.so files being included in the out/ dirs of both the library and application projects as well as the library.jar file.
Another option to maybe consider for this use case: https://github.com/ksoichiro/gradle-eclipse-aar-plugin
It seems that our app had a bad version of the appcompat-v7 support library. The .jars in it were different sizes than the one that comes with the SDK and several resources were missing. I have no idea how we ended up that way or where this bad version came from. Once I replaced it, things went great.
Later, I did encounter the need to drop in the .so files into the libraries I made that came out of the .aar files as Stad Kurdziel said in his answer, but that was causing a different error (the exception explicitly states that the .so is missing) and I arrived at the solution independently.
I would like built a closed source android library using the Gradle. My library has some dependencies to open source projects. How should I structure my library? Can I use gradle?
Can I use gradle?
Short answer:
Yes.
Long answer:
I would assume that your library is packaged as aar (contains resources and compiled bytecode).
First thing you need to know is that at the moment of writing this post there is no way to create fat-aar libraries, which means that you'll have to distribute dependencies of your library separately. The most convenient way to do that, in my opinion, is to generate pom.xml file and publish your library on Maven repository (maven plugin can do all of that), so clients will just fetch all dependencies themselves. Since it is a "private" library, that could be your company's closed repo by access rights (in simple words - create special user for your repo and share password with interested parties).
One downside here is that all dependencies will be exposed in pom.xml and you won't be able to obfuscate them. Personally, I don't think that this is an issue.
Moreover, you get the huge advantage of being able to deploy build automatically and let clients use snapshot versions of the library. This is extremely helpful when you're trying to fix issues and want to deliver them to users fast. On client's side, all they need to do is either just update version in their build.gradle or just re-sync project in case if they were using snapshot.
Second thing. Since your library is closed source, you need to run proguard to obfuscate everything but public interface of your library (all public methods which are exposed to end user).
Remember, that even after obfuscation your code still can be decompiled and all string literals are still there. So, although it was said million times already, avoid storing any critical data in the library (such as passwords, keys, etc.). It is not as hard to extract it as you might think it is: https://www.youtube.com/watch?v=X28Oogg2Q3k
Third thing. I highly suggest you to create internal test project (as a gradle submodule) which will use your library, so you will be sure that you're not making any breaking changes.
Hopefully this answer made things at least a bit easier for you.
So, I've got a handful of "Utility" style classes in some of my projects. I'm curious if I can move them to an Android Library Project that contains all or most of my non-app specific glue code (wrappers and interfaces, mostly).
So, my question is what happens to the files I don't need in that library. I know Android Library Projects basically just copy their code into the other project, so if I say use 25% of the code in my "general purpose" library, will my app actually contain the bytecode for all 100%, or does it properly strip it down to only the stuff I need.
I had some issues with unused classes in Proguard in the past, so I'm just once-bitten, twice shy with the ADT now...
Unfortunately, all your projects will grow when the library is getting bigger - even if most contents of that library are not used. I tested it myself by creating an app A and a library L. If L is a library used in A, the classes.dex file (and therefore the A.apk file) is growing if I add more classes - even if they are not used.
To sum up: Right now I would create a basic library for certain things that are small and that may be used by many projects, and create a new library for every new component that is going to be larger and only is used by some projects. A good candidate for a new library would be a new UI component with multiple images defined in the resources. A good candidate for the base library are commonly-used methods and things like file caches, for example. Compiled code is compressed quite heavily for Dalvik, which you can see here. (The whole presentation is actually fun to watch :-)
Edit: If ProGuard is activated, it will also remove unused code for you. The default proguard.cfg is sufficient. It will not run on the (default) debug built, but when the final .apk is compiled. So it actually is possible!
I have used 3 level deep Android library projects successfully though it is kind of a pain. The primary use-case is when there are a set of resources and classes that you want to share across a few projects. Since I use a version control system, I would rather not use symlinks.
Note that Android Library projects also suffer greatly when dealing with resources. ADT will rebuild R.java once for each library, and each R.java will contain a copy of all resource ids from all libraries. The core problem here is that resources are regenerated for the entire project as a whole, and there is no way to "build a jar" for a dependency as would be expected with normal "libraries". We tried integrating with OpenFeint, and had all kinds of hell dealing with libraries and dependencies. I think we ended up just merging all the OpenFeint source and resource files into our own project and ditching the "Library" project as it was offering little value.
Android Library projects are a clunky way of sharing code between projects and have a number of drawbacks. I've found that everything accomplished with a Library project can also be accomplished with symlinks (symlink source into two projects). I've yet to find a usecase where an Android Library project offered something that wasn't easy to replicate with other, less fragile means.