- Modularizing Android Applications
- Application module
- Core modules
- Abstraction modules
- Feature Modules
- Standard application
- Instant support application
- Dynamic support application
- Instant App Module
- Add build dependencies
- Dependency types
- Groovy
- Kotlin
- Groovy
- Kotlin
- Groovy
- Kotlin
- Groovy
- Kotlin
- Groovy
- Kotlin
- Groovy
- Kotlin
- Native dependencies
- Dependency configurations
- Groovy
- Kotlin
- Deprecated Gradle configurations (available in AGP 1.0–4.2)
- Groovy
- Kotlin
- Groovy
- Kotlin
- Groovy
- Kotlin
- Add annotation processors
- Groovy
- Kotlin
- Pass arguments to annotation processors
- Groovy
- Kotlin
- Groovy
- Kotlin
- Groovy
- Kotlin
- Disable the annotation processor error check
- Groovy
- Kotlin
- Groovy
- Kotlin
- Exclude transitive dependencies
- Groovy
- Kotlin
- Exclude transitive dependencies from test configurations
- Groovy
- Kotlin
- Configure Wear OS app dependencies
- Groovy
- Kotlin
- Groovy
- Kotlin
- Remote repositories
- Groovy
- Kotlin
- Groovy
- Kotlin
- Groovy
- Kotlin
- Google’s Maven repository
- Groovy
- Kotlin
- Groovy
- Kotlin
- Programmatic access
- Offline repository from SDK Manager
- Native dependencies with the Android Gradle plugin
- Using native dependencies
- Native dependencies in AARs
- Build system configuration
- Groovy
- Kotlin
- Publishing native libraries in AARs
- Groovy
- Kotlin
- Dependency order
- View module dependencies
- Fix dependency resolution errors
- Fix duplicate class errors
- Fix conflicts between classpaths
- Apply custom build logic
- Publish variant dependencies to custom logic
- Custom dependency resolution strategies
- Groovy
- Kotlin
- Dependency information for Play Console
Modularizing Android Applications
We’ve all been there. Single module android applications — at some point in our Android development career it’s likely we’ve worked on, or are working on, a project that consists of a single module. Now, there’s nothing wrong with that at all and in a lot of cases, a single module will most likely do the job.
But in cases where our applications may grow, or we may want to take advantage of new distribution features (Instant apps, app bundles) from Google, or even just create a clear separation of concerns to make our project easier to work with— modularizing our applications can help us to achieve all of these things. And because some of these newer tools from Google are aimed at modularized applications, who knows what may become available in the future for applications that are structured this way.
Recently I’ve playing with Instant Apps as well as the new App Bundles, which means there are now more ways to modularize our projects — and knowing a little bit more about these can help us to know the right ways to configure our projects. Maybe you want to modularize your application, or maybe you want to get a clearer understanding on the differences between the different modules that we can create for our project — in this post we’re going to take a quick look at the different kinds of modules we can create and what these can do for our android applications.
Application module
We’ll start here as it’s likely a module that you’ve already encountered or used at some point during your development career. And if not, then it’s the most important place to start! This is essentially the main module of your application and will often be under the app module, using the application plugin in the modules build.gradle file to signify this:
Now if you’re building a single module application then this will be the only module-type plugin that you will be using throughout your project. In single module projects, this base module will contain all of your applications responsibilities — be in User Interface, Networking, Cache, Data operations — you name it and it will be there.
If you’re building a multi-module application, then the application module will still be the installed module, but the way this application module operates will vary depend on what it is you are building:
- If your multi-module project simply abstracts out data responsibilities of your application into data modules, then the application module will operate in the same way that you have worked with it in single-application modules but just with a clearer seperation of concerns.
- If your multi-module project is supporting dynamic feature modules then this application module will behave in pretty much the same way as the single module application . The only difference is that the module will need to define the module references for the dynamic features that are to be supporting for the application instance.
- If your multi-module project is for instant apps then this application module will simply be used to define the feature-module dependencies for the instant app. This is because when an instant app is being built, this application module does not contain any application code as its responsibility is just consume the feature modules which are declared.
Core modules
You can essentially use these modules to separate out related areas of your project so that they are decoupled from the main module of your project. There’s not really a coined term for these but lets call them core modules.
For example, let’s say you have a project that supports both Android wear and Android Phones, you might have a module for the Wear UI layer, Phone UI layer followed by a core module. This core module will contain all of the shared business logic, data operations, caching etc that is used by both of the UI modules. In this case, you are promoting code re-use as all of these aspects can be shared between the two UI modules.
Re-use isn’t the only reason as to why you might want to create this kind of module though. Separating core logic can also help to create a clear separation of concerns that makes your code easier to understand, test and maintain across your team — you may also benefit from build time improvements in some cases.
So for example, you may have a large application with a complex data layer that communicates with both a remote and cache data source. In cases like this, separating out these parts of logic into their own responsibility-specific modules allows you to achieve these things. This could result in us having a data module (to handle the fetching of data and data source), remote module (to handle remote data source communication) and cache module (to handle local data persistence) alongside a presentation module to handle the user-facing parts of our application.
There are no strict guidelines as to what should be in these core modules. My advice would be to not move stuff out into module for the sake of it, but if you feel there will be some benefit for your team (like the ones mentioned above) then moving a responsibility into a module can help you to achieve some of the benefits of modularization.
Core modules that contain android framework references will need to use the library plugin:
Whilst on the other hand, core modules that do not reference the android framework can simply be pure kotlin / java modules:
Abstraction modules
Core modules may often be used to move chunks of shared logic or independent responsibilities, but sometimes we may want to abstract some third-party responsibility out of our application. This can often be a good idea so that our application will not be tightly coupled to a specific implementation of something— in this situation we’d simply communicate via an interface between the app and abstraction module, allowing us to easily switch out the implementation for another if required. Therefore if some library becomes deprecated, a service shuts down or for whatever reason you need to change the implementation then you can do so with minimal interruptions to your code base.
In these cases, your abstraction modules will either use the android library plugin or simply be a pure kotlin / java module (depending on the library dependencies) that we previously looked at.
Feature Modules
As well as this approach to splitting out back-end related tasks into core modules, you can do the same for user facing features within your app — we call these feature modules. These feature modules are going to contain specific features of our application which can help us to again decouple different responsibilities to achieve the same benefits as we’ve previously looked at. Now, the way that these are defined will depend on the kind of multi-project application that you are building, so let’s take a look at the different scenarios:
Standard application
In a ‘standard’ application where you are not using instant apps or dynamic delivery, you may still want to split individual features out into modules to achieve some of the benefits from modularization, whilst also future-proofing yourself incase your app moves in an instant/dynamic direction along the line. In these cases you will simply package up a feature into an android library module and add it as a dependency to your application module.
In cases where you are not supporting instant apps, or not ready (or need) to support dynamic delivery yet, featuring by library modules can still help you to decouple your codebase and achieve other benefits of modularization. In these cases you can simply declare modules as library modules using the library plugin and add them as a dependency to your application module.
Instant support application
On the other hand, if your app supports Play Instant then you will need to use the feature plugin for your desired feature module:
Each feature module will need to have a reference to your base module (in this diagram, the app module) — the feature module in this case will only contain the required code for the implementation of that feature, any code that is required from the core of your app will be obtained from the base module reference.
Dynamic support application
These kind of modules can be used to decouple specific features and resources of your application into their own module, to then be used as part of the new App Bundle format which allows us to dynamically deliver individual features to users. This means we can reduce the initial install size of our applications, instead allowing users to download specific features at run-time as and when they are required.
Just like the instant app focused feature module, these dynamic-feature modules will contain pure feature implementations whilst still having a reference to the base module of the application. The only difference within this module is that the manifest file contained within it has some additional information regarding the distribution of this module.
We can define a module as a dynamic feature module by using the provided plugin below:
Note: In future app bundles will provide support for instant apps.
Instant App Module
Last but not least, if you’re currently wanting to support instant apps then your application will need to be provide an instant module.
The contents of this module will be pretty empty most of the time — this is because the responsibility of the Instant Module is to simply build the instant app when request. Therefore, its sole purpose is to consume the feature modules that have been referenced as a dependency. When defining an instant module you will need to use the instant app plugin in your modules build.gradle file:
The aim of this article was to provide a brief overview of the different modules that are available for android projects, as well as plant some seeds as to how you could approach modularization within your app. If you have any questions about modularization then please do reach out 🙂
Источник
Add build dependencies
The Gradle build system in Android Studio makes it easy to include external binaries or other library modules to your build as dependencies. The dependencies can be located on your machine or in a remote repository, and any transitive dependencies they declare are automatically included as well. This page describes how to use dependencies with your Android project, including details about behaviors and configurations that are specific to the Android plugin for Gradle. For a deeper conceptual guide to Gradle dependencies, you should also see the Gradle guide for dependency management —but remember that your Android project must use only the dependency configurations defined on this page.
Dependency types
To add a dependency to your project, specify a dependency configuration such as implementation in the dependencies block of your build.gradle file.
For example, the following build.gradle file for an app module includes three different types of dependencies:
Groovy
Kotlin
Each of these requests a different kind of library dependency as follows:
Local library module dependency
Groovy
Kotlin
This declares a dependency on an Android library module named «mylibrary» (this name must match the library name defined with an include: in your settings.gradle file). When you build your app, the build system compiles the library module and packages the resulting compiled contents in the app.
Local binary dependency
Groovy
Kotlin
Gradle declares dependencies on JAR files inside your project’s module_name /libs/ directory (because Gradle reads paths relative to the build.gradle file).
Alternatively, you can specify individual files as follows:
Groovy
Kotlin
Groovy
Kotlin
This is actually shorthand for the following:
Groovy
Kotlin
This declares a dependency on version 12.3 of the «app-magic» library, inside the «com.example.android» namespace group.
Note: Remote dependencies like this require that you declare the appropriate remote repositories where Gradle should look for the library. If the library does not already exist locally, Gradle pulls it from the remote site when the build requires it (such as when you click Sync Project with Gradle Files or when you run a build).
Native dependencies
As of Android Gradle plugin 4.0, native dependencies can also be imported in the manner described in this document.
Depending on an AAR that exposes native libraries will automatically make them available to the build system used by externalNativeBuild . To access the libraries from your code, you must link to them in your native build scripts. In this document, see Using native dependencies.
Dependency configurations
Inside the dependencies block, you can declare a library dependency using one of several different dependency configurations (such as implementation shown above). Each dependency configuration provides Gradle with different instructions about how to use the dependency. The following table describes each of the configurations you can use for a dependency in your Android project. The table also compares these configurations to those that were deprecated as of Android Gradle plugin 3.0.0.
Configuration | Behavior | ||||||||
---|---|---|---|---|---|---|---|---|---|
implementation | Gradle adds the dependency to the compile classpath and packages the dependency to the build output. However, when your module configures an implementation dependency, it’s letting Gradle know that you do not want the module to leak the dependency to other modules at compile time. That is, the dependency is available to other modules only at runtime. Using this dependency configuration instead of api or compile (deprecated) can result in significant build time improvements because it reduces the number of modules that the build system needs to recompile. For example, if an implementation dependency changes its API, Gradle recompiles only that dependency and the modules that directly depend on it. Most app and test modules should use this configuration. | ||||||||
api | Gradle adds the dependency to the compile classpath and build output. When a module includes an api dependency, it’s letting Gradle know that the module wants to transitively export that dependency to other modules, so that it’s available to them at both runtime and compile time. This configuration behaves just like compile (which is now deprecated), but you should use it with caution and only with dependencies that you need to transitively export to other upstream consumers. That’s because, if an api dependency changes its external API, Gradle recompiles all modules that have access to that dependency at compile time. So, having a large number of api dependencies can significantly increase build time. Unless you want to expose a dependency’s API to a separate module, library modules should instead use implementation dependencies. | ||||||||
compileOnly | Gradle adds the dependency to the compile classpath only (that is, it is not added to the build output). This is useful when you’re creating an Android module and you need the dependency during compilation, but it’s optional to have it present at runtime. If you use this configuration, then your library module must include a runtime condition to check whether the dependency is available, and then gracefully change its behavior so it can still function if it’s not provided. This helps reduce the size of the final app by not adding transient dependencies that aren’t critical. This configuration behaves just like provided (which is now deprecated). Note: You can’t use the compileOnly configuration with AAR dependencies. | ||||||||
runtimeOnly | Gradle adds the dependency to the build output only, for use during runtime. That is, it is not added to the compile classpath. This configuration behaves just like apk (which is now deprecated). | ||||||||
annotationProcessor | |||||||||
lintPublish | Use this configuration in Android library projects to include lint checks you want Gradle to compile into a lint.jar file and package in your AAR. This causes projects that consume your AAR to also apply those lint checks. If you were previously using the lintChecks dependency configuration to include lint checks in the published AAR, you need to migrate those dependencies to instead use the lintPublish configuration. GroovyKotlinDeprecated Gradle configurations (available in AGP 1.0–4.2)
All of the above configurations apply dependencies to all build variants. If you instead want to declare a dependency for only a specific build variant source set or for a testing source set, you must capitalize the configuration name and prefix it with the name of the build variant or testing source set. For example, to add an implementation dependency only to your «free» product flavor (using a remote binary dependency), it looks like this: GroovyKotlinHowever, if you want to add a dependency for a variant that combines a product flavor and a build type, then you must initialize the configuration name in the configurations block. The following sample adds a runtimeOnly dependency to your «freeDebug» build variant (using a local binary dependency). GroovyKotlinTo add implementation dependencies for your local tests and instrumented tests , it looks like this: GroovyKotlinHowever, certain configurations don’t make sense in this situation. For example, because other modules can’t depend on androidTest , you get the following warning if you use the androidTestApi configuration: Add annotation processorsIf you add annotation processors to your compile classpath, you’ll see an error message similar to the following: To resolve this error, add annotation processors to your project by configuring your dependency using annotationProcessor as shown below: GroovyKotlinNote: Android plugin for Gradle 3.0.0+ no longer supports android-apt plugin. Pass arguments to annotation processorsIf you need to pass arguments to an annotation processor, you can do so using the AnnotationProcessorOptions block in your module’s build configuration. For example, if you want to pass primitive data types as key-value pairs, you can use the argument property, as shown below: GroovyKotlinHowever, when using Android Gradle plugin 3.2.0 and higher, you need to pass processor arguments that represent files or directories using Gradle’s CommandLineArgumentProvider interface. Using CommandLineArgumentProvider allows you or the annotation processor author to improve the correctness and performance of incremental and cached clean builds by applying incremental build property type annotations to each argument. For example, the class below implements CommandLineArgumentProvider and annotates each argument for the processor. The sample also uses the Groovy language syntax and is included directly in the module’s build.gradle file. GroovyKotlinAfter you define a class that implements CommandLineArgumentProvider , you need to create an instance and pass it to the Android plugin using the annotationProcessorOptions.compilerArgumentProvider method, as shown below. GroovyKotlinTo learn more about how implementing CommandLineArgumentProvider helps improve build performance, read Caching Java projects. Disable the annotation processor error checkIf you have dependencies on the compile classpath that include annotation processors you don’t need, you can disable the error check by adding the following to your build.gradle file. Keep in mind, the annotation processors you add to the compile classpath are still not added to the processor classpath. GroovyKotlinIf you use Kotlin and kapt: GroovyKotlinIf you experience issues after migrating your project’s annotation processors to the processor classpath, you can allow annotation processors on the compile classpath by setting includeCompileClasspath to true . However, setting this property to true is not recommended, and the option to do so will be removed in a future update of the Android plugin. Exclude transitive dependenciesAs an app grows in scope, it can contain a number of dependencies including direct dependencies and transitive dependencies (libraries which your app’s imported libraries depend on). To exclude transitive dependencies that you no longer need, you can use the exclude keyword as given below: GroovyKotlinExclude transitive dependencies from test configurationsIf you need to exclude certain transitive dependencies from your tests, the code sample shown above might not work as expected. That’s because a test configuration (e.g., androidTestImplementation ) extends the module’s implementation configuration. That is, it always contains implementation dependencies when Gradle resolves the configuration. So, to exclude transitive dependencies from your tests, you must do so at execution time as shown below: GroovyKotlinNote: You can still use the exclude keyword in the dependencies block as shown in the original code sample from the Exclude transitive dependencies section to omit transitive dependencies that are specific to the test configuration and are not included in other configurations. Configure Wear OS app dependenciesConfiguring dependencies for a Wear OS module is similar to that of any other module; that is, Wear OS modules use the same dependency configurations, such as implementation and compileOnly . Wear modules also support variant-aware dependency management. As a result, if your base app module has a dependency on a Wear module, each variant of the base module consumes the matching variant from the Wear module. If you are building a simple app with a dependency on only one Wear module, where the module configures the same variants as your base module, you need to specify the wearApp configuration in your base module’s build.gradle file as shown below: GroovyKotlinIf you have multiple Wear modules and you want to specify a different Wear module per app flavor, you can do so using the flavor WearApp configuration, as follows (however, you can’t include other dependencies that use the wearApp configuration): GroovyKotlinRemote repositoriesWhen your dependency is something other than a local library or file tree, Gradle looks for the files in whichever online repositories are specified in the repositories block of your build.gradle file. The order in which you list each repository determines the order in which Gradle searches the repositories for each project dependency. For example, if a dependency is available from both repository A and B, and you list A first, Gradle downloads the dependency from repository A. By default, new Android Studio projects specifies Google’s Maven repository and JCenter as repository locations in the project’s top-level build.gradle file, as shown below: GroovyKotlinIf you want something from the Maven central repository, then add mavenCentral() , or for a local repository use mavenLocal() : GroovyKotlinOr you can declare specific Maven or Ivy repositories as follows: GroovyKotlinFor more information, see the Gradle Repositories guide. Google’s Maven repositoryThe most recent versions of the following Android libraries are available from Google’s Maven repository: You can see all available artifacts at Google’s Maven repository index (see below for programmatic access). To add one of these libraries to your build, include Google’s Maven repository in your top-level build.gradle file: GroovyKotlinThen add the desired library to your module’s dependencies block. For example,the appcompat library looks like this: GroovyKotlinHowever, if you’re trying to use an older version of the above libraries and your dependency fails, then it’s not available in the Maven repository and you must instead get the library from the offline repository. Programmatic accessFor programmatic access to Google’s Maven artifacts, you can get an XML list of artifact groups from maven.google.com/master-index.xml. Then, for any group, you can view its library names and versions at: maven.google.com/ group_path /group-index.xml For example, libraries in the android.arch.lifecycle group are listed at maven.google.com/android/arch/lifecycle/group-index.xml. You can also download the POM and JAR files at: maven.google.com/ group_path / library / version / library — version . ext Offline repository from SDK ManagerFor libraries not available from the Google Maven repository (usually older library versions), you must download the offline Google Repository package from the SDK Manager. Then you can add these libraries to your dependencies block as usual. The offline libraries are saved in android_sdk /extras/ . Native dependencies with the Android Gradle pluginAAR libraries can contain native dependencies that the Android Gradle Plugin can consume. AGP is also capable of producing AARs that expose native libraries to their consumers. Using native dependenciesStarting with Android Gradle plugin 4.0, C/C++ dependencies can be imported from AARs linked in your build.gradle file. Gradle will automatically make these available to the native build system, but your build system must be configured to make use of the imported libraries and headers. Since C/C++ dependencies are distributed as AARs, the following links about generic AARs may be helpful:
This document focuses on how to configure your native build system and assumes you’ve already added a C/C++ dependency AAR into your project’s Gradle build environment. Native dependencies in AARsAAR dependencies of your Gradle modules can expose native libraries for use by your application. Inside the AAR, the prefab directory contains a Prefab package, which includes the headers and libraries of the native dependency. Each dependency can expose at most one Prefab package, which comprises one or more modules. A Prefab module is a single library, which could be either a shared, static, or header only library. The package and module names need to be known to make use of the libraries. By convention the package name will match the Maven artifact name and the module name will match the C/C++ library name, but this is not required. Consult the dependency’s documentation to determine what names it uses. Build system configurationAndroid Gradle Plugin 4.0 Android Gradle Plugin 4.1+ The prefab feature must be enabled for your Android Gradle module. To do so, add the following to the android block of your module’s build.gradle file: GroovyKotlinDependencies imported from an AAR are exposed to CMake via CMAKE_FIND_ROOT_PATH. This value will be set automatically by Gradle when CMake is invoked, so if your build modifies this variable be sure to append rather than assign to it. Each dependency exposes a config-file package to your build. These are imported with the find_package command. This command searches for config-file packages matching the given package name and version and exposes the targets it defines to be used in your build. For example, if your application defines libapp.so and it uses cURL, your CMakeLists.txt should include the following: app.cpp is now able to #include «curl/curl.h» , libapp.so will be automatically linked against libcurl.so when building, and libcurl.so will be included with the app. Publishing native libraries in AARsThe ability to create native AARs was first added in AGP 4.1. To export your native libraries, add the following to the android block of your library project’s build.gradle file: GroovyKotlinIn this example, the mylibrary and myotherlibrary libraries from either your ndk-build or CMake external native build will be packaged in the AAR produced by your build, and each will export the headers from the specified directory to their dependents. Dependency orderThe order in which you list your dependencies indicates the priority for each: the first library is higher priority than the second, the second is higher priority than the third, and so on. This order is important in the event that resources are merged or manifest elements are merged into your app from the libraries. For example, if your project declares the following:
Then, the flat dependency order will be as follows: This ensures that both LIB_A and LIB_B can override LIB_C ; and LIB_D is still higher priority than LIB_B because LIB_A (which depends on it) has higher priority than LIB_B . For more information about how manifests from different project sources/dependencies are merged, see Merge multiple manifest files. View module dependenciesSome direct dependencies may have dependencies of their own. These are called transitive dependencies. Rather than requiring you to manually declare each transitive dependency, Gradle automatically gathers and adds them for you. The Android plugin for Gradle provides a task that displays a list of the dependencies Gradle resolves for a given module. For each module, the report also groups the dependencies based on build variant, testing source set, and classpath. The following is sample report for an app module’s runtime classpath of its debug build variant and compile classpath of its instrumented test source set. To run the task, proceed as follows:
For more information about managing dependencies in Gradle, see Dependency management basics in the Gradle User Guide. Fix dependency resolution errorsWhen you add multiple dependencies to your app project, those direct and transitive dependencies might conflict with one another. The Android Gradle plugin tries to resolve these conflicts gracefully, but some conflicts may lead to compile time or runtime errors. To help you investigate which dependencies are contributing to errors, inspect your app’s dependency tree and look for dependencies that appear more than once or with conflicting versions. If you can’t easily identify the duplicate dependency, try using Android Studio’s UI to search for dependencies that include the duplicate class as follows:
The following sections describe the different types of dependency resolution errors you may encounter and how to fix them. Fix duplicate class errorsIf a class appears more than once on the runtime classpath, you get an error similar to the following: This error typically occurs due to one of the following circumstances:
Fix conflicts between classpathsWhen Gradle resolves the compile classpath, it first resolves the runtime classpath and uses the result to determine what versions of dependencies should be added to the compile classpath. In other words, the runtime classpath determines the required version numbers for identical dependencies on downstream classpaths. Your app’s runtime classpath also determines the version numbers that Gradle requires for matching dependencies in the runtime classpath for the app’s test APK. The hierarchy of classpaths is described in figure 1. Figure 1. Version numbers of dependencies that appear across multiple classpaths must match according to this hierarchy. A conflict where different versions of the same dependency appears across multiple classpaths might occur when, for example, your app includes a version of a dependency using the implementation dependency configuration and a library module includes a different version of the dependency using the runtimeOnly configuration. When resolving dependencies on your runtime and compile time classpaths, Android Gradle plugin 3.3.0 and higher attempt to automatically fix certain downstream version conflicts. For example, if the runtime classpath includes Library A version 2.0 and the compile classpath includes Library A version 1.0, the plugin automatically updates the dependency on the compile classpath to Library A version 2.0 to avoid errors. However, if the runtime classpath includes Library A version 1.0 and the compile classpath includes Library A version 2.0, the plugin does not downgrade the dependency on the compile classpath to Library A version 1.0, and you still get an error similar to the following: To resolve this issue, do one of the following:
Apply custom build logicThis section describes advanced topics that are useful when you want to extend the Android Gradle plugin or write your own plugin. Publish variant dependencies to custom logicA library can have functionalities that other projects or sub-projects might want to use. Publishing a library is the process by which the library is made available to its consumers. Libraries can control which dependencies its consumers have access to at compile time and runtime. There are two separate configurations that hold the transitive dependencies of each classpath which must be used by consumers to consume the library as described below:
To learn more about the relationships between the different configurations, go to The Java Library plugin configurations. Custom dependency resolution strategiesA project may include a dependency on two different versions of the same library which can lead to dependency conflicts. For example, if your project depends on version 1 of module A and version 2 of module B, and module A transitively depends on version 3 of module B, there arises a dependency version conflict. To resolve this conflict, the Android Gradle plugin uses the following dependency resolution strategy: when the plugin detects that different versions of the same module are in the dependency graph, by default, it chooses the one with the highest version number. However, this strategy might not always work as you intend. To customize the dependency resolution strategy, use the following configurations to resolve specific dependencies of a variant that are needed for your task:
The Android Gradle plugin includes getters that you can use to access the configuration objects of each variant. Thus, you can use the variant API to query the dependency resolution as shown in the example below: GroovyKotlinDependency information for Play ConsoleWhen building your app using AGP 4.0.0 and higher, the plugin includes metadata that describes the library dependencies that are compiled into your app. When uploading your app, the Play Console inspects this metadata to provide alerts for known issues with SDKs and dependencies your app uses, and, in some cases, provide actionable feedback to resolve those issues. The data is compressed, encrypted by a Google Play signing key, and stored in the signing block of your release app. We recommend keeping this dependencies file for a safe and positive user experience. However, if you’d rather not share this information, you can opt out by including the following dependenciesInfo block in your module’s build.gradle file: For more information on our policies and potential issues with dependencies, see our support page on using third-party SDKs in your app. Content and code samples on this page are subject to the licenses described in the Content License. Java is a registered trademark of Oracle and/or its affiliates. Источник |