Android - Intent

Park Suyong·2022년 2월 10일
1

Android Dev

목록 보기
1/7

안드로이드 개발자 문서를 참고했다.

Intent 및 Intent Filter

Activity Start

Activity의 새 인스턴스를 시작하기 위해서는 IntentstartActivity()로 전달하면 된다. Intent는 시작할 액티비티를 설명하고 모든 필수 데이터를 담게 된다.

액티비티가 완료되었을 때 결과를 수신하기 위해서는 startActivityForResult()를 호출한다. 액티비티는 해당 결과를 이 액티비티의 onActivityResult() 콜백에서 별도의 Intent 객체로 수신하게 된다.

서비스 시작

Service는 사용자 인터페이스 없이 백그라운드에서 작업을 수행하는 구성 요소이다. 4대 컴포넌트(Activity, Service, Brocast Receiver, Content Provider) 중 하나이다.

Android 5.0 (API Level 21) 이상부터는 JobScheduler로 서비스를 시작할 수 있다. 서비스를 시작하여 일회성 작업(파일 다운로드 등)을 수행하도록 하려면 IntentstartService()에 전달하면 된다. Intent는 시작할 서비스를 설명하고 모든 필수 데이터를 담고 있다.

브로드캐스트 전달

Brodcast는 모든 앱이 수신할 수 있는 메시지이다. 시스템이 부팅될 때 혹은 기기가 충전을 시작할 때 등 시스템에서 발생하는 이벤트에 대해 다양한 브로드캐스트를 전달하게 된다. IntentsendBroadcast() 또는 sendOrderedBroadcast()에 전달하면 다른 앱에 브로드캐스트를 전달할 수 있다.

Intent 유형

인텐트에는 두 가지 유형이 존재한다.

1. 명시적 인텐트 (Explicit Intent)

명시적 인텐트는 말 그대로, 명시적으로 인텐트를 충족하는 애플리케이션이 무엇인지 지정하게 된다. 이를 위해서 대상 앱의 패키지 이름 또는 완전한 자격을 갖춘 구성 요소 클래스 이름을 제공하게 된다.

명시적 인텐트는 일반적으로 앱 안에서 구성 요소를 시작할 때 사용한다. 시작하고자 하는 액티비티 혹은 서비스 클래스 이름을 알고 있기 때문이다.

가령, 사용자 작업에 의한 이벤트에 응답하여 새로운 액티비티를 시작하거나, 백그라운드에서 파일을 다운로드 하기 위해 서비스를 시작하는 것 등이 해당된다.

2. 암시적 인텐트 (Implicit Intent)

암시적 인텐트는 특정 구성 요소의 이름을 명시하지 않는다. 대신, 수행할 일반적인 작업을 선언함으로써 다른 앱의 구성 요소가 이를 처리할 수 있도록 한다.

예를 들어, 사용자에게 지도에 있는 하나의 위치를 표시하고자 하는 경우, 암시적 인텐트를 사용하여 해당 기능을 갖춘 다른 앱이 지정된 위치를 지도에 표시할 수 있도록 요청할 수 있다.

Intent 설명

위 그림은 액티비티를 시작할 때 인텐트를 사용하는 방법을 나타낸 것이다. Intent 객체가 특정 액티비티 구성 요소를 명시적으로 지정하게 되면 (명시적 인텐트) 시스템이 해당 구성 요소를 즉각 시작하게 된다.

반면 암시적 인텐트를 사용했을 때, Android 시스템에서 시작할 수 있는 적절한 구성 요소를 찾게 된다. 이 때 인텐트의 내용을 기기에 있는 다른 여러 앱의 Manifest 파일에서 선언된 Intent filter와 비교하는 방법을 사용한다.

해당 인텐트와 일치하는 인텐트 필터가 있으면 시스템에서 해당 구성 요소를 시작하고 이를 Intent 객체에 전달한다. 하지만, 일치하는 인텐트 필터가 여러 개인 경우도 분명 존재할 것이다. 이런 경우에는 시스템에서 Dialog를 표시함으로써 사용자가 어느 앱을 사용할지 직접 선택할 수 있게 된다.

그렇다면 Intent Filter란 무엇인가?

앱의 Manifest 파일에 들어 있는 표현이다. 안드로이드 개발자라면 아래의 코드를 반드시 보게 된다.

<intent-filter>
	<action android:name="android.intent.action.MAIN" />

	<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

위와 같이 액티비티에 대한 인텐트 필터를 선언하면 다른 여러 앱이 특정한 종류의 인텐트를 가지고 위 인텐트 필터가 선언된 액티비티를 직접 시작할 수 있다.

