Rule. When implementing Android Java methods that invoke platform APIs requiring a newer SDK level without internal runtime version checks (Build.VERSION.SDK_INT), annotate the method with @RequiresApi (optionally alongside @TargetApi) rather than relying solely on @TargetApi. Additionally, verify that the specified Build.VERSION_CODES constant accurately matches the actual minimum API level where the underlying platform methods were introduced.
Why. @TargetApi only suppresses Android Lint warnings within the method body and fails to alert callers to verify the API level before invoking the method. Furthermore, understating the required Build.VERSION_CODES level in @RequiresApi bypasses static lint checks at call sites, leading to runtime NoSuchMethodError crashes on intermediate Android OS versions.
Bad:
// Anti-pattern 1: Using @TargetApi alone leaves callers unchecked. @TargetApi(Build.VERSION_CODES.M) public void setPreferredDevice(@Nullable AudioDeviceInfo preferredDevice) { if (audioRecord != null) { audioRecord.setPreferredDevice(preferredDevice); } } // Anti-pattern 2: Understating the required API level (e.g., M instead of Q). @RequiresApi(Build.VERSION_CODES.M) public boolean setPreferredMicrophoneDirection(int direction) { return audioInput.setPreferredMicrophoneDirection(direction); }
Good:
// Idiomatic: @RequiresApi with the accurate Build.VERSION_CODES level ensures Lint checks callers. @RequiresApi(Build.VERSION_CODES.M) @TargetApi(Build.VERSION_CODES.M) public void setPreferredDevice(@Nullable AudioDeviceInfo preferredDevice) { if (audioRecord != null) { audioRecord.setPreferredDevice(preferredDevice); } } @RequiresApi(Build.VERSION_CODES.Q) public boolean setPreferredMicrophoneDirection(int direction) { return audioInput.setPreferredMicrophoneDirection(direction); }