본문 바로가기
안드로이드/기타

안드로이드 버전별 점유율 2024.4(업데이트)

by 나이아카 2024. 2. 25.

 이 글의 주제는 안드로이드 버전별 점유율의 상태에 대해서 작성하는 글인데요. 사실 한 철만 볼 수 있는 주제에 가까운데 왜 포스팅을 하느냐하면, 각 API 별로 분류를 해 볼까 해서입니다.

 현재 안드로이드 버전별 점유율은 Android Studio의 New Project에서 activity 타입을 고르고 난 후  Minimum SDK를 고르는 화면에서 API를 선택할 경우, 각 버전 별로 버전 선택시 제공할 수 있는 device의 퍼센트를 보여주고 있습니다. 이를 토대로 버전별 점유율을 확인해보겠습니다.

 각 안드로이드 버전 별 릴리즈 노트는 아래 링크에서 확인 가능하니, 각 API 별로 점유율 뿐 아니라, 어떤 이점을 가지고 있는지도 파악하면서 선택할 수 있습니다.

https://developer.android.com/studio/releases/platforms?hl=ko

 

SDK 플랫폼 출시 노트  |  Android 개발자  |  Android Developers

SDK Manager의 SDK Platforms 탭에서 다운로드할 수 있는 SDK 패키지에 관한 출시 정보를 확인하세요.

developer.android.com

 


API는 16(Android 4.1, Jelly Bean)

 안드로이드 스튜디오에서 사용할 수 있는 가장 아랫단계입니다. 이를 클릭해보면 your app will run on approximately 100% of devices 라고 적혀있는 글이 아래에 발생합니다. 이는, API 16 버전을 도입하게 되면 전세계 모든 휴대폰에서 사용 가능한 앱을 만들 수 있다는 것을 의미하죠.

 

API 17(Android 4.2, Jelly Bean)

 하지만 재밌는 것은 이 버전을 클릭해도 100%라고 나옵니다. 이는 현재 모든 휴대폰이 17 이상 버전임을 의미한다기 보단, 아직 Android 4.1 버전의 단말이 있을 수 있으나 그 양이 극히 미미 실 사용폰이나 테스트 단말 정도로 간주하는 것으로 보입니다. 뭐, 어쨌든 안드로이드 스튜디오에서는 17버전을 사용하더라도 100% 호환 가능하다고 보고 있습니다.

 

API 18(Android 4.3, Jelly Bean) ~ API 19(Android 4.4, KitKat)

 이 두 버전을 클릭해도 100%입니다. 하지만 20부터는 퍼센트가 달라지기 때문에, 16 ~ 19는 사실상 19로 선택하면 된다 정도로 생각하면 될 거 같습니다. 하지만 API 16을 선택해 얻을 수 있는 이점이 있다면 선택을 해야하기 때문에 릴리즈 노트를 잘 살펴보는 것이 좋을 것 같습니다. 그러나 제가 확인했을 때에는 19 미만을 선택해야 할 이유가 딱히...(사실 19도 너무 낮다..)

 아 그리고 API 19 버전인 KitKat부터는 WebView가 Chromium을 기반으로 변경되었습니다. 그렇다면 API 19버전과 그 이하 버전에서 웹뷰의 동작이 조금 다를 수 있다는 것을 감안해야겠네요.

 

API 20(Android 4.4W, KitKat)

 이 버전을 클릭하면 99.6%의 디바이스를 만족시킬 수 있다고 합니다. 그렇다는 것은 API 19이하를 사용하고 있는 사용자가 전 세계에 0.4%정도 된다는 것을 의미하겠네요. 이 API는 특별히 안드로이드 디바이스에서 변경된 점은 없지만, Android Wear 기기에서 KitKat 버전을 사용할 수 있도록 업데이트를 했다고 합니다. 그래서 버전이 4.4W가 되었네요. 웨어러블 기기를 위한 업데이트이니 웨어러블 앱을 개발중이었다면 이 당시에는 이 버전이 중요한 역할을 했을 것 같습니다(이 당시에 개발자가 아니었던 저는...).

 또한 여기까지는 안드로이드의 컴파일 방식은 Dalvik이었다고 하네요. 이로 인해 multiDex 이슈가 존재합니다. minSDK를 만약 20 이하로 설정하게 되신다면 라이브러리를 사용할 때 이 점 유의하셔야 할 것 같습니다.

 

