[안드로이드] 인텐트와 인텐트 필터

이 글은 깡쌤의 안드로이드 프로그래밍 책을 보고 작성하였습니다.

인텐트와 인텐트 필터

안드로이드 앱은 컴포넌트 기반의 구조인데, 이를 완성시켜 주는 것이 인텐트입니다.


1. 인텐트의 기본 개념

우선 인텐트는 컴포넌트와 관련이 있습니다. 컴포넌트에 대한 개념은 블로그에 있습니다.
인텐트에 대해서 한 마디로 표현하면 "컴포넌트를 실행하기 위해 시스템에 넘기는 정보"라고 표현할 수 있습니다. 즉, 컴포넌트를 직접 자바 코드로 시생성해서 실행하지 못하고 실행하고자 하는 컴포넌트 정보를 담은 인텐트를 구성해서 시스템에 넘기면 시스템에서 인텐트의 정보를 분석해 맞는 컴포넌트를 실행해주는 구조입니다.

그림으로 표현하자면 다음과 같습니다. A 컴포넌트에서 B 컴포넌트를 실행하기 위해서는 Intent에 정보를 담아 시스템에 의뢰하고, 시스템에서 B 컴포넌트를 실행하게 됩니다. 이처럼 컴포넌트 간의 실행 시 두 클래스가 직접 결합하기 않고, 인텐트를 매개로 해서 실행되므로 같은 앱 내의 컴포넌트를 실행하든 다른 앱의 컴포넌트를 실행하든 개발자는 인텐트를 시스템에 의뢰하기만 하면 됩니다. 즉, 같은 코드로 같은 앱이나 외부 앱의 컴포넌트를 실행할 수 있습니다.


2. 명시적 인텐트, 암시적 인텐트

인텐트에 의해 다른 컴포넌트를 실행할 때 인텐트에 어떤 정보를 담는지에 따라 크게 명시적 인텐트암시적 인텐트로 구분됩니다.


2.1 명시적 인텐트

명시적 인텐트는 실행하고자 하는 컴포넌트의 클래스명을 인텐트에 담는 방법입니다. 주로 같은 앱의 컴포넌트를 실행할 때 이용하는 방법입니다.

예를 들어, ListActivity에서 DetailActivity를 실행한다고 가정하겠습니다. ListActivity에서 DetailActivity를 실행하기 위해서 인텐트에 클래스명을 담고 시스템에서는 인텐트에 있는 클래스명을 참조해서 DetailActivity 클래스를 실해하는 방법이 명시적 인텐트입니다.

// 명시적 인텐트 실행
// 생성자로 Context와 실행하고자 하는 Class를 준다
// 이처럼 클래스명을 직접 주는 것은 명시적 인텐트
val intent = Intent(this, DetailActivity::class.java)
// 해당 인텐트를 가지고 시스템에게 의뢰하기 위한 메서드
startActivity(intent)

위의 코드를 보면 Intent 객체의 생성자로 실행하려는 클래스명을 담았습니다. 이처럼 직접 클래스명을 담는 것을 명시적 인텐트라고 합니다. 자신의 앱의 클래스명은 확실히 알고 있으므로 명시적 인텐트를 사용하여 컴포넌트를 실행합니다.


2.2 암시적 인텐트

암시적 인텐트는 클래스명이 아닌 Intent Filter 정보를 활용합니다. 주로 클래스명을 알 수 없는 외부 앱의 컴포넌트를 실행할 때 이용합니다.

예를 들어, A 앱의 액티비티에서 B 앱의 액티비티를 실행한다고 가정하겠습니다. 두 앱은 서로 외부 앱이므로 연동 시 다른 앱의 컴포넌트 클래스명을 알 수도 없고, 코드에서 클래스명을 명시할 수도 없습니다. 그래서 클래스명 대신 Intent Filter 정보를 담아 실행하면 시스템에서 Intent Filter 정보를 해석해서 컴포넌트를 실행하는 것이 암시적 인텐트입니다.

xml

<activity android:name=".DetailActivity" android:exported="false">
    <intent-filter>
        <action android:name="kr.co.lee.ACTION_VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="http" />
    </intent-filter>
</activity>

액티비티 클래스 코드

// 암시적 인텐트 실행
// intent의 action 프로퍼티의 값으로 Manifest.xml에서 정의한 intent-filter의 action 태그의 이름을 설정
val intent = Intent().apply {
    action = "kr.co.lee.ACTION_VIEW"
}

