I'm using a method with boolean return type for api 19 while my app supports min sdk 15, what will the method return incase the api is less than 19 ?
#TargetApi(19)
public static boolean isFeatureXEnabled(Context context) {
some logic
return true/false;
}
what do I get in return for API <19 when calling?
classInstance.isFeatureXEnabled(context);
let's understand #TargetApi(19)
You are using a feature which is not available on minimum SDK so
Compiler : you cannot use this feature , it is not supported by min sdk
You: i know what i am doing so hush , take this #TargetApi(19)
Compiler : so now it is your responsibility to check the API level and call this function accordingly
what will the method return incase the api is less than 19
If the code inside this function is not supported by minsdk then most likely a crash otherwise your result of your logical calculation
you can do something like this
#TargetApi(19)
public static boolean isFeatureXEnabled(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
some logic
return true/false;
}
return false;
}
#TargetApi
Indicates that Lint should treat this type as targeting a given API level, no matter what the project target is — https://developer.android.com/reference/android/annotation/TargetApi.html
It means it is just used by Lint to hide/suppress the warning. It has ho effect in the return value.
Related
I've a problem with checking is device supports Mutli Window Mode. I'm using this function to check it isInMultiWindowMode() but it've added in API 24, and when i'm runs my app on device with lower api version it cause an exception. There is any replacement for this function for lower api versions?
There is any replacement for this function for lower api versions?
Not in the Android SDK. There is no multi-window mode (from the Android SDK's standpoint) prior to API Level 23. And, for whatever reason, Google elected not to add isInMultiWindowMode() to ActivityCompat, perhaps because they cannot support the corresponding event (onMultiWindowModeChanged()).
So, here's a free replacement method:
public static boolean isInMultiWindowMode(Activity a) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
return false;
}
return a.isInMultiWindowMode();
}
Add that to some utility class somewhere and call it as needed.
Also note that isInMultiWindowMode() suffers from a race condition that makes it unreliable, IMHO.
What #CommonsWare explained is true, it is a race condition. Hence, isInMultiWindowMode() will give actual result if you call it from inside post method:
View yourView = findViewById(R.id.yourViewId);
yourView.post(new Runnable() {
#Override
public void run() {
boolean actualResult = isInMultiWindowMode();
}
});
I am using #TargetApi(23) in my app.
#TargetApi(23)
#Override
public void onAttach(Context context) {
super.onAttach(context);
onAttachToContext(context);
}
#SuppressWarnings("deprecation")
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
onAttachToContext(activity);
}
}
protected void onAttachToContext(Context context) {
}
But i can not understand something: #TargetApi(23) annotation's mean "just for Api level 23" or "for Api level 23 and above" ? For example if Api level of device 24, is onAttach(Context context) method called?
TargetApi annotation is just for lint tool purposes and has no outcome in runtime. If you use any API methods just available on 23 within your method and don't declare the TargetApi, you will just get some warnings indicating you're using API's not available in your minimum SDK version. It's your responsibility to call this method with coherence being aware of the API level it will be called from.
#TargetApi does not prevent any code from running,
all it does is to remove lint errors.
You still need to add something along the lines of
if (Build.VERSION.SDK_INT > 7){
//...
}
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
//to do something
}else{
//to do something else
}
I upgraded #Yang's answer.
You can also use
#RequiresApi(Build.VERSION_CODES.N)
which denotes that the annotated element should only be called on the given API level or higher.
Further to #Fivos answer, adding #RequiresApi also means that there will be a build/compile error showing that you are calling a method/functionality that only exists on a target higher than the minimum target you have specified.
Function using #requiresapi
Compilation error after adding requires
In an Android app, we wish to have an iOS-like horizontal scroll wheel, and we are using HorizontalPicker.
This requires API 17 for calls getTextDirection() and getLayoutDirection().
For getTextDirection, the usage is:
switch (getTextDirection()) {
default:
case TEXT_DIRECTION_FIRST_STRONG:
return (defaultIsRtl ? TextDirectionHeuristicsCompat.FIRSTSTRONG_RTL :
TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR);
case TEXT_DIRECTION_ANY_RTL:
return TextDirectionHeuristicsCompat.ANYRTL_LTR;
case TEXT_DIRECTION_LTR:
return TextDirectionHeuristicsCompat.LTR;
case TEXT_DIRECTION_RTL:
return TextDirectionHeuristicsCompat.RTL;
case TEXT_DIRECTION_LOCALE:
return TextDirectionHeuristicsCompat.LOCALE;
}
Looking at dashboard, I see ~20% of devices using APIs 15 or 16, so I'm thinking we should target API 15 rather than 17.
How should I change this code?
Is there a way to only run this code if the device is new enough? If so, what should the function return for an older device?
Is there an alternative mechanism I should rewrite this method to use?
Never mind. Leaving this question here in case it is useful to others, to know how to work around APIs missing in a given version.
My real issue was not knowing how to mark the calls so that "lint" would not complain about them.
Here is the complete implementation in HorizontalPicker:
private TextDirectionHeuristicCompat getTextDirectionHeuristic() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
return TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR;
} else {
// Always need to resolve layout direction first
final boolean defaultIsRtl = (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
switch (getTextDirection()) {
default:
case TEXT_DIRECTION_FIRST_STRONG:
return (defaultIsRtl ? TextDirectionHeuristicsCompat.FIRSTSTRONG_RTL :
TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR);
case TEXT_DIRECTION_ANY_RTL:
return TextDirectionHeuristicsCompat.ANYRTL_LTR;
case TEXT_DIRECTION_LTR:
return TextDirectionHeuristicsCompat.LTR;
case TEXT_DIRECTION_RTL:
return TextDirectionHeuristicsCompat.RTL;
case TEXT_DIRECTION_LOCALE:
return TextDirectionHeuristicsCompat.LOCALE;
}
}
}
It already has logic to handle older builds!
So all that is needed is to mark the two calls getLayoutDirection() and getTextDirection() so that lint does not complain.
The simplest way to do so, without hiding problems elsewhere, is to mark this method to suppress lint complaints about API versions:
#SuppressLint("NewApi")
private TextDirectionHeuristicCompat getTextDirectionHeuristic() {
...
CAUTION: Only make such a change, after examining the lint complaints and the logic, and your AndroidManifest.xml, to be sure the complaints can be safely ignored.
In my case, manifest has minimum API = 15 and target API = 19, the two complaints were "Call requires API level 17, minimum is 15)", and
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
directs all devices less than 17 to use the one-line implementation, which does not use the API 17 calls. That is, the calls will only be made on devices with API 17 or above.
You can use ViewCompat from android.support.v4 with something like :
if (ViewCompat.getLayoutDirection(mView) == ViewCompat.LAYOUT_DIRECTION_RTL) {
//...
}
This question already has answers here:
Programmatically obtain the Android API level of a device?
(7 answers)
Closed 8 years ago.
I have an application, and it behaves differently when I run it on API 18 or 19. This is not a problem, and I know why does it happen.
However, I want to write one code that will deal with both the versions.
Is there any way to get in runtime which API my application was built with? Specifically, I would like to get 18 or 19 if I build my application with these APIs.
EDIT
It seems to be a duplicate question. I thought that the BUILD_VERSION is something else, because, when I compiled both the versions to API 18 and 19, and print the version, I receive 18. It looks like another problem (although I specified API 19, it is compiled according to 18).
I found that the problem was in the emulator configurations.
I didn't understand your problem completely. But if you want to check which Build Version your app is working on and then act accordingly the you can use the following.
if(Build.VERSION.SDK_INT == 18 ){
// Do some stuff
}
else if (Build.VERSION.SDK_INT == 19) {
// Do some stuff
}
else{
// Do some stuff
}
The Android docs provides some sample code of how to load bitmaps effectively that handles this problem.
The code defines static methods in a class Utils that it references when it needs to know what platform the app is running on. The benefit of doing this is that you can reuse the function calls rather than rewriting long conditional statements over and over. The code looks like this:
public static boolean hasFroyo() {
// Can use static final constants like FROYO, declared in later versions
// of the OS since they are inlined at compile time. This is guaranteed behavior.
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO;
}
public static boolean hasGingerbread() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD;
}
public static boolean hasHoneycomb() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
}
public static boolean hasHoneycombMR1() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1;
}
public static boolean hasJellyBean() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
}
public static boolean hasKitKat() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
}
android.os.Build.VERSION.SDK_INT
Check this link out
Build Version Codes
So you can use as follows...
int thisDevicesApi =Build.VERSION.SDK_INT;
if (thisDevicesApi <=Build.VERSION_CODES.GINGERBREAD) {
//for example
//do something
}
In one of our methods, we use smoothScrolling in a list view. As this method is not available before API Level 8 (FROYO), we used the TargetApi annotation to prevent the method from being called in previous SDK versions.
As you can see, we do use TargetApi annotation both in class definition and in statements that use the objects of the class. This is more than needed.
Our problem is that the TargetApi annotation is not taken into account and make our emulator crash in version ECLAIR (SDK 7). By tracing, we just realize that the code that should only be executed in versions 8+ is also executed in version 7.
Are we missing something?
This code is in a listener :
#TargetApi(8)
private final class MyOnMenuExpandListener implements OnMenuExpandListener {
#Override
public void onMenuExpanded( int position ) {
doScrollIfNeeded( position );
}
#Override
public void onMenuCollapsed( int position ) {
doScrollIfNeeded( position );
}
protected void doScrollIfNeeded( int position ) {
if ( mListViewDocuments.getLastVisiblePosition() - 2 < position ) {
mListViewDocuments.smoothScrollToPosition( position + 1 );
}
}
}
And the listener is registered this way :
#TargetApi(8)
private void allowSmothScrollIfSupported() {
if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO ) {
//This if should not be necessary with annotation but it is not taken into account by emulator
Log.d( LOG_TAG, "Smooth scroll support installed." );
folderContentAdapter.setOnMenuExpandListener( new MyOnMenuExpandListener() );
}
}
BTW, we run the code in debug mode, so the issue is not related to obfuscation removing annotations.
#TargetApi does not prevent any code from being run, it is merely for annotating code and preventing compiler errors for new APIs once you know you are only conditionally calling them.
You still need to add something along the lines of
if (Build.VERSION.SDK_INT > 7){
//...
}
With almost one year of more thinking about this, I would like to add a tiny complement to #Guykun 's answer :
The #TargetApi will be only be used by tools to say developers "Hey, don't use this method below XXX android SDK". Typically lint.
So, if you design a method like :
if (Build.VERSION.SDK_INT > 7){
//...
}
then you should add #TargetApi( 7 ) to your method's signature.
BUT, if you add an else statement, and provide an alternative that makes it work for all versions of Android like :
if (Build.VERSION.SDK_INT > 7){
//...
} else {
//...
}
then you should not add #TargetApi( 7 ) to your method's signature. Otherwise, other developers will think they can't use your method belw api level 7, but indeed, it would work for them as well.
So this annotation has to be used, for static analysis, to indicate the minimum api level supported by the method. As in :
#TargetApi( 7 )
public void foo() {
if (Build.VERSION.SDK_INT > 7){
//...
else if (Build.VERSION.SDK_INT > 10){
//...
}
}
and even better, use constants defined in android.Build.VERSION_CODES.*.
BTW, you would have noticed that this is useless for private methods indeed, except to get a cleaner code and help to promote the method public in the future.
To enforce lint error when using a method targeted towards higher Api Level, you can use RequiresApi instead of TargetApi and whenever you'll try to use the method without checking the version code, you'll get compilation error.
This is what the documentation says about RequiresApi
This is similar in purpose to the older #TargetApi annotation, but
more clearly expresses that this is a requirement on the caller,
rather than being used to "suppress" warnings within the method that
exceed the minSdkVersion.