Manifest Android Interview: 질문으로 학습하는 안드로이드 (1)

seunghee song·2025년 9월 17일

최근에 안드로이드 스터디를 진행하고 있는데요 노션에만 정리했는데 이왕이면 찾아보기 편리하게 벨로그에도 옮겨두고자 작성하였습니다.

Q19) 화면 회전과 같은 구성 변경이 발생하면 activity 에 어떤 변화 생기나요?

  • 구성 변경시 Activity에서 일어나는 동작
  1. onResume에서 onPause → onStop을 거쳐 onSaveInstanceState 가 실행된 후 onDestroy된다.
  2. activity 종료 및 재시작 → onCreate() 메서드가 호출된다.
  3. 리소스 재로드 → 새 구성에 따라 리소스 다시 로드하여 앱이 변경사항을 반영할 수 있도록 한다. 이때 onRestoreInstanceState를 지나 onResume으로 앱이 재시작된다.
  4. 데이터 손실 방지: 구성변경에 따른 재생성 중 데이터 손실을 방지하기 위해 onSaveInstanceState()- Activity 파괴시, onRestoreInstanceState 메서드 -(Activity 재생성시) 사용하거나 viewmodel 활용해서 복원한다.
  • Activity의 재생성을 피하기 위한 방법

    • manifest에서 android:configchanges 속성을 추가하면 된다.
    • 구성 변경시 실행되어야하는 로직을 개발자가 수동으로 처리해주어야한다.
  • onSaveInstanceState()
    - Activity가 일시적으로 파괴될 때 (예: 화면 회전, 멀티태스킹으로 인해 메모리 회수) 호출된다.

    • onPause()onStop() 직후, onDestroy() 전에 호출되는 경우가 많음
    • 현재 UI 상태를 Bundle 객체에 저장
    • 직렬화 가능한 데이터(Primitive type, String, Parcelable 등)만 저장 가능

Q20) ActivityManager란?

정의: 기기에서 실행중인 activity, task..에 대한 정보 제공하고 관리하는 시스템

  • Activity 정보: ActivityManager는 실행 중인 태스크, activity 및 해당 스택에 대한 세부 정보를 추적할 수 있다.
  • 메모리 관리: 앱의 메모리 소비를 포함하여 시스템 전체의 메모리 사용량에 대한 정보를 제공한다. 이것은 개발자가 앱 성능 최적화와 메모리 누수 관리에 대한 지표가 되곤한다.
  • 앱 프로세스 관리: 실행중인 앱 프로세스 및 Service에 대한 세부 정보 쿼리할 수 있다. 이를 통해 앱 상태 감지 가능
  • 디버깅 및 진단: 앱 프로파일링과 같은 디버깅 위한 도구 제공하여 성능 병목 현상 식별하는데 도움이 될수있음

<ActivityManager에서 제공하는 메서드>

  • getRunningAppProcesses() : 현재 실행중인 프로세스 목록 반환
  • getMemoryInfo(): 사용가능한 메모리, 기기가 메모리 부족 상태인지 여부 등 시스템에 대한 자세한 메모리정보 검색
  • killBackgroundProcesses: 시스템 리소스 확보하기 위해 지정된 백그라운드 프로세스 종료
  • isLowRamDevice: 기기 저사양 랩으로 분류되는지 확인하여 적절하게 리소스 사용량 최적화한다
  • appNotResponding: 이는 ANR 이벤트를 시험할 수 있게 하여 이 상황에서 어떻게 동작하거나 응답하는지 이해할 수 있음
  • clearApplicationUserData: 파일, DB, SharedPreferences 포함한 앱의 모든 사용자 데이터 지운다

(참고)

LeakCanary 는 메모리 누수 감지 라이브러리인데 내부적으로 메모리 상태 및 정보 추적을 위해 ActivityManager을 활용한다

(결론)

ActivityManager은 성능 튜닝, 앱 동작 모니터링을 위한 것이며 앱의 리소스 사용 최적화하기 위한 도구로 활용가능하다

Q) ActivityManager.getMemoryInfo()를 어떻게 앱 성능 최적화에 활용할 수 있으며, 시스템이 메모리 부족 상태에 들어가면 개발자는 어떤 조치를 취해야 하나요?

memoryInfo.lowMemory 를 활용해서 기기의 메모리가 부족한지 감지하고 이를 기반으로 앱의 리소스를 줄이는 로직을 적용해야 한다. 예를 들어 불필요한 백그라운드 작업을 중지하거나 캐시 지우기, listener 해제 등을 하여 성능 최적화를 이룰수 있다.

Q21) SparseArray를 사용하면 어떤 이점이 있나요?

SparseArray란?