하지만 인텐트 필터가 선언되지 않은 액티비티의 경우에는 반드시 명시적 인텐트로만 시작할 수 있다.

인텐트 빌드

Intent 객체에는 Android 시스템이 어느 구성 요소를 시작할지 판별하는 데 사용하는 정보가 담겨 있다. 또한, 수신자 구성 요소가 작업을 적절히 수행하기 위해 사용할 정보 또한 담겨 있다. Intent에 포함된 기본 사항은 다음과 같다.

1. 구성 요소 이름 (Component Name)

시작할 구성 요소의 이름을 말한다. 구성 요소 이름은 Intent에게 선택 항목이다. 다만, 이는 인텐트를 명시적으로 만들어 주는 핵심이다. 그말은 즉슨, 인텐트가 구성 요소 이름이 정의한 앱 구성 요소에만 전달되어야 한다는 것이다.

구성 요소 이름이 없다면 해당 인텐트는 암시적 인텐트가 되며, 인텐트를 수신해야 하는 구성 요소는 다른 인텐트 정보를 기반으로 시스템이 결정하게 된다. 앱에서 특정한 구성 요소를 시작해야 하는 경우에는 구성 요소 이름을 지정해야 가능하다.

참고로, Service를 시작하는 경우 항상 구성 요소 이름을 지정해야 한다. 그렇지 않으면 인텐트에 어느 서비스가 응답할 지 확신할 수 없으며, 사용자 또한 어느 서비스가 시작되는지 볼 수 없다.

Intent의 필드는 ComponentName 객체로, 이를 지정하려면 대상 구성 요소의 완전히 정규화된 클래스 이름(앱의 패키지 이름 포함)을 사용해야 한다. 구성 요소 이름을 설정하려면 setComponent(), setClass(), setClassName()을 사용하거나 Intent 생성자를 사용한다.

2. 작업 (Action)

작업이란, 수행할 일반적인 작업을 나타내는 문자열이다.(ACTION_VIEW, ACTION_SEND 등이 예시.)

본인의 앱 내에 있는 인텐트가 사용할 Action (혹은 다른 앱이 사용하여 본인의 앱 안의 컴포넌트를 호출하게 할 Action)을 직접 지정할 수도 있으나, 보통은 Intent 클래스나 다른 프레임워크 클래스가 정의한 작업 상수를 지정해야 한다.

아래 두 개의 Action은 액티비티를 시작하는 데 보편적으로 사용되는 Action 이다.

  • ACTION_VIEW

ACTION_VIEW는 액티비티가 사용자에게 표시할 수 있는 어떤 정보를 갖고 있을 때, startActivity()Intent와 함께 사용한다.

  • ACTION_SEND

ACTION_SEND는 공유 인텐트라고도 한다. 사용자가 다른 앱을 통해 공유할 수 있는 데이터를 갖고 있을 때 사용한다. 이메일 공유, SNS 앱 공유 등이 이에 해당된다.

이외의 Action은 Android Developer Intent을 참고한다.

인텐트에 대해 Action을 지정하기 위해서는 setAction() 혹은 Intent 생성자를 이용한다. 아래 코드를 참고한다.

val intent = Intent()
intent.setAction(Intent.ACTION_SEND)
val intent = Intent(Intent.ACTION_SEND)

3. 데이터 (Data)

Action을 수행할 데이터 및 해당 데이터의 MIME 유형을 참조하는 URI(Uri 객체)이다. 제공된 데이터의 유형을 나타내는 것은 일반적으로 인텐트의 작업이다. 가령 ACTION_EDIT인 경우, 데이터에 편집할 문서의 URI가 들어있어야 할 것이다.

인텐트를 생성할 때에는 URI 외에도 데이터 유형을 지정하는 것이 매우 중요한 경우가 많다. 이미지를 표시할 수 있는 액티비티라면 오디오 파일은 재생할 수 없다.

데이터의 MIME 유형을 지정하면 Android 시스템이 인텐트를 수신하기 가장 좋은 컴포넌트를 찾는 데 도움이 된다. 다만 때로는 MIME 유형을 URI를 통해 추론할 수는 있긴 하다. 특히 데이터가 content: 형식의 URI 라면 더더욱 쉽다. content: 형식의 URI는 데이터가 기기에 위치하고 ContentProvider가 제어한다는 것을 의미하기 때문이다.

데이터 URI만 설정하기 위함이라면 setData()를 호출한다. MIME 유형도 설정하려면 setType()을 호출한다. 필요한 경우, setDataAndType()을 사용하여 두 가지를 모두 명시적으로 설정할 수 있다.

