Androidのサポートライブラリに含まれるTargetApiとRequiresApiの違い

ターゲットSDKの警告対処のためにアノテーションが2つ用意されているので、違いをメモ

# 概要
`TargetApi`は、SDKバージョンの警告を単純に無視するアノテーション
`RequiresApi`は、SDKバージョンの責任を呼び出す側に転嫁させるアノテーション

# 具体的な挙動の違い

## アノテーションを付与したメソッド内の挙動
Androidのアプリではしばしば、動作させる環境のSDKバージョンにより処理を分岐させる必要が生じる

例えば、`minSdkVersion`が**21**(=**Android L=5.0**)かつ`targetSdkVersion`が**26**(=**Android O=8.0**)のアプリに通知機能を搭載する場合、SDKバージョン**26**以上限定で「通知チャネル」を作成する必要がある
これを、以下のように実装する

```kotlin:MainActivity.kt
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            createNotificationChannel()
        }
    }

    private fun createNotificationChannel() {
        NotificationUtil.createNotificationChannel(this)
    }
}
```

```kotlin:NotificationUtil.kt
object NotificationUtil {

    fun createNotificationChannel(context: Context) {
        context.applicationContext.apply {
            val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            val channel = NotificationChannel(
                    "channel_news",
                    getString(R.string.notification_channel_news_label),
                    NotificationManager.IMPORTANCE_DEFAULT
            )
            notificationManager.createNotificationChannel(channel)
        }
    }
}
```

NotificationUtilオブジェクトのcreateNotificationChannelメソッド内に存在するNotificationChannelのインスタンス化は、**SDKバージョン26以上を必要とするAndroidのAPI**であるため注意する必要があるが、呼び出し元であるMainActivityのonCreateメソッド内で「SDKバージョン**26(=O)以上**」と分岐しているため、構造上問題ない
しかし、Android Studioでこのコードを入力すると、以下のように**赤警告が出てしまう**（エラーのように見えるが、ビルドは可能）
![target_sdk_alert.png](https://qiita-image-store.s3.amazonaws.com/0/125334/2b026898-48c9-224a-d66d-331b3516ddf0.png)

本ケースのように、**構造上問題ないと保証できるが、表示されてしまっているSDKバージョンの警告**は、`TargetApi`または`RequiresApi`アノテーションを利用して消しておくのが良い

### TargetApiを付与した場合
警告が消える
![surpass_with_target_api.png](https://qiita-image-store.s3.amazonaws.com/0/125334/07cf331b-c93a-4527-b970-e83c4b7bc62b.png)

### RequiresApiを付与した場合
こちらも同様に警告が消え、`TargetApi`との違いはない
![surpass_with_requires_api.png](https://qiita-image-store.s3.amazonaws.com/0/125334/6daa4c69-99c7-6751-226c-d23e287f04fe.png)

## アノテーションを付与したメソッドを呼び出す側の挙動
警告が出ていたメソッドに`TargetApi`を利用した場合、そのメソッドの呼び出し側には特に変化はない
一方で、`RequiresApi`を利用した場合は、呼び出し側がSDKバージョンの条件を満たすかどうか静的解析が波及する

### TargetApiを付与した場合
もともと警告は出ておらず、変化なし
![caller_when_target_api.png](https://qiita-image-store.s3.amazonaws.com/0/125334/9c1cddf2-7d41-7623-4594-ffb96e3159a9.png)

### RequiresApiを付与した場合
呼び出し元のメソッドについて静的解析が走り、先ほど付与した`RequiresApi`の条件を満たすと判断できない場合、呼び出し元にも警告が出現する
![caller_when_requires_api.png](https://qiita-image-store.s3.amazonaws.com/0/125334/0ae59d12-b957-6c68-c695-b601dd7e118e.png)

この場合も同様に構造上問題ないと判断できるため、先ほどと同じアノテーションを付与して警告を消して良い
![surpass_with_requires_api_in_caller.png](https://qiita-image-store.s3.amazonaws.com/0/125334/02e8b9bf-ecda-f4fb-fb1f-fa1c3444625a.png)

以上のように、`RequiresApi`を利用する場合、プログラムの書き方によっては、連鎖的に複数の`RequiresApi`を付与していく必要が出てくる可能性がある

# 参考
- https://stackoverflow.com/questions/40007365/requiresapi-vs-targetapi-android-annotations