Intent는 다른 앱 컴포넌트에 작업을 요청할 때 쓸 수 있는 메시지 객체임, 인텐트는 다양한 방식으로 컴포넌트간의 통신을 가능하게 하지만 기본적인 사용 방식은 다음과 같이 세 가지가 있음
Activity 는 앱의 한 화면을 대표함, startActivity() 에 Intent 를 전달해 Activity의 새 인스턴스를 시작할 수 있음, Intent 는 시작할 액티비티를 설명하고 필요한 데이터를 전달함, startActivityForResult() 를 호출해 액티비티가 종료될 때 결과를 전달받을 수 있음, onActivityResult() 콜백에서 결과를 별도의 Intent 객체로 전달받을 수 있음Service 는 UI가 없이 백그라운드에서 작업을 수행하는 컴포넌트, 안드로이드 5.0 (API 레벨 21) 이상은 JobScheduler 를 사용해 서비스를 시작할 수 있음, 안드로이드 5.0 이전 버전에서는 Service 클래스의 메소드를 사용해 서비스를 시작할 수 있음, startService() 에 Intent 를 전달해 파일 다운로드와 같은 일회성 작업을 수행하기 위해 서비스를 시작할 수 있음, Intent 는 시작할 서비스를 설명하고 필요한 데이터를 전달함, 서비스가 클라이언트-서비스 인터페이스로 설계되었다면 bindService() 에 Intent 를 전달해 서비스를 다른 컴포넌트와 연결할 수 있음sendBroadcast() 나 sendOrderedBroadcast() 에 Intent 를 전달해 다른 앱에 브로드캐스트를 전달할 수 있음명시적 인텐트는 전체 ComponentName 를 지정해 어떤 앱의 어떤 컴포넌트가 인텐트를 처리할 수 있는지 지정함, 일반적으로 같은 앱의 컴포넌트를 시작할 때 시작하려는 액티비티나 서비스의 클래스 이름을 알고 있기 때문에 명시적 인텐트를 사용함, 예시로 사용자의 동작에 대한 응답으로 같은 앱의 새로운 액티비티를 시작하거나, 파일을 백그라운드에서 다운로드 받기 위해 같은 앱의 서비스를 시작할 때 명시적 인텐트를 사용함
암시적 인텐트는 특정 컴포넌트를 지정하지 않는 대신 수행해야할 작업을 설명해 다른 앱의 컴포넌트가 처리할 수 있게 함, 예시로 사용자에게 지도의 장소를 표시하고 싶다면, 암시적 인텐트를 사용해 지도의 장소를 표시할 수 있는 다른 앱에게 요청할 수 있음
그림 1은 액티비티를 시작할 때 인텐트가 어떻게 사용되는지를 나타냄, Intent 객체가 특정 액티비티의 이름을 지정한다면 시스템은 즉시 그 액티비티를 시작함
암시적 인텐트를 사용한다면, 안드로이드 시스템은 기기에 설치된 다른 앱들의 매니페스트 파일에서 인텐트 필터와 현재 인텐트를 비교해 적절한 컴포넌트를 찾아 시작함, 인텐트가 인텐트 필터와 일치하게 되면 시스템은 그 컴포넌트를 시작하고 Intent 객체를 전달함, 만약 여러 인텐트 필터가 호환된다면 시스템은 사용자가 어떤 앱을 사용할지 고를 수 있는 다이얼로그를 표시함