이 때 주의할 사항이 있다. URI와 MIME 유형을 둘 다 설정하고자 하는 경우 반드시 setDataAndType()을 사용한다. 또한, setData()setType()을 둘 다 호출해서는 안 된다. 이 둘은 서로의 값을 무효화 시키기 때문이다.

4. 카테고리 (Category)

인텐트를 처리해야 하는 컴포넌트의 종류에 관한 추가 정보를 담은 문자열이다.

인텐트 안에는 카테고리 설명이 얼마든지 들어 있을 수 있다. 하지만 대부분의 인텐트에는 카테고리가 필요 없다. 우선 보편적으로 사용되는 카테고리를 살펴 본다.

  • CATEGORY_BROWSABLE

대상 액티비티가 웹 브라우저를 통해 시작되도록 허용한다. 이미지, 이메일, 메시지 등의 링크로 참조된 데이터를 표시하게 한다.

  • CATEGORY_LAUNCHER

이 액티비티가 앱의 최초 액티비티임을 표시한다. 시스템의 Application Launcher 리스트에 등록되게 된다.

이외의 카테고리는 Android Developer Intent을 참고한다.

카테고리는 addCategory()를 통해 지정할 수 있다.


여기까지 나열된 인텐트의 속성(컴포넌트 이름, Action, 데이터, 카테고리)은 인텐트를 정의하는 특성을 나타낸다. Android 시스템은 이와 같은 속성을 읽어 어느 앱 구성 요소를 시작해야 할지 확인할 수 있다.

그러나, 인텐트는 추가적인 정보 또한 갖고 있을 수 있다. 그것은 아래와 같다.

5. 엑스트라 (Extra)

엑스트라는 요청된 Action을 수행하는 데 필요한 추가 정보가 담긴 Key-Value 쌍이다. 몇몇 액션이 특정한 종류의 데이터 URI를 사용하는 것과 마찬가지로, 몇몇 액션은 특정한 엑스트라 또한 사용하게 된다.

다양한 putExtra() 함수를 통해 엑스트라 데이털르 추가할 수 있다. 각 메서드는 키 이름, 값 이렇게 두 개의 매개변수를 가진다. 또한, 모든 엑스트라 데이터를 포함하는 Bundle 객체를 만든 다음 BundleIntentputExtras()로 삽입할 수도 있다.

예를 들어, ACTION_SEND로 이메일을 전송할 인텐트를 생성하는 경우 수신자를 지정할 때 EXTRA_EMAIL 키를 사용한 다음, 제목은 EXTRA_SUBJECT 키로 지정한다.

Intent 클래스는 표준화된 데이터 유형에 대해 여러 가지 EXTRA_* 상수를 지정한다. 만약 개발자 지정 엑스트라 키를 지정할 필요가 있는 경우(본인의 앱이 수신할 인텐트에 대해), 앱 패키지 이름을 접두어로 포함시키도록 한다.

주의할 사항이 있다. 다른 앱이 수신할 것으로 예상되는 인텐트를 전송할 때는 Parcelable 혹은 Serializable 데이터를 사용해서는 안 된다. 앱이 Bundle 객체에 있는 데이터에 액세스하려고 시도했지만 Parcelable 됐거나 Serializable된 클래스에 접근하지 못하면 RuntimeException이 발생한다.

6. 플래그 (Flag)

플래그는 Intent 클래스에서 정의되고, 인텐트에 대한 메타 데이터와 같은 기능을 하게 된다. 이러한 플래그는 Android 시스템에 액티비티를 시작할 방법에 대해 지침을 줄 수도 있다. (예를 들어 액티비티가 어느 작업에 소속되어야 하는지 등)

또한, 액티비티를 시작한 다음에 어떻게 처리해야 하는지도 알려줄 수 있다. 자세한 내용은 setFlags() 를 참고한다.

명시적 인텐트 예시

명시적 인텐트는 앱 내의 특정 액티비티나 서비스 등 특정한 앱 구성 요소를 시작하는 데 사용하는 인텐트이다.

// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
val downloadIntent = Intent(this, DownloadService::class.java).apply {
    data = Uri.parse(fileUrl)
}
startService(downloadIntent)

위 코드와 같이 어떤 클래스를 명시적으로 실행할 것인지 인텐트 생성자를 통해 넘길 수 있다.

암시적 인텐트 예시

암시적 인텐트는 작업을 지정하여 기기에서 해당 작업을 수행할 수 있는 모든 앱을 호출하도록 한다. 해당 작업을 수행할 수 있는 앱들은 각 앱의 AndroidManifest.xml 파일에 intent filter로서 지정되어 있다.

