Kotlin common library to reuse in multiple MPP - android

I'm setting up a Kotlin multiplatform project so I can reuse common code in multiple platforms for a single app. While building the common code for the app, I've extracted some base classes that I'd like to be able to reuse as a library in multiple multiplatform projects. I'm trying to add the library as a dependency in commonMain. There are a couple of things I don't understand.
First of all: is this currently possible?
If yes:
The default stdlib-common is a jar file, correct? How come a jar can be referenced as a dependency in commonMain if no Java can be used there? Or is it okay to use a jar compiled from pure Kotlin, as long as it only has Kotlin dependencies?
How do I compile a pure Kotlin jar that can be used in commonMain the same way as stdlib-common is used? Are there any sample build.gradle projects or guides for how this should be packaged?
If no:
What options do I otherwise have to reuse code over multiple multiplatform projects, if I want to avoid duplication? Do I actually need to keep all source within the actual commonMain source folder? Can it be linked from another folder if so? I tried adding additional content roots but it didn't seem to work since Gradle controls the configuration and I'm not sure how to add additional content roots in commonMain through Gradle.
Thanks in advance.

I got it working, mainly from looking through this thread and looking at this example. Although some of it might be dated by now, it helped me understand the following:
MPP1 can have another MPP2 as a dependency. Here is a list of MPP libraries for reference.
MPP2 needs to generate artifacts for the same set of platforms as it is used in by MPP1.
MPP2 generates platform artifacts along with a module file where they are described. MPP1 can then use the below configuration. Thanks to the module file, it's not required to explicitly add each platform's corresponding dependency, the dependency only needs to be declared in commonMain.
commonMain {
dependencies {
implementation kotlin('stdlib-common')
implementation 'com.company:mpp2:1.0'
}
}

Related

What are benefit to use library module in Android Studio?

I have read some sample codes, I find that many project use library module structure, you can see Image A.
Could you tell me the benefit to use library module in Android Studio ?
What code do I need to place it in library ?
And More, both app and lib module use the same namesapce in the sample code, I don't know if it's suitable, could you tell me ?
Image A
Library module gives you two options to create library Android and Java.
Android library module-> Android library compiles into an Android Archive (AAR) file that you can use as a dependency for an Android app module . It allows you to add android specific components like resources and manifest file, which allows you to bundle in shared resources like layouts and drawables in addition to Java classes and methods.
Java Library -> It builds a JAR file. JAR file is useful for many projects especially when you want to share code with other platforms. But it does not allow you to include Android resources or manifest files, which is very useful for code reuse in Android projects. So when you do not need any android specific resources in library you should create a java library.
Library modules are beneficial for projects :-
When you're building multiple apps that use some of the same components, such as activities, services, or UI layouts.
When you're building an app that exists in multiple APK variations, such as a free and paid version and you need the same core components in both.
Quoted from developer.android.com
Other than that same namespace is not problematic unless you have same package name inside App and libraries . You should use a different namespace for libraries.
PS-> If you are familiar with Clean Architecture, The idea behind most of the software design pattern is Separation of concern . In Clean architecture a project is divided into multiple modules. When you implement clean architecture in android you'll see that some of the module you can create as Java library like domain module. Creating module is really useful to follow re-usability and SOLID principles and Inversion of control.
Firstly, don't look into the package name declared in the java directory. Look into the manifest file. You can see that these modules have different package name. It means that all modules in a project must have different package name.
Regarding to your question, what are the benefit of naming library module as lib?
There's no benefit at all. Some people are comfort with lib name, so they can differentiate the demo and library module easily. However, using lib as library's module name requires you to add additional configuration in the lib/build.gradle, i.e. archiveBaseName. This Gradle attribute will rename the JAR/AAR from lib.aar to work-runtime.aar, so people can use it like this:
implementation "androidx.work:work-runtime:$work_version"
If archiveBaseName is not set, people will use it like this:
implementation "androidx.work:lib:$work_version"
In real case, let's take my open source library as the example, MaterialPreference. I used to use lib name on this project, but now I think lib is not a good module name. Using materialpreference as module name will remove additional configuration archiveBaseName. So I feel it is more simple.

Android plugin - Cannot reference AAR's resources

