Can I preserve older versions of library used by a dependency? - android

I have an Android project where Glide v4 is one of its dependency.
This project has another dependency, let's call it dependency A , where it depends on Glide v3 instead.
I don't know if it matters, but dependency A can only be included as an aar.
So this is part of my build.gradle:
implementation(name: 'dependency_a', ext: 'aar')
implementation ("com.github.bumptech.glide:glide:4.7.1") {
exclude group: "com.android.support"
}
annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'
The app can be compiled; but when code in dependency A runs that uses Glide v3:
Glide.with(context).load(imageUrl).asBitmap().into(new SimpleTarget<Bitmap>() {...}
The app crashes with this message:
java.lang.NoSuchMethodError: No virtual method load(Ljava/lang/String;)Lcom/bumptech/glide/DrawableTypeRequest; in class Lcom/bumptech/glide/RequestManager; or its super classes (declaration of 'com.bumptech.glide.RequestManager' appears in /data/app/{my.package.name}}-LItMzBkBqXw3lyYYdKp-SA==/base.apk:classes15.dex)
I am finding a way to preserve Glide v3 in dependency A, but still use Glide v4 for my app and other dependencies.
Is it even possible?
Why don't I simply use Glide v3 for my app as well
This is because another dependency B needs me to use Glide v4.

Gradle's dependency resolution is about choosing one version when multiple alternatives are available or required.
Ultimately there can only be one Class for a given class full name in a given ClassLoader, so the possibilities are :
Change the package name. For example Spongy Castle moved from org.bouncycastle.* to org.spongycastle.* to avoid conflicts with the platform's version.
Use multiple class loaders. I believe that Android support custom class loader, but this would probably involve quite a bit of work with subtle pitfalls.
I think that unfortunately, none of this is a practical solution in your case.

Gradle resolves version conflicts by picking the highest version of a module as mentioned here. So if you have v3 and v4 as your depencency, v4 will be used.
You are getting the crash since there are major changes from v3 to v4 for Glide, Dependency A cannot use methods of v4.
Solution 1 - Dependency B has to use v3 to avoid conflicts. Upgrade to v4 when Dependency A has upgraded to v4.
Solution 2 - If Dependency A can function normally without Glide dependency then, Glide can be excluded from Dependency A.

I guess you don't have any options but to used the updated one which is Glide v4, because as you already state you are also using Glide v4. Also, it would be good if you used updated dependency/libraries because there are performance improvements and bug fixes applied in new versions.

Could your app be split into multiple binaries? Could one binary be linked with dependency A and Glade 3, and the other binary be linked with dependency B and Glade 4?
This would only make sense if the parts of the app that depend on A and Glade 3, and the parts of the app that depend on B and Glade 4, could be cleanly differentiated, so that each binary serves a particular, well-defined purpose. This approach would be conceptually similar to object oriented design, or the Unix philosophy of combining single-purpose tools to facilitate complex workflows, or the COM architecture on Windows.
When evaluating whether or not such a split is feasible, you'll want to consider user workflows and data stores, as well as characteristics that are specific to your app. If users would move sequentially from binary A to binary B, for example, if binary A was part of the startup sequence, and users never return to binary A, that suggests that a split may be possible. On the other hand, if users would bounce back and forth between binary A and binary B, that could make a split much more difficult. Likewise, if data is stored in a database, and each binary can access the data independently, a split may be feasible. Conversely, if the data is primarily stored in process, and/or there is a lot of data to stream between the processes, then a split might not be feasible.
When working with multiple binaries in this fashion, they likely need to communicate using some sort of API, for example, the command line, pipes, file sockets, network sockets, or even simply a shared external data store that both binaries access asynchronously.
In general, the more you can limit interactions between the binaries, the better. You may be able to create a simple wrapper around dependency A and Glade 3, and call that wrapper from the remainder of your code.
Finally, if you evaluate and discover it may be feasible to split your app into multiple binaries, before proceeding, also consider the relative effort of splitting your app versus specifying new dependencies for A and B.

Overriding one of the package names can be a possible solution
Try creating 2 AAR libs for each part that use different versions.

Related

Question: Databinding not working with runtimeOnly gradle dependncies

