Good day. I've written a kotlin android library and uploaded it on bintray. But when I try to use it (via gradle compile) in some project, it fails to build with following errors:
> com.android.build.api.transform.TransformException: com.android.builder.packaging.DuplicateFileException: Duplicate files copied in APK META-INF/library_release.kotlin_module
File1: C:\Users\Charlie\.android\build-cache\2939fbc6b0336396c9c4912d615880645b2c729d\output\jars\classes.jar
File2: C:\Users\Charlie\OneDrive\Dev\Projects\AndroidStudio\MetuCardLib\demo\build\intermediates\bundles\default\classes.jar
I've looked up both of these .jar files and they both contained META-INF folder with library_release.kotlin_module file. But more importantly generated .aar (android archive published in bintray) contained this folder as well as this file. I've checked other decent bintray android libraries and they don't seem to include any META-INF files. However those that do include it (in most cases they contain license files) produce the same DuplicateFileException and the way to resolve it is to explicitly exclude them in use-project's gradle file.
What's the use of this library_release.kotlin_module file and how can I disable its generation during publishing? Because I don't want to explicitly exclude from every project which is using this library and I don't want to ask other developers to do so.
Here's the library's repo: https://github.com/arslancharyev31/Anko-ExpandableTextView/
And it's bintray repo: https://bintray.com/arslancharyev31/android/Anko-ExpandableTextView
The original purpose of .kotlin_module files is to store the package parts mapping to the compiled class files. The compiler uses them to resolve top-level function calls in a library attached to the project. Also, Kotlin reflection uses the .kotlin_module files to construct the top level members metadata at runtime. See more: Improving Java Interop: Top-Level Functions and Properties
Given that, you don't want to disable their generation in library projects, because it might break compilation against the libraries.
Instead, you can get rid of the duplicates by using packagingOptions in your app build.gradle, as said here:
android {
// ...
packagingOptions {
exclude 'META-INF/library_release.kotlin_module'
}
}
Note: excluding these files from an application APK interferes with reflection at runtime and therefore is is not the best solution either.
Another option is to use unique module names in your libraries:
compileReleaseKotlin.kotlinOptions.freeCompilerArgs += ["-module-name", "my.library.id"]
Choose the task that produces the output that is then packed into the AAR.
It might not be compileReleaseKotlin, and also note that doing this might affect the tests compilation for this variant.
Or, what's more reliable, just choose unique Gradle module names, because the Kotlin module is named after the Gradle project.
I was able to resolve this by renaming modules to be uniquem when simpler module names conflicted.
EDIT: as we just learned the hard way - kotlin module name should only contain all filesystems supported characters.
We used ':' in the module name causing jar to contain file "company:product.kotlin_module". This will fail to extract on windows.
I thin in android realm currently(tested on Android gradle plugin 7.0.2)best solution would be to use android kotlin options section
android{
kotlinOptions{
moduleName = "com.example.mylibrary"
}
}
It results in META-INF/com.example.mylibrary.kotlin_module - which should be sufficently unique
Related
I have a MyLocationService library, which has dependency from huawei_location_service.Inside I have HMSLocationService class which is the only one using huawei_location_service classes and I use relfection to access that class. Meaning is, if we run app on Huawei and if there is dependency from huawei_location_service, I will get location, otherwise will not. And application should run perfectly on non-hauwei devices without dependency from huawei_location_service.
So when I build MyLocationService.aar I removed huawei_location_service dependency from it's pom file. After that I created a new application and added dependency from MyLocationService.aar. When I check dependencies with command gradlew app:dependencies I don't see any dependency from huawei, but when I create an apk and analyze it, in classes.dex there are classes from huawei_location_service.
Question: How it is possible? And is there any other way to achieve what I want?
P.S. I analyzed also MyLocationService.aar, didn't find any huawei dependency. Is there another way to check dependencies of *.aar files instead of pom or analyzing tool of android studio?
So if someone will be mistaken as me, this answer will help.
The repositories and classes I saw in classes.dex were not coming from hms libraries. As I have imports in my custom classes, that imports' texts were the reason I was seeing huawei folder in classes.dex. Also take attention on the size, and you can see that they are kind of 20 bytes.
So I removed the imports, generate my library again, created apk and analyzed it and woala, no huawei folder is visible.
P.S. *.aars doesn't contain any library if you not put transitive=true. And you need to add dependencies required by your lib in your own applicaiton.
P.S.S. If you have locally or globally publishing your library, maven(Gradle uses maven) creates metadata, so called POM file, as a helper to identify all dependencies that the library needs.
Is there any difference in the size of the resulting Android APK file between:
Adding a dependency via a local AAR file (e.g., via File > New Module > Import .JAR or .AAR package)
Adding a dependency via a Gradle dependency (e.g., implementation "com.foo.bar:bar:1.2.3") that points to same AAR file
If there is a difference, what could be the cause of that difference?
No, it doesn't matter where the AAR is sourced from. The size should be equivalent in both cases. Gradle simply provides an easy method to include the AAR without having to put it and all its dependencies in your repository.
One situation in which it might differ is if the Gradle dependency has additional dependencies that aren't strictly necessary. In that case, those dependencies would also be included, increasing the size of your APK. That would be a misconfiguration on the part of the dependency author, though, and something they should fix. In general, this shouldn't happen.
In Android, why the aar files can speed up compilation
I guess maybe because the content in aar file already compiled or it has the cache of compilation result
AAR files are more similar to Jars than to Dlls for the following reason:
Dlls can be shared across applications where as AARs and jars are
packaged in with your app.
AARs vs Jars:
The main difference between a Jar and a AAR is that AARs include
resources such as layouts, drawables etc. This makes it a lot easier
to create self-contained visual components. For example if you have
multiple apps that use the same login screen, with Jars you could
share classes but not the layout, styles, etc., you still had to
duplicate them. With AARs everything is bundled in one neat package.
In conclusion, AARs are a big step in the right direction.
Note:
Similar attempts were made with apk-libs but they are now obsolete as AARs are much better.
Because it can include everything needed to build an app, including source code, resource files, and an Android manifest. However, instead of compiling into an APK that runs on a device, an Android library compiles into an Android Archive (AAR) file that you can use as a dependency for an Android app module.
My scenario is like i'm using same jar in multiple aar and integrating into single project. How to avoid the jar duplication issue.Multiple dex files issue.
I'm not sure if I understand but maybe this will help.
If you're using gradle for every dependency you can define a closure and use exclude property. That way you can define groups, modules, etc, that are to be ignored.
It is often used to solve dependency conflicts in libraries.
Discussion in gradle forums: https://discuss.gradle.org/t/how-to-exclude-transitive-dependency/2119/2
I am new in Gradle.
I've been made android library and I will upload this into maven repository.
But is it possible to add "Readme.txt" into my library?
for example,
When someone add my library into dependencies section in build.gradle and sync.
then Gradle creates(or copy) "Readme.txt" into target project(probably $projectDir or $projectDir/app) and user can read it(Like NuGet)
(Important things of this library, how to use example or something like that.)
I think it is really annoying visit project web-site and read "How to use" every using single library.
I want my library contains how to use text file.
Thank you.
I don't think this is possible, you don't know how the calling use is referencing your project. They are likely only referencing the compiled source code and never running your gradle file.
This is not a good idea, you don't know their exact folder structure, and even if you did you can not be sure that your readme.txt would have a unique name that did not conflict with their project file.s
When using a .jar file you cannot include this as resources cannot be compiled into it. But you can export your library as an *.aar file which can contain resource files. You get this by using "com.android.library" as your plugin type and can then find the aar-file in your build folder after you have built it.
This can then be included in your other project e.g. as a file reference.