Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TargetApi not taken into account

Tags:

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.

like image 938
Snicolas Avatar asked Aug 31 '12 07:08

Snicolas


People also ask

Which Android API should I use?

When you upload an APK, it must meet Google Play's target API level requirements. New apps must target Android 12 (API level 31) or higher; except for Wear OS apps, which must target Android 11 (API level 30) or higher.

What is RequiresApi?

@RequiresApi - Denotes that the annotated element should only be called on the given API level or higher. @TargetApi - Indicates that Lint should treat this type as targeting a given API level, no matter what the project target is.

What is target API in Android?

The Target Android Version (also known as targetSdkVersion ) is the API level of the Android device where the app expects to run. Android uses this setting to determine whether to enable any compatibility behaviors – this ensures that your app continues to work the way you expect.


2 Answers

@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){     //... } 
like image 194
Guykun Avatar answered Nov 10 '22 00:11

Guykun


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.

like image 45
Snicolas Avatar answered Nov 10 '22 00:11

Snicolas