HashMap과 유사하게 정수 키 객체값에 매핑하는 안드로이드에 최적화된 데이터 구조, 정수인 키와 함께 사용하도록 최적화되어 java kotlin의 Map이나 HashMap 보다 메모리 관리 측면에서 효율이 좋음

HashMap, Map보다 SparseArray가 효율적인 이유

(AutoBoxing: 컴파일러가 primitive 데이터에 대응하는 wrapper 클래스 객체로 변경)

HashMap을 사용하게 되면 key가 int → integer로 오토박싱되는데 sparseArray는 key를 그냥 int로 저장하기 때문에 오토박싱 오버헤드가 발생하지 않고 단순 배열기반이라 메모리 오버헤드가 작다.

  • 장점
    • 오토 박싱 방지: sparseArray는 key를 그냥 int로 저장
    • 메모리 절약: 기본 배열 사용하여 여러 객체를 추가적으로 생성해야하는 HashMap에 비해 메모리 차지공간 줄여준다.
    • 적은 수의 데이터 저장에 효율적: 밀도가 낮은 데이터 셋이나 키가 넓은 정수범위에 걸쳐 드문드문 분포된 데이터 셋에 적합
    • 안드로이드 특화: view id를 객체에 매핑하는 방식으로 활용시 효과적
  • 한계
    • 성능 트레이드 오프: 키 조회위해 이진탐색해야하기에 매우 큰 데이터의 경우 HashMap보다 느리다
    • 정수 키만 사용가능

Q) HashMap 대신 SparseArray를 사용하는 것이 어떤 시나리오에서 더 효율적이며, 성능 및 사용성 측면에서 트레이드오프는 무엇인가요?

적은 수의 데이터 저장을 해야할때 SparseArray가 유리하다. 이는 sparseArray같은 경우 단순 배열 방식으로 저장되기 때문이다.

데이터가 많아질때 이진탐색 기반으로 해야하기에 Hashmap보다 느려진다. 또한 key가 int 형식에 한정되어 있다.

Q22) 런타임 권한(runtime permissions)을 어떻게 처리하나요?

왜 나오게 되었나요?

안드로이드 6.0 부터 원할한 사용자 경험을 위해 앱 설치시 자동으로 권한을 획득하는 것이 아닌 런타임에 위험 권한을 명시적으로 요청하여 사용자가 필요시에만 권한을 명시적으로 부여하도록 하는 방법

  • 권한 선언
    • AndroidManifest.xml에 요청하고자 하는 권한을 선언해야한다.
    • 런타임시 사용자가 해당 기능을 사용해야 할 경우에만 권한을 요청한다.
  • 권한 요청
    • 권한 처리를 단순화하는 ActivityResultLauncher api 사용이 권장됨
  • 권한 요청 근거 제공
    • shouldShowRequestPermissionRationale을 사용해 권한 요청하기 전 추가적인 정보나 근거를 표시할 수 있다.
    • true: 사용자가 권한을 한번 거부한 적 있음
    • false: 처음 요청, 다시 묻지 않음을 체크하고 거부했거나 이미 권한이 허용됨
  • 일회성 권한
    • 안드로이드 11은 위치, 카메라, 마이크에 대해 일회성 권한(앱을 사용하는 동안에만 허용) 과같은 것을 도입했다. 사용자는 임시적으로 접근 권한 부여할 수 있으며 앱 종료시 해당 권한 사라진다.

Q23) Looper, Handler, HandlerThread의 역할은 무엇인가요?

큰 카테고리에서 스레드를 관리하고 비동기 통신을 처리하기 위해 작동하는 컴포넌트, 백그라운드 스레드에서 작업하며 ui 업데이트를 위해 메인 스레드와 상호작용하기 위한 필수적인 컴포넌트 이다.

Looper

목적: 메시지 큐를 지속적으로 모니터링하고 적절한 핸들러에 가져와 패치한다.

사용법: 메시지 처리하는 모든 스레드에는 looper가 필요하다. 메인 스레드에는 디폴트로 looper가 있지만 워커 스레드의 경우 명시적으로 정의해야한다

초기화: Looper.prepare()를 사용해 스레드에 looper 연결하고 Looper.loop() 사용해 루프 시작

val thread = Thread{
	Looper.prepare() //스레드에 연결
	val handler = Handler(Looper.myLooper()!!) //looper사용해서 handler생성
	Looper.loop()
}
thread.start()

Handler

목적:

  • 한스레드에서 다른 스레드로 메시지 전달 비유 하자면 "이 작업을 저 looper에 연결된 스레드에서 실행시켜줘" 라는 목적을 가진 컴포넌트
  • Handler는 Looper가 있는 스레드에서만 동작할 수 있다

