Study

안드로이드 면접 북 스터디 Q10 ~ Q19

hegunhee 2025. 8. 9. 15:16

안드로이드 기술 면접 책 북 스터디를 하면서
보다 간결하게 실제 면접하는 것처럼 글을 정리해보려 한다.

 

Q10) BroadcastReceiver란 무엇인가요?

BroadcastReceiver는 안드로이드 운영 체제 전체의 브로드캐스크 메시지나

앱 특정 브로드캐스트를 수신하고 응답할 수 있도록 하는 컴포넌트

 

동적인 시스템 또는 앱 수준 이벤트에 반응하는 응답성 있는 애플리케이션을 구축하는데 유용합니다.

 

BroadcastReceiver의 목적

Activity나 Service의 생명주기에 직접적으로 연결되지 않을 수 있는 이벤트를 처리하는 데 사용됩니다.

백그라운드에서 계속 실행되지 않고도 변경 사항에 반응할 수 있도록 하는 메시징 시스템 역할을 하여 리소스를 절약합니다.

 

BroadcastReceiver의 유형

  1. 시스템 브로드캐스트
    - 안드로이드 운영 체제에서 배터리 잔량 변경, 시간대 업데이트 또는 네트워크 연결 변경과 같은
       시스템 이벤트를 앱에 알리기 위해 보냅니다.
  2. 커스텀
    - 앱 내부 또는 앱간에 특정 정보나 이벤트를 전달하기 위해 보냅니다.

BroadcastReceiver 사용 사례

  • 네티워크 연결 변동 모니터링
  • SMS 또는 통화 이벤트에 응답
  • 충전 상태와 같은 시스템 이벤트에 대한 UI 업데이트
  • 커스텀 브로드캐스트로 작업 또는 알람 예약

Q11) ContentProvider의 목적은 무엇이며, 애플리케이션 간의 안전한 공유를 어떻게 용이하게 하나요?

ContentProvider는 구조화된 데이터에 대한 접근을 관리하고 애플리케이션 간 데이터 공유를 위한 인터페이스를 제공하는 컴포넌트

다른 앱과의 중앙 저장소 역할을 하며 앱 간의 안전하고 일관된 데이터 공유를 보장

 

ContentProvider의 목적

데이터 접근 로직을 캡슐화하여 앱 간 데이터 공유를 더 쉽고 안전하게 만드는 것입니다.

SQLite, 파일 시스템, 네트워크 기반 데이터같은 기본 데이터를 추상화하고 데이터와 상호작용 하기 위한 통합 인터페이스를 제공합니다.

 

ContentProvider의 주요 구성 요소

데이터 접근 주소로 URI를 사용합니다, URI는 다음으로 구성됩니다.

  1. 권한 : ContentProvider를 식별합니다
  2. Path : 데이터 유형을 지정합니다 (/users, /products)
  3. ID (optional) : 데이터 셋 내의 특정 항목을 참조합니다.

ContnetProvider에서 데이터 접근하기

다른 앱에서 ContentProvider와 상호 작용하려면 ContentProvider 클래스를 사용해야 합니다.

ContentProvider는 데이터를 쿼리, 삽입, 업데이트 또는 삭제하는 메서드를 제공합니다.

val contentResolver = context.contentResolver

// 데이터 쿼리
val cursor = contentResolver.query(
  Uri.parse("content://com.example.myapp.provider/users"),
  null,
  null,
  null,
  null
)

// 데이터 삽입
val values = ContentValues().apply {
  put("name", "John Doe")
  put("email", "johndoe@example.com")
}
contentResolver.insert(Uri.parse("content://com.example.myapp.providers/users"), values)

 

추가 질문 : Room과 ContentProvider를 같이 사용할때 유의해야 할것들

이건 스터디 도중 이야기가 나온것입니다.

 

Room과 ContentProvider는 같은 데이터베이스를 바라볼 수 있는데

ContentProvider가 외부 데이터에 접근하고 데이터베이스에 값을 넣고 Room에서 조회할때는

트랜잭션을 통해 마무리를 짓고 Room에서 작업을 이어가는것이 중요하다고 생각합니다.

 

Q12) 구성 변경(configuration changes)을 어떻게 처리하나요?

구성 변경을 올바르게 처리하는 것은 화면 회전, 언어 변경, 다크/라이트 모드 전환이 되었을 때

원활한 사용자 경험을 유지하는 데 중요한 역할을 합니다.

기본적으로 안드로이드 시스템은 구성 변경이 발생할 때 Activity를 다시 시작하며 일시적으로 UI의 상태가 손실될 수 있습니다.

 

