API 레벨 호환성 문제는 gradle 에 minSdkVersion, targetSdkVersion 을 설정한다고 해결되는게 아니다. 사용하는 클래스, 함수들까지 고려해야한다.
안드로이드 문서 보면 makeText() 는 Added in API level 1 이라고 설명되어 있고, addCallback() 은 Added in API level 30 이라고 쓰여있다. API level 1 부터 지원되던 makeText() 는 아마도 왠만한 최신 버전에도 적용이 될 것이다.(아니면 더이상 추천되지 않거나) 하지만 API level 30부터 적용되는 addCallback() 은 그 이전 API level 에서는 거의 확실하게 작동하지 않을 것이다.
이러한 사실은 androidStudio 가 알려주기 때문에 우리는 일단 써보고 그 사실을 인지하게 된다. 그런데 어떻게 해결할 수는 없을까?
java 이곳저곳에 난무하던 에너테이션이 드디어 코틀린에도 등장한다. API 레벨 호환성에 문제가 있는 API를 사용한 함수나 클래스 선언부 위에 @RequiresAp 에너테이션을 추가하면 안드로이드 스튜디오에서 오류가 발생하지 않게된다. 해당 에너테이션 대신 @TargetApi 에너테이션을 이용해도 된다.
이 에너테이션은 단지 안드로이드 스튜디오에서 오류를 무시하는 설정일 뿐이다. 앱이 실행될때 호환성 문제를 막으려면 직접 코드로 처리해줘야한다.
// API R 버전에서만 시행되게끔
@RequiresAp(Build.VERSION_CODES.R)
fun showToast() {
toast.addCallback( ... )
}
@TargetApi(Build.VERSION_CODES.R)
fun showToast() {
toast.addCallback( ... )
}
// API R 버전 이상이면 시행되게끔 직접 처리해줘야한다.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
toast.addCallback( ... )
}
그런데 왜 같은 기능을 하는 에노테이션이 두개나 있는 걸까? 그건 당연히 같은 기능이 아니기 때문이다. stack over flow 에서 내용을 가져왔다.
이 설명을 보면 @TargetApi 는 lint 에러를 없애는 용도지만, @RequiresApi 는 실제 호출되는 코드에 영향을 줄 것으로 보인다. 실제로도 그럴까?
Tola.makeText() 는 API level 1 이상 부터 사용할 수 있으므로, 메시지를 띄우는 함수를 만들고 @RequiresApi를 이용해 API30부터 호출되도록 만들어보자 그리고 API level 29 의 가상 안드로이드 디바이스를 띄워보자.
@RequiresApi(Build.VERSION_CODES.R)
fun justToast(context: Context) {
Toast.makeText(context, "Hello toast", Toast.LENGTH_SHORT).show()
}
일단, lint 에러가 보인다. 에노테이션을 @TargetApi(Build.VERSION_CODES.R) 로 바꿔주면 해당 에러도 사라진다. @TargetApi 에노테이션의 기능을 명확히 알 수 있게 되었다.
하지만, API 레벨 29인 디바이스에서 버튼을 클릭했을때 토스트 메시지가 그냥 나와버린다. @RequiresApi 역시 실제 코드를 제어하는 기능보다는 오류를 띄우는 기능이 있다고 생각해야 할 것 같다. 아마도 코드를 관리하면서 제대로 제어하기 위해 개발자들이 서로서로에게 주는 경고가 아닐까?