API 21(Android 5.0, Lollipop)

 이 버전을 클릭하면 99.6%의 디바이스를 만족시킬 수 있다고 합니다. 사실상 웨어러블 디바이스 중 API가 20인 디바이스는 없다고 봐도 무방하다는 의미 같습니다. 이 버전부터는 안드로이드 ART(Android Runtime) 컴파일 방식을 도입하게 되어 위에 썼던 multiDex의 문제가 해결되었습니다.

 그리고 코딩과는 큰 연관성이 없지만(디테일하게 들어가면 중요할 수도 있고...), 최초로 64bit CPU를 지원한다고 합니다. 또한 구글의 머티리얼 디자인이 처음 적용된 버전이라고 합니다. 현재는 익숙한 머티리얼 디자인이라는 것이 이때 적용되었군요.

 또한 이때부터 DreamService class를 사용하기 위해서 BIND_DREAM_SERVICE 권한을 필요로 한다고 합니다. 그러니 21 이하 버전과 이상 버전에서 DreamService를 사용하기 위해서는 분기가 필요하겠네요.

 

API 22(Android 5.1, Lollipop)

 아직 롤리팝입니다. 이 버전을 클릭하면 99.4%의 디바이스를 만족시킬 수 있다고 합니다. API 21에 멈춰있는 폰이 전 세계적으로 0.2%정도 된다고 하는군요. 5.1 버전에서의 특이사항은 org.apache.httpandroid.net.http.AndroidHttpClient 클래스가URLConnection으로 대체되었다는 것입니다. 이 당시 위 코드를 사용하고 있었다면 API 버전을 올리는데 시간이 좀 걸렸을 수도 있겠습니다.

 

API 23(Android 6.0, Marshmellow)

 대망의 마시멜로입니다. Build.VERSION_CODES_M 으로 대표되는 이 버전은 현재 98.2%의 디바이스를 케어할 수 있군요. 이는 22버전이 1.2% 정도 존재한다는 말이 되겠습니다.

 이 버전부터 이제 런타임으로 대표되는 사용자가 실행을 인지할 수 있는 시점에 권한 허용을 직접 요청하도록 변경되었습니다. 안드로이드 앱을 사용시에 쉽게 보이는, 권한 허용 다이얼로그가 이때 첫 등장을 한 것이죠.

 또한 android:usesCleartextTraffic="true" 를 통해 http 통신을 처리하는 Manifest 구문을 만들었습니다. 물론 API 27 이하까지는 default가 true이나, 28부터 false가 default가 되었기 때문에 현재는 http 웹뷰 통신이 있는 코드라면 무조건 적용할 수 밖에 없는 구문이 이때 등장했습니다. 

 

API 24(Android 7.0, Nougat)

 누가의 버전 케어율은 96.3%입니다. 1.9%정도가 API 23 버전을 사용중이라는 의미겠네요. 여기부터는 그래도 체감될 정도로 사람들이 사용하고 있긴 합니다. 한국에서 누가 아직도 API 24를 사용하냐 묻겠지만, 한국에서보다는 저가폰 및 중국폰을 주로 구매하는 나라의 시장에서 자주 보입니다. 특히 삼성전자의 안드로이드와 다르게 vivo 등의 중국폰은 안드로이드를 바탕으로 한 자기네 OS를 만들었는데, 이 버전이 구글에서 권유하는 버전보다 낮은 버전에서 OS를 설계하는 경우가 있어 체감보다 낮은 휴대폰이 더 많을 수 있습니다.

 24 버전부터는 사용자가 폰을 재실행했을 때 아직 기기 암호화를 해제하지 않았다면, 앱에서 알림을 받을 수 없습니다. 이는 API 24 에서 추가된 보안 정책과 연관되어 있습니다. 이를 처리하기 위해서는 여러가지 방법이 있지만, 기본적으로 아래 리시버를 manifest에 등록하는 것으로 해결할 수 있습니다. 자세한 내용은 여기서 확인해보면 되겠습니다.

<receiver
  android:directBootAware="true" >
  ...
  <intent-filter>
    <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
  </intent-filter>
</receiver>

 

API 25(Android 7.1.1, Nougat)

 이 버전부터는 95%까지 케어율이 낮아집니다. 그래도 아직 5%를 제외하면 전부 케어가 가능합니다. 그리고 API 24 버전을 사용하고 있는 디바이스가 1.3%라는 것을 확인할 수 있습니다. 여기서는 특별한 것은 크게 없습니다. 다른 버전들처럼 각 버전별로 등장한 메소드들을 사용하기 위해 minSDK를 조절해주지 않으면 syntax Error를 뱉는 것만 주의하면 되지만, 당장 25에서 등장한 메소드들 중 크게 중요한 것들은 없었던 걸로 기억합니다.

 

