최근에 안드로이드 스터디를 진행하고 있는데요 노션에만 정리했는데 이왕이면 찾아보기 편리하게 벨로그에도 옮겨두고자 작성하였습니다.
Activity의 재생성을 피하기 위한 방법
onSaveInstanceState()
- Activity가 일시적으로 파괴될 때 (예: 화면 회전, 멀티태스킹으로 인해 메모리 회수) 호출된다.
onPause() 와 onStop() 직후, onDestroy() 전에 호출되는 경우가 많음Bundle 객체에 저장정의: 기기에서 실행중인 activity, task..에 대한 정보 제공하고 관리하는 시스템
<ActivityManager에서 제공하는 메서드>
(참고)
LeakCanary 는 메모리 누수 감지 라이브러리인데 내부적으로 메모리 상태 및 정보 추적을 위해 ActivityManager을 활용한다
(결론)
ActivityManager은 성능 튜닝, 앱 동작 모니터링을 위한 것이며 앱의 리소스 사용 최적화하기 위한 도구로 활용가능하다
Q) ActivityManager.getMemoryInfo()를 어떻게 앱 성능 최적화에 활용할 수 있으며, 시스템이 메모리 부족 상태에 들어가면 개발자는 어떤 조치를 취해야 하나요?
memoryInfo.lowMemory 를 활용해서 기기의 메모리가 부족한지 감지하고 이를 기반으로 앱의 리소스를 줄이는 로직을 적용해야 한다. 예를 들어 불필요한 백그라운드 작업을 중지하거나 캐시 지우기, listener 해제 등을 하여 성능 최적화를 이룰수 있다.
SparseArray란?
HashMap과 유사하게 정수 키 객체값에 매핑하는 안드로이드에 최적화된 데이터 구조, 정수인 키와 함께 사용하도록 최적화되어 java kotlin의 Map이나 HashMap 보다 메모리 관리 측면에서 효율이 좋음
(AutoBoxing: 컴파일러가 primitive 데이터에 대응하는 wrapper 클래스 객체로 변경)
HashMap을 사용하게 되면 key가 int → integer로 오토박싱되는데 sparseArray는 key를 그냥 int로 저장하기 때문에 오토박싱 오버헤드가 발생하지 않고 단순 배열기반이라 메모리 오버헤드가 작다.
Q) HashMap 대신 SparseArray를 사용하는 것이 어떤 시나리오에서 더 효율적이며, 성능 및 사용성 측면에서 트레이드오프는 무엇인가요?
적은 수의 데이터 저장을 해야할때 SparseArray가 유리하다. 이는 sparseArray같은 경우 단순 배열 방식으로 저장되기 때문이다.
데이터가 많아질때 이진탐색 기반으로 해야하기에 Hashmap보다 느려진다. 또한 key가 int 형식에 한정되어 있다.
왜 나오게 되었나요?
안드로이드 6.0 부터 원할한 사용자 경험을 위해 앱 설치시 자동으로 권한을 획득하는 것이 아닌 런타임에 위험 권한을 명시적으로 요청하여 사용자가 필요시에만 권한을 명시적으로 부여하도록 하는 방법
큰 카테고리에서 스레드를 관리하고 비동기 통신을 처리하기 위해 작동하는 컴포넌트, 백그라운드 스레드에서 작업하며 ui 업데이트를 위해 메인 스레드와 상호작용하기 위한 필수적인 컴포넌트 이다.
목적: 메시지 큐를 지속적으로 모니터링하고 적절한 핸들러에 가져와 패치한다.
사용법: 메시지 처리하는 모든 스레드에는 looper가 필요하다. 메인 스레드에는 디폴트로 looper가 있지만 워커 스레드의 경우 명시적으로 정의해야한다
초기화: Looper.prepare()를 사용해 스레드에 looper 연결하고 Looper.loop() 사용해 루프 시작
val thread = Thread{
Looper.prepare() //스레드에 연결
val handler = Handler(Looper.myLooper()!!) //looper사용해서 handler생성
Looper.loop()
}
thread.start()
목적:
동작: 생성된 스레드 및 해당 스레드의 Looper에 연결된다. handler로 전송된 작업은 해당 스레드에서 처리됨
val handler = Handler(Looper.getMainLooper()) // 메인스레드에서 실행됨
handler.post{
textview.text = "djkafjsl"
}
목적: 자체 looper를 가진 워커 스레드를 생성하여 해당 스레드에서 작업을 순차적으로 처리할 수 있도록 함
생명주기: start로 시작한 다음 getlooper로 looper얻는다. 리소스 해제하려면 항상 quit사용해서 종료해야함
주요 차이점
Q) Handler는 Looper와 어떻게 작동하여 스레드 간 통신을 용이하게 하며, Handler의 일반적인 사용 사례를 말씀해주세요.
Looper은 스레드 마다 하나씩 존재할 수 있으며 메시지 큐를 돌면서 메시지 꺼내 실행하는 방식으로 동작한다. Handler는 Looper에 연결되며 해당 스레드에 적합한 작업을 배정해주는 인터페이스이다. 즉 스레드간 메시지 전달 도구이다.
사례: DB 업데이트와 같은 백그라운드 작업 후 UI스레드로 안전하게 결과 전달, 지연 실행 (일정시간 딜레이 후 코드 실행)
Q) HandlerThread란 무엇이며, Looper.prepare()를 사용하여 수동으로 스레드를 생성하는 것과 비교하여 백그라운드 스레드 관리를 어떻게 단순화하나요?
Thread를 상속한 클래스 지만 looper과 MessageQueue를 내장하고 있다. 때문에 바로 handler 만들어 해당 스레드에서 메시지 처리를 할 수 있다. 개발자가 수동으로 스레드를 생성하고 관리하는 번거로움을 줄여주며 생명주기에 따른 관리도 비교적 간편하게 할 수 있다. 또한 백그라운드 스레드 관리와 메시지 처리를 훨씬 단순화한다.
예외 발생한 유형, 코드 줄 포함한 자세한 스택 트레이스를 기록하여 알려준다.
try-catch
예외를 제어된 방식으로 처리하여 앱 크래시 방지
전역 예외 핸들러
Thread.setDefaultUncaughtExceptionHandler 를 사용하여 앱 전체에서 처리되지 않은 예외 포착하는데 도움이 됨. 중앙집중식 로깅에 이점이 있음
Firebase Crashlytics 사용하기
브레이크포인트(Breakpoints)를 이용한 디버깅
개발중 예외의 원인을 식별하는데 유용, 브레이크 포인트 도달할때 변수, 메서드 호출 및 예외 스택 트레이스 상세 분석 가능
Q) Logcat을 사용하여 개발 환경에서 예외를 디버깅하는 것과 Firebase Crashlytics와 같은 도구를 사용하여 프로덕션 환경에서 예외를 처리하는 것의 차이점은 무엇인가요? 또한, Logcat과 같은 로컬 환경에서 추적된 예외랑 프로덕션에서 추적된 예외를 각각 어떻게 해결하시나요?
개발환경에서 Logcat을 통해 간단한 오류를 빠르게 분석하고 수정할때 효과적이다. 반면 릴리즈 환경에서는 Firebase crashlytics 같은 도구 사용해 실제 사용자 환경에서 발생한 예외 자동으로 수집하고 발생 빈도, OS 오류에 미치는 다양한 요소들을 분석할 수 있다.
+추가

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