// 인텐트를 실행하려고 시도
try {
    startActivity(intent)
} catch(e: ActivityNotFoundException) {
    // 인텐트를 다룰 액티비티가 없는 경우 앱이 무엇을 해야할지 정의
}

위의 Manifest.xml에 컴포넌트를 다음과 같이 등록하면 시스템에서는 이 액티비티를 위해 클래스명과 intent-filter 정보를 가지게 됩니다. 그리고 코드에서는 생성자에 클래스명을 대입하지 않고 Action 정보를 주었습니다. Action 문자열이 "kr.co.lee.ACTION_VIEW"로 등록된 액티비티를 실행해 달라는 의미입니다. 결국, 실행하려는 컴포넌트의 AndroidManifest.xml 파일에 정의된 intent-filter 정보와 동일한 값을 주어 실행하는 것이 암시적 인텐트입니다.

이때, 해당하는 인텐트에 반응 할 앱이 한개라면 바로 그 앱이 실행되고, 반응 할 액티비티가 여러 개라면(지도를 실행하는 경우 - 구글 지도, 네이버 지도, 카카오 지도 등) 다이얼로그 형식으로 사용자에게 선택 할 수 있게 합니다. 그리고 반응 할 앱이 없다면 ActivityNotFoundException이 발생해 그 안에서 코드를 작성하면 됩니다.


3. 인텐트 필터(Intent-filter)

AndroidManifest.xml에 컴포넌트 하위 태그로 <intent-filter> 태그가 등록되는데 그 안의 태그는 다음과 같습니다.

<activity android:name=".DetailActivity" android:exported="false">
    <intent-filter>
        <action android:name="kr.co.lee.ACTION_VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="http" />
    </intent-filter>
</activity>
  • action : 컴포넌트가 어떤 능력을 갖추고 있는지에 대한 문자열입니다. 개발자가 임의로 지정하는 단어도 가능하며, 라이브러이에서 지정한 문자열을 이용해도 됩니다.

  • category : 컴포넌트에 대한 추가 정보로 어느 점주의 컴포넌트인지를 표현하는 데 사용됩니다. 개발자가 임의로 지정하는 단어도 가능하지만, 거의 대부분 라이브러리 내에서 준비된 단어를 사용합니다. 만약 startActivity()나 startActivityForResult() 메서드로 암시적 인텐트를 실행할 경우 코드에서 지정하지 않아도 CATEGORY_DEFAULT가 선언된 것처럼 행동합니다. 따라서 category 태그로 DEFAULT를 무조건 넣어야 합니다.

  • data : data는 컴포넌트를 실행하기 위해 필요한 데이터에 대한 상세 정보를 명시하기 위해 사용됩니다. data는 URL 형식으로 표현되어 android:scheme, android:host, android:port , android:mimeType 등으로 선언하게 됩니다.

<intent-filter> 요소를 포함하는 컴포넌트안에 android:exported 값을 명시적으로 넣어야 합니다. 이 속성은 앱 구성 요소가 다른 앱에 엑세스 할 수 있는지 여부를 나타냅니다. 만약 intent-filter의 category 태그에 LAUNCHER를 포함한다면 이 속성을 true로 설정하는 것이 좋습니다. 그렇지 않다면 이 속성을 false로 설정하는 것이 안접합니다.

액티비티나, 서비스, 브로드캐스트 리시버에서 인텐트 필터를 사용하고 android:exported 속성을 명시적으로 지정하지 않는다면 안드로이드 12버전 이상에서는 스마트폰에 앱이 설치되지 않는다고 합니다.

인텐트필터 추가


4. Extra 데이터

인텐트를 이용하여 컴포넌트를 실행할 수 있는데, 컴포넌트를 실행하면서 데이터를 전달해야 할 수도 있습니다.

ListActivity에서 DetailActivity로 데이터를 넘긴다고 가정하겠습니다. 그러면 ListActivity에서 DetailActivity를 실행하기 위한 인텐트에 데이터를 담아 전달하는 방식을 이용합니다. 이 데이터를 Extra 데이터 라고 부릅니다.

// intent로 객체를 넘기기 위해 Serializable를 구현
// 데이터를 직렬화 하기 위한 인터페이스
data class User(val name: String, val age: Int): Serializable

// 객체 생성
val user = User("User1", 20)

val intent = Intent(this, DetailActivity::class.java)
// putExtra 메서드로 String, int, 객체 등 전달
intent.putExtra("data", "hello")
intent.putExtra("data2", 1)
intent.putExtra("data3", user)
startActivity(intent)