API 26(Android 8.0, Oreo)

 이 버전의 케어율은 93.7%로 API 25이 1.3% 정도라는 것을 알 수 있습니다. 아직까지는 이전 버전의 사용이 크게 높아지고 있지 않습니다. 그리고 오레오 버전부터 알림에 카테고리가 추가됩니다. 이는 각 알림 채널이 세분화되었다는 것을 의미하고, 현재 휴대폰에서 각각 알림 카테고리의 알림을 켜고 끌 수 있는 기능이 있는데, 이 카테고리가 이때 추가되었음을 알 수 있습니다. 이는 바꿔말하면 API 25 이하가 minSDK일 때 분기처리가 필요하다는 것을 의미합니다.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    builder = new NotificationCompat.Builder(context, channel);
} else {
    builder = new NotificationCompat.Builder(context);
}

 

 대략 이런 느낌으로 분기됩니다(옛날 API라 자바 버전으로 가져와 봤습니다). 여기서 channel은 분기된 카테고리를 의미합니다.

 

API 27(Android 8.1, Oreo)

 이 버전의 케어는 91.8%까지 가능합니다. 이 버전의 이전 버전인 API 26 버전의 사용률은 1.9%입니다. 이 버전으로 작업하는 경우에 EditText.getText가 Editable을 반환합니다. 처음 안드로이드를 접했을 때 부터 EditText의 text는 Editable이어서 몰랐는데, 이전에는 CharSequence를 반환했다고 하네요. 이외에는 8.1 버전으로 8과 큰 차이는 없는 버전이기 때문에 크게 특이사항은 없는 것 같습니다.

 

API 28(Android 9.0, Pie)

 이 버전의 케어는 86.4%입니다. API 27은 아직 꽤 사용하고 있다는 것을 알 수 있는데요. 자그마치 사용률은 5.4%네요. 크게 특별한 건 없지만, 유의점이 있는데요. 이전 버전에서 API 28로 마이그레이션 하는 경우, 기존에 포그라운드 서비스를 사용했다면 변경점이 있습니다. Pie 부터는 FOREGROUND_SERVICE 권한을 허용하지 않은 상태로 포그라운드 서비스를 이용하려는 경우 securityException을 일으킨다고 합니다.

 

API 29(Android 10.0, Q)

 이 버전의 케어는 75.9%입니다. 이젠 확실히 케어할 수 있는 범위가 많이 좁아졌습니다. 그렇다는 것은 API 28 버전이 아직 9.5%나 사용중이라는 것이네요.

 이 버전에서는 많은 것들이 변경되었습니다! 일단 안드로이드 OS가 폴더블 기기를 지원하게 됨으로 폴더블 기기를 지원하기 위해서는 무조건 안드로이드 10을 사용해야 합니다. 또한 기존 저장소 접근에 대해 EXTERNAL_STORAGE 권한을 READ and WRITE하는 것으로 스마트폰의 여러 공간을 사용할 수 있었지만, 이 버전부터는 범위 지정 저장소라는 개념을 가지고 왔습니다. 이제 앱은 앱에게 할당된 저장소와 mediaStore를 통해 쓴 영상 및 이미지, 그리고 오디오 만 호출할 수 있게 되었습니다.

 그리고 이제 앱이 포그라운드 상황에서 권한을 요청하는 경우, 특정 권한에서 "앱 사용중에만 허용" 문구가 추가되었습니다. 이는 별 거 아닌 것 같아 보이지만, 백그라운드에서 미디어를 다운로드하거나 업로드 하는 기능이 존재한다고 했을 때 이전 버전의 경우에는 사용자가 한 번 권한을 허용하는 경우 문제 없이 기능이 동작되었지만, 이제 앱 사용중에만 허용으로 권한을 허용한 경우에는 백그라운드에서 정상적으로 미디어를 다운로드하거나 업로드하는 기능이 이루어지지 않을 수 있음을 의미합니다. 그래서 앱 종료 후에도 동작해야 하는 작업이 있는 경우 사용자에게 명시를 통해 권한을 받는 것이 중요해졌습니다.

 이 버전부터 백그라운드에서 바로 액티비티를 호출하는 것에 제약사항이 생깁니다. 기존 버전의 경우 제약없이 액티비티를 백그라운드에서 호출해서 실행할 수 있었지만, 이제는 특정 예외사항에만 액티비티를 호출할 수 있게 됩니다.

 제한은 아니지만, 알아두면 좋은 것은 AndroidX 라이브러리를 사용하기 위해서는 compileSDK가 29로 지정되어야 한다는 점입니다.

 이 버전에서는 여러가지 제약사항들이 많이 등장합니다. 이는 주로 보안 정책이나 개인정보 보호, 향상된 스마트폰 사용성에 대한 이슈로 발생한 것들입니다. 이때부터 구글이 안드로이드 OS의 초점을 보안에 두고 있다는 것을 알 수 있습니다. 이때부터 개발에 제약사항이 계속해서 늘어나지만, 덕분에 안드로이드 OS는 조금 더 깨끗해지게 되었습니다.

 

