1. 안드로이드 4대 요소 Activity, Service, Broadcast Receiver, Content Provider 정리
안드로이드 앱을 만들고 프로그래밍 하기위해 기본이 되는 개념인 안드로이드 4대 요소인 엑티비티, 서비스, 방송 수신자, 컨텐츠 제공자에 대해서 정리하려고합니다. 🤓
안드로이드의 4대 요소
- Activity
- Service
- Broadcast Receiver
- Content Provider
그외 알아두면 좋은 것
- Intent
- View
- Fragment
아래는 이를 표현한 그림인데 발로그려서 그림 상태가 좋지 않아도 양해바랍니다.
그럼 하나씩 까보도록 하죠.
2. 엑티비티(Activity)
엑티비티는 사용자가 보게되는 인터페이스 화면을 가지고 있는 요소로 UI 화면을 구성하게 되며 activity_main.xml 과 같은 xml 레이아웃 파일로 구성합니다.
또 화면 구성 외에 실제로 앱의 동작을 구현하는 코드 부분도 필요합니다. java 또는 kotlin 코드로 사용자의 이벤트를 처리하거나 UI를 업데이트합니다.
앱은 하나 이상의 엑티비티를 가질 수 있어서 여러 화면으로 전환하며 알맞는 엑티비티를 보여줄 수도 있습니다. (우리가 쓰는 거의 모든 앱이 하나 이상의 액티비티를 가지고 있죠.)
프래그먼트?
프래그먼트는 작은 화면 단위로 액티비티 내부에 속하도록 구현할 수 있습니다. 프래그먼트 혼자는 존재할 수 없으므로 반드시 액티비티에 속하도록 구성해야합니다.
이러한 액티비티는 메인 스레드가 실행해줍니다. 눈에는 안보여도 뒤에서 엑티비티를 제어하면서 통제합니다. UI를 그려주는 것도 이 메인 스레드가 담당합니다. 그래서 메인 스레드를 UI 스레드라고 부르기도합니다.
2.1. 뷰(View)
뷰는 2가지로 구성됩니다.
- 보이지 않는 요소
- 보이는 요소
보이지 않는 요소는 레이아웃의 배치 등을 담당합니다. (LinearLayout, ConstraintLayout ...)
보이는 요소는 텍스트 박스, 버튼 등 이 있으며 이는 위젯(Widget) 이라고도 합니다.
class MainActivity : AppCompatActivity() {
private var _viewBinding: ActivityMainBinding? = null
private val viewBinding: ActivityMainBinding get() = requireNotNull(_viewBinding)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
_viewBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(viewBinding.root)
}
}
해당 코드는 뷰바인딩을 사용하여 메인 엑티비티를 inflate 해서 레이아웃을 화면에 나타내주는 코드입니다.
AppCompatActiviy 를 상속해서 구현해주고 있는데 이는 호환성을 위한 것 입니다.
앱이 구동되면 메인 스레드가 오버라이딩 해준(위 코드 참고) onCreate() 를 호출하면서 엑티비티가 실행됩니다. setContentView()는 xml 리소스를 코드를 통해 변환하여 UI 를 구성하는 처리를해줍니다.
2.2. 액티비티의 생명주기(Activity Lifecycle)
액티비티는 onCreate() 외에도 메인 스레드가 제어할 수 있는 여러 콜백함수를 제공합니다.
필요한 콜백함수를 알맞게 재정의 해주어 앱의 원활한 동작이 이루어질 수 있게 해주어야하는 것이 개발자의 숙명입니다.
해당 콜백은 메인 스레드의 명령 이벤트에 따라서 호출되게 되며 예를 들어 onCreate() 함수는 액티비티아 생성될 때 호출됩니다. onPause() 함수는 앱이 다른 것에 의해서 가려졌을 때 호출되며 앱의 일시 중단을 나타냅니다. 다시 시작시 onResume()
안드로이드에서 뒤로가기(back) 키를 눌러주면 백 스택에 저장되어있던 액티비티가 빠져나가면서 순차적으로 스택에 있던 액티비티를 다시 보여주는데 이때 백스택에 액티비티가 없는 경우에는 onDestroy() 가 호출되어 완전히 종료됩니다.
때로는 안드로이드 시스템 메모리가 부족하면 강제적으로 앱이 죽는 경우가 있는데 이때도 onDestroy() 호출이 일어날 수 있습니다.
2.3. 갑작스런 종료로 인한 데이터 보호하기
만약 위에서 말한 메모리 부족으로 앱이 죽는 현상이 발생했다면 사용자는 사용중인 데이터를 잃어버릴 수 있습니다. 이때문에 이러한 데이터를 저장하기위해서 onSaveInstanceState() 및 onRestoreInstanceState() 를 오버라이드 하여 상태 저장과 복구를 해줄 수 있습니다.
2021.07.11 - [Android] - [Android] 화면 돌려도(회전시) 데이터 유지 시키기 : onSaveInstanceState
백 스택(Back Stack)?
백 스택은 말 그대로 앱이 여러 액티비티를 가진경우 새 액티비티가 위에 띄워질 때마다 액티비티를 쌓아 올릴 수 있는 스택입니다. 후입 선출로 뒤로가기 시 차례대로 이전 엑티비티를 꺼내옵니다.
2.4. 프래그먼트의 생명주기(Fragment Lifecycle)
프래그먼트는 액티비티와 비슷하면서도 다른 생명주기를 가지고 있는데 아래와 같습니다.
- onAttach() - 액티비티에 프래그먼트 추가
- onDetach() - 제거
- onCreateView() - UI를 구성하는 뷰 반환
- onActivityCreated() - UI가 구성된 경우. 해당 부분에 동작 코드를 구현
기타
생명주기를 활용하여 뷰를 lateinit, by lazy 등으로 재밌게 가져올 수도 있습니다.
3. 서비스(Service)
서비스는 백그라운드에서 계속 실행됩니다. 서비스 또한 메인 스레드가 제어 합니다. 서비스는 UI가 없기 때문에 눈에 보이지 않아요.
애플리케이션(프로세스) 내에 서비스가 있다면 startService() 또는 stopService() 로 서비스를 실행/중단 할 수 있습니다.
다른 앱이나 시스템의 또 다른 프로세스에 있는 경우에는 다른 메모리에 위치하고 있는 (원격) 서비스를 구동하게 됩니다.
원격 서비스의 경우에는 [원격 서비스명]Manager 를 중간 매개체(Proxy)로 해당 매니저의 서비스를 거쳐서 통신을 하게되는데, AIDL(Android Interface Definition Language) 라는 정의 언어를 통해서 원격 서비스에 명령을 줄 수 있습니다.
통신 시 필요한 데이터는 Parcel 을 통해서 전달될 수 있고 원격 서비스의 경우 서비스를 start 한다는 개념 보다는 bindService(), unbindService() 개념으로 사용하게됩니다.
안드로이드는 프로세스 간에 통신을 할때 운영 체제 내부에 있는 바인더(Binder) 라는 요소를 사용하는 통신 기법을 사용합니다. 바인더는 두 프로세스 간의 메세지를 전달해 주는 역할을 하는 것이지요. 이때 통신에 사용되는 인터페이스를 만들기위해 AIDL 을 사용합니다.
한 번 실행된 서비스는 앱이 죽어도, 다른 앱으로 이동해도 계속해서 백그라운드에 남아 실행될 수 있습니다.
//알람 매니저 가져오기.
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
2021.06.08 - [Android/App] - [Android] 알람 어플 구현 (Kotlin, AlarmManager, Broadcast)
3.1. 서비스의 생명주기(Service Lifecycle)
서비스 또한 생명주기를 갖습니다.
- startService()
- onCreate()
- onStartCommand()
- stopService()
- onDestroy()
- bindService()
- onBind()
- unbundService()
- onUnbind()
시스템 서비스
안드로이드 내부 프레임워크에서 기본적으로 구동되고 있는 서비스 (네트워크, 알람, 디스플레이, 위치 등)이 백그라운드에서 구동중이다. getSystemService() 로 앱에 가져와서 사용할 수 있다.
4. 방송 수신기(Broadcast Receiver)
브로드케스트 리시버는 안드로이드에서 생기는 다양한 이벤트와 정보를 받고 전달하는 요소입니다.
예시
- 시스템 부팅
- 배터리 잔량 부족
- 전화 수신
- 메세지 수신
- 네트워크 상황
- ... 등
해당 방송은 애플리케이션 필터를 통해서 받거나 무시가 가능합니다.
5. 콘텐츠 제공기(Content Provider)
컨텐츠 프로바이더는 데이터를 관리, 다른 앱으로 데이터를 제공 등을 하는 요소입니다. 데이터 저장에는 SQLite, file, web 등을 이용할 수 있습니다.
데이터의 고유한 이름으로 URI(Uniform Resource Identifier)를 사용합니다.
SAF(Storage Access Framework)을 사용한 이미지 가져오기
private fun navigatePhotos() {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*" // 모든 이미지 타입들만 설정 (필터링)
startActivityForResult(intent, 2000) // 선택된 컨텐츠를 콜백을 통해 받아오려고 (onActivityResult)
}
2021.05.18 - [Android/App] - [Android] simple 전자 액자 어플 만들기 with Kotlin
6. 인텐트(Intent)
인텐트가 하는 역할은 안드로이드 4대 요소 간에 메세지를 전달하는 역할을 합니다. 인텐트에는 2가지 방식이 있습니다.
- 명시적 인텐트
- 묵시적 인텐트
명시적 인텐트는 특정 클래스를 지정하며, 묵시적 인텐트는 특정 데이터에 대해 수행할 액션을 지정합니다.
묵시적 인텐트의 예로는 사진을 공유할 때 공유할 수 있는 여러 어플리케이션이 뜨는 경우에 해당합니다. 사진을 공유할 수 있는 어플만 해당 인텐트를 수신한 것인데 만약 기본앱만 있는 경우에는 자동으로 기본앱으로 공유가 실행되게 되는 것이지요.
7. 핸들러와 메세지 큐
안드로이드 앱을 실행하게 되면 그와 동시에 메인 스레드가 하나 생성되고 여기에 메세지 큐(MessageQueue)와 루퍼(Looper)가 항상 자동으로 만들어집니다.
일반 적인 스레드(Normal Thread)의 경우에는 시스템이나 사용자에 의해 나중에 만들어지는데 이러한 일반 스레드에서 UI 요소에 접근해서 변경 또는 업데이트를 해주려고 하는 경우에는 앱이 죽을 수 있습니다.
이는 UI에 의한 변경은 오로지 메인 스레드를 통해서만 가능하기 때문이라서 그렇습니다. UI 변경 시에는 해당 명령을 메인 스레드의 메세지 큐에 순차적으로 넣어서 처리하게됩니다.
루퍼(looper)는 반복적으로 큐를 쳐다보고 있다가 메세지가 온다면 처리하는 반복 루틴에 해당합니다.
이러한 UI 처리를 보다 쉽게 할 수 있도록 코루틴을 사용할 수도 있습니다. (또는 핸들러를 사용해서 다른 스레드에서 UI 작업을 처리)
2021.06.22 - [Android] - [Android] 코루틴으로 url 이미지 불러오기 (String 👉 Bitmap)
메인(UI) 스레드와 일반(worker) 스레드의 분리
안드로이드에서는 이렇게 메인 스레드와 일반 스레드를 분리시켜 놓음으로써 메인 스레드는 오로지 UI만을 처리하도록 유도하고 일반 스레드로 다른 작업을 처리하도록 유도하고 있습니다.
만일 UI를 처리하는 스레드에서 서버 통신이나 이미지 다운로드, 네트워크 작업을 한다면 사용자 입장에서 UI 작업이 아닌 다른 작업의 시간이 오래 소요되는 경우 화면이 멈추게 되어 앱에 답답함을 느낄 수 있는데 아예 메인 스레드는 UI를 담당하도록 해주고 다른 작업은 다른 스레드에서 하도록 권유하면서 좀 더 좋은 앱을 만들 수 있게 해주는 것이지요.
그래서 안드로에서는 보통 메인 스레드에서는 시간이 오래 걸리는 작업을 지양하고 워커 스레드에서는 UI 처리를 하지 않는 것이죠.
마무리
위와 같은 안드로이드의 기본 요소와 배경을 알면서 뼈대를 잡고 간다면 보다 더 즐겁게 안드로이드 앱을 만들 수 있습니다. 저도 계속 해서 배워나가는 입장이라 다소 모호하거나 틀린 표현이 있을 수 있는데 덧글로 피드백 주시면 감사하겠습니다!
'Android' 카테고리의 다른 글
[Android] LayoutInflater attachToParent 파라미터 의미를 알아보자 (1) | 2021.08.12 |
---|---|
안드로이드 아키텍처 개요 : 전체적인 구조를 알아보자 (Android Architecture) (0) | 2021.08.06 |
[Android] 화면 돌려도(회전시) 데이터 유지 시키기 : onSaveInstanceState (0) | 2021.07.11 |
[Android] 커스텀 뷰 에서 엑티비티 종료 시키기 (customView finish) (0) | 2021.06.23 |
[Android] 코루틴으로 url 이미지 불러오기 (String 👉 Bitmap) (1) | 2021.06.22 |