인텐트를 발생하기 전에 putExtra() 함수를 이용하여 데이터를 인텐트 객체에 담아주면 됩니다. Extra 데이터는 key, value 형식으로 담아주면 되고 코드와 같이 문자열, 숫가, 객체 등 모든 타입의 데이터를 넘길 수 있습니다. 이처럼 인텐트를 통해 데이터를 넘기면 받는 쪽에서는 자신을 실행했던 인텐트를 얻어서 그 객체에 담긴 데이터를 얻는 구조입니다.

// 자신을 실행한 intent 객체 얻어오기(getIntent)
val intent = intent
// getXXXExtra를 사용하여 데이터를 받아오는데 두 번째 매개변수는 기본값
val stringData = intent.getStringExtra("data")
val intData = intent.getIntExtra("data2", 0)
val objectData = intent.getSerializableExtra("data3") as User

intent 프로퍼티를 이용해 자신을 실행한 인텐트 객체를 얻고, 그 안에 담긴 ExtraData를 getXXXExtra() 함수를 이용하여 얻습니다.


5. 결과 되돌리기

액티비티는 화면 출력을 주목적으로 하는 컴포넌트이므로 인텐트가 발생하면 화면이 전환됩니다. 이때 사용자가 뒤로가기 버튼을 누르면 화면이 되돌아오겠지만, 사용자가 뒤로가기 버튼을 누리지 않고도 코드에서 자동으로 화면이 되돌아오게 처리해야 할 때가 있습니다.

화면을 자동으로 되돌리려면 인텐트를 발생시킬 때, startActivity() 함수를 이용하지 않고 startActivityForResult() 함수를 이용합니다.

ListActivity

// Intent 객체 생성
val intent = Intent(this, DetailActivity::class.java)
// startActivityForResult의 두 번째 매개변수로 개발자 숫자 값을 지정해 결과를 되돌려 받았을 때
// 어느 요청이 들어온지 구분하기 위해 사용
startActivityForResult(intent, 10)

startActivityForResult() 함수를 이용하여 액티비티를 시작하는데, 두 번째 매개변수로 숫자 값을 지정합니다. 이 값은 불러온 액티비티가 종료되어 다시 돌아올 때 자동으로 호출되는 onActivityResult() 함수에서 결과를 구분하기 위해 사용합니다.

ListActivity

// 화면이 되돌아 왔을 때 자동 호출
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if(requestCode == 10 && resultCode == RESULT_OK) {
        // 종료된 액티비티에서 넘어온 데이터
        val data = data?.getStringExtra("data")
        Toast.makeText(this, data, Toast.LENGTH_SHORT).show()
    }

    super.onActivityResult(requestCode, resultCode, data)
}

이 함수가 자동으로 호출되는데 첫 번째 매개변수가 startActivityForResult()에서 지정한 숫자 값이고 resultCode는 불러온 Activity가 종료되기전에 지정한 값입니다.

DetailActivity

// 자신을 실행한 intent 객체 얻어오기(getIntent)
val intent = intent
// 받아온 intent에 데이터 넣기
intent.putExtra("data", "데이터 넘기기")
// 자신의 상태 입력하기(RESULT_OK를 지정하여 정상으로 처리되어 되돌린 것임을 명시)
setResult(RESULT_OK, intent)
// 종료
finish()

불려진 액티비티의 코드로 intent 프로퍼티를 이용해 자신을 호출 한 인텐트를 얻어옵니다. 그 인텐트에는 putExtra로 데이터도 넣을 수 있습니다. 그리고 setResult 메서드를 통하여 상태를 지정하고 종료하면 이 매개변수가 위의 onActivityResult 함수의 매개변수로 들어가게 됩니다.

__startActivityForResult 함수는 현재 deprecated 되었습니다. 현재는 Registering a callback for an Activity Result를 사용하는 것 같습니다.
정리 후 링크달기


참조

MIME Type : MIME(영어: Multipurpose Internet Mail Extensions)는 전자 우편을 위한 인터넷 표준 포맷이다. 전자우편은 7비트 ASCII 문자를 사용하여 전송되기 때문에, 8비트 이상의 코드를 사용하는 문자나 이진 파일들은 MIME 포맷으로 변환되어 SMTP로 전송된다. 실질적으로 SMTP로 전송되는 대부분의 전자 우편은 MIME 형식이다.

좋은 웹페이지 즐겨찾기