액티비티는 안드로이드 앱에서 화면을 구성하는 컴포넌트로, 이미 알아보았었다.
하지만 액티비티를 제대로 이해하려면 화면 구성 방법 이외에 동작 방식 등을 구체적으로 알아야 한다.
안드로이드 앱은 모두 4개의 컴포넌트로 개발하는데, 이때 핵심 클래스가 바로 Intent다.
인텐트는 하나의 액티비티가 다른 액티비티를 론칭(시작)시킬 수 있는 매커니즘이다.
또는 인텐트는 '컴포넌트를 실행하려고 시스템에 전달하는 메시지'라고 할 수 있다.
예를 들어, 한 앱에 MainActivity와 DetailActivity가 있다고 가정하자.
MainActivity가 실행되고 나서 DetailActivity로 화면을 전환한다면 DetailActivity의 객체를 생성해서 실행하면 될 것이다.
하지만 DetailActivity가 만약 안드로이드의 컴포넌트 클래스라면, 개발자가 코드에서 직접 생성해서 실행할 수 없다. 왜냐하면 컴포넌트 클래스는 시스템이 생성해서 실행하는 클래스이므로, 개발자가 작성하는 코드로 생명주기를 관리할 수 없기 때문이다.
결국 MainActivity 클래스에서 DetailActivity 클래스를 실행하려면 시스템에 인텐트를 전달해 줘야 한다. 그러면 시스템에서 인텐트의 정보를 분석해서 그에 맞는 컴포넌트를 실행해 준다.
이러한 인텐트의 중재 역할을 같은 앱의 컴포넌트 뿐만 아니라 외부 앱의 컴포넌트와 연동할 때도 마찬가지다.
Intent는 앱 내의 다양한 컴포넌트(액티비티, 서비스, 브로드캐스트 리시버 등) 간의 통신을 가능하게 한다. Intent는 두 가지 주요 유형으로 나뉜다.
컴포넌트 간의 통신: Intent는 Android의 컴포넌트들(예: 액티비티, 서비스, 브로드캐스트 리시버)이 서로 상호작용할 수 있게 한다. 예를 들어, 한 액티비티에서 다른 액티비티를 시작하거나, 서비스를 시작하는 데 사용된다.
데이터 전달: Intent를 통해 한 컴포넌트에서 다른 컴포넌트로 데이터를 전달할 수 있다.
예를 들어, 사용자 입력 데이터를 한 액티비티에서 다른 액티비티로 전달할 때 Intent를 사용한다.
시스템 기능 요청: 시스템에서 제공하는 기능을 사용할 수 있다. 예를 들어, 웹 페이지 열기, 연락처 앱 실행, 사진 촬영 등의 작업을 수행할 수 있다.
사용 예시:
사용 예시:
Intent.ACTION_VIEW
, Intent.ACTION_SEND
등이 있다.Intent.CATEGORY_DEFAULT
를 사용한다.인텐트 필터는 컴포넌트가 처리할 수 있는 인텐트의 종류를 정의한다.
필터는 AndroidManifest.xml
파일에 정의되며, 컴포넌트가 어떤 인텐트를 받을 수 있는지를 명시한다. 인텐트 필터를 사용하여 컴포넌트가 특정 작업이나 데이터를 처리할 수 있는지 여부를 결정할 수 있다.
<activity
android:name=".DetailActivity"
android:exported="true" />
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
val intent: Intent = Intent(this, DetailActivity::class.java)
startActivity(intent)
이 코드는 안드로이드 앱에서 현재 화면에서 다른 화면(액티비티)으로 이동하는 기본적인 방법을 보여준다.
Intent
객체 생성:
val intent: Intent = Intent(this, DetailActivity::class.java)
Intent
: 다른 화면(액티비티), 서비스, 또는 다른 작업을 요청할 때 사용하는 Android의 기본 클래스다.this
: 현재 화면(액티비티)을 가리킨다. 이 코드를 작성하고 있는 액티비티의 인스턴스를 가리킨다.DetailActivity::class.java
: 이동하고자 하는 대상 화면(액티비티)의 클래스다. 여기서는 DetailActivity
라는 이름의 액티비티로 이동하겠다는 의미다.이 줄의 코드는 현재 액티비티(this
)에서 DetailActivity
로 이동하겠다는 의도를 가진 Intent
객체를 만든다.
새로운 화면 시작:
startActivity(intent)
startActivity(intent)
: 방금 생성한 Intent
객체를 사용해서 DetailActivity
를 시작한다.DetailActivity
라는 새 화면으로 이동한다.Intent
는 택시를 호출하는 것과 비슷하다. 여기서 택시를 부르는 명령이 Intent
객체를 만드는 것이고, 택시의 목적지가 DetailActivity
이다.startActivity(intent)
는 택시에 탑승하여 목적지(DetailActivity)로 이동하는 것과 같다. 그런데 MainActivity에서 인텐트를 이용해 DetailActivity를 실행할 때 데이터를 전달해야 한다면, 어떠한 방법을 사용할까?
인텐트에 컴포넌트 실행을 요청할 때 데이터를 함께 전달하려면 EXTRA DATA를 이용해야 한다.
엑스트라 데이터란, 인텐트에 담는 부가정보이다.
// 엑스트라 데이터 추가
val intent: Intent = Intent(this, DetailActivity::class.java)
intent.putExtra("data1", "hello")
intent.putExtra("data2", 10)
startActivity(intent)
// 엑스트라 데이터 가져오기
val data1 = intent.getStringExtra("data1")
val data2 = intent.getIntExtra("data2", 0)
엑스트라 데이터를 추가하거나 가져올땐 위와 같이 작성하면 된다.
액티비티는 화면을 구성하는 컴포넌트이다. 따라서 한 액티비티에서 인텐트로 다른 액티비티를 실행하면 화면이 전환된다. 그런데 화면을 전환했다가 다시 돌아왔을 때 사후 처리가 필요한 때가 있고, 불필요한 때가 있다.
이처럼 사후 처리 여부에 따라 인텐트로 액티비티를 시작하는 방법은 3가지로 나뉜다.
public void startActivity(Intent intent)
: 사후 처리가 필요 없을 때 사용한다.public void startActivityForResult(Intent intent, int requestCode)
: 사후 처리가 필요 할 때 사용한다 (but deprecated)ActivityResultLauncher
: 사후 처리가 필요할 때 사용한다 (권장)다음 코드는 액티비티에서 결과를 되돌리는 코드다. 사용자가 뒤로가기 버튼을 누르지않고 자동으로 화면을 되돌릴 때는 finish() 함수를 사용한다.
finish()는 현재 화면에 보이는 액티비티를 종료해 달라고 시스템에 요청한다.
intent.putExtra("resultData", "world")
setResult(RESULT_OK, intent)
finish()
또한 setResult()를 통해 결과를 어떻게 되돌릴지 지정할 수 있다.
Contract
, ActivityResultLauncher
, 그리고 launch
는 Android에서 액티비티 간의 데이터를 주고받는 최신 방법인 Activity Result API와 관련된 용어들입니다. 이 API는 이전에 사용되던 startActivityForResult
방식보다 더 간결하고 구조화된 방식으로 액티비티 간의 결과 처리를 관리할 수 있게 해줍니다.
Contract는 어떤 작업을 요청하고, 그 결과를 어떻게 처리할지 정의한 계약이라고 생각할 수 있다. ActivityResultContract
는 일반적으로 두 가지 주요 역할을 한다.
Android는 몇 가지 기본적인 ActivityResultContract
클래스를 제공하며,
이를 사용하면 파일 선택, 사진 촬영, 권한 요청 등 다양한 작업을 수행할 수 있다.
또한, 개발자가 필요에 따라 사용자 정의 계약을 만들 수도 있다.
예를 들어:
ActivityResultContracts.StartActivityForResult
: 다른 액티비티를 호출하고 결과를 받아오는 계약.ActivityResultContracts.RequestPermission
: 특정 권한을 요청하고, 그 결과를 받아오는 계약.ActivityResultLauncher
는 Contract에 따라 액티비티를 시작하고 그 결과를 처리할 수 있게 하는 객체이다. 이 객체는 Contract와 연결되며, launch
메서드를 통해 특정 작업을 시작할 수 있다.
ActivityResultLauncher
는 registerForActivityResult
메서드를 통해 생성된다. 이 메서드는 두 가지를 인수로 받는다:ActivityResultContract
: 어떤 작업을 수행할지 정의한다.launch
는 ActivityResultLauncher
를 사용하여 Contract에 정의된 작업을 시작하는 메서드다. launch
를 호출할 때, Contract에서 정의된 인풋 데이터를 전달할 수 있다.
Contract 설정:
ActivityResultContract
를 사용하여, 어떤 작업을 요청할지와 그 결과가 무엇인지 정의한다.ActivityResultLauncher 등록:
registerForActivityResult
를 사용하여 ActivityResultLauncher
를 등록하고, Contract와 결과를 처리할 콜백을 설정한다.launch 호출:
launch
를 호출하여 작업을 시작하고, 필요한 인풋 데이터를 전달한다. // 1. Contract 설정 및 ActivityResultLauncher 등록
val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
// 2. 결과 처리 (예: 선택한 이미지 URI 처리)
imageView.setImageURI(uri)
}
// 3. launch 호출 (예: 버튼 클릭 시)
button.setOnClickListener {
getContent.launch("image/*") // 이미지 파일 선택
}
ActivityResultContracts.GetContent()
: 사용자가 콘텐츠를 선택할 수 있는 기본 계약(예: 이미지 선택).getContent.launch("image/*")
: 특정 MIME 타입의 콘텐츠를 선택하도록 요청. 여기서는 이미지 파일 선택.Contract로 주고받을 데이터의 형식을 정의하고,
ActivityResultLauncher로 이 과정을 준비한 다음,
launch로 실제 데이터를 보내고 결과를 받아온다.
Contract, ActivityResultLauncher, launch 이 세 가지를 합치면 결국 큰 하나의 데이터 전달 과정이 된다.