EDIT:
After further exploring the question actually is: How to include a AAR file in a plugin for an app?
I am writing a plugin, then the plugin is loaded by another app. In this case I cannot find the resources. But I can access all classes.
If I turn my plugin into a standalone app. I can access even resources.
How to make the aar work if included in the plugin and loaded in another app?
So I know there is a million questions like this, however nothing seems to work, so maybe I am missing something.
I created a simple android library - File -> New -> New module -> Android library
I added the library into my project
flatDir{ dirs "./aar" } // project gradle
implementation (name: "the_library_name", ext: "aar") // module gradle
I can access any Kotlin class from the library
I cannot access any resource neither from aar itself or the project referencing the aar
In my project: Resouces$NotFoundException: String resource ID...
context.getString(R.string.some_string)
// R -> com.my_project.test.R
In my library code: Resouces$NotFoundException: Resource ID type #0x5 is not valid
inflater.inflate(R.layout.some_layout,....)
// R -> com.my_library.name.R
In both cases the code fails when trying to reference AAR's resource. But this works:
com.my_library.name.LibraryClass()
Did a generate the AAR wrong? Is there a special step to take to allow the access to resources? Am I including it wrong in my app?
NOTE: The library is written in kotlin, the app is a combination of kotlin and Java. I am accessing the resources from kotlin classes. Not sure if that matters or not.
NOTE2: I first started my library in the app project - meaning I could see the libraries code in a module and change the code. That wasn't working either. Same problem with resources.
NOTE3: The application does not have any default activity. It is a plugin for another app. I think that has something to do with the fact it is not working. I tried some "sample" projects, and it is working fine there.
Answering this question as it seems it is not related to AAR. It looks like the third party apps fault, because this approach seems to be working anywhere else.

What dependencies should one be putting in each module of an instant app?

I'm in the process of writing an instant app for others to learn how to write an instant app and hoping to get some ideas on how to best structure the app for dependencies.
Now reading the Android developer docs on project structure and referencing the diagram below:
I'm wondering with the new gradle 3.0 dependency configurations, what libraries should live in which modules?
Base Feature
I was thinking pretty much anything in the base feature module should be using the api gradle configuration since the base feature module essentially compiles down to an AAR library file. One question I might have for this module, if one was to use ROOM would this be the module to place it in?
Feature
Now in the feature modules, it is my understanding that everything should be utilizing the implementation gradle configuration since these modules should not leak there dependencies out to any other modules in order to truly make them independent from one another.
Just looking for some confirmation of my understanding and also any ideas to help with the project. Here is the github repo if you want to check out the code I got so far. It is really simple at the moment, but I was thinking about messing around with the Star Wars API using Retrofit.
Thanks for any help and gladly accept any contributions if you want to to try and make a pull request yourself for any other concepts in making an instant app that others should know.
Shared details in your question are correct. Consider some of the below suggestions which add to the points mentioned by TWL:
Adding certain libraries to specific feature module which should
be included in the feature module only, instead of being added in the
base APK.
For example, let's say you have an application that depends on
libraries X, Y, and Z. Initially, you may pack all the libraries in
the base module by placing all the dependencies in the base
gradle.build file. But if only the code in the feature module requires
library Z, it makes sense to move that dependency from the base module
to the feature module.This works as long as no other feature modules
depend on the same library. If multiple feature modules use the same
library it definitely makes sense to keep it in the base module.
Taking care of Transitive dependencies.
Transitive dependencies occur when the library your project relies
upon depends on another library, which in turn may depend on yet
another library. Sometimes those transitive dependencies may contain
unexpected surprises such as libraries you do not need at all (i.e. a
JSON processing library you never use in your code.)
I hope this adds some information to your query,
I'm wondering with the new gradle 3.0 dependency configurations, what libraries should live in which modules?
Some of these links can also be referred for additional data:
Android Instant Apps(best-practices)
AIA structure
As mentioned by keyboardsurfer, your dependency assumption is in the right direction.
Base is at the root and acts like a library shared by all the
non-base feature modules, so its shared dependencies should be set with
api so that modules that depend on it can also access them. (though, base doesn't have to act only like a library, it can
also be a feature APK itself)
Features, as an instant app, each one extends out to the end as its own APK, so there's no reason it should be leaking its dependencies to any other modules and therefore dependencies should be set with implementation here.
Within the Google Samples, the cookie-api and install-api are some samples that more clearly demonstrate the dependency configuration usage as how I explained above.

creating a gradle dependency - remove access to its own dependencies

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.

Android Studio Gradle External Library Project