그림 1. 암시적 인텐트가 다른 액티비티를 시작하기 위해 어떻게 시스템을 통해 전달되는지.
[1] 액티비티 A가 동작 설명을 담은Intent를startActivity()에 전달함.
[2] 안드로이드 시스템은 모든 앱을 검색해 인텐트와 일치하는 인텐트 필터를 가진 앱을 찾음.
[3] 시스템이 일치하는 액티비티(액티비티 B)를 시작하기 위해onCreate()메소드를 호출하고Intent를 전달함
주의: 앱의 안전을 보장하려면, 서비스를 시작할 때 항상 명시적 인텐트를 사용하고 서비스를 위한 인텐트 필터를 선언하지 마십시오. 암시적 인텐트를 사용해 서비스를 시작하는 것은 어떤 서비스가 인텐트에 대응할 지 장담할 수 없고, 사용자가 어느 서비스가 시작하는 지 볼 수 없기 때문에 보안상으로 위험합니다. 안드로이드 5.0 (
API레벨 21) 이상부터 암시적 인텐트를 사용해bindService()를 호출한다면 시스템은 예외를 발생시킵니다.
Intent 객체는 정확한 컴포넌트 이름이나 인텐트를 전달받아야 하는 컴포넌트의 카테고리와 같이 안드로이드 시스템이 어떤 컴포넌트를 시작할 지 결정할 수 있는 정보를 가지고 있음, 추가로 해야할 동작이나 처리할 데이터와 같이 전달받는 컴포넌트가 동작을 수행하기 위해 사용하는 정보도 가지고 있음, Intent 다음과 같은 주요 정보가 있음노트: 서비스를 시작할 때는 항상 컴포넌트 이름을 지정해야 합니다. 그렇지 않으면, 어떤 서비스가 인텐트에 응답할 지 장담할 수 없고, 사용자가 어떤 서비스가 시작했는지 볼 수 없기 때문입니다.
Intent 의 이 필드는 ComponentName 객체로 목표로 하는 컴포넌트의 앱의 패키지 이름을 포함한 전체 클래스 이름(예: com.example.ExampleActivity)을 사용해 지정할 수 있음, setComponent(), setClass(), setClassName() 또는 Intent 생성자를 이용해 컴포넌트 이름을 설정할 수 있음Action)view 나 pick 과 같이 수행해야 하는 일반적인 동작을 지정하는 문자열, 브로드캐스트 인텐트의 경우 이는 이미 발생된 작업을 뜻하고 이를 보고하고 있는 것임, 동작은 인텐트의 나머지 부분, 특히 데이터와 엑스트라에 저장된 정보를 결정하는데 큰 영향을 줌
앱 내의 인텐트에서 사용하기 위해 고유의 동작을 지정하거나, 다른 앱이 이 앱의 컴포넌트를 실행하도록 할 수 있지만, 일반적으로 Intent 클래스나 다른 프레임워크 클래스에 정의된 동작 상수로 지정함, 다음은 액티비티를 시작하기 위한 일반적인 동작임
ACTION_VIEW : 사진을 보여주기 위한 갤러리 앱이나 주소를 보여주기 위한 지도 앱처럼 사용자에게 정보를 보여줄 수 있는 액티비티가 있을 때 startActivity() 에 이 동작을 가진 인텐트를 사용함ACTION_SEND : 이메일 앱이나 소셜 공유 앱과 같이 다른 앱을 통해 사용자가 데이터를 공유해야 할 때 이 동작을 가진 인텐트를 이용해 startActivity() 를 사용할 수 있음, 공유 인텐트라고도 불림Intent 클래스 참조에서 더 많은 동작 상수에 대해 확인할 수 있음, 다른 동작은 시스템의 설정 앱의 특정 화면을 열기위한 동작과 같이 안드로이드 프레임워크의 다른 곳에서 정의되어 있음
setAction() 이나 Intent 생성자를 이용해 동작을 지정할 수 있음
다음 예시와 같이 동작을 정의한다면 앱의 패키지 이름을 점두사로 포함해야 함
const val ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL"
작업할 데이터를 참조하는 URI(uri 객체) 및/또는 데이터의 MIME 유형임, 제공되는 데이터의 유형은 일반적으로 인텐트의 동작에 의해 결정됨, 예시로 동작이 ACTION_EDIT 이라면 데이터는 수정할 문서의 URI를 포함함
인텐트를 생성할 때, URI에 추가로 데이터의 유형(MIME 유형)을 지정하는 것이 종종 중요함, 예시로 이미지를 표시할 수 있는 액티비티가 URI 형식이 비슷해도 오디오 파일을 재생하지 못할 수 있음, 데이터의 MIME 유형을 지정하는 것은 안드로이드 시스템이 인텐트를 전달받을 최적의 컴포넌트를 찾는 데 도움이 됨, 하지만 MIMI 유형은 가끔 URI(특히 데이터가 content: URI인 경우)로부터 추론될 수 있음, content: URI는 시스템이 데이터 MIME 유형을 볼 수 있게 하는 ContentProvider 에 의해 제어되는 데이터를 가리킴
오직 데이터 URI만 설정하고 싶다면 setData() 를 호출하면 됨, 오직 MIME 유형만 설정하고 싶다면 setType() 을 호출하면 됨, 만약 둘 다 명시적으로 설정하고 싶다면 setDataAndType() 을 호출할 수 있음
주의: URI 와 MIME 유형을 모두 설정하고 싶다면,
setData()와setType()은 서로를 무효화하기 때문에 호출해서는 안됩니다. URI 와 MIME 유형을 모두 설정할 때는 항상setDataAndType()을 사용하세요.
인텐트를 처리할 수 있는 컴포넌트의 유형에 대한 추가 정보를 포함하는 문자열, 여러 개의 카테고리 설명을 인텐트에 추가할 수 있으나 대부분의 인텐트는 카테고리가 필요하지 않음
다음은 몇 가지 일반적인 카테고리임
CATEGORY_BROWSABLE : 목적지 액티비티가 이미지나 이메일 메시지와 같이 링크로 참조되는 데이터를 표시하기 위해 웹 브라우저로 시작될 수 있음을 나타냄CATEGORY_LAUNCHER : 액티비티가 태스크의 첫 액티비티이고 시스템의 앱 런처에 나열되어 있음Intent 클래스 설명에 전체 카테고리 리스트가 있음
addCategory() 를 이용해 카테고리를 지정할 수 있음
위에 나열된 속성들(컴포넌트 이름, 동작, 데이터, 카테고리)는 인텐트의 정의적 특성을 나타냄, 안드로이드 시스템은 이 속성들을 읽음으로써 어떤 앱 컴포넌트를 시작해야 할지 결정할 수 있음, 그러나 인텐트에는 이에 영향을 주지 않는 다음과 같은 추가 정보들을 포함할 수 있음
요청된 동작을 완료하기 위해 필요한 추가 정보를 포한하는 키-값 쌍임, 일부 동작이 특정 데이터 URI를 사용하는 것처럼, 일부 동작은 특정 엑스트라를 사용함, 두 가지 인수로 키의 이름과 값을 받는 다양한 putExtra() 메소드를 사용해 엑스트라를 추가할 수 있음, 또한 모든 엑스트라를 담은 Bundle 객체를 만들고 putExtras() 를 이용해 Intent 에 추가할 수 있음
예시로, ACTION_SEND 로 이메일을 전송하는 인텐트를 생성할 때 EXTRA_EMAIL 키를 이용해 수신자를 지정하고 EXTRA_SUBJECT 키를 이용해 제목을 지정할 수 있음
Intent 클래스는 표준화된 데이터 유형들을 지정하기 위해 많은 EXTRA_* 상수들을 가지고 있음, 앱이 전달받는 인텐트를 위해 엑스트라 키를 선언해야 한다면 다음 예시와 같이 앱의 패키지 이름을 전치사로 포함해야 함
const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"
주의: 다른 앱이 전달받을 것이라 예상하는 인텐트를 전달할 때
Parcelable이나Serializable한 데이터를 사용하지 마십시오. 앱이parcel화 되거나 직렬화된 클래스에 접근할 수 없을 때Bundle객체에서 데이터에 접근하려고 하면 시스템은RuntimeException을 발생시킵니다.
플래그는 인텐트의 메타데이터 역할을 하며 Intent 클래스에 정의됨, 이런 플래그들은 안드로이드 시스템이 어떻게 액티비티를 실행할 지(예: 어떤 태스크에 액티비티가 속해야 하는지)와 실행된 뒤에 어떻게 다뤄져야 하는지(예: 최근 액티비티 목록에 포함되어야 하는지)를 지시할 수도 있음
setFlags() 메소드에 추가 정보가 있음
명시적 인텐트는 앱의 특정 액티비티나 서비스와 같이 특정 앱 컴포넌트를 실행하기 위해 사용함, 명시적 인텐트를 생성하려면 Intent 객체에 컴포넌트 이름을 정의해야 하고 다른 인텐트 속성은 추가 항목임
예시로 웹에서 파일을 다운로드 받기 위해 DownloadService 라는 서비스를 만들었다면 다음과 같은 코드로 시작할 수 있음
// 액티비티에서 실행되므로, this는 Context 임
// fileUrl은 "http://www.example.com/image.png"와 같은 URL 문자열임
val downloadIntent = Intent(this, DownloadService::class.java).apply {
data = Uri.parse(fileUrl)
}
startService(downloadIntent)
Intent(Context, Class) 생성자는 앱의 Context와 컴포넌트의 Class 객체를 인자로 받음, 이 인텐트의 경우 앱의 DownloadService 클래스를 명시적으로 시작함암시적 인텐트는 동작을 지정해 기기에서 동작을 수행할 수 있는 앱을 호출할 수 있음, 암시적 인텐트는 앱이 동작을 수행할 수 없지만 다른 앱이 가능하고 사용자가 사용할 앱을 고르게 하고 싶을 때 유용함
예시로 사용자가 다른 사람과 공유하고 싶은 내용이 있다면 ACTION_SEND 동작과 공유하고 싶은 내용을 엑스트라로 추가한 인텐트를 생성하면 됨, 이 인텐트를 이용해 startActivity() 를 호출하면 사용자가 내용을 공유할 앱을 선택할 수 있음
// 문자열로 텍스트 메시지를 생성합니다.
val sendIntent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, textMessage)
type = "text/plain"
}
// 인텐트 실행 시도
try {
startActivity(sendIntent)
} catch (e: ActivityNotFoundException) {
// 인텐트를 처리할 수 있는 액티비티가 없는 경우의 처리
}
startActivity() 가 호출되면 시스템은 설치된 모든 앱을 조사해 어떤 앱이 이런 종류의 인텐트(ACTION_SEND 동작과 text/plain 데이터를 포함한 인텐트)를 처리할 수 있을지 결정함, 처리할 수 있는 앱이 하나밖에 없다면 앱이 즉시 실행되고 인텐트가 전달됨, 만약 처리할 수 있는 앱이 없을 경우에 발생하는 ActivityNotFoundException 을 앱에서 직접 처리할 수 있음, 만약 여러 앱이 인텐트를 처리할 수 있다면 시스템은 사용자가 사용할 앱을 선택할 수 있게 그림 2와 같이 다이얼로그를 표시함
그림 2. 앱 선택 다이얼로그
암시적 인텐트에 응답할 수 있는 앱이 여러개라면, 사용자는 어떤 앱을 사용할지 고를 수 있고 그 앱을 동작에 대한 기본 앱으로 만들 수 있음, 기본 앱으로 만드는 것은 웹 페이지를 여는 행동처럼 사용자가 매번 같은 앱을 사용하길 원하는 동작을 수행할 때 도움이 됨
인텐트에 응답할 수 있는 앱이 여러개이고 사용자가 매번 다른 앱을 사용하길 원한다면, 선택 다이얼로그를 명시적으로 표시해야함, 선택 다이얼로그는 사용자에게 동작을 위해 어떤 앱을 선택할 것인지 물어봄(사용자는 동작을 위한 기본앱을 선택할 수 없음), 예시로 앱에서 ACTION_SEND 를 이용해 공유 기능을 수행한다면, 사용자는 현재 상황에 따라 다른 앱을 사용해 공유하고 싶을 수 있음, 그러므로 그림 2와 같이 항상 선택 다이얼로그를 사용해야함
선택기를 표시하려면 다음 예제와 같이 createChooser() 를 사용해 Intent 를 생성하고 startActivity() 에 전달하면 됨, 예제는 createChooser() 에 전달된 인텐트에 응답할 수 있는 앱의 리스트를 다이얼로그로 표시하고 전달된 문자를 다이얼로그 제목으로 사용함
val sendIntent = Intent(Intent.ACTION_SEND)
...
// 항상 UI 텍스트에는 string 리소스를 사용하세요.
// 예시에서는 "다음 앱으로 이 사진을 공유"와 같은 문장입니다.
val title: String = resources.getString(R.string.chooser_title)
// 선택기 대화상자를 표시하는 인텐트 생성
val chooser: Intent = Intent.createChooser(sendIntent, title)
// 원래의 인텐트가 처리될 수 있는지 확인
if (sendIntent.resolveActivity(packageManager) != null) {
startActivity(chooser)
}
앱에서 같은 앱의 컴포넌트들 간에 탐색을 하거나 다른 앱을 대신해 작업을 수행하기 위해 인텐트를 실행(전달)할 수 있음, 플랫폼 보안을 향상하기 위해서 안드로이드 12(API 레벨 31) 이상에서 안전하지 않은 인텐트 실행을 앱에서 수행하면 경고를 해주는 디버깅 기능을 제공함, 예시로 앱에서 중첩된 인텐트를 실행할 수 있음, 중첩된 인텐트는 다른 인텐트에 엑스트라로 전달된 인텐트를 뜻함
앱이 다음과 같은 행동들을 하면 시스템은 안전하지 않은 인텐트 실행을 감지하고 StrictMode 위반이 발생함
unparcel 할 때startActivity(), startService() 또는 bindService() 에 전달하는 것과 같이 해당 인텐트를 사용해 즉시 앱 컴포넌트를 시작할 때VmPolicy 를 설정할 때 detectUnsafeIntentLaunch() 를 호출해야 함, 앱이 StrictMode 위반을 감지하면 민감할 수 있는 정보를 보호하기 위해서 앱 실행을 중단할 수 있음노트: 만약 앱이 안드로이드 12를 타겟팅하고
VmPolicy정의에서detectAll()메소드를 사용한다면,detectUnsafeIntentLaunch()메소드는 자동으로 호출됩니다.
fun onCreate() {
StrictMode.setVmPolicy(VmPolicy.Builder()
// StrictMode를 확인하기 위해 이전에 추가한 다른 것들
// ...
.detectUnsafeIntentLaunch()
.penaltyLog()
// penaltyDeath()를 추가하는 것도 고려해보세요
.build())
}
안전하지 않은 인텐트 실행과 StrictMode 위반을 줄이려면 다음 모범 사례를 따를 것
인텐트의 엑스트라에는 필수적인 정보만 복사하고, 필요한 모든 정제와 검증을 수행할 것, 앱이 putExtras(Intent) 또는 putExtras(Bundle) 을 호출해 한 인텐트에서 새 컴포넌트를 실행하는 데 사용되는 인텐트에 엑스트라를 복사하는 경우가 있음, 앱이 만약 이런 작업을 수행한다면 전달받는 컴포넌트가 사용하는 엑스트라만 복사해야 함, 만약 엑스트라를 복사 받는 인텐트가 export 되지 않은 컴포넌트를 실행한다면 실행하기 전에 엑스트라를 복사하기 전에 엑스트라를 정제하고 검증해야 함
앱 컴포넌트를 불필요하게 export 하지 않기, 예시로 내부에서 중첩된 인텐트를 사용해 앱 컴포넌트를 실행할 예정이라면, 해당 컴포넌트의 android:exported 속성을 false 로 설정해야 함
중첩된 인텐트 대신 PendingIntent 사용하기, 그렇게 하면 다른 앱이 PendingIntent 에서 Intent 를 unparcel 할 때 PendingIntent 를 전달한 앱의 identity 를 사용해 실행할 수 있음, 이 방식을 사용하면 다른 앱이 안전하게 앱의 export 되지 않은 컴포넌트를 포함한 모든 컴포넌트를 안전하게 실행하게 허용할 수 있음
아래 그림 2의 도표는 어떻게 시스템이 앱에서 다른 앱으로 제어를 전달하고 돌려받는지 보여줌
export 되지 않은 컴포넌트를 호출하는 PendingIntent 객체를 엑스트라로 추가함PendingIntent 객체를 추출함PendingIntent 객체의 send() 메소드를 호출함pending 인텐트를 호출함그림 2. 중첩된 pending 인텐트를 사용해 앱 간 통신을 할 때의 도표
<intent-filter> 요소를 사용해 하나 이상의 인텐트 필터를 선언해야 함, 각 인텐트 필터는 인텐트의 동작, 데이터, 카테고리를 기반으로 수락할 인텐트를 지정함, 시스템은 인텐트 필터를 통과할 수 있는 암시적 인텐트만 앱 컴포넌트로 전달함노트: 명시적 인텐트는 컴포넌트가 선언한 인텐트 필터와 상관 없이 항상 목적지로 전달됩니다.
앱 컴포넌트는 수행 가능한 고유한 작업마다 별도의 필터를 선언해야 함, 예시로 이미지 갤러리 앱의 한 액티비티는 두개의 필터를 가지고 있을 수 있음, 하나는 이미지를 보기 위한 필터이고, 나머지는 이미지를 수정하기 위한 필터임, 액티비티가 시작되면 인텐트의 정보에 기반해 어떻게 동작할 지 결정함(예: 편집기를 표시할지 말지)
각 인텐트 필터는 매니페스트 파일 안에서 <activity> 요소와 같은 앱 컴포넌트에 중첩되어 있는 <intent-filter> 요소에 의해 정의됨
<intent-filter> 요소를 포함하는 앱 컴포넌트들은 android:exported 의 값을 명시적으로 설정해야 함, 이 속성은 다른 앱이 앱 컴포넌트를 접근할 수 있는지를 나타냄, LAUNCHER 카테고리가 포함된 인텐트 필터를 가지고 있는 액티비티의 경우엔 이 속성을 true 로 설정하는 것이 유용하고, 아니라면 false 로 설정하는 것이 안전함
주의: 만약 앱에서 인텐트 필터를 사용하는 액티비티, 서비스, 브로드캐스트 리시버에
android:exported값을 명시적으로 설정하지 않았다면 안드로이드 12 이상인 기기에서 앱이 설치될 수 없습니다.
<intent-filter> 에는 다음과 같은 요소를 사용해 수락할 인텐트의 유형을 지정할 수 있음
<action> : name 속성에 수락할 인텐트의 동작을 선언함, 값은 클래스 상수가 아니라 반드시 작업의 문자열 리터럴 값이어야 함<data> : 데이터 URI(scheme, host, port, path)와 MIME 유형을 지정하는 하나 이상의 속성을 사용해 수락할 데이터의 유형을 선언함<category> : name 속성에 수락할 인텐트의 카테고리를 선언함, 값은 클래스 상수가 아니라 반드시 작업의 문자열 리터럴 값이어야 함노트: 암시적 인텐트를 수신하려면, 인텐트 필터에서 반드시
CATEGORY_DEFAULT카테고리를 포함해야 합니다.startActivity()와startActivityForResult()메소드는 모든 인텐트를CATEGORY_DEFAULT카테고리가 선언된 것처럼 다루기 때문입니다. 만약 인텐트 필터에 이 카테고리를 선언하지 않는다면 암시적 인텐트는 액티비티에 연결되지 않습니다.
수신할 인텐트가 ACTION_SEND 동작이고 데이터 유형이 text 인 인텐트 필터를 가지고 있는 액티비티 선언 예시
<activity android:name="ShareActivity" android:exported="false">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
둘 이상의 <action>, <data>, <category> 들을 포함하는 필터를 생성할 수 있음, 단 이 경우에는 필터 속성들을 조합한 모든 경우의 인텐트를 처리할 수 있는 컴포넌트이어야 함
여러 종류의 인텐트를 처리하지만 동작, 데이터, 카테고리 유형의 특정 조합만 처리할 수 있다면 인텐트 필터를 여러개 생성해야 함
암시적 인텐트는 필터의 세가지 요소와 각각 비교되며 검사됨, 컴포넌트에 전달되려면 인텐트는 세가지 요소 모두 통과되어야 함, 만약 하나라도 일치하지 않는다면 안드로이드 시스템은 컴포넌트로 인텐트를 전달하지 않음, 하지만 컴포넌트가 여러개의 인텐트 필터를 가지고 있을 수 있기 때문에 컴포넌트의 한 필터를 통과하지 못하더라도 다른 필터를 통과할 수 있음
주의: 인텐트 필터를 사용하는 것은 다른 앱이 컴포넌트를 실행하는 것을 예방하는 안전한 방법이 아닙니다. 비록 인텐트 필터가 컴포넌트를 오직 특정 유형의 암시적 인텐트에만 응답하게 제한할 수 있지만, 만약 개발자가 컴포넌트 이름을 알고 있다면 다른 앱이 명시적 인텐트로 컴포넌트를 시작할 수 있습니다. 만약 오직 같은 앱에서만 컴포넌트를 시작하는 것이 중요하다면, 매니페스트에 인텐트 필터를 선언하지 마십시오. 대신 그 컴포넌트의
exported속성을false로 설정하십시오.이와 비슷하게, 실수로 다른 앱의 서비스를 실행하는 것을 피하려면, 항상 서비스를 시작할 때 명시적 인텐트를 사용하십시오.
노트: 모든 액티비티들은 매니페스트 파일에 인텐트 필터를 반드시 선언해야 합니다. 하지만 브로드캐스트 리시버의 필터는
registerReceiver()를 호출해 동적으로 등록될 수 있습니다. 그러면unregisterReceiver()로 등록 해제를 할 수 있습니다. 이는 앱이 실행중일 때 오직 특정 기간에만 특정 브로드캐스트를 수신하는 것을 허용합니다.
인텐트 필터의 동작 방식을 설명하기 위한 소셜 공유 앱의 매니페스트 파일 예시
<activity android:name="MainActivity" android:exported="true">
<!-- This activity is the main entry, should appear in app launcher -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="ShareActivity" android:exported="false">
<!-- This activity handles "SEND" actions with text data -->
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
<!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.SEND_MULTIPLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="application/vnd.google.panorama360+jpg"/>
<data android:mimeType="image/*"/>
<data android:mimeType="video/*"/>
</intent-filter>
</activity>
MainActivity 는 사용자가 런처 아이콘으로 앱을 실행하면 초기에 열리는 액티비티임
ACTION_MAIN 동작은 기본 진입점이고 어떤 인텐트 데이터도 전달될 것으로 예상되지 않을 것을 나타냄CATEGORY_LAUNCHER 카테고리는 시스템 앱 런처에 이 액티비티의 아이콘을 배치해야 함을 나타냄, 만약 <activity> 요소에 icon 으로 아이콘이 지정되지 않는다면, 시스템은 <application> 요소의 아이콘을 사용함액티비티를 앱 런처에 나타내기 위해 이 두가지가 항상 같이 있어야 함
ShareActivity 는 텍스트와 미디어 컨텐츠를 공유하기 위해 설계됨, 사용자는 이 액티비티를 MainActivity 에서 탐색해 진입할 수도 있고, 또는 다른 앱에서 인텐트 필터 중 하나와 일치하는 암시적 인텐트를 보내 이 액티비티에 직접 진입할수 있음.
노트: MIME 유형
application/vnd.google.panorama360+은 파노라마 사진을 지정하는 특별한 데이터 유형이며 Google panorama API로 처리할 수 있습니다.
만약 다른 앱이 안드로이드 13(API 레벨 33) 이상을 타겟팅한다면 <intent-filter> 요소의 동작과 카테고리와 일치하는 인텐트만 처리함, 만약 시스템이 암시적 인텐트와 일치하는 컴포넌트를 찾지 못하면 ActivityNotFoundException 예외가 발생함, 전송하는 앱은 반드시 이 예외를 처리해야 함
비슷하게 앱을 안드로이드 13 이상을 타겟팅하게 업데이트할 때 다른 앱에서 export 되는 컴포넌트에 전달하는 모든 인텐트는 <intent-filter> 요소에 선언한 동작과 카테고리가 일치하는 인텐트만 전달됨, 이는 전달하는 앱의 SDK 버전과 무관하게 발생함
다음과 같은 경우엔 인텐트 일치가 강제되지 않음
system UID (uid=1000) 에서 전달되는 인텐트, 시스템 앱은 android:sharedUserId 를 android.uid.system 으로 설정하는 앱과 system_server 를 포함함pending 인텐트 사용PendingIntent 객체는 Intent 객체를 감싸는 wrapper 임, PendingIntent 의 주 목적은 다른 앱이 포함된 인텐트를 내 앱의 프로세스에서 실행하는 것처럼 사용할 수 있도록 허용하는 것임
pending 인텐트의 주요 용도는 다음과 같음
NotificatonManager 가 Intent 를 실행함)Intent 를 실행함)AlarmManager 가 Intent 를 실행함)Intent 객체가 앱 컴포넌트(Activity, Service, BroadcastReceiver)의 특정 유형에 의해 처리되게 설계된 것처럼, PendingIntent 도 같은 방식으로 생성되어야 함, pending 인텐트를 사용하면 startActivity() 와 같이 인텐트를 호출로 실행하지 않음, 대신 PendingIntent 를 생성할 때 다음과 같은 생성자 메소드를 호출해 의도된 컴포넌트 타입을 반드시 선언해야 함
PendingIntent.getActivity() : Activity 를 시작하는 IntentPendingIntent.getService() : Service 를 시작하는 IntentPendingIntent.getBroadcast() : BroadcastReceiver 를 시작하는 Intent앱이 다른 앱에서 pending 인텐트를 전달받지 않는 한, PendingIntent 를 생성하기 위해 필요한 메소드는 위의 메소드들 만으로 충분함
각 메소드는 현재 앱의 Context, 감쌀 Intent, 인텐트가 어떻게 사용되어야 하는지(예: 인텐트가 두 번 이상 사용되어야 하는지) 지정하는 하나 이상의 플래그를 인수로 받음
앱이 안드로이드 12 이상을 타겟팅한다면 앱에서 생성하는 PendingIntent 객체마다 가변성을 지정해야 함, PendingIntent.FLAG_MUTABLE 과 PendingIntent.FLAG_IMMUTABLE 플래그를 사용해 가변성을 지정할 수 있음
가변성 플래그를 설정하지 않고 PendingIntent 객체를 생성하려고 시도한다면 시스템은 IllegalArgumentException 예외를 발생시키고 다음과 같은 메시지를 Logcat 에서 확인할 수 있음
PACKAGE_NAME: Targeting S+ (version 31 and above) requires that one of \
FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if \
some functionality depends on the PendingIntent being mutable, e.g. if \
it needs to be used with inline replies or bubbles.
pending 인텐트 생성PendingIntent 객체를 생성함, PendingIntent 객체가 불변이라면 다른 앱은 인텐트의 호출 결과를 변경하기 위해 인텐트를 수정할 수 없음val pendingIntent = PendingIntent.getActivity(applicationContext,
REQUEST_CODE, intent,
/* flags */ PendingIntent.FLAG_IMMUTABLE)
다음 경우들은 가변 PendingIntent 객체를 필요로 함
PendingIntent 객체의 클립 데이터 변경을 필요로 함, 일반적으로 fillIn() 메소드에 FILL_IN_CLIP_DATA 를 플래그로 전달해 클립 데이터 변경을 요청함CarAppExtender 의 인스턴스를 사용해 안드로이드 오토 프레임워크에 알림을 연동할 때PendingIntent 의 인스턴스를 사용해 대화를 bubbles에 배치할 때, 가변 PendingIntent 객체는 FLAG_ACTIVITY_MULTIPLE_TASK 와 FLAG_ACTIVITY_NEW_DOCUMENT 와 같은 플래그를 시스템이 적용할 수 있게 허용함requestLocationUpdates() 나 비슷한 API로 기기의 위치 정보를 요청할 때, 가변 PendingIntent 객체는 시스템이 위치 생명주기 이벤트(위치 변경, 제공자 사용 가능 등)를 인텐트 엑스트라로 추가하는 것을 허용함AlarmManager 를 사용해 알람을 예약할 때, 가변 PendingIntent 객체는 시스템이 인텐트 엑스트라로 EXTRA_ALARM_COUNT 를 추가하도록 허용함, 이 엑스트라는 반복 알람이 몇 번 트리거 되었는지 횟수를 나타냄, 이 엑스트라를 포함하는 것으로 인텐트는 기기가 잠들어 있는 상황과 같이 반복 알람이 여러번 트리거되었는지를 정확히 앱에게 알릴 수 있음앱이 가변 PendingIntent 객체를 생성한다면 ComponentName 을 채우고 명시적 인텐트를 사용할 것을 강력하게 추천함, 이렇게 한다면 다른 앱이 PendingIntent 를 호출해서 앱으로 제어가 돌아왔을 때 앱은 항상 같은 컴포넌트를 실행함
pending 인텐트 안에 명시적 인텐트 사용하기pending 인텐트를 어떻게 사용할 지 더 잘 설명하기 위해 항상 명시적 인텐트를 감싸야 함, 이를 잘 따르기 위해 다음 단계를 거쳐야 함PendingIntent 를 전달받은 앱이 채워지지 않은 속성을 채우는 것을 방지하기 위해 안드로이드 6.0 (API 레벨 23)에 추가된 FLAG_IMMUTABLE을 사용할 것, 앱의 minSdkVersion 이 22 이하라면 다음 코드를 사용해 안전성과 호환성을 제공할 수 있음if (Build.VERSION.SDK_INT >= 23) {
// FLAG_IMMUTABLE을 사용해 PendingIntent 생성
} else {
// 기존 방식으로 PendingIntent 생성
}
<action> 요소를 선언함<intent-filter>
<action android:name="android.intent.action.EDIT" />
<action android:name="android.intent.action.VIEW" />
...
</intent-filter>
필터를 통과하려면 Intent 에 지정된 동작이 필터에 나열된 동작 중 하나와 반드시 일치해야 함
만약 필터에 어떤 동작도 들어있지 않다면 인텐트가 일치하는지 확인할 것이 없기 때문에 모든 인텐트가 테스트를 통과하지 못함, 만약 Intent 에 동작을 지정하지 않았다면 필터가 하나 이상의 동작을 포함하는 한 테스트를 통과함
<category> 요소를 선언함<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
...
</intent-filter>
Intent 의 모든 카테고리가 필터의 카테고리와 일치해야 함, 인텐트 필터에 카테고리가 인텐트에 지정된 카테고리보다 더 많이 선언된 반대의 경우에도 여전히 인텐트는 통과함, 인텐트에 카테고리가 없다면 필터에 어떤 카테고리가 선언되어 있어도 항상 이 테스트를 통과함노트: 안드로이드는
startActivity()와startActivityForResult()에 전달되는 모든 암시적 인텐트에CATEGORY_DEFAULT카테고리를 적용합니다. 만약 액티비티가 암시적 인텐트를 전달받게 하려면 이전의<intent-filter>예제에서 본 것처럼android.intent.category.DEFAULT카테고리를 인텐트 필터에 반드시 포함해야 합니다.
<data> 요소를 선언함<intent-filter>
<data android:mimeType="video/mpeg" android:scheme="http" ... />
<data android:mimeType="audio/mpeg" android:scheme="http" ... />
...
</intent-filter>
각 <data> 요소는 URI 구조와 데이터 유형(MIME 미디어 유형)을 지정할 수 있음, URI는 다음과 같이 scheme(스키마), host(호스트), port(포트), path(경로) 의 속성들로 이루어짐
<scheme>://<host>:<port>/<path>
content://com.example.project:200/folder/subfolder/etc위의 URI에서 content 가 스키마이고, com.example.project 가 호스트, 200 이 포트, folder/subfolder/etc 가 경로임
각각의 속성들은 <data> 요소의 선택 사항이지만, 다음과 같은 선형 종속성이 있음
인텐트 내의 URI가 필터의 URI 명세와 비교될 때 다음 예시와 같이 오직 필터에 포함된 URI의 부분만 비교됨
authority(여기서는 호스트와 포트를 얘기함)만 지정하고 경로를 지정하지 않았다면, 경로와 상관없이 같은 스키마와 authority 를 포함하는 모든 URI가 필터를 통과함authority, 경로를 지정했다면 오직 같은 스키마와 authority, 경로의 URI만 필터를 통과함노트: 경로 명세는 경로 이름과 부분적으로 일치하도록 요구하기 위해서 와일드카드 아스테리스크(
*)를 포함할 수 있습니다.
content: 또는 file: URI 일 경우에 필터가 URI를 지정하지 않았으면 테스트를 통과함, 즉 필터에 MIME 유형만 지정했다면 컴포넌트가 content: 와 file: 데이터를 지원하는 것으로 간주됨노트: URI 또는 MIME 유형을 지정한 인텐트의 경우
<intent-filter>에<data>요소가 없을 경우 데이터 테스트를 실패합니다.
content: 와 file: 스키마를 지정할 필요가 없음<data> 요소로 해당 컴포넌트가 컨텐츠 제공자로부터 이미지 데이터를 불러와 표시할 수 있다고 안드로이드에 알리는 일반적인 예시
<intent-filter>
<data android:mimeType="image/*" />
...
</intent-filter>
대부분의 이용가능한 데이터가 컨텐츠 제공자에 의해 제공되기 때문에 필터가 데이터 유형을 지정하지만 URI를 지정하지 않는 경우가 대부분임
또 다른 필터의 흔한 설정은 스키마와 데이터 타입만 지정하는 경우임, 예시로 다음과 같이 <data> 요소로 해당 컴포넌트가 네트워크에서 비디오 데이터를 가져와 작업을 한다고 안드로이드에 알리는 경우가 있음
<intent-filter>
<data android:scheme="http" android:mimeType="video/*" />
...
</intent-filter>
인텐트 매칭은 활성화할 목적지 컴포넌트를 찾는데 뿐만 아니라 기기의 특정 컴포넌트 집단을 찾을때도 쓰임, 예시로 홈 앱은 ACTION_MAIN 동작과 CATEGORY_LAUNCHER 카테고리를 지정하는 인텐트 필터를 가지고 있는 모든 액티비티를 찾아 앱 런처를 채움
앱에서 홈 앱에서 한 것과 마찬가지로 인텐트 매칭을 사용할 수 있음, PackageManager 는 특정 인텐트를 수락할 수 있는 모든 컴포넌트를 반환하는 여러 query...() 메소드들이 있고 비슷하게 인텐트에 응답할 수 있는 최적의 컴포넌트를 결정하는 resolve...() 메소드들도 있음, 예시로 queryIntentActivities() 는 인수로 전달된 인텐트를 수행할 수 있는 모든 액티비티의 리스트를 반환하고, queryIntentServices() 는 인텐트를 수행할 수 있는 모든 서비스의 리스트를 반환함, 두 메소드는 응답할 수 있는 컴포넌트를 나열할 뿐 컴포넌트를 활성화하지 않음, 브로드캐스트 리시버는 비슷하게 동작하는 queryBroadcastReceivers() 메소드가 있음
원문: https://developer.android.com/guide/components/intents-filters