0

What's the best way to implement around deprecated APIs in Android without over-suppressing warnings? This is really more a general Java question, but it keeps coming up in the Android context.

In Android (java) code, it is common for a feature or API to be deprecated and replaced with another option or implementation. The IDE and/or compiler (rightly) flags this with a warning. For example, the getParcelableExtra(String) API is deprecated as of API33 and we're encouraged to use getParcelabelExtra(String,Class).

So, if we have some code

    someMethod(someTypeSomeArgs) {
        ...
        someObj = intent.getParcelableExtra(EXTRA_STRING_PARAMETER)
        ...
    }

It will generate a deprecation warning.

Since we're building for a variety of targets and the new API is often not available on an older platform, we can follow the documentation suggestions and use an SDK version check:

    someMethod(someTypeSomeArgs) {
        ...
        if (Build.VERSION.SDK_INT >= 33) {
            someObj =intent.getParcelableExtra(EXTRA_STRING_PARAMETER,someClass);
        } else {
            someObj = intent.getParcelableExtra(EXTRA_STRING_PARAMETER);
        }
        ... 
    }

This seems to be the recommended best practice for runtime, but the compiler warning is still there. We can suppress the compiler warning, but only at method scope, which works, but completely eliminates the ability to track deprecation at other locations in the method or track future deprecation while maintaining the code.

So...is there an accepted best practice for handling this?

  • Have you checked this question https://stackoverflow.com/q/73019160/7666442 – AskNilesh May 16 '23 at 13:11
  • @AskNilesh, thanks for the pointer, and yes, it reinforces the runtime version check structure. The trick here is that I'm not really asking about how to make the code work at runtime -- rather how to make things compile cleanly without harming maintainability. It seems like the consensus is to use a wrapper of some kind, since that allows suppression at a reasonable scope. I'm curious about other options, though. – Andrew Kephart May 16 '23 at 20:07

1 Answers1

0

Better create an extension function like ParcelableExt.kt

inline fun <reified T : Parcelable> Intent.parcelable(key: String): T? = when {
  SDK_INT >= TIRAMISU -> getParcelableExtra(key, T::class.java)
  else -> @Suppress("DEPRECATION") getParcelableExtra(key) as? T
}

inline fun <reified T : Parcelable> Bundle.parcelable(key: String): T? = when {
  SDK_INT >= TIRAMISU -> getParcelable(key, T::class.java)
  else -> @Suppress("DEPRECATION") getParcelable(key) as? T
}

inline fun <reified T : Parcelable> Bundle.parcelableArrayList(key: String): ArrayList<T>? = when {
  SDK_INT >= TIRAMISU -> getParcelableArrayList(key, T::class.java)
  else -> @Suppress("DEPRECATION") getParcelableArrayList(key)
}

inline fun <reified T : Parcelable> Intent.parcelableArrayList(key: String): ArrayList<T>? = when {
  SDK_INT >= TIRAMISU -> getParcelableArrayListExtra(key, T::class.java)
  else -> @Suppress("DEPRECATION") getParcelableArrayListExtra(key)
}

For example you can use this in you resultLauncher like this

result.data!!.parcelable(RESULT_KEY)
Mohan Raj
  • 58
  • 8
  • thanks -- a good clean example of extracting to wrapper, which seems to be the consensus model. I didn't realize Kotlin could annotate at the statement scope, so maybe that'll be some motivation to start migrating. – Andrew Kephart May 16 '23 at 20:12
  • Thanks Andrew. If you found it helpful pls make this answer accepted. Thank you so much. – Mohan Raj May 17 '23 at 03:37
  • Mohan, I've accepted the answer. While it doesn't really address the problem (Java can't be annotated at the statement scope), it does present the idea of a wrapper method as a solution, so that seems to fit with other things I've seen, and in that way it's quite helpful. – Andrew Kephart May 22 '23 at 19:53