안드로이드 기술 면접 책 북 스터디를 하면서
보다 간결하게 실제 면접하는 것처럼 글을 정리해보려 한다.
Q20) ActivityManager란 무엇인가?
ActivityManager는 기기에서 실행중인 Activity, 태스크, 프로세스에 대한 정보를 제공하고
관리하는 안드로이드 시스템 서비스 입니다.
안드로이드 프레임워크의 일부로, 개발자가 앱 생명주기, 메모리 사용량 및 태스크 관리 측면에서 상호작용하고
제어할 수 있도록 합니다.
ActivityManager의 주요 기능
- 태스크 및 Activity 정보
- 실행 중인 태스크, Activity 및 해당 스택 상세에 대한 세부 정보를 추적할 수 있습니다.
- 앱 동작 및 시스템 리소스 사용량을 모니터링 하는데 도움이 됩니다. - 메모리 관리
- 앱의 메모리 소비 및 시스템 전체 메모리 상태를 포함하여 시스템 전체의 메모리 사용량에 대한 정보를 제공합니다.
- 앱 성능을 최적화하고 메모리 부족 상태를 처리할 수 있습니다.\ - 앱 프로세스 관리
- 실행중인 앱 프로세스 및 Service에 대한 세부 정보를 쿼리할 수 있습니다.
- 앱 상태를 감지하거나 프로세스 수준의 변화에 응답할 수 있습니다. - 디버깅 및 진단
- 힙 덤프 생성 또는 앱 프로파일링과 같이 디버그를 위한 도구를 제공합니다.
- 성능 병목 현상이나 메모리 누수를 식별하는 데 도움이 될 수 있습니다.
현재 프로세스 상태, 메모리 상태등을 조회하는 메서드들이 존재함
Q21) SparseArray는 무엇인가요?
SparseArray는 HashMap과 유사하게 정수 키를 객체 값에 매핑하는 안드로이드에 최적화된 데이터 구조입니다.
정수인 키와 함께 사용하도록 최적화되어 있어 정수 기반 키를 사용할 때 Map이나 HashMap보다
메모리 관리 측면에서 효율이 좋고 상황에 따라 성능이 더 좋습니다.
SparseArray의 주요 특징
- 메모리 효율성
- SparseArray는 오토박싱을 피하고 Entry 객체와 같은 추가 데이터 구조에 의존하지 않습니다.
- 훨씬 적은 메모리를 소비합니다. - 성능
- 메모리 최적화 덕분에 중간 크기의 데이터 셋에서 더 나은 성능을 제공합니다. - Null 키 값 사용 불가
- 키 값으로 기본 정수를 사용하므로 키 값에 null을 포함하지 않습니다.
import android.util.SparseArray
val sparseArray = SparseArray<String>()
sparseArray.put(1, "One")
sparseArray.put(2, "Two")
// 요소 접근
val value = sparseArray[1] // "One"
// 요소 제거
sparseArray.remove(2)
// 요소 순회
for (i in 0 until sparseArray.size()) {
val key = sparseArray.keyAt(i)
val value = sparseArray.valueAt(i)
println("Key: $key, Value: $value")
}
SparseArray 사용의 이점
- 오토박싱 방지
- SparseArray는 int 키로 직접 작동하여 메모리와 계산 작업을 절약합니다. - 메모리 절약
- 키와 값을 저장하기 위해 내부적으로 기본 배열을 사용하여 Entry와 같은 여러 객체를 생성하는
HashMap 구현에 비해 메모리 차지 공간을 줄입니다. - 안드로이드 특화
- 제한된 리소스를 처리하기 위해 안드로이드에 특화된 구조로 설계, View ID를 객체에 매핑하는 등의 시나리오에 효과적
SparseArray의 한계
- 성능 트레이드오프
- 요소 접근은 키 조회를 위해 이진 탐색을 사용하기 때문에 매우 큰 데이터 셋의 경우 HashMap보다 느립니다. - 정수 키만 사용 가능
- 정수 키로 제한되어 다른 유형의 키가 필요한 사용 사례에는 적합하지 않습니다.
Q22) 런타임 권한은 어떻게 처리하나요?
안드로이드 6.0(API 23)부터는 앱은 설치 시 자동으로 권한을 획득하는 대신
런타임에 위험 권한을 명시적으로 요청해야 합니다.
권한 선언 및 확인
권한을 요청하기 전에 앱은 AndroidManifest.xml 파일에 해당 권한을 선언해야 합니다.
런타임 시에는 사용자가 해당 권한이 필요 기능과 상호 작용할 때만 권한을 요청해야 합니다.
사용자에게 요청하기 전에 ContextCompat.checkSelfPermission()을 사용하여
권한이 이미 부여되었는지 확인하는 것이 중요합니다.
권한이 부여되지 않았으면 사용자에게 권한을 올바르게 요청해야 합니다.
when {
ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED -> {
// 권한 부여됨, 동작 이어서 진행
}
ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) -> {
// 사용자가 권한 요청을 거부한 경우
showPermissionRationale()
}
else -> {
// 사용자 권한 요청
requestPermissionLauncher.launch(Manifest.permission.CAMERA)
}
}
권한 요청하기
권한 요청에 권장되는 방법은 ActivityResultLauncher API를 사용하는 것입니다.
그러면 시스템은 사용자에게 권한 요청을 허용하거나 거부하도록 안내합니다.
val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
if (isGranted) {
// 권한 부여됨, 동작 이어서 진행
} else {
// 권한 거부됨
}
}
Q23) Looper, Handler, HandlerThread의 역할은 무엇인가요?
Looper, Handler, HandlerThread는 안드로이드에서 스레드 간 비동기 작업과 메시지 처리를 위해 함께 동작하는 핵심 컴포넌트입니다.
이들은 백그라운드 작업을 효율적으로 처리하면서, UI 스레드와 안전하게 통신할 수 있도록 설계되었습니다.
Looper
- 정의 : 스레드를 살아있게 유지하며, 메시지 큐를 순차적으로 처리하는 역할
- 용도 : 메인 스레드에는 기본적으로 루퍼가 존재하며, 워커 스레드는 직접 생성해야 함
Handler
- 정의 : 메시지나 Runnable을 Looper가 있는 스레드의 메시지 큐에 전달하고 처리하는 역할
- 용도 : 한 스레드에서 다른 스레드로 작업을 전달할 때 사용
HandlerThread
- 정의 : Looper가 내장된 특수한 Thread로 별도의 워커 스레드에서 메시지 큐를 처리할 수 있게 됨
- 용도 : 백그라운드에서 순차적으로 작업을 처리해야 할 때 사용
Q24) 예외를 어떻게 추적하나요
Logcat을 이용한 예외 로깅
예외가 발생하면 자세한 스택 트레이스를 Logcat에 기록합니다.
E/AndroidRuntime과 같은 키워드를 사용하여 Logcat 로그를 필터링하여 예외에 집중할 수 있습니다.
try-catch 혹은 Result를 이용한 예외 처리
try-catch 블록을 사용하면 예외를 제어된 방식으로 처리하고
코드의 중요한 부분에서 앱 크래시를 방지할 수 있습니다.
try {
val result = performRiskyOperation()
} catch (e: Exception) {
Log.e("Error", "Exception occurred: ${e.message}",e)
}
runCatching {
performRiskyOperation()
}.onSuccess {
}.onFailure {
Timber.e("Exception occurred: ${e.message}")
}
전역 예외 핸들러 사용하기
Thread.setDefaultUncaughtExceptionHandler를 설정하여 전역 예외 핸들러를 설정하면
앱 전체에서 처리되지 않은 예외를 포착하는 데 도움이 됩니다.
이는 중앙 집중식 오류 보고 또는 로깅에 특히 유용합니다.
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()
Thread.setDefaultUncaughtExceptionHandler { thread, exception ->
Log.e("GlobalHandler", "Uncaught exception in thread ${thread.name}: ${exception.message}", exception)
// 예외 세부 정보 저장 또는 서드 파티 솔루션으로 전송 (Crashlytics 등)
// FirebaseCrashlytics.getInstance().recordException(exception)
// 기존 핸들러 호출 (선택 사항, 시스템 기본 크래시 동작 유지)
defaultHandler?.uncaughtException(thread, exception)
}
}
}
Firebase Crasylatics나 브레이크 포인트를 이용한 디버깅 방법도 존재합니다.
Q25) 빌드 변형과 플레이버란 무엇인가요?
빌드 변형과 플레이버는 단일 코드베이스에서 애플리케이션의 다양한 버전을 생성하는 유연한 방법을 제공합니다.
예) 프로덕션 빌드, 유료 및 무료 버전
빌드 변형 (Build Variants)
빌드 변형은 특정 빌드 타입과 제품 플레이버를 결합한 결과입니다.
안드로이드 Gradle 플러그인은 각 조합에 대해 빌드 변형을 생성하며 다양한 사용 사례에 맞는 API 또는 번들을 생성할 수 있도록 합니다.
빌드 타입은 애플리케이션이 어떻게 빌드되는지를 나타내며 일반적으로 아래 타입을 포함합니다.
- 디버그 : 개발 중에 사용되는 빌드 입니다. 디버그 도구, 로그 및 테스트용 디버그 툴을 활용
- 릴리즈 : 배포에 최적화된 구성으로, 종종 리소스 최적화와 최소화, 난독화가 적용되거 배포를 위해서는 별도의 릴리즈 키로 서명되어야 합니다.
제품 플레이버 (Product Flavors)
제품 플레이버를 통해 개발자는 무료 및 유로 버전이나 us 및 en과 같은 지역별 버전과 같이
앱의 다양한 변형을 정의할 수 있습니다.
각 플레이버는 애플리케이션 ID, 버전 이름 또는 리소스와 같은 고유한 구성을 가질 수 있습니다.
Q26) 접근성을 어떻게 보장하나요?
접근성은 시각, 청각 또는 신체 장애가 있는 사람들을 포함하여 모든 사람이
애플리케이션을 사용할 수 있도록 보장하는 것입니다.
- 콘텐츠 설명 활용하기
- android:contentDescription 속성을 사용하여 해당 컴포넌트를 알릴 수 있습니다. - 동적 글꼴 지원하기
- sp 단위를 사용하여 자동으로 크기가 조정되도록합니다. - 포커스 관리 및 탐색
- 색상 대비 및 시각적 접근성
- 커스텀 뷰 및 접근성
Q27) 안드로이드 파일 시스템의 주요 구성 요소
안드로이드 파일 시스템은 리눅스의 파일 시스템 아키텍처 위에 구축되어
엄격한 보안 및 권한 모델을 준수하면서 애플리케이션을 위한 비공개 및 공유 저장공간을 제공합니다.
안드로이드 파일 시스템의 주요 구성 요소
- System Partition (/system)
- 안드로이드 프레임워크 라이브러리, 시스템 및 앱 구성 파일들을 포함한 핵심 운영체제에 대한 파일이 들어있습니다.
- 악의적인 수정을 막기 위해 읽기 전용입니다. - Data Partition (/data)
- 데이터베이스, SharedPreference 및 사용자가 생성한 파일을 포함한 앱 별 데이터가 저장됩니다.
- 각 앱은 /data/data 내에 해당 앱만 접근할 수 있는 비공개 디렉터리를 가지며 보안이 보장됩니다. - Cache Partition (/cache)
- 업데이트나 재시작 시 유지할 필요가 없는 캐시된 파일과 같은 임시 데이터 저장에 사용됩니다. - External Storage (/sdcard 또는 /storage)
- 여러 앱에서 접근할 수 있는 공유 저장 공간을 주로 제공합니다.
- 이미지, 비디오, 문서와 같은 미디어 파일에 주로 사용됨 - Temporary Files (/tmp)
- 임시 파일을 저장하는 위치입니다. 앱이나 시스템이 재시작할 때 지워집니다.
안드로이드에서 파일 접근하기
- 내부 저장소
- 비공개 저장 공간으로, 해당 앱만 접근할 수 있습니다, 민감하거나 앱별 데이터 저장에 이상적입니다. - 외부 저장소
- 여러 앱에서 접근할 수 있는 공유 저장 공간으로
사용자가 앱 외부에서 접근할 것으로 예상하는 사용자 생성 콘텐츠 및 미디어를 저장하는 데 사용됩니다.
Q28) 안드로이드 런타임, Dalvik, Dex 컴파일러란 무엇인가요?
애플리케이션은 고유한 런타임 환경과 런타임 프로세스에 의존합니다.
런타임과 컴파일러는 중요한 역할을 하며
앱이 성능, 메모리 효율성 및 안드로이드 기기와의 호환성을 위해 최적화되도록 보정합니다.
안드로이드 런타임
안드로이드 4.4에서 도입되어 5.0에서는 디폴트로 사용되는 관리형 런타임 환경입니다.
ART는 Ahead-of-Time (AOT) 컴파일을 사용하여 애플리케이션을 컴파일하며
앱 설치 중에 바이트코드를 기계 코드로 변환합니다.
이는 런타임 시 Just-in-Time 컴파일러의 필요성을 없애 앱 시작 시간을 단축하고 실행 중 CPU 사용량을 줄입니다.
ART의 특징
- 개선된 성능 : AOT 컴파일은 최적화된 기계 코드를 생성하여 런타임 오버헤드를 줄입니다.
- 가비지 컬렉션 : ART는 더 나은 메모리 관리를 위해 개선된 가비지 컬렉션 기술을 도입했습니다.
- 디버깅 및 프로파일링 지원 : 상세한 스택 트레이스 및 메모리 사용량 분석과 같은 향상된 도구를 제공합니다.
Dalvik
Dalvik은 ART 이전에 안드로이드에서 사용된 런타임
가상 머신 환경에서 애플리케이션을 실행하도록 설계되었으며
제한된 메모리와 처리 능력을 위해 최적화되었습니다.
Dalvik은 Just-in-Time 컴파일을 사용하여 런타임에 바이트 코드를 기계 코드로 변환합니다.
이 접근 방식은 앱 설치에 필요한 시간은 줄이지만, 즉석 컴파일로 인해 런타임 오버헤드가 증가합니다.
Dex 컴파일러
Dex 컴파일러는 Java/Kotlin 컴파일러에서 생성된 Java 바이트코드를
.dex 파일로 변환합니다. 이러한 .dex 파일은 컴팩트하여 Dalvik 및 ART 런타임 환경이 최적화되어 있습니다.
Q29) APK 파일과 AAB 파일의 차이점은 무엇인가요?
두 포맷 모두 안드로이드 앱을 패키징하는 역할을 하지만
목적, 구조 및 설치 중 리소스 처리 방식에서 차이가 있습니다.
APK (Android Package)
APK 파일은 안드로이드 애플리케이션을 배포하고 설치하는 전통적인 포맷입니다.
앱이 기기를 작동하는 데 필요한 리소스, 코드 및 메타데이터를 포함하는 완전하고 즉시 설치가 가능한 패키지입니다.
모든 기기구성, 모든 리소스를 포함하여서 사용자의 기기와 관련 없는 리소스를 포함하게 되어 파일 크기가 커질 수 있습니다.
AAB (Android App Bundle)
Google에서 도입한 AAB 형식은 APK와 같은 설치 가능한 형식이 아닌 게시 포맷입니다.
AAB를 구글 플레이에 업로드하면, Google Play는 이를 개별 기기에 맞게 최적화된 APK로 처리합니다.
'Study' 카테고리의 다른 글
| 안드로이드 면접 북 스터디 Q60 ~ Q66 (0) | 2025.10.23 |
|---|---|
| 안드로이드 면접 북 스터디 Q50 ~ Q59 (0) | 2025.10.20 |
| 안드로이드 면접 북 스터디 Q40 ~ Q49 (0) | 2025.10.16 |
| 안드로이드 면접 북 스터디 Q30 ~ Q39 (0) | 2025.10.13 |
| 안드로이드 면접 북 스터디 Q10 ~ Q19 (4) | 2025.08.09 |