Android의 기본 Application component에는 다음 네 가지가 있다.
Components | |
---|---|
Activity | 사용자 인터페이스가 있는 화면 하나를 뜻한다. |
Service | Background에서 실행되는 component이다. 오랫동안 실행되는 작업이나 원격 프로세스를 위한 작업이 주로 이루어지며 사용자 인터페이스를 제공하지 않는다. (ex. 음악 재생, 파일 다운로드) |
Content Provider | File system, Database 등의 저장소를 통합 관리한다. 권한이 허가된 경우 다른 application에서 접근 및 수정까지도 가능하다. |
Broadcast Reciever | System이나 application은 특정한 상태나 data에 대해 broadcast를 날릴 수 있는데 이를 수신하여 처리한다. |
위 기본 요소 중 Activity
, Service
, Broadcast Reciever
는 Intent
라는 비동기 메시지가 전달되어 활성화된다.
Intent
Message Object의 일종으로 intent를 사용하여 다른 component들에게 작업을 요청할 수 있다.
기본적으로는 다음 세 가지 목적으로 사용한다.
- Activity의 시작
- Service의 시작
- Broadcast의 전달
Activity의 시작
- Intent를
startActivity()
로 전달하면 새 activity가 실행된다. - Activity의 실행 결과를 돌려받고 싶다면
startActivityForResult()
를 호출한다.- 돌려받은 결과는
onActivityResult()
를 구현하여 처리할 수 있다. 결과 또한 intent로 수신한다.
- 돌려받은 결과는
Service의 시작
- Intent를
startService()
로 전달하면 일회성 작업이 수행된다. - Client-Server interface로 설계된 service라면
intent
를bindService()
로 전달하면 바인딩하여 사용할 수 있다.
Broadcast의 전달
- Intent를
sendBroadcast()
,sendOrderedBroadcast()
,sendStickyBroadcast()
중 하나에 담아서 전달한다. 모든 application
들에message를 전달
할 때 사용한다. (system에서 전달할 때는 특정 application을 지정해서 전달할 수도 있다.)
Intent의 기본 요소
ComponentName
, Action
, Data
, Category
, Extra
, Flags
가 있다.
ComponentName
Optional 항목으로 Implicit intent로 사용할 경우 반드시 이름을 명시해야 한다.
Service를 시작하는 경우에는 무조건 이 항목을 지정해야 한다. 그렇지 않으면 해당 intent에 어느 service가 응답할지 확신할 수 없고 사용자고 어떤 service가 시작되는지 알 수 없게 된다.
ComponentName으로는 application의 패키지명이 포함된 Full-Qualified class name을 사용해야 한다. (ex. com.example.Example.Activity).
Intent 생성자
를 사용하거나 setComponent()
, setClass()
, setClassName()
을 사용하여 ComponentName을 설정
할 수 있다.
Action
수행할 작업을 나타내며, 특정 application에서 커스터마이징한 action name을 사용할 수도 있지만 일반적으로는 Intent class나 다른 Framework class가 정의한 Action 상수
를 사용한다.
커스터마이징할 경우 패키지명을 Prefix(접두사)에 포함시켜야 한다.
|
Activity를 시작할 때 아래 두 가지 ACTION
을 사용할 수 있다.
- ACTION_VIEW
startActivity()
를 사용하며, 해당 Activity가 사용자에게 표시할 정보를 가지고 있을 때(ex. 갤러리) 사용한다.
- ACTION_SEND
Shared intent
라고도 하며,startActivity()
를 사용한다. 사용자가 다른 application을 통해 공유할 수 있는 data를 가지고 있을 때(ex. 이메일) 사용한다.
Intent 생성자
를 사용하거나 setAction()
을 사용하여 Action을 설정
할 수 있다.
Data
Action을 수행할 data
또는 해당 data의 MIME type
을 참조하는 URI Object
이다. 일반적으로 action명을 보면 data를 추측할 수 있는데 예를 들어 action이 ACTION_EDIT
라면 data에는 편집할 문서의 URI
가 들어있어야 한다.
Intent 생성 시 URI
와 함께 Data type(MIME type)
의 지정이 중요하다. 만약 이미지 처리용 application이 있을 때, URI가 비슷하다 할지라도 이 application에서는 오디오 처리를 할 수는 엇다.따라서 MIME type
을 지정해주는 것이 좋다.
Data URI
만 설정하려면 setData()
를 사용하고, MIME type
만 설정하려면 setType()
을 사용하면 된다.
두 가지 모두 사용할 경우 setData()
와 setType()
은 서로의 값을 덮어버리는 특성이 있으므로 반드시 setDataAndType()
을 사용해야 한다.
일반적으로 Action과 Data는 다음과 같은 짝을 갖는 경우가 많다.
Action | URI | Description |
---|---|---|
ACTION_VIEW | content://contacts/people/1 | 1번 사람에 대한 정보를 표시한다. |
ACTION_DIAL | content://contacts/people/1 | Dialer에 1번 사람의 번호를 채워서 보여준다. |
ACTION_VIEW | tel:123 | Dialer에 ‘123’을 채워서 보여준다. |
ACTION_DIAL | tel:123 | Dialer에 ‘123’을 채워서 보여준다. |
ACTION_EDIT | content://contacts/people/1 | 1번 사용자에 대한 정보를 수줭한다. |
ACTION_VIEW | content://contacts/people/ | 주소록 리스트를 띄운다. 이 리스트에서 특정 사용자를 선택할 경우 ACTION_VIEW content://contacts/N 이 시작된다. |
위의 두 가지 주 속성에 더하여, 아래에 설명할 몇 가지의 부 속성을 사용할 수 있다.
Category
Optional 항목이며 Intent를 처리해야 하는 component에 대한 추가 정보를 담고있다.
몇 가지 보편적인 Category의 예이다.
- CATEGORY_LAUNCHER
- Application 진입 시 최초의 Activity임을 의미한다.
- CATEGORY_ALTERNATIVE
- Data의 일부에 대해 사용자가 사용할 수 있는 대체 action들에 대한 목록을 포함할 것을 의미한다.
- CATEGORY_BROWSABLE
- 대상 Activity가 스스로 Web browser에게 자신을 시작할 권한을 주며, link를 통해 참조된 data를 표시하게 한다. 이미지, 이메일, 메시지 등이 해당된다.
Category 지정은 addCategory()
를 통해서 설정한다.
Type
- Intent data의 명시적인 type(MIME type)을 정의한다. 일반적인 경우 type은 data 자신으로부터 추측할 수 있다.
Component
- Intent에 사용하기 위한 component class의 명시적인 이름을 정의한다. 일반적으로 이 항목은 intent 내의 다른 정보(action, data/type, category)에 의해 정의되고 이를 다룰 수 있는 component에 매칭된다. 만약 이 속성이 세팅될 경우 아무 동작도 수행하지 않으며 이 component는 그 자체로만 사용된다. 이 속성을 정의함으로 인해 모든 다른 Intent 속성들은 optional 항목이 된다.
Extras
요청한 작업을 수행하기 위한 추가 정보를 담고 있다. Key-Value pair
로 이루어져 있다.
putExtras()
메소드를 사용하며, 모든 extra data를 갖는 Bundle
을 생성하여 직접 putExtras()
로 삽입할 수도 있다.
예를 들어, ACTION_SEND
로 이메일을 전송할 경우, “받는 사람”을 EXTRA_EMAIL
로 지정하고, “제목”을 EXTRA_SUBJECT
로 할 수 있다.
Intent class
는 표준화된 data type들에 대해 많은 EXTRA_*를 지원하고 있다. 자신의 application만의 특정 extra key를 사용해야 할 경우 Package name을 prefix(접두사)로 포함시켜야 한다.
|
Flags
Intent class
에서 정의하고 있으며, Meta-data 역할을 수행한다. Android system에 activity를 시작할 방법을 알려줄 수도 있고, activity를 시작한 후 어떻게 처리해야 하는 지도 알려줄 수 있다.
지정은 setFlags()
로 하면 된다.
Intent의 type
Intent type은 Explicit
, Implicit
두 가지가 있다.
Explicit intent
는 무엇을 할지
, Implicit intent
는 누구에게 던질지
가 중요하다.
application의 보안을 위해 Service의 시작 시에는 항상 Explicit intent만 사용하고 Intent filter는 선언하지 않도록 하라.
Explicit(명시적) intent
새 activity를 시작하거나 service를 시작할 때 사용한다. (ex. File download)
일반적으로 application 안에서 component를 시작할 때 사용한다.
시작할 component의 이름을 Fully-Qualified class name
(ex. Abc.Class)으로 지정한다.
Explicit intent
를 사용할 경우 system이 즉시 지정된 component를 시작한다.
아래 예시는 웹에서 파일을 다운로드 하도록 한 DownloadService를 시작하는 코드이다.
|
Implicit(암시적) intent
특정 component가 뭔지는 모르지만 현재 application이 수행할 수 없는 일반적인 작업을 다른 application의 component가 처리할 수 있도록 한다.
Implicit intent
를 사용하면 system이 시작시킬 적절한 component를 찾게 된다. 이 때, intent
의 내용을 다른 application들의 Manifest file
에 선언된 Intent filter
와 대조하는 작업을 거치고, 해당 기능을 수행할 수 있는 application이 여러개가 있다면, 사용자가 선택할 수 있도록 화면을 띄워준다.
Intent filter
란Manifest file
에 선언한 해당 component가 수신하고자 하는Intent type
에 대한 내용이다. 다른 application들이 여기에 선언한 내용을 기반으로 내 application의 기능을 사용할 수 있게 된다.Intent filter
에 아무 것도 선언하지 않는다면explicit intent
로만 수행할 수 있다.
아래 예시는 URI를 사용하지 않고 “text/plain” 정보를 통해 extra 정보를 지정한 후 ACTION_SEND
를 통해 implicit intent를 날리는 것이다.
|
Implicit intent 사용 시 주의할 점은 startActivity()
를 통해 날려도 처리할 application이 전혀 표시되지 않을 수 있다. 이 경우 호출 실패는 물론 application이 죽는다.
어떤 Activity에서라도 해당 intent를 확실히 수신할 수 있도록 하려면 위의 코드처럼 resolveActivity()
를 호출하여 미리 확인하자. 결과가 null이 아닌 경우 해당 intent를 처리할 수 있는 application이 최소 하나는 있다는 것을 의미한다.
Implicit intent에 응답하는 application이 하나 이상일 경우, 사용자가 수행될 application을 선택할 수 있는데 이 때 띄워주는 메뉴를 app chooser(앱 선택기)
라 한다.
Android는 사용자에게 항상 같은 application을 사용할 수 있는 옵션을 제공한다.
하지만 특정한 케이스에서, 사용자가 항상 다른 application을 사용해야 한다면 앱 선택기를 명시적으로 표시할 필요가 있다.
앱 선택기를 항상 표시하기 위해 아래와 같이 chooser intent생성 후 createChooser()
를 사용하면 된다.
|
Intent filter
내 application이 수신할 수 있는 Implicit intent
가 어떤 것이 있는지 알리려면 application Component에 대한 하나 이상의 intent filter
를 Manifest file
에 선언해야 한다.
Explicit intent
는 Component가 어떤intent filter
를 선언했든 무관하게 항상 정의해둔 곳으로 전달된다.
각 Intent filter는 다음과 같은 세 가지 요소 중 하나 이상을 사용하여 허용할 intent type을 정의할 수 있다.
<action>
- 허용된 intent 작업을 name 속성에서 선언한다. Literal string이며 Class 상수가 아니다.
<data>
- 허용된 data type을 선언한다. Data URI(scheme, host, port, path 등)와 MIME type 의 여러가지 내용 중 하나 이상의 속성을 사용한다.
<category>
- 허용된 intent category를 name 속성에서 선언한다. Literal string이며 Class 상수가 아니다.
Explicit intent를 수신하기 위해서는 Intent filter 내에 반드시
CATEGORY_DEFAULT
를 포 함시켜야 한다.startActivity()
,startActivityForResult()
메소드들은 모든 intent를CATEGORY_DEFAULT
를 선언한 것처럼 취급하기 때문에 이 Category를 intent filter에 선언하지 않으면 Activity의 어떤 암시적 intent도 확인되지 않는다.
아래 예시는 Data type
이 text
인 ACTION_SEND
intent를 수신하겠다는 뜻이다. Intent filter 내에 선언한 것들과 하나라도 맞지 않으면 intent가 application으로 전달되지 않는다.
|
Intent filter에 등록하지 않았다고 하더라도 다른 application에서 내 application Component의 경로를 알아챌 경우 내 component를 수행할 수 있게 된다. 이 경우를 방어하기 위해 Manifest의
안에 android:exported = false
를 설정하면 된다.
아래는 Android developer 사이트에서 제공하는 소셜 공유 application의 Manifest file이다.
|
Pending Intent
PendingIntent
는 Intent
의 wrapper
이다.
다른 application에 권한을 위임하여 그 안에 들어있는 intent를 마치 본인 application의 자체 프로세스에서 실행하는 것처럼 사용하는 것이다.
뭔 말인가 싶다.
현재의 application A가 PendingIntent를 만들어서 다른 application이나 컴포넌트에 “내가 너에게 이 intent를 전달할 수 있는 권한을 줄테니 이따가 나 대신 좀 보내줘.” 라고 하는 것이다.
주요 사용 사례는 다음과 같다.
- 사용자가 이 application의
notification
을 통해 task를 수행할 때 intent가 실행되도록 한다(Android system의 NotificationManager가 Intent를 실행한다.). - 사용자가 이 application의
Application Widget
으로 task를 수행할 때 intent가 실행되도록 한다(Mainscreen application에 Intent를 실행한다.). - 지정된 시간에 intent가 실행되도록 선언한다(Android system의 AlarmManager가 Intent를 실행한다.).
예를 들어, 아래 코드는 실행하면 Notification bar에 새 notification이 등록되고 사용자가 이 notification을 터치했을 때 MainActivity로 진입한다.
|
각 Intent object는 특정한 유형의 component(Activity, Service, BroadcastReceiver)가 처리하도록 설계되어 있다. 따라서 PendingIntent도 이러한 사항을 염두에 두고 생성해야 한다.
PendingIntent를 사용하는 경우 직접적으로 startActivity() 등의 호출을 사용하지 않으므로 적절하게 세팅해줘야 한다.
- Activity 시작 Intent:
PendingIntent.getActivitiy()
- Service 시작 Intent:
PendingIntent.getService()
- BroadcastReceiver 시작 Intent:
PendingIntent.getBroadcast()
Intent 살펴보기
system이 Activity를 시작하라는 implicit intent를 수신하면 system은 해당 intent에 대한 최선의 activity를 검색한다. 이 때 판단 근거가 아래 세 가지이다.
- Intent Action
- Intent Data (URI와 data type)
- Intent Category
Action test
이 Filter를 통과하려면 아래 나열된 작업 중 하나와 일치해야 한다.
|
Category test
Intent 내의 모든 Category가 filter 내의 category와 일치해야 한다. Filter 내의 category 수가 더 많은 것은 상관 없다.
Intent의 category가 아무 것도 선언되어 있지 않으면 모두 통과할 수 있다.
|
Android는
CATEGORY_DEFAULT
category를startActivity()
및startActivityForResult()
에 전달된 모든 implicit intent에 적용한다. 따라서 Activity가 implicit intent를 수신하기 위해서는 intent filter 내에 “android.intent.category.DEFAULT” category 선언이 반드시 포함되어 있어야 한다.
Data test
URI 구조 및 Data type(MIME type)에 대해 나타내는데, URI의 각 부분에 대해서는 별도의 속성 (scheme, host, port, path)이 사용될 수 있다.
|
- content://com.example.project:200/folder/subfolder/etc
- scheme: content
- host: com.example.project
- port: 200
- path: folder/subfolder/etc
위의 네 가지 속성에는 linear한 종속 관계가 존재한다.
- scheme이 지정되지 않으면 host를 무시한다.
- host가 지정되지 않으면 port를 무시한다.
- scheme과 host 둘 다 지정되지 않으면 path를 무시한다.
Intent의 URI이 Filter의 URI와 비교될 때에는 filter 내에 포함된 URI와 부분적으로 비교된다.
- filter가 scheme만 지정한 경우, 해당 scheme을 가지는 모든 URI는 filter와 매칭된다.
- filter가 scheme과 authority를 지정하고 path를 지정하지 않는 경우, 같은 scheme과 authority를 갖는 모든 URI는 path와 관계 없이 filter를 통과한다.
filter가 scheme, authority, path를 모두 지정할 경우, 같은 scheme, authority, path를 가진 URI만이 filter를 통과할 수 있다.
path에는 * 을 사용할 수 있다.
Data의 유효성을 판별하기 위해서는 intent의 URI, MIME type과 filter의 URI, MIME type을 모두 비교해야 한다.
a. URI와 MIME type을 모두 갖지 않는 intent는 URI와 MIME type을 아무 것도 정의하지 않은 filter만을 통과할 수 있다.
b. URI를 갖고 MIME type을 갖지 않는 intent는 filter의 URI와 일치하고 filter가 MIME type을 지정하지 않은 경우 통과할 수 있다.
c. URI가 없고 MIME type만 갖는 intent는 filter가 URI를 지정하지 않고 MIME type을 가지고 있을 때 통과할 수 있다.
d. URI와 MIME type을 모두 갖는 intent는 filter에 나열된 type과 매치되는 경우에만 통과한다. Intent의 URI가 filter의 URI와 일치하거나 content:
또는 file:
을 가지고 있는 경우, 그리고 filter가 URI를 정의하지 않는 경우 통과할 수 있다. 다른 말로, filter가 MIME type만 가지고 있을 경우 component는 content:
와 file:
을 당연히 지원하는 것으로 여겨진다.
규칙 d는 component가 file 또는 content provider로부터 local file을 가지고 올 수 있다는 기대를 가지고 반영된다. 따라서, 이러한 filter는 data type만 나열해도 되고 content:
와 file:
scheme을 명시적으로 작성하지 않아도 된다.
아래 예시는 Content provider로부터 image data를 가지고 와서 표시할 수 있다는 의미를 갖는다.
|
아래 예시는 Network에서 Video data를 검색할 수 있다는 의미를 갖는다.
|
Intent matching
Intent를 Intent filter와 비교를 해보면 target component를 활성화 시킬 수 있을 뿐만 아니라 단말의 component set에 대한 정보를 발견할 수 있다. 예를 들어, Home application이 application 런쳐를 채우기 위해 ACTION_MAIN action과 CATEGORY_LAUNCH category를 갖는 intent filter들을 찾아볼 수 있다.
개발한 application에서 비슷한 방법을 사용할 수 있는데, Packagemanager는 query...()
메소드들을 가지고 있고 이는 특정 intent로 접근할 수 있는 모든 component를 return한다. 이와 비슷한 것들로 resolve...()
메소드들이 있다. 이는 intent를 응답하기 위한 최적의 component를 정의한다.
예를 들어, queryIntentActivities()
는 intent가 argument로써 통과할 수 있는 모든 activity의 list를 return하고, queryIntentServices()
는 service에 대한 list를 return한다. 두 가지 메소드 모두 component를 활성화 시키지는 않고 단지 list만 나열할 뿐이다. BroadcastReceiver에서 사용하는 것은 queryBroadcastReceivers()
가 있다.
출처
- https://developer.android.com/guide/components/fundamentals.html
- https://developer.android.com/reference/android/content/Intent.html
- https://developer.android.com/guide/components/intents-filters.html
- https://developer.android.com/guide/components/tasks-and-back-stack.html
- https://developer.android.com/reference/android/app/PendingIntent.html