[Android] 5(1). 액티비티
액티비티
안드로이드는 4개의 핵심 컴포넌트를 제공합니다. 아래 그림에서 1~4는 컴포넌트, 5와 6은 컴포넌트를 사용하기 위한 도구입니다.
- 액티비티: 화면 UI를 담당하는 컴포넌트
- 브로드캐스트 리시버: 시스템 또는 사용자가 발생하는 메시지를 수신하는 컴포넌트
- 서비스: 백그라운드 코드 처리를 담당하는 컴포넌트(서브 스레드와 비슷한 개념으로 화면이 없는 Activity)
- 콘텐트 프로바이더: 앱끼리 데이터를 공유하기 위한 컴포넌트
- 인텐트: 1~3 까지 3개의 컴포넌트를 실행하기 위해 시스템에 전달되는 메시지 도구
- 콘텐트 리졸버: 콘텐트 프로바이더가 제공하는 데이터를 사용하기 위한 도구
이 중 먼저 액티비티에 대해 살펴봅니다. 액티비티는 사용자가 직접 보고 입력하는 화면을 담당하는 컴포넌트입니다.
컨텍스트
- 컨텍스트
- 시스템을 사용하기 위한 정보(프로퍼티)와 도구(메서드)가 담겨 있는 클래스 (액티비티 실행, 서비스 실행, 파일 읽고 쓰기, 권한 등)
- 대부분의 컨텍스트는 컴포넌트 실행 시 함께 생성되고 생성된 컴포넌트가 가지고 있는 메서드를 호출하여 각각의 도구를 이용
- 액티비티는 컨텍스트를 상속받았기 때문에 코드상에서 baseContext를 호출하는 것만으로 안드로이드의 기본 기능 사용 가능
-
컨텍스트의 종류
-
애플리케이션 컨텍스트
애플리케이션의 핵심 기능을 담고 있는 클래스로 앱을 통틀어 단 하나의 인스턴스만 생성. 액티비티나 서비스 같은 컴포넌트에서 applicationContext를 직접 호출해서 사용. (단 하나)
-
베이스 컨텍스트
안드로이드의 4대 메이저 컴포넌트인 액티비티, 서비스, 컨텐트 프로바이더, 브로드캐스트 리시버의 기반 클래스. 각각의 컴포넌트에서 baseContext 또는 this로 호출해서 사용하며 컴포넌트의 개수만큼 컨텍스트도 생성. (여러 개)
-
컴포넌트별 컨텍스트의 기능
화면과 관련된 기능은 액티비티의 컨텍스트에서만 가능.
기능 \ 컨텍스트 | Application | Activity | Service | Content Provider | Broadcast Receiver |
---|---|---|---|---|---|
Show a Dialog | No | Yes | No | No | No |
Start an Activity | No | Yes | No | No | No |
Layout inflation | No | Yes | No | No | No |
Start a Service | Yes | Yes | Yes | Yes | Yes |
Bind to a Service | Yes | Yes | Yes | Yes | No |
Send a Broadcast | Yes | Yes | Yes | Yes | Yes |
Register Broadcast Receiver | Yes | Yes | Yes | Yes | No |
Load Resource Values | Yes | Yes | Yes | Yes | Yes |
인텐트
액티비티를 실행하기 위해선 단순히 컨텍스트가 제공하는 메서드를 호출하면 되는데, 이때 실행할 액티비티가 명시된 인텐트를 해당 메서드에 전달해야 합니다.
이 인텐트는 ‘개발자의 의도’로, 어떤 의도를 가지고 메서드를 실행할 것인지를 인텐트에 담아서 안드로이드에 전달하면 안드로이드는 해당 인텐트를 해석하고 실행합니다.
프로젝트 생성 시 자동으로 만들어지는 MainActivity는 특별한 설정을 하지 않아도 안드로이드에 자동으로 등록되고 실행되지만, 이외에 다른 액티비티를 사용할 때는 반드시 인텐트에 새 액티비티의 이름과 데이터를 담아서 시스템에 전달해야 합니다.
- 실행할 대상의 액티비티 이름과 전달할 데이터를 담아서 인텐트를 생성
- 생성한 인텐트를 startActivity( ) 메서드에 담아서 호출하면 액티비티 매니저에 전달됨
- 액티비티 매니저는 인텐트를 분석하여 지정한 액티비티를 실행
- 전달된 인텐트는 최종 목적지인 타깃 액티비티까지 전달
- 타깃 액티비티에서는 전달받은 인텐트에 데이터가 있다면 이를 꺼내 쓸 수 있음
새 액티비티 만들고 실행하기
1. 액티비티 생성
[Project(Android)] - [java] - 패키지명 우클릭 - [New] - [Activity] - [Empty Activity] 로 새 프로젝트 생성.
~Activity 라고 액티비티 이름을 입력하면 activity_~ 라는 xml 파일 자동 생성.
2. 뷰 바인딩
gradle 파일에서 뷰 바인딩 허용하고 MainActivity.kt 에서 binding 프로퍼티 생성, SetContentView 에 binding.root 전달.
3. 인텐트 생성: Intent(컨텍스트, 액티비티)
액티비티 호출을 위해서는 의도를 포함하는 Intent 인스턴스를 전달해야 함.
val intent = Intent(this, SubActivity::class.java)
❗ class::java 는 Intent를 사용하기 위한 작성 규칙
4. 액티비티를 호출하며 인텐트 전달: startActivity(인텐트)
startActivity(intent)
액티비티 사이에 값 주고받기
액티비티는 인텐트에 실행 메시지, 데이터 등을 주고 받을 수 있음.
인텐트 내부에는 번들(Bundle)이라는 저장 공간이 있고, 이 번들에 데이터를 담아 주고받음.
인텐트에 값을 입력할 때는 키와 값의 조합으로 넣고, 꺼낼 때는 키로 참조한다.
액티비티로부터 값을 돌려받는 구조
메인 액티비티
1. 인텐트에 값 넣기: putExtra( )
intent.putExtra("from", "Hello Bundle") // String 키, String 값
intent.putExtra("from2", 2021) // String 키, Int 값
2. 액티비티를 호출하며 데이터를 가진 인텐트 전달: startActivityForResult( )
앞에서 액티비티 호출 시에는 startActivity( ) 메서드를 사용한다고 했지만, 데이터를 가지는 인텐트를 전달할 때는 startActivityForResult( )를 사용해야 함
startActivityForResult(intent, 99)
✋ startActivityForResult의 파라미터
- 파라미터 1: 인텐트 인스턴스.
- 파라미터 2: requestCode. 메인 액티비티에서 여러 개의 서브 액티비티 호출 시 어디에서 호출했는 지 구분하기 위한 용도
3. 서브 액티비티가 보낸 값 받기: onActivityResult( )
서브 액티비티 또한 종료할 때 자신을 호출한 메인 액티비티에 값을 보낼 수 있다. 이때 onActivityResult( ) 메서드를 사용하고, 함수가 정의되는 위치는 MainActivity 클래스 안이다. (onCreate 안 아님)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?){
super.onActivityResult(requestCode, resultCode, data)
// 아래는 전형적인 코드 구성
// resultCode가 정상인지 체크
if (resultCode == RESULT_OK){
when(requestCode) {
99 -> {
// 서브 액티비티에서 보낸 정보 받기. Safe Call 사용.
val message = data?.getStringExtra("returnValue")
// 화면에 토스트로 보여주기. show()를 호출해야 화면에 나타남
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}
}
}
}
✋ onActivityResult 의 파라미터
- 파라미터 1: 메인 액티비티에서 서브 액티비티 호출 시 전달했던 requestCode.
- 파라미터 2: resultCode. 서브 액티비티에서 보내는 종료 신호. RESULT_OK 또는 RESULT_CANCELED
- 파라미터 3: 서브 액티비티에서 보낸 인텐트 인스턴스.
✋ Toast.makeText의 파라미터
- 파라미터 1: 컨텍스트 객체
- 파라미터 2: 화면에 띄울 메시지.
- 파라미터 3: 화면에 띄우는 시간. Toast.LENGTH_SHORT 또는 Toast.LENGTH_LONG.
✋ [Override methods] 와 [Implement methods]
적절한 스코프 내에 마우스 포인터를 두고, [Ctrl + o] 또는 [Ctrl + i] 를 누르면 각각 그 위치에서 오버라이드 가능한 메서드들과 구현 가능한 메서드들의 목록을 보여준다. 또는 마우스 오른쪽 버튼을 누르고 [Generate] - [Override/Implement methods …] 로 찾을 수도 있다.
서브 액티비티
1. 메인 액티비티에서 보낸 값 받기
binding.to1.text = intent.getStringExtra("from1") // 문자열 값을 꺼낼 때는 getStringExtra()
binding.to2.text = "${intent.getIntExtra("from2", 0)}" // 정수 값을 꺼낼 때는 getIntExtra()
인텐트에 들어있는 값을 받을 때는 **get<데이터타입>Extra(키 이름)** 모양의 메서드를 사용합니다. intent 프로퍼티는 자신을 호출하는 메인 액티비티에서 보낸 인텐트 객체입니다.데이터타입>
Int, Float 등의 데이터 타입을 가진 값을 받을 경우 위처럼 0(defaultValue)을 지정해주고, 문자열로 사용하고 싶다면 “${ }” 로 값을 감싸서 문자열로 변환합니다.
2. 메인 액티비티로 보낼 인텐트 생성하고 데이터 넣기
// 메인 액티비티에 돌려줄 인텐트 인스턴스
// 돌려줄 때는 대상을 지정 안해도 됨
val returnIntent = Intent()
// 돌려줄 인텐트에 값 전달
returnIntent.putExtra("returnValue", binding.editMessage.text.toString())
서브 액티비티에서 메인 액티비티로 돌아갈 때 보낼 인텐트를 만들 때는 대상을 지정할 필요없음 (당연하니까).
마찬가지로 인텐트에 데이터를 넣을 때는 putExtra() 메서드를 사용하고 키와 값 쌍을 전달
3. 상태 값 설정하고 메인 액티비티로 인텐트 전달: setResult( )
// setResult(상태 값, 인텐트) 메서드 실행 시 자신을 호출한 액티비티로 인텐트 전달
setResult(RESULT_OK, returnIntent)
서브 액티비티에서 메인 액티비티로 돌아갈 때는 setResult( ) 사용.
✋ setResult 의 파라미터
- 파라미터 1: 메인 액티비티로 보낼 종료 코드. RESULT_OK 또는 RESULT_CANCELED
- 파라미터 2: 메인 액티비티로 보낼 인텐트 인스턴스.
4. 액티비티 종료: finish( )
finish()
액티비티를 종료할 때는 finish( ) 메서드 호출
전체 코드
- MainActivity.kt
package kr.co.hanbit.activitypractice
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import kr.co.hanbit.activitypractice.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
val binding by lazy { ActivityMainBinding.inflate(layoutInflater)}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
// 인텐트 생성
// ::class.java 라고 정확하게 입력, 인텐트 사용 규칙
val intent = Intent(this, SubActivity::class.java)
// 인텐트에 값 전달
intent.putExtra("from1", "Hello Bundle")
intent.putExtra("from2", 2021)
// 액티비티 호출 -> startActivity() 메서드는 호출한 액티비티에서 값을 받을 수 없음
// binding.btnStart.setOnClickListener { startActivity(intent) }
// 호출한 액티비티에서 값을 받고 싶을 때는 startActivityForResult() 메서드 사용
// 두번째 파라미터인 requestCode는 메인 액티비티에서 서브 액티비티를 호출하는 버튼이 여러 개 있을 때
// 어떤 버튼에서 호출된 것인지를 구분
binding.btnStart.setOnClickListener { startActivityForResult(intent, 99) }
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// resultCode가 정상인지 체크
if (resultCode == RESULT_OK){
when(requestCode) {
99 -> {
// 서브 액티비티에서 보낸 정보 받기
val message = data?.getStringExtra("returnValue")
// 화면에 토스트로 보여주기. show()를 호출해야 화면에 나타남
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}
}
}
}
}
- SubActivity.kt
package kr.co.hanbit.activitypractice
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kr.co.hanbit.activitypractice.databinding.ActivitySubBinding
class SubActivity : AppCompatActivity() {
val binding by lazy {ActivitySubBinding.inflate(layoutInflater)}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
// 텍스트뷰에 인텐트에 담겨온 값 입력
binding.to1.text = intent.getStringExtra("from1") // 문자열 값을 꺼낼 때는 getStringExtra()
binding.to2.text = "${intent.getIntExtra("from2", 0)}"
// 서브 액티비티 종료 시 자신을 호출했던 메인 액티비티로 값을 돌려주기
binding.btnClose.setOnClickListener {
// 메인 액티비티에 돌려줄 인텐트 인스턴스
// 돌려줄 때는 대상을 지정 안해도 됨
val returnIntent = Intent()
// 돌려줄 인텐트에 값 전달
returnIntent.putExtra("returnValue", binding.editMessage.text.toString())
// setResult(상태 값, 인텐트) 메서드 실행 시 자신을 호출한 액티비티로 인텐트 전달
setResult(RESULT_OK, returnIntent)
// 액티비티 종료
finish()
}
}
}
액티비티 생명주기
안드로이드는 앱이 실행된 후 다른 액티비티 화면으로 전환되거나, 스마트폰 화면이 꺼지거나, 앱이 종료될 때 와 같이 상태 변화가 있을 때마다 화면에 보여지는 액티비티의 생명주기 메서드를 호출하여 상태 변화를 알려줍니다.
액티비티 생명 주기 메서드
생명주기 메서드 | 액티비티 상태 | 설명 |
---|---|---|
onCreate( ) | 만들어짐 | 액티비티가 생성됩니다. 우리가 실제 코드를 가장 많이 작성하는 메서드입니다. |
onStart( ) | 화면에 나타남 | 화면에 보이기 시작합니다. |
onResume( ) | 화면에 나타남 | 실제 액티비티가 실행되고 있습니다. |
현재 실행 중 | (실행 중은 생명 주기 메서드가 따로 없고, onResume 이 호출되었다면 실행 중이라는 의미입니다.) | |
onPause( ) | 화면이 가려짐 | 액티비티 화면의 일부가 다른 액티비티에 가려집니다. |
onStop( ) | 화면이 없어짐 | 다른 액티비티가 실행되어서 화면이 완전히 가려집니다. |
onDestroy( ) | 종료됨 | 종료됩니다. |
- 생명주기 메서드 오버라이딩
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
}
override fun onStart() {
super.onStart()
}
override fun onResume() {
super.onResume()
}
override fun onPause() {
super.onPause()
}
override fun onStop() {
super.onStop()
}
override fun onDestroy() {
super.onDestroy()
}
생명주기 메서드를 실행할 때는 반드시 super를 먼저 호출해야 합니다.
대표적으로 영상 플레이어를 사용할 때 화면 전환 시 리소스를 아끼기 위해 onPause( ) 또는 onStop( ) 메서드를 사용합니다.
생명주기 콜백의 이해
- 액티비티 생성 시
- onCreate 메서드로 생성된 후 화면 구성 요소를 메모리에 로드.
- onStart와 onResume을 호출하여 화면의 구성 요소를 나타내고 사용자와 상호작용 시작.
- 액티비티 제거 시 (finish 메서드 호출 또는 뒤로가기)
- onPause와 onStop 메서드를 동시에 호출
- 최종적으로 onDestroy 메서드를 호출하며 액티비티가 메모리에서 제거.
- 새로운 액티비티 생성 시
- 메인 액티비티는 onPause를 거쳐 onStop 메서드까지만 호출. 종료되지는 않음.
- 서브 액티비티는 onStart와 onResume을 연속적으로 호출한 후 실행 상태.
- 새로운 액티비티가 기존 액티비티를 모두 가리지는 않으며 생성 시
- 메인 액티비티는 onPause 까지만 호출한 후 일시 정지(Paused) 상태 대기.
- 서브 액티비티가 종료되면 메인 액티비티는 onStart 를 거치지 않고 바로 onResume 호출.
액티비티 백스택
- 백스택: 액티비티가 호출(실행)되며 쌓이는 것을 담아두는 저장 공간. 사용자는 맨 위 액티비티를 보게 되며, 뒤로가기 버튼을 누르거나 현재 액티비티를 종료하면 스택에서 제거되어 다음에 쌓여있던 액티비티가 화면에 보임.
테스크와 프로세스
-
프로세스: 애플리케이션의 실행 단위. 하나의 프로세스는 여러 개의 액티비티를 관리 및 실행.
-
테스크: 애플리케이션에서 실행되는 프로세스를 관리하는 작업 단위.
안드로이드에서 테스크는 다른 프로세스의 액티비티를 함께 담을 수 있고 서로 공유할 수 있습니다.
예를 들어 카메라 기능을 간단한 코드로 호출해서 사용하면 실제로는 카메라 앱의 독자적인 프로세스가 실행되고 카메라 액티비티 또한 카메라 앱의 프로세스에 의해 처리됩니다.
다음은 특정 앱의 액티비티에서 카메라를 사용할 때 인텐트를 시스템을 통해 카메라 앱에 전달하는 예제 코드입니다. 카메라 앱을 호출하는 코드를 간략하게 구현하면 다음과 같습니다.
class Activity_B: AppCompatActivity(){
val REQ_CAMERA = 100
// ... 중략
fun openCamera(){
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
startActivityForResult(intent, REQ_CAMERA)
}
}
액티비티 태스크 관리하기
액티비티 태스크는 두 가지 방법으로 관리할 수 있습니다.
- 방법 1. AndroidManifest.xml 에서 매니페스트 설정
// 예시 코드
// <activity> 태그 안에 사용할 때는 모든 속성 이름 앞에 android: 가 붙어야 함.
<activity android: name=".MainActivity" android:launchMode="singleInstance"></activity>
속성 | 설명 |
---|---|
launchMode | 호출할 액티비티를 새로 생성할 것인지 재사용할 것인지 결정. 기본 값은 항상 새로 생성. 4가지 모드: standard, singleTop, singleTask, singleInstance |
taskAffinity | affinity가 동일한 액티비티들은 같은 task에 들어감. 기본 값은 manifest에 정의된 패키지명이르모 기본적으로 한 앱의 모든 액티비티들을 동일한 affinity를 가짐. affinity를 사용하여 액티비티를 서로 다르게 그룹화하거나, 서로 다른 앱(프로세스)에 정의된 액티비티를 같은 태스크에 둘 수 있음. |
allowTaskReparenting | 기본값은 false. true일 경우 호출한 액티비티를 동일한 affinity를 가진 태스크에 쌓이도록 함. |
clearTaskOnLaunch | 기본값은 false. true일 경우 액티비티가 재실행될 때 실행된 액티비티의 수와 관계없이 메인 액티비티를 제외하고 모두 제거. |
alwaysRetainTaskState | 기본값은 false. 사용자가 특정 시간 이상동안 앱을 사용하지 않을 경우 시스템이 루트 액티비티(태스크에서 가장 많이 실행된 액티비티)를 제외한 액티비티들을 제거. true일 경우 시스템이 관여하지 않음. |
finishOnTaskLaunch | 앱을 다시 사용할 때 태스크에 이 옵션이 true인 액티비티가 있다면 해당 태스크를 종료시킴. |
- 방법 2. startActivity 메서드에 전달되는 intent에 플래그 값 추가
플래그 | 설명 |
---|---|
FLAG_ACTIVITY_CLEAR_TOP | 호출하는 액티비티가 스택에 있으면 해당 액티비티를 Top으로 만들기 위해 그 위에 존재하던 액티비티를 모두 삭제. 예를 들어 액티비티 A, B, C, D, E 순으로 스택에 쌓여있을 때 C를 호출하면 D, E를 삭제하여 C 를 화면에 나타냄. |
FLAG_ACTIVITY_NEW_TASK | 새로운 태스크를 생성하여 안에 액티비티를 추가할 때 사용. 단, 기존에 존재하는 태스크 중 생성하려는 액티비티와 동일한 affinity를 가지고 있는 태스크가 있으면 해당 태스크로 액티비티가 들어감. |
FLAG_ACTIVITY_MULTIPLE_TASK | 호출되는 액티비티를 메인으로 하는 새로운 태스크를 생성. 이렇게 하면 동일한 액티비티를 하나 이상의 태스크에서 열 수 있다. FLAG_ACTIVITY_NEW_TASK와 함께 사용해야 함. |
FLAG_ACTIVITY_SINGLE_TOP | 호출되는 액티비티가 Top에 있으면 해당 액티비티를 다시 생성하지 않고, 존재하던 액티비티를 재사용함. 액티비티 A, B, C 가 있을 때 C를 호출하면 기존과 동일하게 A, B, C가 나옴. |
정리
-
안드로이드 4대 컴포넌트: 액티비티, 브로드캐스트 리시버, 서비스, 컨텐트 프로바이더
컴포넌트 보조 도구: 인텐트, 컨텐트 리졸버
-
컨텍스트에는 애플리케이션 컨텍스트와 베이스 컨텍스트가 있음
- 애플리케이션 컨텍스트는 앱의 핵심 기능을 담고 있는 단 하나의 컨텍스트.
- 베이스 컨텍스트는 개별 컴포넌트 생성 시 함께 생성되는 컨텍스트. 각각의 컴포넌트에서 baseContext 또는 this로 호출.
-
인텐트는 컴포넌트가 다른 컴포넌트를 호출할 때 그 대상, 데이터 등을 가지고 있는 보조 도구이다.
- 컴포넌트에서 인텐트를 만들고 컴포넌트 호출 메서드에 인텐트를 담아서 실행하면, 컴포넌트 매니저가 그 값을 분석하여 목적지 컴포넌트까지 전달해준다. 호출된 컴포넌트에서는 호출한 컴포넌트에서 보낸 데이터 값 등을 참조할 수 있다.
-
액티비티 사이에 값을 주고받는 과정
- 인텐트 생성 ➡ 인텐트에 값 넣기 ➡ 액티비티를 호출하며 데이터를 가진 인텐트 전달 ➡ 메인 액티비티에서 보낸 값 받기 ➡ 메인 액티비티로 보낼 인텐트 생성하고 값 넣기 ➡ 상태값 설정하고 메인 액티비티로 인텐트 전달 ➡ 액티비티 종료
-
액티비티의 생명주기 메서드는 6가지로, onCreate, onStart, onResume, onPause, onStop, onDestroy가 있다.
- 액티비티 생성 시: onCreate ➡ onStart ➡ onResume
- 액티비티 제거 시: onPause, onStop ➡ onDestroy
- 새로운 액티비티 생성 시: onPause, onStop(메인 액티비티) ➡ onStart, onResume(서브 액티비티)
- 새로운 액티비티가 기존 액티비티를 모두 가리지는 않으며 생성 시: onPause(서브 액티비티 진행) ➡ onResume(서브 액티비티 종료)
-
액티비티 백스택이란 액티비티가 호출(실행)되며 쌓이는 것을 담아주는 저장 공간.
-
프로세스는 애플레케이션의 실행 단위이며, 테스크는 이러한 프로세스를 관리하는 작업 단위.
- 서로 다른 프로세스는 동일한 테스크에 함께 담을 수 있고 데이터를 공유할 수 있다.
- 액티비티 태스크는 매니페스트 설정 또는 인텐트에 플래그 값 설정을 통해 관리할 수 있다.
Leave a comment