본문 바로가기

Book/안드로이드 프로그래밍 Next Step(일부 공개)

[안드로이드 프로그래밍 Next Step] Chapter 1. 안드로이드 프레임 워크

Chapter 1. 안드로이드 프레임 워크

안드로이드 아키텍처의 소프트웨어 스택을 살펴보고, 프레임워크 소스 활용 방안과 안드로이드 버전에 따른 이슈를 알아봄.

Android Architecture

안드로이드 아키텍처

출처 : 플랫폼 아키텍처  |  Android Developers

Application

안드로이드에서 제공하는 선탑재 기본 앱(홈, 카메라, 전화, 브라우저 등)과 다운로드해서 설치하는 일반 앱은 애플리케이션 스택에 있다. 애플리케이션은 애플리케이션 프레임워크 스택 위에서 동작한다.
선탑재된 기본 앱은 시스템 권한을 사용할 수 있고, 프로세스 우선순위를 높일 수 있다. 프로세스 우선 순위는 단말에 메모리가 부족한 상황에서 시스템에서 강제로 종료시키는 프로세스를 정하는 기준이다.

Application Framework

애플리케이션 프레임워크는 안드로이드 OS 위에서 애플리케이션의 기반이 되는 기본 구조다. 앱에서는 애플리케이션 프레임워크에서 정한 규칙에 따라서 만들기만 하면 된다. 나머지는 애플리케이션 프레임워크의 몫이다.
애플리케이션의 여러 Manager들이 이 역할을 하고 있다.

Thin Client & Server

앱 프로세스는 컴포넌트 탐색, 액티비티 스택 관리, 서비스 목록 유지, ANR 처리 등을 직업하지 않는다. 서버인 system_server 프로세스에 모두 위임하고 컴포넌트 실행 등 최소한의 역할만을 한다. 따라서 앱 프로세스는 thin client이고, system_server는 서버 기능으로서 동작한다.

-example-brightgreen.svg
startActivity() → system_server가 Activity를 찾음 → system_server는 Activity Stack에도 Activity 내용을 반영하고, Activity를 가진 App Process에 Activity를 띄우라고 메시지 전송(App Process가 떠있지 않으면 system_server가 Process를 띄우기도 함) → Activity 실행

시스템 서비스 접근

Context의 getSystemService(String name) 메서드를 사용한다.

Android Runtime

달빅 가상 머신은 자바/C/C++로 작성되어 있으며, 자바 가상 머신보다 명령이 단순하고 속도가 빠르다.

Core Library(/system/core)

  • Bionic이라는 커스텀 C 라이브러리(libc)
  • WebKit/SQLite/OpenGL 같은 기능 라이브러리
  • 네이티브 시스템 서비스인 Surface Manager, Media Framework(/system/bin/surfaceflinger와 /system/bin/mediaserver 프로세스로 실행)

[부연 설명] Bionic

  • 라이센스 : 사용자 영역에서 GPL를 배제시키기 위해서 BSD 라이센스를 사용하여 Bionic을 개발함.
  • 크기 : libc는 모든 프로세스에서 독립적으로 로드되기 때문에 크기가 작아야 한다. Bionic은 약 200KB 크기로 glibc(GNU libc)의 절반 크기이다.
  • 속도 : 임베디드와 같은 CPU 파워가 제한된 환경에서는 빠른 처리 속도가 중요하기 때문에, Bionic은 최적화된 pthread 구현을 포함하여 작은 크기와 빠른 코드 패스를 가진다.

Linux Kernel

안드로이드 커널은 리눅스 커널을 기반으로, 불필요한 것은 제거하고 기능을 확장 패치(Binder, Ashmem, Low Memory Killer 등)한 것이다.

Binder IPC

확장 패치한 기능 가운데 Binder IPC는 프로세스 간 통신에 사용된다. 안드로이드 컴포넌트 가운데 Service와 Content Provider는 Binder를 통해 다른 프로세스에서 접근할 수 있다.
(IPC(Inter Process Communication)는 하부 메커니즘이고, RPC(Remote Procedure Call)는 IPC의 용도(리모트 콜)이다.)

Binder Thread

App Process는 Binder Thread라는 네이티브 스레드 풀이 있고, 최대 16개까지 스레드가 생성된다. 다른 프로세스에서 Binder IPC 통신을 할 때, 이 스레드 풀을 통해서 접근한다. DDMS에서의 Binder_1, Binder_2아 같은 이름의 스레드가 바로 Binder Thread에 속한다.

호환성 모드