API 30(Android 11.0, R)

 이 버전은 59.8%의 케어율을 지닙니다. API 29에 머무른 기기가 16.1%나 된다는 것을 의미합니다. 이 버전은 갤럭시 S9은 사용할 수 없는 버전입니다. 사실상 API 29를 사용하는 기종은 갤럭시 S9 이전 기종임을 의미한다는 것을 알 수 있습니다. 사실 갤럭시가 지나치게 지원을 빨리 끊는다는 점을 볼 때 사실상 휴대폰을 4년 이상 사용하는 분들이 많거나 휴대폰을 변경할 때 이전 폰을 계속 들고 계시는 분이 많다는 것으로 여겨질 수 있겠네요.

 안드로이드 11부터는 위치, 마이크, 카메라 권한을 요청하는 앱에선 해당 권한을 1회성 권한으로 취급해 임시 접근 권한을 요청할 수 있으며, 기기에선 이번만 허용이라는 팝업 버튼으로 표시되는 기능도 추가되었습니다. 이전 버전에서는 앱 사용중에만 허용하는 형태의 권한이 등장하더니 이제는 아예 1회성 권한까지 추가되었습니다. 하지만 앱 내에서만 작업이 이루어지는 경우, 버전을 올릴 때 큰 무리는 없을 것 같습니다(이전 버전과 마찬가지로).

 

API 31(Android 12, S)

 이 버전은 38.2%의 케어율을 지닙니다. 전 세계에 안드로이드 12 이상을 설치한 단말이 40%가 안된다는 의미죠. 또한 API 30 버전은 26.1%가 사용하고 있음을 알려줍니다. 어쩌면 제일 사용율이 높은 버전이 30이 되겠네요.

 이 버전에서는 자주 보이는 문제점 중 하나가 manifest에 android:exported를 선언하지 않아서 플레이스토어에 업로드가 되지 않는 현상일 겁니다.

 그리고 앱의 스플래시 스크린 API가 추가되었습니다. 물론 아직까지도 이 기능을 제대로 구현해서 사용하는 앱이 많아보이지는 않지만, 이 버전부터 좀 더 쉽게 스플래시 스크린을 그릴 수 있게 되었습니다(그러나 정작 API 31미만에서는 스플래시 API를 사용할 수 없는 이슈로 인해 오히려 코드를 분기해야 하는 번거로움이 늘어났습니다).

 

API 32(Android 12L, S)

 이 버전은 동일하게 Android 12입니다. 그러나 앞에 L이 붙어서 기존 버전과는 조금 다르게 표시된다는 것을 알 수 있습니다. 이 버전은 22.4%를 케어할 수 있네요. 이는 API 31인 Android 12가 15.8% 정도 사용중이라는 의미네요. 31에서 32로 마이그레이션 하는 과정을 작년(23년 초)에 진행했었는데, 31에서 사용중이던 코드 중 32에서 deprecated되는 코드는 없었어서 어렵지 않게 마이그레이션했던 기억이 있습니다.

 

