[Android | Kotlin] 인텐트 이해하기
액티비티 컴포넌트는 화면을 구성하는 컴포넌트이며, 앱의 화면을 안드로이드폰에 출력하려면 액티비티 컴포넌트를 만들어야 하며, 앱이 실행되면 액티비티에서 출력된 내용이 안드로이드폰에 나옵니다.
인텐트란?
- '컴포넌트를 실행하려고 시스템에 전달하는 메시지'
- 컴포넌트 클래스는 개발자가 코드에서 직접 생성해서 실행할 수 없습니다.
- 시스템에서 인텐트의 정보를 분석해서 그에 맞는 컴포넌트를 실행해 줍니다.
- 외부 앱의 컴포넌트와 연동할 때도 마찬가지입니다.
기본적으로 한 MainActivity가 실행되고 나서 DetailActivity로 화면을 전환한다면 객체를 생성해서 실행하면 될 것 같지만, DetailActivity가 component class라면 시스템이 생성해서 실행하는 클래스이므로 개발자가 작성하는 코드로 생명주기를 관리할 수 없습니다. 이때, 시스템에 Intent를 전달해 주게 되면 시스템에서 Intent의 정보를 분석해서 그에 맞는 컴포넌트를 실행하게 됩니다.
매니페스트 파일에 컴포넌트를 등록해 시스템에 컴포넌트를 알려주어야 합니다.
시스템은 런타임 때 매티페스트 파일의 정보를 참조하여 앱을 실행하기 때문에 매니패스트 파일에 컴포넌트를 등록해 놓지 않으면 시스템은 해당 컴포넌트를 알 수 없어 해당 컴포넌트를 실행할 수 없습니다.
<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>
<activity
android:name=".DetailActivity"
android:exported="true">
</activity>
val intent: Intent = Intent(this, DetailActivity::class.java)
startActivty(intent)
MainActivity에서 DetailActivity를 실행하고자 인텐트를 시스템에 전달하는 코드입니다.
결국, DetailActivity를 실행해 달라는 정보를 Intent 객체에 대입한 것입니다.
엑스트라 데이터란?
엑스트라 데이터는 '인텐트에 담는 부가 정보'라 할 수 있습니다.
인텐트를 이용해 데이터를 전달해야 할 때, 컴포넌트 객체는 시스템이 생성하므로 직접 접근할 수 없기에 엑스트라 데이터를 이용해야 합니다.
val intent: Intent = Intent(this, DetailActivity::class.java)
intent.putExtra("data1", "hello")
intent.putExtra("data2", 123)
startActivty(intent)
putExtra( ) 함수를 이용하여 MainActivity에 엑스트르 데이터를 추가해서 전달합니다.
첫 번째 매개변수는 데이터의 식별자이며 두 번째 매개변수는 전달할 데이터입니다.
val data1 = intent.getStringExtra("data1")
val data2 = intent.getIntExtra("data2", 0) // 0은 defaultValue
반대로 인텐트로 실행된 컴포넌트에서 엑스트라 데이터를 가져오려면
getIntExtra( ), getStringExtra( ), getDoubleExtra( )... 를 사용합니다.
액티비티 화면 되돌리기
화면을 전환했다가 다시 돌아왔을 때 사후 처리를 해야 할 수도 있습니다.
인텐트로 액티비티를 시작하는 방법은 3가지
- public void startActivity(Intent intent)
- public void startActivityForResult(Intent intent, int requestCode) // requestCode는 intent를 식별하는 값
- ActivityResultLauncher
사후 처리가 필요 없을 때는 startActivity( )함수를 사용
사후 처리가 필요할 때는 startActivityForResult( ) or ActivityResultLauncher를 사용
♬ 안드로이드 11 버전이 나올 즈음부터는 androidx의 ActivityResultLauncher를 권장
액티비티 화면 되돌리기 - startActivityForResult( )
intent.putExtra("resultData", "world")
setResult(RESULT_OK, intent)
finish()
화면을 되돌릴 때는 finish( )함수를 사용하며 현재 화면에 보이는 액티비트를 종료해 달라고 시스템에 요청
setResult( ) 함수는 결과를 어떻게 되돌릴지 지정
RESULT_OK or RESULT_CANCLED 등 상수를 지정
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 10 && resultCode == Activity.RESULT_OK) {
val result = data?.getStringExtra("resultData")
}
}
- 결과가 되돌아와서 다시 자신이 화면에 출력되면 onActivityResult( ) 함수가 자동으로 호출
- resultCode: 인텐트를 시작한 곳에서 인텐트를 구분하려고 설정한 요청 코드
- resultCode: 인텐트로 실행된 곳에서 돌려받은 결과 코드
- data: 인턴트 객체이며 이 객체에 결과 데이터가 있습니다.
액티비티 화면 되돌리기 - ActivityResultLauncher
ActivityResultLauncher는 액티비티에서 다양한 결과에 대한 사후 처리를 제공
ActivityResultLauncher를 이용하려면 Contract 객체가 필요합니다.
Contract는 ActivityResultLauncher로 실행될 요청을 처리하는 역할을 합니다.
☞ ActivityResultLauncher로 인텐트를 발생시켜 액티비티를 실행할 때 실제 인텐트를 발생시키는 역할을 합니다.
Contract
- PickContact: 선택한 연락처의 Uri 획득
- RequestPermission: 권한 요청, 허락 여부 파악
- RequestMultiplePermissions: 여러 권한을 동시에 요청
- StartActivityForResult: 인텐트 발생, 액티비티 실행 결과 획득
- TakePicturePreview: 사진 촬영 후 비트맵 획득
- TakePicture: 사진 촬영, 저장, 비트맵 획득
val requestLauncher: ActivityResultLauncher<Intent> = registerForActivityResult(
ActivityResultContracts.StartActivityForResult())
{
val resultData = it.data?.getStringExtra("result")
binding.mainResultView.text = "result : $resultData"
}
ActivityResultLauncher는 registerForActivityResult( ) 함수로 만드는 객체이며 매개변수에 Contract 객체와 결과를 처리하는 Callback 객체를 등록해야 합니다.
val intent: Intent = Intent(this, DetailActivity::class.java)
requestLauncher.launch(intent)
launch 함수를 호출하는 순간 ActivityResultLauncher에 등록된 Contract 객체가 실행됩니다.
인텐트 필터
인텐트는 실행할 컴포넌트를 어떻게 설정하는지에 따라 2가지로 나뉩니다.
- 명시적 인텐트: 클래스 타입 레퍼런스 정보를 활용한 인텐트
- 암시적 인텐트: 인텐트 정보를 활용한 인텐트
♬ 클래스 타입 레퍼런스를 이용하는 것을 명시적 인텐트
val intent: Intent = Intent(this, DetailActivity::class.java)
♬ 외부 앱의 컴포넌트는 클래스 타입 레버런스를 활용할 수 없으므로 암시적 인텐트를 이용
암시적 인텐트는 매니페스트 파일에 선언된 인텐트 필터를 이용
<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>
앱 내부에서만 이용되는 컴포넌트라면 android:name 속성만 선언하면 됩니다.
외부에서도 인텐트를 실행할 수 있어야 한다면 해당 컴포넌트가 있는 앱의 매니페스트 파일에 암시적 인텐트로 실행할 수 있게 <intent-filter>를 설정해 줘야 합니다.
<intent-filter> 하위에는 <action>, <category>, <data> 태그를 이용해 정보를 설정
어떤 정보를 설정할 것인지는 개발자의 선택
- <action>: 컴포넌트의 기능을 나타내는 문자열
- <category>: 컴포넌틀가 포함되는 범주를 나타내는 문자열
- <data>: 컴포넌트에 필요한 데이터 정보
액티비티 인텐트 동작 방식
- 없을 때: 인텐트를 시작한 곳에 오류가 발생 (예외 처리를 해줘야 함)
- 1개일 때: 문제없이 실행
- n개일 때: 사용자 선택으로 하나만 실행 (사용자가 선택하는 대로 하나만 실행됨)
만약 액티비티가 여러 개 있더라도 사용자에게 묻지 않고 특정 앱의 액티비티를 실행하고 싶은 경우
해당 앱의 패기지명을 지정하면 됩니다.
setPackage( ) 함수로 실행할 앱의 패기지를 지정합니다.
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("geo:37.7749,127.4194"))
intent.setPackage("com.google.android.apps.maps")
startActivity(intent)