동작: 생성된 스레드 및 해당 스레드의 Looper에 연결된다. handler로 전송된 작업은 해당 스레드에서 처리됨

val handler = Handler(Looper.getMainLooper()) // 메인스레드에서 실행됨
handler.post{
textview.text = "djkafjsl"
}

HandlerThread

  • Thread의 서브클래스, HandlerThread는 내장된 Looper 가진 특수한 thread. 메시지 큐 처리할 수 있는 백그라운드 스레드 생성하는 과정 단순화
  • 일반 Thread는 looper 없어서 handler 붙일수없는데 이것은 자동으로 looper가 내장된 형태

목적: 자체 looper를 가진 워커 스레드를 생성하여 해당 스레드에서 작업을 순차적으로 처리할 수 있도록 함

생명주기: start로 시작한 다음 getlooper로 looper얻는다. 리소스 해제하려면 항상 quit사용해서 종료해야함

주요 차이점

  1. Looper: 메시지 처리의 중추이며, 스레드를 살아있게 유지하고 메시지 큐를 처리합니다.
  2. Handler: Looper와 상호 작용하여 메시지와 작업을 큐에 넣거나 처리합니다.
  3. HandlerThread: 자동 Looper 설정으로 백그라운드 스레드 생성을 단순화합니다.

Q) Handler는 Looper와 어떻게 작동하여 스레드 간 통신을 용이하게 하며, Handler의 일반적인 사용 사례를 말씀해주세요.

Looper은 스레드 마다 하나씩 존재할 수 있으며 메시지 큐를 돌면서 메시지 꺼내 실행하는 방식으로 동작한다. Handler는 Looper에 연결되며 해당 스레드에 적합한 작업을 배정해주는 인터페이스이다. 즉 스레드간 메시지 전달 도구이다.

사례: DB 업데이트와 같은 백그라운드 작업 후 UI스레드로 안전하게 결과 전달, 지연 실행 (일정시간 딜레이 후 코드 실행)

Q) HandlerThread란 무엇이며, Looper.prepare()를 사용하여 수동으로 스레드를 생성하는 것과 비교하여 백그라운드 스레드 관리를 어떻게 단순화하나요?

Thread를 상속한 클래스 지만 looper과 MessageQueue를 내장하고 있다. 때문에 바로 handler 만들어 해당 스레드에서 메시지 처리를 할 수 있다. 개발자가 수동으로 스레드를 생성하고 관리하는 번거로움을 줄여주며 생명주기에 따른 관리도 비교적 간편하게 할 수 있다. 또한 백그라운드 스레드 관리와 메시지 처리를 훨씬 단순화한다.

Q24) 예외(exceptions)를 어떻게 추적하나요?

  1. Logcat을 활용한 로깅

예외 발생한 유형, 코드 줄 포함한 자세한 스택 트레이스를 기록하여 알려준다.

  1. try-catch

    예외를 제어된 방식으로 처리하여 앱 크래시 방지

  2. 전역 예외 핸들러

    Thread.setDefaultUncaughtExceptionHandler 를 사용하여 앱 전체에서 처리되지 않은 예외 포착하는데 도움이 됨. 중앙집중식 로깅에 이점이 있음

  3. Firebase Crashlytics 사용하기

  4. 브레이크포인트(Breakpoints)를 이용한 디버깅

    개발중 예외의 원인을 식별하는데 유용, 브레이크 포인트 도달할때 변수, 메서드 호출 및 예외 스택 트레이스 상세 분석 가능

Q) Logcat을 사용하여 개발 환경에서 예외를 디버깅하는 것과 Firebase Crashlytics와 같은 도구를 사용하여 프로덕션 환경에서 예외를 처리하는 것의 차이점은 무엇인가요? 또한, Logcat과 같은 로컬 환경에서 추적된 예외랑 프로덕션에서 추적된 예외를 각각 어떻게 해결하시나요?

개발환경에서 Logcat을 통해 간단한 오류를 빠르게 분석하고 수정할때 효과적이다. 반면 릴리즈 환경에서는 Firebase crashlytics 같은 도구 사용해 실제 사용자 환경에서 발생한 예외 자동으로 수집하고 발생 빈도, OS 오류에 미치는 다양한 요소들을 분석할 수 있다.

+추가

스터디에서 알게된 추가적인 내용이다. 권한이 일부허용된 경우 앱이 백그라운드로 갈시 권한이 자동으로 해지된다는 내용이다. 그렇기에 백그라운드에서 특정 권한이 필요한 작업이 있다면 UX적으로 전체 허용을 할 수 있도록 유도를 하거나 예외처리를 통해 에러를 예방할 수 있을 것이다.

profile
초코송이 개발자

0개의 댓글