대응 방법

  1. UI 상태 저장 및 복원
    onSaveInstanceState() 및 onRestoreInstanceState() 메서드를 구현하여
    Activity 재생성 중 UI 상태를 보존하고 복원
  2. Jetpack ViewModel
    ViewModel 클래스를 활용하여 구성 변경에도 유지되어야 하는 UI 관련 데이터를 저장합니다.
    ViewModel은 재시작 범위를 넘어서 존재하도록 설계되었으므로
    구성 변경이 발생했을 때 데이터를 보존하고 다시 복원하는 데 이상적입니다.
  3. 구성 변경 수동으로 처리하기
    애플리케이션이 특정 구성 변경중에 리소스를 업데이트할 필요가 없고 Activity 재시작을 피하고 싶다면
    AndroidManifest.xml에 Activity가 처리할 구성 변경 사항을 android:configChanges 속성을 사용하여 선언할 수 있습니다.
  4. Jetpack Compose에서 rememberSaveable 활용
    rememberSaveable을 사용하여 구성 변경이 발생했을 때 UI 상태를 보존할 수 있습니다.
  • 네비게이션 및 백스택 보존
    Navigation 컴포넌트를 사용하면 구성 변경 시 네비게이션 백 스택이 보존됩니다.
  • 앱 구성에 의존적인 데이터 피하기
    앱 구성에 의존적인 값을 UI 레이어에 직접 저장하지 않는 것이 좋습니다.
    ViewModel과 같은 대안을 고려할 수 있습니다.

Q13) 안드로이드는 메모리를 어떻게 효율적으로 관리하며, 메모리 누수를 어떻게 방지하는지 설명해주세요

안드로이드는 사용되지 않는 메모리를 자동으로 회수하여 활성 중인 애플리케이션 및 서비스에게

효율적인 메모리 할당을 보장하는 가비지 컬렉션 매커니즘을 통해 관리합니다.

 

https://developer.android.com/guide/components/activities/process-lifecycle?hl=ko
시스템 메모리가 부족할 때 포그라운드 애플리케이션의 원활한 작동을 우선시하며
백그라운드 프로세스를 종료하기 위해 low-memory killer를 사용합니다

 

안드로이드에서 메모리 누수의 원인

애플리케이션이 더 이상 필요하지 않는 객체에 대한 참조를 유지하여 가비지 컬렉터가 메모리를 회수하지 못하게 할 때 발생합니다.

일반적인 원인으로는 부적절한 생명주기 관리, 정적 참조 또는 Context에 대한 장기 참조 유지 등이 원인입니다.

 

메모리 누수를 피하기 위한 모범 사례

  1. 생명주기를 인지하는 컴포넌트 사용
    Jetpack ViewModel, collectAsStateWithLifecycle, Flow, LiveData
  2. Context에 대한 오랜 참조 피하기
    정적 필드나  싱글턴과 같은 오래 지속되는 객체에서 Context에 대한 참조를 유지하지 않아야 합니다.
  3. 리스너 및 콜백 등록 올바르게 해제하기
  4. 중요하지 않은 객체는 WeakReference 사용하기
  5. 누수 감지 툴 사용
  6. View에 대한 정적 참조 피하기
  7. 리소스 닫기
  8. Fragment와 Activity 원활하게 사용하기

Q14) ANR이란 무엇인지, ANR이 발생하는 주요 원인은 무엇이며, 어떻게 예방할 수 있는지 설명해주세요

ANR(Application Not Responding)은 앱의 메인 스레드가 너무 오랫동안
통상 5초 이상 차단될 때 발생하는 안드로이드 시스템 오류입니다.
ANR이 발생하면 안드로이드는 사용자에게 앱을 닫거나 응답은 기다리도록 안내합니다.

 

ANR이 발생하는 원인

  • 메인 스레드에서 5초 이상 걸리는 무거운 작업
  • 장시간 실행되는 네트워크 또는 데이터베이스 등의 I/O 작업
  • UI 스레드 차단 작업

ANR 예방 작업

ANR을 예방하려면 무겁거나 시간이 많이 걸리는 작업을 오프로드하여 메인 스레드의 응답성을 유지하는 것이 중요합니다.

  1. 무거운 작업을 메인 스레드 밖으로 이동
    I/O, 네트워크 요청, 데이터베이스 쿼리와 같은 무거운 작업을 백그라운드 스레드로
  2. WorkManager 사용하기
    백그라운드에서 실행되어야 하는 장기적인 작업은 WorkManager를 사용
    WorkManager는 작업을 사전에 스케줄링하고, 메인 스레드 외부에서 실행되도록 보장
  3. 데이터 불러오기 최적화(Paging)
  4. 구성 변경 시 UI 작업 최소화
    ViewModel 활용
  5. Android Studio로 모니터링 및 프로파일링
  6. 블로킹 호출 피하기 (Coroutine)
  7. 가벼운 지연 작업에 Handler tkdyd

Q15) 딥 링크를 어떻게 처리하는지 설명해주세요

딥 링크는 사용자가 URL이나 알림과 같은 외부 소스에서 앱 내의 특정 화면이나 기능으로 직접 이동할 수 있도록 합니다.

AndroidManifest.xml에서 이를 정의하고 해당 Activity나 Fragment에 들어오는 Intent를 작접 처리해야 합니다.

<activity android:name=".MyDeepLinkActivity
    android:exported="true">
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <!-- 딥 링크 URI 정의 -->
    <data
      android:scheme="https"
      android:host="example.com"
      android:pathPrefix="/deepLink" />
  </intent-filter>
