How to check the Support Library / AppCompat library version at Runtime? - android

We have an SDK for Android that requires Support Library v4 with targetSdk 23 (meaning that the Support Library should be also 23). For commercial reasons, I have to upgrade the SDK so it should work even if the App is compiled against API 21.
This means that some calls to the Support Library should be done conditionally depending on the current Support Library version of the App (not the current API level), so I have 3 options here (ranked by best to worst):
1 - Check the Support Library version before the method call (This is what I have not figured out how to do, (if possible at all)).
2 - Use reflection.
3 - Use Try Catch blocks.
So for the 1st option, is it possible to check the support library version at runtime?

I'll answer my question with the approach I finally chose:
It is not possible to check the Support Library / AppCompat library revisions at Runtime.
Typically you already know which version of the Support Library / AppCompat Library version you are going to use, as it is declared explicitly in the App dependencies. However, due that in this case the SDK code should run under any condition (or at least a min version of the libraries), some different things were done in order to solve the problem:
1 - Check method availability using Java Reflection.
2 - Invoke methods using also reflection and try-catch blocks (in order to ensure App stability)
3 - Determine the AppCompat version using the info provided by the catch block.
4 - Subsequent calls don't use reflection as existence of the methods has been already checked, and min version has been already found.

Related

How to prevent "Call requires API level" from 3rd party libraries

App Code
When we use some code in our Android application that does not exist in the min-version SDK, Android Studio will show an error about it.
Example:
Let's assume my app has target SDK 26 and min SDK 21 and my code tries to call Objects.isNull(var);: then we get a nice error-message:
Call requires API level 24 (current min is 21): java.util.Objects#isNull
AFAIK this is a Lint message (not a compiler warning or error).
Third party code
How can we have the same checks for 3rd party java libraries?
Example:
when the project depends on a lib.jar file and this library internally uses Objects.isNull, then we don't get any error/warning and the app will crash on older devices.
One way to avoid this is to make Instrumentation Tests - but these tests are slow and you never have 100% code coverage.
e.g. I actually thought that ProGuard should report this issue. But it doesn't.
Maybe because it uses the target Android SDK min SDK?
So what can we do about this in the following 2 situations:
we must use the 3rd party library as it is
we can fork the 3rd party library and can use this fork: what could we do to find/avoid these errors: e.g. should we convert it to an aar?
How can we have the same checks for 3rd party java libraries?
Add the source code to your project.
we must use the 3rd party library as it is
Ask its developers what the relevant minSdkVersion is for the library, then set yours to match, or otherwise avoid calling their library on older devices.
we can fork the 3rd party library and can use this fork: what could we do to find/avoid these errors: e.g. should we convert it to an aar?
Add the source code to your project. Or, create some other project with the source code. Lint checks source code, not compiled code. If you create some other project with the source code, you would need to have it compile a JAR or AAR and make that available to your main project.

Exclude android fragment support from my application

I am developing an application to SDK version 16 and above, As I understood the fragment framework is included in the OS framework in those versions.
I want to remove the support library fragment framework (now that the v4 support library is split) but all the other support libraries are using it (com.android.support:design:25.1.0 and more).
So what is the advantage of splitting the v4 support library?
With respect to that particular library (com.android.support:design), they have not updated it to use the more-granular dependencies. With luck, they will do that someday.
In general, the advantage is to allow for flexibility. Not all apps use com.android.support:design, com.android.support:appcompat-v7, or other libraries that Google has not yet updated to use the more-granular dependencies. Apps that avoid those libraries can use individual dependencies (e.g., com.android.support:compat).
Also, you can use Gradle exclusion rules to attempt to block portions of the aggregate support-v4 that you feel that you are not going to use. For example, you could exclude support-fragment, then see if your app holds up under testing (though that will not work in your specific case, since the Design Support library requires appcompat-v7, which in turn requires FragmentActivity).

How does android merge the custom library's support library with that of application? [duplicate]