Ok, I realize that Gradle and Android Studio seem to think that all Library Applications are built for one project and one project only, but that is not the case. I have many shared Library Applications with common purposes that are shared throughout the organization. Gradle does not seem to be very accomodating to this desired solution. Can someone offer any insight?
My current Structure at a very rudimentary level is like this:
|--Directory
| |--PROJECT A
| |---Module 1
| |--Project B
| |---Module 2
| |--Project c
| |--Module 3
/////////////////////////////////////////////
My Current dependency structure is like this:
/////////////////////////////////////////////
Project A: (FYI, Builds Just Fine)
Project A's settings.gradle
include ':Module 1', ':Module 2'
project(':Module 2').projectDir = new File('../Project B/Module 2')
Module 1's build.gradle
dependencies {
compile project(':Module 2')
}
Project C: (FYI, BROKEN)
Project C's settings.gradle
include ':Module 3', ':Module 1'
project(':Module 1').projectDir = new File('../Project A/Module 1')
Module 3's build.gradle
dependencies {
compile project(':Module 1')
}
Breaks: Cannot resolve Module 2 inside of Module 1's build.gradle file.
This is because the directory structure for Module 2 is established inside Project A's settings.gradle so Project B has no idea where to render this from.
I understand that I can add
project(':Module 2').projectDir = new File('../Project B/Module 2')
to Project C and everything will work just fine. However Project C doesn't use or know about Module 2. I want other developers to have the freedom to use my common shared library project without having to dig in and see what library projects I used and include those in their settings as well. How can I specify my own dependency directory structure in the build.gradle instead of the settings.gradle to make it accessible to all that use it?
On a second note, but similar topic. I'm having the exact same issue with JAR files. If i specify a REPO in a Library Project's build.gradle like: myRepo1 and have a myJar1. Then when that library project is used in a parent project that doesn't define the repo that contains the jar in the library projects dependeny section, it fails to resolve the jar file from the library project when compile project(':libproject') is used. I have to duplicate the repo pointers in the parent's build.gradle file as well so that the libproject will build from the parent app. Any help on this one would be appreciated as well. As not every repo is used in every app so this can become redundant.
Ok this is a really old post, but still gets traction so let me update 3 years later since I originally wrote it lol.
Shout out to CommonWare who had the right best practice idea right from the start, but didn't provide an answer to mark up.
Let me start by saying that using project references like I was doing above should be limited to development stages only and should only be if the library project is also in development stage at the same time as the main project. Otherwise a dependency management server like Nexus, Apache Archiva, or S3 with Maven directory structure or equivalent would be preferred. I have learned many ways to manage dependencies since this, including transitive dependency management.
My preferred method is to deploy artifacts with POM files to Apache Archiva and then use these dependencies within the parent project instead of using relative paths to reference code projects now. This is the first choice.
However, if you are too new to dependency management and choose not to have a server for this purpose, you may package your AAR files or JAR files and put them in one centralized repo like artifact_repo and have everyone include that repo at the same folder structure and reference them relatively, but this is not good practice so I would steer clear if you can.
You can also take the artifacts and nest them in you libs directory and bring them in that way if you would like, but it becomes more of a manual update process which some people like and others do not.
Now this opens a whole different set of issues that you need to handle.
Transitive Dependencies and Child Repo pointers.
For example, if you wrapped your own Crash Reporting Library around Fabric or Hockey or other hoping to make it easy to trade libraries later, then you have found that the repo pointer has to live in the parent build.gradle files or the transitive dependencies are not found.
You could of course use one of those hacky Fat_AAR or Fat_JAR scripts that works "sometimes" until updated gradle then they break again until someone hacks it back together, but this is also poor practice as you are creating potential mismatch dependencies on support or other important child libraries and the "exclude transitives" only works if you are using pom files to control the transitives and not making the AAR or JAR file fat. So you are limiting your ability to control the dependencies.
So what i have finally come to terms with is that transitive dependencies should be managed through POM files to allow excluding or including without nesting into children libraries. Also libraries that require repo pointers inside of them, should probably not exist as they require parent boiler plate, introduce room for human error and typically don't save much time on wrapping analytics or crash libraries for example or you start getting into json configs that need to live in parent files for PUSH or other reasons. Just avoid it.
So long story short lol. Stick to dependency management tools they way they were intended to be used and you will be fine. It is when you are new to it or start getting hacky that you run into ugly code and ugly problems. Hope this encourages someone to do it the right way :)
One last thing :). I have recently started writing Gradle Plugins to manage my versions and dependencies as a separate file so that I can use intellisense to pull in dependencies and make sure all support, gms, and tool versions are the same across all projects. You can even copy down live templates with your plugin to enable intellisense for Gradle to work with your stuff. It's not too bad to do. Best of luck and happy Gradling :).

Categories

Resources