</activity>
  • android:scheme: URL 스키마(가령, https)를 지정합니다.
  • android:host: 도메인(example.com)을 지정합니다.
  • android:pathPrefix: URL의 경로(가령, /deepLink)를 정의합니다.

Activity에서는 intent 데이터를 검색하고 처리하여 적절한 화면으로 이동하거나 작업을 수행합니다.

 

Q16) 태스크와 백 스택이란 무엇인가요?

  • 태스크 : 사용자가 특정 목적을 달성하기 위해 사용하는 Activity의 집합
  • 백스택 : 후입선출 구조로 이뤄져있음

태스크

태스크는 일반적으로 런처나 Intent를 통해 Activity가 실행될 때 시작됩니다.

태스크는 Intent및 Activity 런치 모드가 어떻게 구성되었는지에 따라 여러 애플리케이션과 해당 Activity에 속해있을 수 있습니다.

태스크는 연관된 Activity가 소멸될 때까지 상태를 유지합니다.

백 스택

태스크 내의 Activity의 기록을 유지합니다.

말 그대로 액티비티를 저장해놓는 스택

직관적인 탐색과 사용자 워크플로우의 연속성을 보장함

Q17) Bundle의 사용 목적에 대해서 설명해주세요

Bundle은 Activity, Fragment, Service와 같은 컴포넌트 간에 데이터를 전달하는 데 사용되는 키-값 쌍 데이터 구조

작은 용량의 데이터를 효율적으로 전송하는 데 사용됩니다.

가볍고 쉽게 관리하고 전송할 수 있는 형태로 직렬화하도록 설계되었습니다.

 

사용 사례

  1. Activity간 데이터 전달
  2. Fragment간 데이터 전달
  3. 인스턴스 상태 및 복원
  4. Service에 데이터 전달

작동 방식

키-값 구조로 직렬화하여 작동합니다.

키는 문자열이며 값은 기본 유형, Serializable, Parcelable 객체 또는 다른 Bundle일 수 있습니다.

이를 통해 데이터를 효율적으로 저장하고 전송할 수 있습니다.

 

내부적으로 mMap 이라는 ArrayMap<String, Object>에 저장함

intent.putExtra()에서는 새 번들을 만들어서 사용중

getExtra()는 번들을 반환

 

요약

Bundle은 컴포넌트 및 생명주기 이벤트 간에 데이터를 효율적으로 전달하고 보존하기 위한 안드로이드의 중요한 구성 요소입니다.

가볍고 유연한 구조로 인해 애플리케이션 상태 및 데이터 전송 관리에 필수적인 도구입니다.

 

Q18) Activity 또는 Fragment 간에 데이터를 어떻게 전달하나요

일반적인 매커니즘은 Intent 입니다.

 

Activity간 데이터 전달

val intent = Intent().apply {
    putExtra("USER_NAME", "John Doe")
}

class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // view 설정

        val userName = intent.getStringExtra("USER_NAME")
    }
}

 

Fragment간 데이터 전달

val fragment = MyFragment().apply {
    arguments = Bundle().apply {
        putString("user_name, "Jane Doe")
        putInt("user_age", 30)
    }
}

val name = arguments?.getString("user_name")
val age = arguments?.getInt("user_age")

 

그 외에도

  • Jetpack Navigation Safe Args
  • Shared ViewModel
  • Fragment Result API

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

시스템은 새 구성을 적용하기 위해 현재 Activity를 종료하고 다시 실행하게 됩니다.

이러한 동작은 앱의 리소스가 변경된 구성을 새롭게 반영하고 앱이 다시 로드되도록 보장합니다.

 

구성 변경 중 기본 동작

  1. Activity 종료 및 재시작
    구성 변경이 발생하면 Activity가 종료된 다음 다시 시작합니다.
    - 시스템은 현재 실행중인 Activity의 onPause부터 onDestory까지 호출합니다.
    - 구성을 변경하면 Activity는 다시 시작되고, onCreate() 메서드가 호출됩니다.
  2. 시스템 다시 로드하기
    시스템은 새 구성에 따라 다시 로드하여 앱이 화면방향, 테마 또는 언어와 같은 변경사항이 반영될 수 있도록 합니다.
  3. 데이터 손실 방지
    개발자는 재생성 중 데이터 손실을 방지하기 위해 onSaveInstanceState() 및 onRestoreInstanceState() 메서드를 사용하거나
    ViewModel을 활용하여 인스턴스 상태를 저장하고 복원할 수 있습니다.

재생성을 유발하는 구성 변경

  1. 화면 회전
  2. 다크/라이트 테마 변경
  3. 글꼴 크기 변경
  4. 언어 변경

Activity 재생성 피하기

Activity를 다시 시작하지 않고 구성 변경을 처리하려면 매니페스트 파일에서 android:configChanges 속성을 추가하면 됩니다.

이 방식은 변경 사항을 개발자가 수동적으로 처리하는 형태로 책임을 개발자에게 위임합니다.