Android
고친 사람 github-actions[bot]
Android 는 스마트폰 한 대에서 여러 앱이 안전하게 돌도록 관리해 주는 운영체제입니다. Google 이 중심이 되어 만듭니다. 휴대폰 제조사들이 이것을 받아 자기 기기에 올립니다. 앱은 화면이나 카메라를 직접 만지지 않고 Android 에 부탁해서 씁니다.
쉽고 빠른 이해
Android 는 휴대폰 속 아파트 관리사무소입니다. 앱은 입주자이고, 집마다 문이 잠겨 있어 남의 집에 못 들어갑니다. 카메라나 연락처를 쓰려면 사무소를 거쳐 사용자의 허락을 받아야 합니다.
이게 없으면 앱마다 휴대폰 부품을 다루는 코드를 따로 짜야 합니다. 앱 하나가 다른 앱의 파일을 마음대로 읽을 수도 있습니다.
돌아가는 방식은 이렇습니다.
- 개발자가 앱을 짜서 설치 파일 하나로 묶습니다
- 휴대폰에 설치하면 Android 가 그 앱에게 칸막이 친 방을 하나 줍니다
- 앱이 뒤로 가면 Android 가 멈춰 두고, 메모리가 모자라면 끝내 버립니다
대가가 있습니다. 앱이 언제든 끝날 수 있어서, 하던 일을 스스로 저장해 둬야 합니다. 휴대폰마다 깔린 버전이 제각각이라 오래된 버전까지 챙겨야 합니다.
상세
Android 는 휴대 기기용 운영체제입니다. 운영체제는 메모리와 저장 공간, 화면 같은 하드웨어를 여러 프로그램에게 나눠 주는 프로그램입니다. 프로그램끼리 서로 망가뜨리지 못하게 막는 일도 합니다. 이 절은 차례로 봅니다. Android 의 계층 구조, 앱이 설치 파일이 되는 과정, 앱을 가두는 샌드박스와 매니페스트, 앱끼리 부르는 인텐트, 앱의 생명주기, API 레벨, 접근성입니다.
Android 의 뼈대는 AOSP(Android Open Source Project, 안드로이드 오픈 소스 프로젝트)라는 이름으로 소스 코드가 공개되어 있습니다. 휴대폰 제조사는 이 코드를 받아 자기 기기에 맞게 고치고 자기 앱을 얹어 내놓습니다. 그래서 같은 Android 라도 기기마다 화면 모양과 기본 앱이 다릅니다.
Linux 커널 위의 계층 구조
Android 의 맨 밑에는 Linux 커널이 있습니다. 커널은 운영체제의 한가운데에서 메모리와 실행 순서, 장치를 직접 다루는 부분입니다. Android 는 커널을 새로 짜지 않고 Linux 의 것을 가져와 휴대폰에 맞게 고쳐 씁니다.
커널 위에는 계층이 몇 겹 더 올라갑니다. 계층을 나눈 까닭은 제조사마다 다른 부품을 아래 계층에서 감추려는 것입니다. 그러면 위 계층의 앱은 어느 기기에서나 같은 방식으로 부를 수 있습니다.
flowchart TD
A["앱 · 전화 · 메신저 · 내가 만든 앱"] --> F["애플리케이션 프레임워크 · 앱이 부르는 함수 묶음"]
F --> R["안드로이드 런타임 · 네이티브 라이브러리"]
R --> H["하드웨어 추상화 계층 · 부품마다 다른 차이를 감춘다"]
H --> K["Linux 커널 · 메모리 · 프로세스 · 장치 드라이버"]
그림은 앱의 요청이 내려가는 길입니다. 앱은 맨 위에서 애플리케이션 프레임워크가 내놓은 함수만 부릅니다. 애플리케이션 프레임워크는 화면을 띄우거나 알림을 보내는 일을 함수로 묶어 앱에 내놓는 계층입니다.
그 아래 안드로이드 런타임은 앱의 코드를 실제로 실행하는 부분입니다. 아래 「DEX 파일과 APK」 소절에서 ART 라는 이름으로 다시 나옵니다. 네이티브 라이브러리는 그림 그리기나 데이터베이스처럼 속도가 중요한 일을 C·C++ 로 짜 둔 코드 묶음입니다.
하드웨어 추상화 계층(Hardware Abstraction Layer, HAL)은 카메라 칩이 제조사마다 달라도 위 계층에는 같은 모양으로 보이게 해 줍니다. 맨 밑 커널 안의 장치 드라이버는 부품 하나하나를 직접 움직이는 코드입니다.
DEX 파일과 APK
Android 앱은 주로 Kotlin 이나 Java 로 짭니다. 두 언어는 원래 JVM(Java Virtual Machine, 자바 가상 머신)에서 도는 바이트코드로 컴파일됩니다. 바이트코드는 특정 프로세서가 아니라 가상 머신이 읽도록 만든 중간 명령어입니다.
Android 는 서버에서 쓰는 JVM 을 쓰지 않습니다. 빌드 도구가 바이트코드를 한 번 더 바꿔 DEX(Dalvik Executable) 파일로 만듭니다. DEX 는 메모리가 적은 기기에서 읽기 좋게 다시 짠 Android 전용 형식입니다.
기기에서 DEX 를 돌리는 쪽은 ART(Android Runtime, 안드로이드 런타임)입니다. ART 는 DEX 를 그 기기 프로세서의 기계어로 옮겨 실행합니다. ART 이전에는 Dalvik 이라는 런타임이 같은 일을 맡았습니다.
flowchart TD
S["Kotlin · Java 소스"] -->|컴파일| B["바이트코드"]
B -->|변환| D["DEX 파일"]
D -->|리소스와 함께 묶기| P["APK 설치 파일"]
P -->|설치| T["기기의 ART"]
T -->|기계어로 옮김| M["실행"]
그림 가운데의 APK(Android Package)는 DEX 파일과 이미지, 화면 배치 파일을 한데 묶은 설치 파일입니다. 휴대폰은 이 파일 하나를 받아 앱을 설치합니다.
소스를 컴파일해 APK 로 묶는 일은 보통 Gradle 이라는 빌드 도구가 맡습니다. 개발자는 개발 환경인 Android Studio 에서 버튼 하나로 이 빌드를 돌립니다. 빌드에 쓰는 도구와 라이브러리 묶음은 Android SDK(Software Development Kit, 소프트웨어 개발 키트)라고 부릅니다.
앱 스토어에 올릴 때는 APK 대신 Android App Bundle 로 올리기도 합니다. App Bundle 은 모든 기기용 코드와 리소스를 한데 담은 업로드용 묶음입니다. 스토어가 이 묶음에서 사용자의 기기에 필요한 것만 골라 APK 를 만들어 내려보냅니다. 덕분에 사용자가 받는 파일이 작아집니다.
앱마다 따로 가두는 샌드박스
Android 는 앱을 설치할 때마다 새 Linux 사용자를 하나 만들어 줍니다. 사용자마다 UID(User ID, 사용자 식별 번호)가 다릅니다. 앱의 파일은 그 사용자만 읽을 수 있게 잠깁니다. 서버에서 서비스마다 계정을 따로 두는 것과 같은 방식입니다.
앱은 실행될 때 자기 UID 로 된 프로세스 하나로 돕니다. 앱 하나가 사용자 하나, 프로세스 하나에 대응하는 셈입니다. 이 뒤에 나오는 「프로세스」는 앱 하나가 도는 단위로 읽으면 됩니다.
이렇게 앱마다 따로 가두는 것을 샌드박스라고 부릅니다. 앱 하나가 악성이거나 버그가 있어도 옆 앱의 데이터에 손대지 못합니다. 대가는 앱끼리 뭔가를 나누려면 운영체제가 열어 준 방법만 써야 한다는 것입니다.
샌드박스 밖의 것을 쓰려면 권한이 있어야 합니다. 인터넷, 카메라, 위치, 연락처가 그런 것입니다. 위치나 연락처처럼 사생활에 닿는 권한은 앱이 실행 중에 사용자에게 창을 띄워 묻습니다. 사용자는 거절할 수도 있습니다. 그러니 앱은 권한이 없는 경우의 길을 늘 따로 짜 둡니다.
매니페스트
앱이 무엇으로 이루어졌고 어떤 권한이 필요한지는 매니페스트라는 설정 파일에 적습니다. 파일 이름은 AndroidManifest.xml 입니다. XML(Extensible Markup Language, 확장 가능한 마크업 언어)로 씁니다.
Android 는 앱을 설치하고 띄울 때 이 파일부터 읽습니다.
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application android:label="주문">
<activity android:name=".MainActivity" />
<service android:name=".SyncService" />
</application>
</manifest>
방금 본 파일은 세 가지를 말합니다. uses-permission 은 인터넷을 쓰겠다는 권한입니다. activity 는 화면 하나인 액티비티, service 는 화면 없이 뒤에서 도는 작업인 서비스입니다.
화면이나 백그라운드 작업을 코드로만 만들고 여기 안 적으면 Android 는 그 존재를 모릅니다.
매니페스트에 적는 앱의 구성 요소는 넷입니다. 이 넷을 앱 컴포넌트라고 부릅니다.
| 컴포넌트 | 하는 일 |
|---|---|
| 액티비티 | 사용자가 보는 화면 하나 |
| 서비스 | 화면 없이 뒤에서 도는 작업. 음악 재생 · 동기화 |
| 브로드캐스트 리시버 | 기기 전체에 퍼지는 알림을 받는다. 충전 시작 · 네트워크 바뀜 |
| 콘텐츠 프로바이더 | 앱의 데이터를 다른 앱에 내준다. 연락처 목록 |
인텐트와 Binder
앱은 샌드박스에 갇혀 있어서 다른 앱의 코드를 직접 부르지 못합니다. 대신 「이 일을 해 줄 컴포넌트를 찾아 달라」는 메시지를 운영체제에 보냅니다. 이 메시지가 인텐트입니다. 사진을 찍어 달라는 인텐트를 보내면 Android 가 카메라 앱을 찾아 띄워 줍니다.
인텐트가 샌드박스를 넘어갈 수 있는 까닭은 운영체제가 가운데서 전해 주기 때문입니다. 앱마다 프로세스가 따로이므로, 이 전달은 프로세스와 프로세스 사이의 일입니다. 서로 다른 프로세스끼리 데이터를 주고받는 일을 IPC(Inter-Process Communication, 프로세스 간 통신)라고 부릅니다.
Android 는 IPC 에 Binder 라는 전용 방식을 씁니다. 인텐트도 속으로는 Binder 를 타고 다른 프로세스로 건너갑니다.
앱의 생명주기
휴대폰은 메모리가 적고 배터리로 돕니다. 그래서 Android 는 앱이 원하는 대로 계속 돌게 두지 않습니다. 사용자가 다른 앱으로 넘어가면 앞 앱을 멈춰 둡니다. 메모리가 모자라면 뒤에 있는 앱의 프로세스를 끝냅니다.
화면 하나에도 생명주기가 있습니다. 생명주기는 화면이 만들어지고, 보이고, 가려지고, 없어지는 단계의 순서입니다. 단계가 바뀔 때마다 Android 가 앱의 함수를 불러 알려 줍니다. 그림의 화살표 이름이 그 함수입니다.
stateDiagram-v2
[*] --> 만들어짐: onCreate
만들어짐 --> 보임: onStart
보임 --> 포그라운드: onResume
포그라운드 --> 보임: onPause
보임 --> 가려짐: onStop
가려짐 --> 보임: onRestart 뒤 onStart
가려짐 --> [*]: onDestroy
가려짐 --> [*]: 프로세스 종료 (알림 없음)
포그라운드에 있어야 사용자의 손가락 입력을 받습니다. 다른 화면에 가려지면 onStop 까지 내려옵니다. 가려졌던 화면이 다시 돌아올 때는 onRestart 가 먼저 불립니다. 이어서 처음 보일 때와 같은 onStart 가 불립니다.
가려진 화면이 끝나는 길은 둘입니다. 사용자가 화면을 닫거나 앱이 스스로 끝내면 onDestroy 가 불립니다. 메모리가 모자라 Android 가 프로세스를 통째로 끝낼 때는 onDestroy 조차 불리지 않을 수 있습니다. 그림의 마지막 화살표가 이 경우입니다.
알림 없이 끝날 수 있으니, 쓰던 글 같은 상태는 가려지기 전에 저장해 둬야 합니다. 프로세스가 끝나면 그 앱이 서버로 보내던 네트워크 요청도 같이 끊깁니다. 백엔드에서는 앱의 요청이 응답을 받기 전에 사라지는 일이 흔하다는 뜻입니다.
API 레벨과 파편화
앱이 운영체제에 부탁할 때 쓰는 함수 목록을 API(Application Programming Interface)라고 부릅니다. Android 는 새 버전이 나올 때마다 이 목록이 늘어납니다. 이 목록의 버전을 정수 하나로 매긴 것이 API 레벨입니다.
문제는 휴대폰마다 깔린 버전이 다르다는 것입니다. 제조사가 새 버전을 늦게 내거나 아예 안 내면, 사용자는 오래된 버전에 머뭅니다. 이렇게 버전이 여럿으로 흩어진 상태를 파편화라고 부릅니다.
앱은 매니페스트나 빌드 설정에 「이 버전 아래에서는 설치 안 된다」는 최소 API 레벨을 적습니다. 낮게 잡을수록 더 많은 기기에 깔립니다. 대신 새 기능을 쓸 때마다 기기의 버전을 확인하는 코드가 붙습니다.
백엔드 개발자에게도 이 사정이 닿습니다. 사용자는 앱을 바로 업데이트하지 않으므로 서버에는 여러 버전의 앱이 동시에 요청을 보냅니다. 여기서 말하는 서버 API 는 앞의 운영체제 API 와 다른 것입니다. 앱이 서버에 보내는 요청의 형식이고, 백엔드가 정하는 약속입니다. 서버 API 를 바꿀 때는 옛 앱이 보내는 요청도 받도록 하위 호환성을 지켜야 합니다.
접근성과 스크린 리더
접근성은 눈이나 손이 불편한 사용자도 앱을 쓸 수 있게 만드는 일을 다루는 분야입니다. 이 소절은 Android 가 그 일을 어떻게 돕는지를 버튼 하나로 봅니다.
Android 에는 앱 화면을 대신 읽거나 조작해 주는 접근성 서비스를 끼울 수 있습니다. 대표적인 것이 눈이 안 보이는 사용자가 쓰는 스크린 리더입니다. 스크린 리더는 화면에 있는 버튼과 글자를 소리로 읽어 줍니다.
Android 기본 스크린 리더의 이름은 TalkBack 입니다.
글자가 없는 아이콘 버튼은 스크린 리더가 읽을 것이 없습니다. 그래서 개발자가 버튼에 contentDescription 속성으로 「공유」 같은 설명을 적어 둡니다. 이 속성은 화면 배치 XML 의 버튼 요소에 붙입니다.
이 설명이 빠지면 사용자는 「버튼」이라는 소리만 듣고 무슨 버튼인지 모릅니다.
관련 항목
Android 가 속하는 운영체제 갈래
운영체제 · 모바일 운영체제 · Linux · 유닉스 · 임베디드 시스템
Android 와 같은 일을 두고 겨루는 운영체제
iOS · iPadOS · HarmonyOS · Windows · macOS
Android 를 이루는 계층
커널 · 하드웨어 추상화 계층 · ART · Dalvik · 네이티브 라이브러리 · 애플리케이션 프레임워크 · Binder · 장치 드라이버
Android 앱을 이루는 구성 요소
앱 컴포넌트 · 액티비티 · 서비스 (Android) · 브로드캐스트 리시버 · 콘텐츠 프로바이더 · 인텐트 · 프래그먼트 · 매니페스트 · 생명주기
Android 앱을 짓는 언어와 도구
Kotlin · Java · Jetpack Compose · Android Studio · Android SDK · Android NDK · Gradle · adb · 에뮬레이터
Android 앱이 거치는 빌드와 배포 형식
바이트코드 · DEX · APK · Android App Bundle · 코드 서명 · Google Play · 앱 스토어
Android 가 앱을 가두는 보안 장치
샌드박스 · UID · 권한 · 런타임 권한 · SELinux · 프로세스 격리
Android 앱과 서버가 만나는 통로
푸시 알림 · Firebase Cloud Messaging · REST API · 하위 호환성 · API 버전 관리 · 모바일 개발
Android 에서 버전과 기기를 다루는 개념
API 레벨 · 파편화 · AOSP · Google · Android Developers
Android 에서 화면을 읽고 조작하는 수단
다른 이름: 안드로이드 · android · Android OS