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.
Related
I want to build an app for Android, iOS and web from a single Codebase using Flutter. Since web does not support all Flutter plugins yet, I'll have to use alternatives that have dependencies (for example dart:html) which aren't available on Android and iOS.
How can I inject the right implementation depending on the platform on which the application runs, without loading unnecessary/unavailable packages?
This is possible using conditional imports. You can find an example of the syntax here: https://github.com/dart-lang/site-www/issues/1569. However, I can't seem to find the official documentation for this language feature.
import 'stub.dart'
if (dart.library.io) 'io.dart'
if (dart.library.html) 'html.dart';
Define methods in stub.dart throwing UnsupportedOperationException or something the like. It doesn't really matter since stub.dart isn't going to be imported anyway. Put the actual implementations in io.dart and html.dart, respectively. The signatures have to match those in stub.dart.
You probably only want to do this conditional import at a single point in your program so I highly recommend hiding everything behind a common interface defined somewhere else than in stub.dart (common.dart in this example). You can then import and implement common.dart in io.dart and html.dart and use conditional import to chose your implementation at your program root. This way everything else only needs to depend on common.dart.
You could put the common parts into a third hierarchy, then include that in your mobile and web hierarchies using local pubspec includes. I'm not sure how you'd publish that to pub if you wanted to share it, although if you're already sharing it, it'd just be three pub repos like you have locally.
We're trying to use ArcGIS's Android Runtime SDK in NativeScript (it has no nativescript plugin) but we have accessed that rewriting the whole library as a multi-platform plugin would take too much time.
My question is, how can we utilize the native library directly but only the android version of it?
This is the library: https://developers.arcgis.com/android/latest/api-reference/reference/packages.html
Also, is it possible to use it without a custom UI plugin? I don't understand how to add the mapView to the app .xml
For example, in their AndroidStudio tutorial they mention the following steps and I'm not sure how to translate them to NativeScript
Source : https://developers.arcgis.com/android/latest/guide/develop-your-first-map-app.htm
I'm not quite sure what you mean by re-writing the whole library, you never have to do that.
Plugins are being written to wrap the native library with simple user friendly JS api / methods, it necessarily need not to be cross (or multi) platform either.
You may even directly access any third party library within your project as soon you mark them as dependency in your app gradle file.
Here is how you access native apis.
For instance if you want to create an instance of LocatorTask, this should work once you add the library as dependency in your NativeScript project.
const locatorTask = com.esri.arcgisruntime.tasks.geocode.LocatorTask("URI_HERE");
locatorTask.loadAsync();
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.
I have an Android app that's downloaded primarily from Android Market (now, Google Play). We made a few tweaks to the source and also submitted to the Amazon App Store to see what sort of traction it gets. I'm now looking for a sustainable way to develop from a common code base and yet build so that I can submit to either/both.
Amazon's store has some restrictions about available APIs, and hence I'd like to conditionally remove/modify features from that version. Since Java doesn't support traditional conditional compilation, and conditionally including files in Eclipse doesn't seem trivial (is it even possible?), I wanted to ask what others are doing to solve this.
Admittedly, I'm no Eclipse/Java expert so feel free to school me.
What I'm looking for in a solution:
Building/debugging using Eclipse.
Static code files, with environment/settings toggles to control what to build.
No duplicate code or conditional logic in code to pick code flow at runtime
Is this something you've solved for Android apps specifically, or for other Java/Eclipse based projects? Suggestions for where to begin?
It's quite easy to do in the newest versions of ADT (version 17), though I do find it makes compilation a bit longer:
Create a new Android project (proj-A)
Go to Project->Properties, select Android, and check "Is Library"
Move all your common code to proj-A, import all the necessary libraries
Create a new Android project for Google Play (proj-B)
Go to Project->Properties, select Android, and add Proj-A to the Library
Repeat #4&5 for the Amazon version
If you have some variables that should be set differently for each sub project (i.e. boolean GOOGLE_PLAY_VERSION to enable Google Play specific functions), you have to create another project to contain these values since you can't have projects that reference one-another in a circular fashion. You can solve this by adding the following steps:
Pull all of your sub-project specific variables into one or more Classes that just serves as container(s) for these variables
Create a "dummy" Java project (dummy)
Config proj-A to add a new Source link to the bin directory of dummy
Add the config Classes in each sub-project with project-specific changes
Profits!
Note that the variables in dummy should not be set as final, otherwise it will override sub-project's setting.
This may seem like quite a bit of up-front work, but has worked quite well for me as far as version control goes.
Edit:
Now with Google's move to Android Studio & Gradle, it may be better to move to that if you are starting a new project if you want to support multiple APKs, see Android dev site's Building Your Project with Gradle#Work with build variants. It definitely doesn't hurt to evaluate that option before deciding.
Unfortunately, it's sort of a convention in Android to change flow at runtime based on what would be in C/C++-land conditional compilation.
Our app has to maintain different behavior for different API levels, so we've created some application-level constants that are initialized statically based on API-level information available to us, and used throughout the code. This is the way that Google does things in their examples (for example, see the ActionBarCompat compatibility library, and in particular the factory method used here).
You could create an interface CustomBuild, and implement it in AmazonBuild and GooglePlayBuild, then use a static getBuild() method to switch functionality as necessary:
if(getBuild().shouldEnableFeatureX()){
doStuff();
} else {
doDifferentStuff();
}
Then all you've got to worry about switching between builds is a line or two of code in the factory along with maintaining which things you want enabled in which versions. Or you could include a different version of a static class CustomBuild for each build.
I'm going to second the suggestion of others above re: switching to something like Maven for building; it should make your life much easier once you have it set up.
I'm also going to say you should make the core of the app a library as suggested above, and have two different modules (one for amazon, one for play store) that depend on the library but each only contain the one custom factory file (or just a static class for each type of build that contains the same "should I do this thing?" methods... once you have the infrastructure it's just a matter of preference).
I haven't actually tried this yet, but it's something I've thought about.
How about using Eclipse's ability to link to files from a directory outside your workspace?
Start with one Eclipse project: for the sake of argument, say it's the Google Play version.
Now build a second project, beginning with asking Eclipse to link (not copy) the source files from your first project.
To develop the second project, add classes that subclass ones from the original project to realize your modifications. For resources, you can use some combination of includes, attribute overrides, and selectors.
Where it's not possible to subclass or extend, then obviously you'll have to just copy the original source file and hack on it. If you're really OCD about it, you can probably just maintain a patch set rather than a whole redundant set of files.
What do you think, will it work?
You may create manually two projects in Eclipse pointing to the same source folders but with different inclusion/exclusion filters and different target directories.
Then two Ant targets using properties to switch excluded files from javac fileset are enough to generate corresponding jar files.
The aim is to get a clean application for each target, without any code from the other one.
With features listed as pluggable behaviors in a property file or XML configuration, your runtime will adapt itself with the addition of menu entries.
I know there are a lot of questions out there about multiple Android versions pertaining to free/paid versions but this might be a little different.
My app currently uses AdMob for advertising and it's published on the Android Market and on the Samsung App Store. Unfortunately, the Samsung store will require everyone to migrate to their own Ad Network in the future, Samsung AdHub. Both AdMob and AdHub have their own libraries, their own SDKs.
I'm looking for a solution to build 2 different versions, one including AdMob the another including AdHub (and all the necessary code). What solutions do I have to easily build 2 versions without much hassle when it's time for a new version release?
Lots of solutions recommend to move the main project into a library project and then build 2 other apps which include the library project (the base project). But I'm not very fond of that solution (I prefer to keep my app in one single project, if possible) and I'm trying to look for alternatives and then make up my mind about which one is better for my needs.
I'd think you should make this possible in your code using the Strategy design pattern. It suites well and can be switched at any trigger your like (even on runtime). If you make a facade for each jar file you will be able to change the dependencies while building, having the same source code.
Other option with this method is just making some configuration in your application that determines which library to use.
Some interesting solutions can be found here:
https://groups.google.com/d/topic/android-developers/8pRugcnzR_E/discussion
The way to go now is to use Android Studio and use different Gradle flavors for each app. Thus, if you fix core functionality, you can quickly do a build for each appstore with it's own ad network.
Library Projects is the way to go. Create a base project where you implement all the common stuff and then create two separate project that use the common one as a "Library". then just implement the rest needed to make the Apps behave differently.