예를 들어, 사용자가 공유하고자 하는 콘텐츠가 있는 경우 ACTION_SEND 인텐트를 생성한다. 그 다음, 공유할 콘텐츠를 지정하도록 하는 엑스트라를 추가한다. 이후 startActivity()를 호출하면 사용자가 어느 앱을 통해 콘텐츠를 공유할 지 선택할 수 있게 된다.

// Create the text message with a string
val sendIntent = Intent().apply {
    action = Intent.ACTION_SEND
    putExtra(Intent.EXTRA_TEXT, textMessage)
    type = "text/plain"
}

// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(packageManager) != null) {
    startActivity(sendIntent)
}

startActivity()를 호출하면 시스템이 설치된 앱을 모두 살펴 보고 이런 종류의 인텐트를 처리할 수 있는 앱이 어느 것인지 알아 보게 된다. 위 예시에서는 ACTION_SEND 인텐트이며, text/plain 데이터가 담겨 있다.

앱 선택기 강제 적용

암시적 인텐트에 응답할 수 있는 앱이 두 가지 이상인 경우가 흔할 것이다. 이런 경우에 사용자는 어떤 앱을 선택할 지 결정할 수 있다.

하지만, 개발자가 직접 해당 앱을 강제하고 싶은 경우가 있을 수 있다. 이 경우에는 Intent에 setPackage()를 통해 강제할 수 있다.

다만, 그러한 경우가 아니고 사용자가 어느 앱이던 사용하게 두고 싶은 경우 createChooser()를 사용한다.

암시적 인텐트 수신

앱이 수신할 수 있는 암시적 인텐트가 어느 것인지 알리기 위해, <intent-filter> 요소를 사용한다. 이는 AndroidManifest.xml 파일에 선언하며, 각 인텐트 필터는 인텐트의 action, data, category를 기반으로 인텐트를 수락하는지 지정한다.

<intent-filter>에서는 다음과 같은 속성이 있다.

  • <action>
    name 특성에서 허용된 인텐트 작업을 선언한다.
  • <data>
    허용된 데이터 유형을 선언한다. 이 때 데이터 URI(scheme, host, port, path)와 MIME 유형의 여러 가지 측면을 나타내는 하나 이상의 특성을 사용한다.
  • <category>
    name 특성에서 허용된 인텐트 카테고리를 선언한다.

가령, ACTION_SEND 인텐트를 수신할 인텐트 필터가 있는 액티비티는 다음과 같이 선언한다.

<activity android:name="ShareActivity">
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
</activity>

만약, 다른 앱이 나의 앱을 인텐트 필터를 통해 시작하는 것을 막고 싶은 경우 exported 속성을 false로 설정한다.

보류 인텐트 사용

PendingIntent 객체는 Intent 객체를 감싸는 래퍼다. PendingIntent의 기본 목적은 외부 어플리케이션에 권한을 허가하여 안에 들어 있는 Intent를 마치 본인 앱의 자체 프로세스에서 실행하는 것처럼 사용하게 하는 것이다.

이러한 보류 인텐트의 사용은 주로 다음과 같다.

  • 사용자가 알림으로 특정한 작업을 수행할게 될 때 인텐트가 실행되도록 선언한다. (NotificationManager가 인텐트를 실행한다.)

  • 사용자가 AppWidget을 통해 특정한 작업을 수행하게 될 때 실행되는 인텐트를 생성한다. (홈 스크린이 인텐트를 실행한다.)

  • 미래의 특정 시점에 실행되는 인텐트를 선언한다. (Android AlarmManager가 인텐트를 실행)

각 인텐트 객체는 특정한 유형의 앱 컴포넌트(Activity, Service or Broadcast Receiver)가 처리하도록 설계되어 있다. 따라서, PendingIntent도 같은 고려 사항을 생각해서 생성해야 한다.

PendingIntent를 사용하는 경우 startActivity()와 같은 호출이 있는 앱을 실행하지 않게 된다. 대신, PendingIntent를 생성할 때 원래 의도한 컴포넌트 유형을 선언해야 한다. 아래와 같다.

  • Activity를 시작하는 Intent의 경우, PendingIntent.getActivity()

  • Service를 시작하는 Intent의 경우, PendingIntent.getService()

  • BroadcastReceiver를 시작하는 Intent의 경우, PendingIntent.getBroadcast()

이외 PendingIntent에 관한 내용은 문서를 참조한다.

profile
Android Developer

1개의 댓글

comment-user-thumbnail
2024년 1월 9일

정리가 잘되어있네요 감사합니다

답글 달기