This question already has an answer here:
How do make sure there is no conflict of "v7 appcompat or support" of my library when any application uses my library?
(1 answer)
Closed 6 years ago.
I have a android library with com.android.support:appcompat-v7:23.0.1 dependency in the gradle with compliedSDK of 23
I have following doubts
case 1) Say any applicaion with different version com.android.support:appcompat-v7:23.3.0 uses my library.
which version does the android take? Lower one or Higher one? How do i see it?
How do i make sure there is no conflict of v7 appcompat when any app uses my library?
case 2) Say any applicaion with different version com.android.support:appcompat-v7:25.0.1 or com.android.support:appcompat-v7:24+ and different compiledSDk(24 or 25) uses my library.
I know that The support library should not use a different version than the compileSdkVersion.
How does the android merge the support libraries now? (Since the support library version(appcompat-v7:23.0.1) of my library is different from that of the application's compiledSDK (25) )
How do i make sure there is no conflict of v7 appcompat when any app uses my library?
Anyone Please clear my doubts
which version does the android take? Lower one or Higher one? How do i see it?
When building library you put the dependency explicitly in build.gradle. The same is done by the creator of app, that uses your library as a dependency declared in his build.gradle. If the creator explicitly declares support library as a dependency, that version is taken (regardless the versions declared by dependencies). If he does not do that, the highest version declared by any dependency is taken (such support library is regarded as a transitive dependency).
Example: Your library uses appcompat-v7:23.3.0. The creator of app declared appcompat-v7:25.0.1. Simple case: appcompat-v7:25.0.1 is taken.
Example 2: Your library uses appcompat-v7:23.3.0. The creator of app does not use appcompat-v7. appcompat-v7:23.3.0 will be in output app.
Example 3: Your library uses appcompat-v7:23.3.0. Another library uses appcompat-v7:24.1.0. If the creator does not explicitly declare appcompat-v7:xx.x.x the version appcompat-v7:24.1.0 will be in output app.
Hope you understand.
How do i make sure there is no conflict of v7 appcompat when any app uses my library?
You can't assure that. That is why you should always put the highest version of support libraries in the library. I can't even assure you the support libraries maintain backward compatibility. Here is the example that they don't.
I know that The support library should not use a different version than the compileSdkVersion.
That is only a suggestion. However, you should conform to it, but you don't have to.

In Android, how to force the native code to use my version of a shared library, instead of the system's?

Android already has the PCRE shared library ("/system/lib/libpcre.so"), but it's compiled without Unicode support, so I've built my own version of PCRE for Android. My native code is linked to the PCRE shared library. However, when I run my app, it uses the system's version of PCRE, rather than the one I've built, even though my APK does include my version of PCRE. How do I make it use my version of PCRE rather than the system's? Generally in Linux I use "LD_LIBRARY_PATH" or "RPATH", but on Android I don't know how to use them, if that's at all possible.
You can load("your-path-to-lib") before you load the main library. With this API you can load a native library at arbitrary location. But the easiest way is to rely on the default behavior of build and installer, which will pack the native libraries (named libsomething.so) that it finds in libs/<ABI> folders, into the APK file, and unpack the ABI variant that matches the target into
getContext().getApplicationInfo().nativeLibraryDir
(this was added in API level 9)
If the library is pre-loaded, you cannot have your library side-by-side with the system one, due to a bug that invloved older versions of Android. Still, you may succeed to unload it manually, using dlclose():
handle = dlopen("<libname>", RTLD_NOLOAD);
dlclose(handle);
dlclose(handle); // twice, because dlopen() increments ref count
You will probably do these manipulations in a separate small dlclose_helper.so. Load this helper load before you load the main library, which needs the private version of <libname>.
A fix was introduced for API level 23 that lets us finally load both dir1/libx.so and dir2/libx.so (see the official doc).
Note that for API level 24, new restrictions have also been introduced that limits access to system libraries (you can only load white-listed ones).
I believe the only way is to rename the library (libpcre_myapp, for example).
Note that renaming just the file probably is not sufficient, but changing the SO_NAME ELF property.

Why does AOSP add new APIs to support libraries without adding them to SDK?

I've been developing for Android for a few months now, and this question arises again and again: what is the motivation behind addition of completely new features (APIs) to support libraries without them being available in the standard SDK?
Example:
FragmentTabHost API is available only through android.support.v4.app.FragmentTabHost. It is fine most of the time, but if you want to use fragments nesting and PreferenceFragment together, you're dead in the waters - nesting requires you to switch to android.support.v4.app.Fragment (for supporting below 4.2), but there is no implementation of PreferenceFragment in this support lib. As a consequence you either implement custom workarounds, or use third party libraries that have already implemented them (see this question)
Edit: as pointed out by #eugen in his answer, I myself referenced FragmentTabHost from support-v13, which supports native Fragments. However, this information does not change the overall question. In fact, if I would've used this API with fragments nesting, my app wouldn't run on ~30% of devices today (native fragments support nesting from 4.2 onwards).
Example:
Another issue that I encountered today (and wasted too much time on) is with ActionBarDrawerToggle available through android.support.v7.app.ActionBarDrawerToggle. I wanted to use this functionality, therefore I added the entire com.android.support:appcompat-v7:21.0.+ to project's dependencies. I guessed that this will do, but I was wrong - my app did not compile (missing resources referenced by the support library). After trying few tricks from the web, I found this question which finally provided the solution. In short: v7 support library has dependency on SDK, therefore I had to define compileSdkVersion 21.
Consequences:
Now, I tell myself: FragmentTabHost haven't been added to any SDK version yet, which implies that even 4-5 years from now developers will continue to use support-v4 lib for providing this functionality (because even if this API is added today, it will take years before you could safely assume that all target devices support it natively), right? The same argument applies to android.support.v4.widget.DrawerLayout and other APIs present only in support libraries.
If developers bound to use support libraries for years, this also imply that they are bound to experience the above inconsistency/dependency issues long after these issues could have already been resolved, right?
Question:
Why would Google want to keep these libraries forever? Won't it be better for everybody if all the new APIs were added to SDK in parallel to compatibility libs, such that compatibility libraries could age and "retire" at some point?
Edit: following #eugen's answer I'm now puzzled even more - some APIs "evolve" with newer support libs, but do not make it into main SDK (like FragmentTabHost, which evolved from support-v4 to support-v7). What is the reason for not adding them to SDK?
FragmentTabHost API is available only through android.support.v4.app.FragmentTabHost.
Incorrect. The link you provided leads to android.support.v13.app.FragmentTabHost which (as opposed to android.support.v4.app.FragmentTabHost) works with native Fragments but makes APIs introduced above API 13 available since API 13.
In short: v7 support library has dependency on SDK, therefore I had to define compileSdkVersion 21.
Of course the version 21 of the library needs version 21 of tools and version 21 of SDK. Always use corresponding versions of build tools, compile SDK and support libraries!
As of today these values are recommended:
compileSdkVersion 22
buildToolsVersion '22.0.1'
targetSdkVersion 22
compile 'com.android.support:support-v4:22.0.0'
compile 'com.android.support:appcompat-v7:22.0.0' // includes support-v4
compile 'com.android.support:support-v13:22.0.0' // includes support-v4
Now, I tell myself: FragmentTabHost haven't been added to any SDK version yet, which implies that even 4-5 years from now developers will continue to use support-v4 lib for providing this functionality (because even if this API is added today, it will take years before you could safely assume that all target devices support it natively), right?
Which means that if you implement it using the support-v13 library, you have nothing to worry about. It's going to work as far as API 13.
Read about the difference between support-v4 and support-v13 above.
Why would Google want to keep these libraries forever?
Because you'll most likely want to support older platforms. Which means you need a support library.
Even now you use ActionBarActivity from appcompat-v7 (which was originally intended to backport action bar to API 7) with minSdkVersion 14 because you want that material design backport. This also means you're stuck with support fragments because ActionBarActivity is using them.
Won't it be better for everybody if all the new APIs were added to SDK in parallel to compatibility libs, such that compatibility libraries could age and "resign" at some point?
Support libraries age. You may have noticed that recyclerview-v7 (and other recent android libraries) is available since API 7 and not API 4. And you may also have noticed that RecyclerView is not in the native SDK. Why would you add it to a native SDK making it available only to the newest platform when you can put out a support library making it available for everyone?

Categories

Resources