Hoping someone can explain why data binding per module seems not work (returns null) when a specific module/s are declared as runtimeOnly vs when it's declared as implementation.
e.g.
Say I've got some feature modules which I want to include or exclude (similar to dynamic feature modules) except I'm not using that right now for other reasons I will not go into, this is more of an experiment. So the idea was to have multiple components detached from the main application that are only made available at runtime, so in other words virtual no coupling between app and any of the add-on features and this works fine until I add data binding into the mix, crashing with a *Binding cannot be null.
The only way I've gotten this setup to work is by switching back from runtimeOnly to implementation and from what I know so far the difference between runtimeOnly and impelentation are on the gradle website:
So my question is why does this happen, does enabling databinding in modules require the module to be configured with implementation? Or perhaps I'm doing it all wrong and have misunderstood the purpose of runtimeOnly.
Thank you in advance, and apologies if the question is not comprehensive enough
implementation: mostly we use implementation configuration. It hides the internal dependency of the module to its consumer to avoid accidental use of any transitive dependency, hence faster compilation and less recompilation.
runtimeOnly: when we want to change or swap the behaviour of the library at runtime (in final build).
In the case of runtimeOnly, you need two dependencies one which will help you to access the code at compile-time and one which will replace/use at runtime.
Example of Runtime:
SLF4J is one of the best examples of runtimeOnly, where we will use slf4j-api as implementation configuration and implementation of slf4j-api (like slf4j-log4j12 or logback-classic, etc) as runtimeOnly configuration.
I have created a post with an in-depth understanding of each one with Working Example: source code
https://medium.com/#gauraw.negi/how-gradle-dependency-configurations-work-underhood-e934906752e5

Reducing Android library size if it is depended on other library's specific parts

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.

Android & iOS: How to handle dependencies when building an SDK

I'm currently working on an SDK which is available on both Android & iOS platform.
For Android, we list dependencies in our Gradle file and use Maven to provide the SDK (so our dependencies are listed in the .pom file).
For iOS, we use cocoapods to handle dependendies.
The problem is the following:
* Our SDK use a dependency in version X
* One of our client might use the same dependency but in version Y
* Another client might also use the exact same dependency in version Z
So, this leads to our SDK potentially being broken on one of our client (if not both) because we ensure that it works with dependency X, but not Y and Z.
For now, the legacy code has simply imported source code of libraries causing this problem and namespaced it, such that it simulates we do not use the same library.
But in my opinion, this is not an appriopriate solution: we do not have latest fixes, it is painful to update, client has two times the library instead of one.
So, for now, I'm trying to think about a potential good solution, but couldn't find what I want on Google (maybe I'm not using the right keywords :/).
What I was thinking about was to provide support for a range of versions for each dependency. A bit like "if this method is here, execute it, otherwise, use that method of the previous version" (like selector respondTo on iOS). Then, the client should be able to use any version of the dependency at the condition it is in the supported range.
However, I don't know if it is the right way?
Is there any other solutions?
Thanks :)
For android, there are two possible solutions, a build-tool based one, and an architectural one:
1.-If you build your library with maven, you can use the "provided" scope to force your library to get the dependencies from the container running it. That way, the dependency can be provided by the app consuming your library. Note that this will not help you if the dependencies are wildly different.
2.-Abstraction to the rescue! You can subdivide your project into the main library and plugin libraries. The main library will show the user every class an method and that will be the one they will call from their apps. Inside the main library, all classes will import every external SDK or dependency in an indirect form, a generic wrapper which can be either an abstract class or an interface, and use them that way. For example, maybe you are providing an enhanced facebook login UI. Then, instead of referencing the facebook SDK directly into your View, you will reference a facebookLoginInterface and call it. Then, you'll have a secondary project, facebookLogin41, where you will implement the facebookLoginInterface using the facebook sdk 4.1, and a second one, facebookLogin418, where you implement the same interface using the facebook sdk 4.1.8. Then, implement some sort of providing logic, like a Dependency Injection framework (Roboguice providers are a very good example), maven dependency scope (provided, for example), etc, to make the library instance the facebookLoginInterface. Finally, the client just imports both the main library and the secondary project needed and uses the main library.

Prevent reaching 65k methods in Android

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.

How to import only one specific package from module to avoid 65k issue

I have several modules in project (A, B, C). Module B is using by another modules. In result I'm getting 65k method limit issue.
So. Is there a way to import only specific packages from module B to module A? May it can be realized using proguard?
Your best bet is use only needed libraries, search for alternative libraries that has lower method count. For example Picasso library uses less than 1k methods, Glide around 4k but it has more features.
For Google services use granular dependency - import only needed ones.
You can repackage existing libraries, just get needed features or just search for particular class in github repository
Proguard will strip unused classes.
Multidex is your last resort - for minSdk lower than 21 your build time will increase. I'm switching to minSdk 21 when working on code (it builds a lot faster) and going back to target minSdk when testing. Still you shouldn't be scared by multidex, when you will work on big project that has a lot of views and features, you will need get used to it.
I think, there's an easier solution (not better though):
You can use multidex option in your build configuraion. See google doc
If you are using play services, you can use only those you really need since they have been divided into separate services

Categories

Resources