API 33(Android 13, Tiramisu)

 현재(24년 2월 기준) GooglePlay에서 공식적으로 타겟해야 하는 버전입니다. 그리고 현재 Android는 15가 프리뷰 중입니다. 이 버전 역시 22.4%를 케어하고 있습니다. 사실상 Android 12L을 사용중인 사람은 없다는 것을 알 수 있겠네요. 이는 현재 Android 12L을 지원하는 모든 기종이 Android 13까지 공식 지원이 되기 때문에 아마 유저들 대부분이 자동으로 업데이트를 하고 있기 때문에 발생하는 이슈일 것입니다. 저 역시 Android 13을 사용중이네요. 하지만 이 버전으로 마이그레이션 할 때에는 의외로 고려해야 할 사항들이 조금 있습니다.

 특히 MediaMetadataRetriever와 같이 Android 13 버전부터 명시적으로 IOExeption이 발생한다고 알려주는 것처럼 문법에 대한 변경사항이 있습니다. 더 여러가지가 있지만 그 부분은 마이그레이션시에 자신의 코드에 적용되어 있는 부분을 확인해야 할 것입니다.

 또한 Android 12까지는 알림 권한이 세분화되어 있지만 따로 사용자에게 권한 허용을 받을 필요는 없었던 것과는 달리 이 버전에서부터는 알림 권한을 명시적으로 허용하지 않으면 자동으로 권한이 허용 안됨 상태로 유지되게 됩니다. 이로 인해 기존 Android 12를 타겟하는 앱이 Android 13 단말에 설치되는 경우 알림 채널이 생성되는 시점에 자동으로 알림 권한 팝업이 뜨게 되나, Android 13을 타겟팅하게 되면 알림 권한 팝업이 동작하도록 명시적으로 작성해줘야 합니다.

 

API 34(Android 14, Upside Down Cake)

 현 시점에서는 정식으로 고정된 지 얼마 되지 않은 시점이라 1%도 케어할 수 없습니다. 사실상 대부분의 유저는 33까지만 설치되어 있는 것이죠. 더욱이 매년 8월 31일부로 Google Play의 Target 버전을 올린다고 명시해놓은 부분을 봤을 때 아직 6개월 정도는 남아있다고 보시면 될 것 같습니다. 아직 이름조차 Android Studio에서 나오지 않느 것을 보면 기간이 좀 남았다는 것을 알 수 있습니다. 물론 그렇다고 손 놓고 있으면 안되니 어떤 부분이 변경되고 있는지 확인할 필요는 있겠네요.

 Android 14에서 가장 중요한 것은 이미 Android 13부터 세분화된 미디어 권한을 더 세분화 시켰다는 것입니다. 그와 동시에 Android 13에서 시작된 PhotoPicker를 이용해 권한 없이 구글에서 제공해주는 UI를 사용하기를 권장하고 있습니다. 이 PhotoPicker는 커스텀이 어려우니 디자이너나 기획팀과 협의할 때 이 부분을 상세히 설명할 필요가 있겠네요.

 그리고 또 하나, 요즘은 코드에 BroadCastReceiver를 잘 사용하고 있지 않지만, 기존 프로젝트에서 BroadCast를 사용한다면 필시, registerReceiver 메소드를 사용할 때 Context.RECEIVER_NOT_EXPORTED 혹은 Context.RECEIVER_EXPORTED를 지정해서 써야한다는 것입니다. 저는 아래처럼 분기해서 사용하고 있습니다.

fun registerReceiverNotExportedWithoutVersion(
    contextWrapper : ContextWrapper?,
    receiver: BroadcastReceiver,
    filter: IntentFilter
) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        contextWrapper?.registerReceiver(
            receiver,
            filter,
            Context.RECEIVER_NOT_EXPORTED
        )
    } else { contextWrapper?.registerReceiver(receiver, filter) }
}

fun registerReceiverExportedWithoutVersion(
    contextWrapper : ContextWrapper?,
    receiver: BroadcastReceiver,
    filter: IntentFilter
) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        contextWrapper?.registerReceiver(
            receiver,
            filter,
            Context.RECEIVER_EXPORTED
        )
    } else { contextWrapper?.registerReceiver(receiver, filter) }
}

 exported여부를 default로 하고 싶다면, 기존처럼 filter.actionCount가 0이 아닌지에 따라 정해주면 됩니다. 기존 default의 경우 filter에 정의된 action이 있는 경우 RECEVIER_EXPORTED를 지정해주고, 없는 경우 RECEIVER_NOT_EXPORTED를 지정해주고 있습니다.


 이 글은 사실 1월 말부터 쓰기 시작했는데 벌써 2월 말이 다되어 가네요... 회사에서 버전 관련 정리를 하면서 조금씩 작성하던 문서인데 정돈이 덜 된 느낌이라 아쉽습니다. 거기다 제가 직접 마이그레이션 하지 않은 버전들은 자세히 알기도 어려웠고 말이죠... 그래도 정리하면서 조금씩 각 버전별 특징들을 되새길 수 있었습니다. 

댓글