안드로이드 개발자 문서를 참고했다.
Activity
의 새 인스턴스를 시작하기 위해서는 Intent
를 startActivity()
로 전달하면 된다. Intent
는 시작할 액티비티를 설명하고 모든 필수 데이터를 담게 된다.
액티비티가 완료되었을 때 결과를 수신하기 위해서는 startActivityForResult()
를 호출한다. 액티비티는 해당 결과를 이 액티비티의 onActivityResult()
콜백에서 별도의 Intent
객체로 수신하게 된다.
Service
는 사용자 인터페이스 없이 백그라운드에서 작업을 수행하는 구성 요소이다. 4대 컴포넌트(Activity, Service, Brocast Receiver, Content Provider)
중 하나이다.
Android 5.0 (API Level 21) 이상부터는 JobScheduler
로 서비스를 시작할 수 있다. 서비스를 시작하여 일회성 작업(파일 다운로드 등)을 수행하도록 하려면 Intent
를 startService()
에 전달하면 된다. Intent
는 시작할 서비스를 설명하고 모든 필수 데이터를 담고 있다.
Brodcast
는 모든 앱이 수신할 수 있는 메시지이다. 시스템이 부팅될 때 혹은 기기가 충전을 시작할 때 등 시스템에서 발생하는 이벤트에 대해 다양한 브로드캐스트를 전달하게 된다. Intent
를 sendBroadcast()
또는 sendOrderedBroadcast()
에 전달하면 다른 앱에 브로드캐스트를 전달할 수 있다.
인텐트에는 두 가지 유형이 존재한다.
명시적 인텐트는 말 그대로, 명시적으로 인텐트를 충족하는 애플리케이션이 무엇인지 지정하게 된다. 이를 위해서 대상 앱의 패키지 이름 또는 완전한 자격을 갖춘 구성 요소 클래스 이름을 제공하게 된다.
명시적 인텐트는 일반적으로 앱 안에서 구성 요소를 시작할 때 사용한다. 시작하고자 하는 액티비티 혹은 서비스 클래스 이름을 알고 있기 때문이다.
가령, 사용자 작업에 의한 이벤트에 응답하여 새로운 액티비티를 시작하거나, 백그라운드에서 파일을 다운로드 하기 위해 서비스를 시작하는 것 등이 해당된다.
암시적 인텐트는 특정 구성 요소의 이름을 명시하지 않는다. 대신, 수행할 일반적인 작업을 선언함으로써 다른 앱의 구성 요소가 이를 처리할 수 있도록 한다.
예를 들어, 사용자에게 지도에 있는 하나의 위치를 표시하고자 하는 경우, 암시적 인텐트를 사용하여 해당 기능을 갖춘 다른 앱이 지정된 위치를 지도에 표시할 수 있도록 요청할 수 있다.
위 그림은 액티비티를 시작할 때 인텐트를 사용하는 방법을 나타낸 것이다. 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
에 포함된 기본 사항은 다음과 같다.
시작할 구성 요소의 이름을 말한다. 구성 요소 이름은 Intent
에게 선택 항목이다. 다만, 이는 인텐트를 명시적으로 만들어 주는 핵심이다. 그말은 즉슨, 인텐트가 구성 요소 이름이 정의한 앱 구성 요소에만 전달되어야 한다는 것이다.
구성 요소 이름이 없다면 해당 인텐트는 암시적 인텐트가 되며, 인텐트를 수신해야 하는 구성 요소는 다른 인텐트 정보를 기반으로 시스템이 결정하게 된다. 앱에서 특정한 구성 요소를 시작해야 하는 경우에는 구성 요소 이름을 지정해야 가능하다.
참고로, Service
를 시작하는 경우 항상 구성 요소 이름을 지정해야 한다. 그렇지 않으면 인텐트에 어느 서비스가 응답할 지 확신할 수 없으며, 사용자 또한 어느 서비스가 시작되는지 볼 수 없다.
Intent
의 필드는 ComponentName
객체로, 이를 지정하려면 대상 구성 요소의 완전히 정규화된 클래스 이름(앱의 패키지 이름 포함)을 사용해야 한다. 구성 요소 이름을 설정하려면 setComponent()
, setClass()
, setClassName()
을 사용하거나 Intent
생성자를 사용한다.
작업이란, 수행할 일반적인 작업을 나타내는 문자열이다.(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)
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()
을 둘 다 호출해서는 안 된다. 이 둘은 서로의 값을 무효화 시키기 때문이다.
인텐트를 처리해야 하는 컴포넌트의 종류에 관한 추가 정보를 담은 문자열이다.
인텐트 안에는 카테고리 설명이 얼마든지 들어 있을 수 있다. 하지만 대부분의 인텐트에는 카테고리가 필요 없다. 우선 보편적으로 사용되는 카테고리를 살펴 본다.
CATEGORY_BROWSABLE
대상 액티비티가 웹 브라우저를 통해 시작되도록 허용한다. 이미지, 이메일, 메시지 등의 링크로 참조된 데이터를 표시하게 한다.
CATEGORY_LAUNCHER
이 액티비티가 앱의 최초 액티비티임을 표시한다. 시스템의 Application Launcher 리스트에 등록되게 된다.
이외의 카테고리는 Android Developer Intent을 참고한다.
카테고리는 addCategory()
를 통해 지정할 수 있다.
여기까지 나열된 인텐트의 속성(컴포넌트 이름, Action, 데이터, 카테고리)은 인텐트를 정의하는 특성을 나타낸다. Android 시스템은 이와 같은 속성을 읽어 어느 앱 구성 요소를 시작해야 할지 확인할 수 있다.
그러나, 인텐트는 추가적인 정보 또한 갖고 있을 수 있다. 그것은 아래와 같다.
엑스트라는 요청된 Action을 수행하는 데 필요한 추가 정보가 담긴 Key-Value
쌍이다. 몇몇 액션이 특정한 종류의 데이터 URI를 사용하는 것과 마찬가지로, 몇몇 액션은 특정한 엑스트라 또한 사용하게 된다.
다양한 putExtra()
함수를 통해 엑스트라 데이털르 추가할 수 있다. 각 메서드는 키 이름, 값 이렇게 두 개의 매개변수를 가진다. 또한, 모든 엑스트라 데이터를 포함하는 Bundle
객체를 만든 다음 Bundle
을 Intent
에 putExtras()
로 삽입할 수도 있다.
예를 들어, ACTION_SEND
로 이메일을 전송할 인텐트를 생성하는 경우 수신자를 지정할 때 EXTRA_EMAIL
키를 사용한 다음, 제목은 EXTRA_SUBJECT
키로 지정한다.
Intent
클래스는 표준화된 데이터 유형에 대해 여러 가지 EXTRA_*
상수를 지정한다. 만약 개발자 지정 엑스트라 키를 지정할 필요가 있는 경우(본인의 앱이 수신할 인텐트에 대해), 앱 패키지 이름을 접두어로 포함시키도록 한다.
주의할 사항이 있다. 다른 앱이 수신할 것으로 예상되는 인텐트를 전송할 때는 Parcelable
혹은 Serializable
데이터를 사용해서는 안 된다. 앱이 Bundle
객체에 있는 데이터에 액세스하려고 시도했지만 Parcelable
됐거나 Serializable
된 클래스에 접근하지 못하면 RuntimeException
이 발생한다.
플래그는 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>
<data>
scheme
, host
, port
, path
)와 MIME 유형의 여러 가지 측면을 나타내는 하나 이상의 특성을 사용한다.<category>
가령, 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
에 관한 내용은 문서를 참조한다.
정리가 잘되어있네요 감사합니다