호환성 모드는 안드로이드 버전이 올라가더라도 앱의 기존 동작이 바귀는 것을 방지하기 위한 것이다. framework 소스를 보면 targetSdkVersion으로 체크하는 부분이 많다. 따라서 targetSdkVersion을 설정해야하고 가급적 높게 설정하는 것을 권장한다(설정하지 않을 경우 minSdkVersion을 따라감).

-example-brightgreen.svg

AsyncTask 병렬/순차 실행

허니콤 이전 버전의 AsyncTask는 Task를 실행하면 병렬 실행되었지만, 허니콤부터는 순차 실해이으로 변경되었다.하지만 targetSdkVersion이 10이하이면 안드로이드 버전이 4.X라고 해도 기존과 동일하게 병렬 실행으로 동작한다.

Main Thread상에서 네트워크 통신

Main Thread상에서 네트워크 통신은 진저브레드 API 레벨 9까지 허용되었으나, 그 이후에는 에러(NetworkOnMainThreadException)가 발생한다.
API 레벨 9 이후에는 Background Thread에서 네트워크 통신을 하도록 변경해야 한다.

하드웨어 가속(Hardware Acceleration)

하드웨어 가속은 GPU를 가지고 View에서 Canvas에 그리는 작업을 하는 것이다. targetSdkVersion이 14 이상이면 디폴트 옵션이다. 하드웨어 가속을 하면 SlidingPaneLayout이나 여러 슬라이딩 메뉴(sliding menu) 라이브러리에서 Animation이 끊김 없이 작동한다(항상 속도가 향상되는 것은 아니다).

명시적 인텐트로 서비스 시작

targetSdkVersion이 21 이상일 때는 startService()나 bindService() 메서드를 실행할 때 명시적 인텐트를 사용해야 한다. 암시적 인텐트를 사용하면 예외가 발생한다. targetSdkVersion이 20 이하이면 예외가 발생하지 않는다.

단말 버전 체크

targetSdkVersion VS compileSdkVersion

tagetSdkVersion은 런타임 시에 비교해서 호환성 모드로 동작하기 위한 값이고, compileSdkVersion은 컴파일 시에 사용할 버전을 정하는 것이다.

버전 체크 필요

높은 버전에만 있는 클래스나 메서드를 낮은 버전의 단말에서 실행하면 크래시가 발생한다. 따라서 버전에 따른 분기가 필요하다.

12345678if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    // Coarse Location Permission
    if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        ...
    }
} else {
    ...
}

support 패키지의 -Compat 클래스 사용

support-v4에는 많은 -Compat 클래스가 있다. ViewCompat, ActivityCompat, WindowCompat, NotificationCompat, AsyncTaskCompat, SharedPreferencesCompat.EditorCompat 등이 있다.

123if(Build.VERSION.SDK_INT >= 9) {
    listview.setOverScrollMode(View.OVER_SCROLL_NEVER);
}

의 코드를 ViewCompat을 사용하면 간단하다.

1ViewCompat.setOverScrollMode(listview, ViewCompat.OVER_SCROLL_NEVER);

ViewCompat 구조 활용

support-v4에 없을 경우 별도로 작성하는데 버전마다 동작이 달라지도록 코드를 작성할 때는 ViewCompat 클래스의 구조를 활용하는 것도 좋다.

12345678910111213141516171819202122232425262728// ViewCompat 소스 참고
...
    
static final ViewCompatBaseImpl IMPL;
static {
    if (Build.VERSION.SDK_INT >= 26) {
        IMPL = new ViewCompatApi26Impl();
    } else if (Build.VERSION.SDK_INT >= 24) {
        IMPL = new ViewCompatApi24Impl();
    } else if (Build.VERSION.SDK_INT >= 23) {
        IMPL = new ViewCompatApi23Impl();
    } else if (Build.VERSION.SDK_INT >= 21) {
        IMPL = new ViewCompatApi21Impl();
    } else if (Build.VERSION.SDK_INT >= 19) {
        IMPL = new ViewCompatApi19Impl();
    } else if (Build.VERSION.SDK_INT >= 18) {
        IMPL = new ViewCompatApi18Impl();
    } else if (Build.VERSION.SDK_INT >= 17) {
        IMPL = new ViewCompatApi17Impl();
    } else if (Build.VERSION.SDK_INT >= 16) {
        IMPL = new ViewCompatApi16Impl();
    } else if (Build.VERSION.SDK_INT >= 15) {
        IMPL = new ViewCompatApi15Impl();
    } else {
        IMPL = new ViewCompatBaseImpl();
    }
}
...

Reference

안드로이드 프로그래밍 Next Step - 노재춘
수라이 월드 :: Bionic