API GUIDE/2. App Components
2014. 2. 20. 23:40

 

이번 글을 인텐트와 인텐트 필터(Intents and Intent Filter)입니다.

글을 읽으시기 전에 간략히 설명을 드리려고 합니다.


앞서 올린 글에서도 나온 적이 있지만 안드로이드는 이미 다른 프로그램에서 구현되어 있는 기능을 내 프로그램에서 힘들게 구현할 필요없이 내 것인양 가져다 쓸 수 있습니다.


하지만 가져다 쓰려면 다른 프로그램에게 '내가 너의 기능을 가져다 쓰려고 한다. 내가 그 기능을 쓰려는 목적(intent)은 이러이러하다.'라고 알려주어야 하죠. 이 때 사용하는 것이 인텐트(Intent; 계획, 의지, 목적) 되겠습니다.


그와는 반대로 다른 프로그램 입장에서는 '내 기능은 이러이러한 목적으로만 가져다 쓸 수 있어.'라고 선을 그어 다른 프로그램에서 들어오는 요구들을 걸러내야 하겠죠. 이 때 사용하는 것이 인텐트 필터(Intent Filter)입니다.


물론, 인텐트의 기능이 다른 프로그램의 일부를 가져다 쓰는 데에만 있는 것은 아닙니다만 가장 주요한 기능임에는 틀림없습니다.


그럼 공부 열심히 하시고 언제나 그렇듯 오역, 오타, 깨진 링크가 있으면 제보 부탁드립니다.


 

저작권 표시 : 

 

Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.

 

현재 보시는 페이지는 안드로이드 오픈 소스 프로젝트에 의해 작성되고 공유된 작업물의 수정/번역본이며 크리에이티브 커먼즈 저작자 표시 2.5 라이센스에 기술된 조건의 사용 근거를 따른 것입니다.

 

본 문서의 원본은 http://developer.android.com/guide/components/intents-filters.html이며 안드로이드 4.4 Kitkat 기준으로 설명되었습니다.

 

인텐트와 인텐트 필터(Intents and Intent Filters)


Intent는 다른 앱 컴포넌트에 어떤 행동을 요구할 때 사용하는 메시징 오브젝트다. 컴포넌트간에 커뮤니케이션을 할 때 인텐트를 사용하는 데에는 여러 방법이 있지만 기초적인 사용법은 다음의 3가지 경우다.


  • 액티비티(Activity)를 실행시키기 위해
    Activity는 앱의 한 화면을 의미한다. startActivity()를 호출할 때 Intent를 전달함으로써 Activity의 새로운 인스턴스를 만들어 실행시킬 수 있다. Intent는 액티비티를 실행시킬 수 있으며 그에 필요한 데이터를 담고 있다.
    만일, 시작시킨 액티비티가 종료될 때 어떤 결과를 돌려받으려면 startActivityForResult() 메서드를 호출한다. 그러면 여러분의 액티비티의 onActivityResult() 콜백함수에 또다른 Intent 오브젝트가 결과로써 돌아온다. 자세한 내용은 Activities 가이드를 참조하기 바란다.

  • 서비스를 실행시키기 위해
    Service는 UI없이 백그라운드에서 동작하는 컴포넌트다. 서비스는 일회성 동작(파일을 다운로드하는 것 같은)을 위해 사용되며 startService()를 호출할 때 Intent를 전달함으로써 Service를 시작시킬 수 있다. Intent는 서비스를 실행시킬 수 있으며 그에 필요한 데이터를 담고 있다.
    만일 서비스가 클라이언트-서버 인터페이스로 디자인되었다면 bindService()Intent와 함께 전달함으로써 다른 컴포넌트의 서비스에 바인딩할 수 있다. 자세한 내용은 Services 가이드를 참조하기 바란다.

  • 브로드캐스트를 전달하기 위해
    브로드캐스트는 어떤 앱이라도 받을 수 있는 메시지다. 시스템은 부팅이 완료되었다거나 기기의 충전이 시작되었다거나 하는 것과 같은 다양한 시스템 이벤트를 브로드캐스팅한다. 여러분도 다른 앱들에게 sendBroadcast(), sendOrderedBroadcast(), sendStickyBroadcast()와 같은 메서드를 통해 브로드캐스트 할 수 있으며 그 브로드캐스트의 내용은 함수를 호출할 때 Intent를 통해 전달하게 된다.


인텐트의 종류(Intent Types)



인텐트는 다음과 같이 두 종류로 나뉜다.

  • 명시적 인텐트는 이름(클래스 전체이름)으로 컴포넌트를 지정하여 실행시킬 수 있다. 보통 여러분이 작성하는 앱에 있는 컴포넌트는 이 방법을 사용해서 실행시킨다. 여러분이 시작시키려고 하는 액티비티나 서비스의 클래스 이름을 알고 있기 때문이다. 예를 들어, 사용자의 행동에 따라 새로운 액티비티를 띄우거나 백그라운드에서 파일을 다운로드 받는 등의 작업이 이에 해당한다.

  • 암시적 인텐트는 특정 컴포넌트의 이름을 지정하지는 않지만 수행하고자 하는 일반적인 행동을 선언한다. 그러면 그 행동을 수행할 수 있는 다른 앱의 컴포넌트가 실행되어진다. 예를 들어, 맵상에 사용자의 위치를 표시하려고 할 때 지도위에 위치표시가 가능한 앱들에게 암시적 인텐트를 사용하여 요청할 수 있다.


액티비티나 서비스를 시작하기 위해 명시적 인텐트를 생성하면 시스템은 Intent 오브젝트에 지정된 앱 컴포넌트를 즉시 시작시킨다.


 

 그림 1. 시스템이 다른 액티비티를 실행시키기 위해서 암시적 인텐트를 전달하는 방법

[1] 액티비티 "A"는 특정 액션을 정의한 Intent를 만들어서 startActivity() 메서드에 전달한다. [2] 안드로이드 시스템은 인텐트와 매칭되는 인텐트 필터를 가진 앱을 찾는다. 매칭되는 앱을 찾으면, [3] 시스템은 매칭된 액티비티(액티비티 "B")의 onCreate()Intent를 넘겨 액티비티 "B"를 실행시킨다.


암시적인 인텐트를 생성하게 되면 안드로이드 시스템은 기기에 설치된 앱들이 manifest 파일에 선언한 인텐트 필터에 따라 인텐트의 내용과 부합되는 앱 컴포넌트를 찾는다. 조건과 일치하는 앱 컴포넌트를 찾았다면 실행을 시키고 Intent 오브젝트를 전달한다. 만일 여러 개의 앱 컴포넌트를 찾았다면 시스템은 대화상자를 띄워 사용자가 실행된 앱을 선택하도록 한다.


인텐트 필터는 앱의 컴포넌트가 특정 인텐트를 받아서 처리할 능력이 있다고 선언하는 것이다. 다시 말하면 여러분의 앱이 어떤 인텐트의 내용을 처리할 수 있는 능력이 있고 그 인텐트에 대한 필터를 선언했다면 다른 앱이 해당 인텐트를 가지고 여러분의 컴포넌트를 직접 실행시킬 수도 있다는 말이 된다. 같은 말이지만 액티비티에 대해 어떤 인텐트 필터도 선언하지 않았다면 그 액티비티를 실행시킬 수 있는 방법은 명시적 인텐트를 사용하는 것 뿐이다.


Caution : 여러분이 작성한 앱의 안전성을 유지하고자 한다면 서비스에 대해서는 인텐트 필터를 사용하지 말고 항상 명시적 인텐트만으로 Service가 시작될 수 있도록 하는 게 좋다. 서비스를 암시적 인텐트로 시작시키는 것은 매우 위험하다. 왜냐하면 그 인텐트에 대해 어떤 서비스가 응답할 지 확실하지도 않을 뿐더러 사용자도 서비스가 시작되는 것을 볼 수 없기 때문이다.


인텐트 만들기(Building an Intent)



Intent 오브젝트는 어떤 컴포넌트가 시작될 지에 대한 정보(정확한 컴포넌트의 이름이나 인텐트의 요청을 처리할 수 있는 컴포넌트의 범주)와 인텐트를 수신한 컴포넌트가 적절한 업무를 수행하기 위한 정보를 포함하고 있다.


Intent에 포함되어 있는 주요 정보는 다음과 같다.


컴포넌트의 이름(Component name)

실행시킬 컴포넌트의 이름이다.


이 항목은 옵션사항이다. 하지만 인텐트를 명시적으로 만드는 아주 중요한 정보다. 명시적이라는 것은 해당 인텐트에 명시된 컴포넌트 명을 갖는 컴포넌트에만 인텐트가 전달 될 수 있다는 것을 의미한다. 컴포넌트 이름이 명시되지 않으면 암시적인 인텐트가 되며 시스템은 인텐트에 있는 부차적인 정보(이후에 설명될 액션, 데이터, 카테고리 같은 정보들)에 기반하여 인텐트를 수신할 컴포넌트를 결정하게 된다. 그렇기 때문에 여러분의 앱 내에 있는 컴포넌트에 인텐트를 전달하려고 한다면 컴포넌트 이름을 지정해 주어야 한다.


Note : Service를 시작시킬 때는 항상 컴포넌트 이름을 정해야 한다. 이름을 정해주지 않으면 여러분이 보낸 인텐트에 대해 어떤 서비스가 응답했는지 알 수가 없고 사용자도 서비스가 시작되는 것을 볼 수가 없다.


Intent의 이 필드는 ComponentName 오브젝트로써 앱의 패키지명을 포함하는 전체 클래스명을 사용하여 정해줄 수 있다. com.example.ExampleActivity 처럼 말이다. 컴포넌트의 이름은 setComponent(), setClass(), setClassName() 메서드 중의 하나를 호출하여 설정하거나 Intent의 생성자를 통해 설정할 수 있다.


액션(Action)

액션은 'view' 또는 'pick'과 같이 수행해야 하는 일반적인 행동에 대한 문자열이다.


브로드캐스트에 사용되는 인텐트는 앞으로 수행할 액션이 아니다. 이미 발생해서 보고되어야 할 이벤트다. 액션은 인텐트의 나머지 부분 - 데이터와 부가정보 - 에 어떤 정보가 들어있을 지 정하게 된다.


여러분이 만든 앱 내에서 또는 여러분이 만든 앱에서 다른 앱으로 보내는 인텐트에 여러분이 만든 액션을 사용할 수도 있다. 하지만 일반적으로 사용할 때는 Intent 클래스와 다른 프레임워크 클래스에 정의된 액션 상수(각 종류의 액션은 사실상 상수로 정의되어 있다. 아래에 기술된 'ACTION_VIEW'나 'ACTION_SEND'도 특정 상수를 표현하고 있는 상수 이름에 불과하다.)를 사용하는 것이 좋다. 아래는 액티비티를 시작시키는 몇몇 일반적인 액션들이다.


ACTION_VIEWE

사용자에게 갤러리앱에서 사진을 보여주거나 지도앱에서 특정 주소의 위치를 보여주는 것과 같이 뭔가를 보여줘야 할 때 이 액션을 가진 인텐트를 사용하여 startActivity()를 호출한다.


ACTION_SEND

'공유하기' 인텐트로도 알려져 있으며 이메일 앱이나 소셜 공유앱과 같은 앱을 통해 사용자가 어떤 정보를 공유하려고 할 때 인텐트에 이 액션을 담아 startActivity()를 호출한다.


일반적인 액션에 대해 정의된 더 많은 상수를 알고 싶다면 Intent 클래스의 도움말을 참고하기 바란다. 환경 설정앱의 특정 화면을 불러오는 액션이 Settings에 정의되어 있는 것처럼 또다른 액션들이 안드로이드 프레임워크 내의 어딘가에 정의되어 있다.


인텐트에 액션을 지정하려면 setAction() 함수를 호출하거나 Intent의 생성자를 이용할 수 있다.


사용자 정의 액션을 정의하고 싶다면 아래 예제처럼 여러분이 작성한 패키지의 이름을 접두사로 사용하는 것이 좋다.


1
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";


데이터(Data)

액션을 수행하기 위해 필요한 URI(Uri 오브젝트) 그리고/또는 데이터의 MIME 타입. 제공되는 데이터의 타입은 일반적으로 인텐트의 액션에 좌우된다. 예를 들어, 액션이 ACTION_EDIT라면 데이터는 수정하려고 하는 문서의 URI를 포함해야 한다.


인텐트를 생성할 때 데이터의 URI뿐만 아니라 URI의 종류(MIME 타입)까지 지정하는 것이 중요하게 여겨질 때가 자주 있다. 예를 들어, 이미지를 보여주는 액티비티는 URI의 포맷이 비슷하다고 하더라도 오디오 파일을 플레이하지는 못할 것이다. 그렇기 때문에 시스템이 인텐트에 가장 적합한 컴포넌트를 찾기 위해서는 MIME 타입이 지정되어 있어야 한다. 그러나 때로는 URI를 통해 MIME 타입을 추측할 수도 있다. 데이터가 기기내에 존재하며 MIME 타입을 알 수 있게 해주는 ContentProvider에 의해 제어되는 URI라면 특히나 쉽게 추측이 가능하다.


데이터 URI만 설정하려면 setData()를 호출한다. MIME 타입만 설정하려면 setType()을 호출한다. 필요하다면 setDataAndType()을 사용해서 데이터 URI와 MIME 타입을 한꺼번에 설정할 수도 있다.


Caution : URI와 MIME 타입을 둘 모두 설정하려면 setData()setType()을 호출하면 안된다. 왜냐하면 이 두개의 함수들은 다른 하나의 값을 null로 만들어버리기 때문이다. 둘 모두를 설정하려면 반드시 setDataAndType() 함수를 사용한다.


카테고리(Category)

인텐트를 다루게 될 컴포넌트의 종류에 대한 추가 정보를 담고 있는 문자열로써 몇 개든 설정할 수 있다. 그러나 대부분의 인텐트는 카테고리 값을 필요로 하지 않는다. 다음은 몇몇 일반적인 카테고리들에 대한 설명이다.


CATEGORY_BROWSABLE

타겟 액티비티는 이미지나 이메일 메시지 같이 링크로써 참조되는 데이터를 디스플레이하기 위하여 웹 브라우저로써 시작되어야 한다.


CATEGORY_LAUNCHER

태스크 초기화 액티비티여야 하며 시스템 어플리케이션 런쳐의 목록 중 하나여야 한다.


카테고리 전체 목록은 Intent 클래스에 대한 도움말을 참고하기 바란다.


addCategory() 메서드를 사용하여 카테고리를 설정할 수 있다.


앞서 설명한 컴포넌트 이름, 액션, 데이터, 카테고리는 인텐트의 특성을 정하게 된다. 안드로이드 시스템은 이러한 속성들을 읽어들여 시작되어야 할 앱 컴포넌트를 결정할 수 있다.


그러나 인텐트에는 어떤 앱 컴포넌트가 시작될 지 결정하는데는 아무런 영향을 끼지지 않는 부가정보를 담을 수가 있다. 다음은 인텐트에 담을 수 있는 부가정보(Extras)에 대한 설명이다.


부가정보(Extras)

키(key)와 값(value)의 조합으로 구성되며 요청된 액션을 수행하기 위해 필요한 추가 정보다. 어떤 액션들은 특별한 URI가 필요하기 때문에 부가정보(Extras)를 사용한다.


엑스트라 데이터는 다양한 putExtra() 메서드(시리즈로 준비되어 있다.)를 통해 추가할 수 있다. 이 함수들은 모두 키(key)와 값(value)을 파라메터로 넘겨줘야 한다. 모든 부가 정보를 Bundle 오브젝트로 만들 수 있으며  putExtras() 메서드를 통해 Intent에 집어넣을 수 있다.


예를 들어, ACTION_SEND라는 액션으로 이메일을 보내기 위해 인텐트를 만든다면 EXTRA_EMAIL키에 "수신인"을 지정할 수 있으며 EXTRA_SUBJECT키에 "제목"을 지정할 수 있다.


Intent 클래스에는 "EXTRA_*"로 시작하는 많은 표준 데이터 타입의 상수들이 준비되어 있다. 만일, 여러분만의 부가정보 키를 만들고 싶다면 아래 예제처럼 앱의 패키지명을 접두사로 포함시키는 것을 잊지 않도록 한다.(반드시 이렇게 해야하는지는 모르겠지만 이미 존재하는 다른 변수와의 충돌을 피하기 위해서가 아닌가 싶다.)


1
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";


플래그(Flags)

플래그는 Intent 클래스에 속하며 인텐트의 메타 데이터로써의 기능을 한다. 이것은 안드로이드 시스템이 액티비티를 어떻게 시작할 지(액티비티가 어떤 task에 속해야할 지 같은) 그리고 시작된 후에 어떻게 처리되어야 할 지(최근 사용 액티비티의 목록에 포함시킬 지 여부와 같은)를 지시하기도 한다.


더 많은 정보는 setFlags()를 참조하기 바란다.


명시적 인텐트의 사용 예제(Example explicit intent)

명시적 인텐트는 여러분의 앱에 있는 액티비티나 서비스와 같이 특정 앱 컴포넌트가 실행되도록 하는 데 사용된다. 명시적 인텐트를 생성하려면 Intent 오브젝트에 컴포넌트 이름을 정해준다. 그 외 인텐트의 다른 모든 속성은 선택사항이다.


예를 들어보자. 웹상에서 파일을 다운로드하도록 만들어진 DownloadService라는 서비스가 여러분든이 만드는 앱내에 구현되어 있다면 아래 코드와 같이 이 서비스를 실행시킬 수 있다.


1
2
3
4
5
// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);

Intent(Context, Class) 생성자에 앱의 Context와 컴포넌트의 Class 오브젝트를 넘겨준다. 그러면 명시적인 인텐트가 생성되어 DownloadService 클래스를 실행시킨다.


서비스를 만들고 시작시키는 방법을 더 알고 싶다면 Services 가이드를 참고하기 바란다.


암시적 인텐트의 사용 예제(Example implicit intent)

암시적 인텐트는 기기에 설치된 앱 중에서 특정 액션을 수행할 수 있는 앱들에게 일을 대신 수행하도록 부탁(지시)하기 위하여 액션 정보를 지정한다. 암시적 인텐트를 사용해서 여러분의 앱은 해당 액션을 수행할 수 없지만 다른 앱은 수행이 가능할 것 같을 때 아주 유용하다.


예를 들어, 사용자가 다른 사람에게 공유하고자 하는 컨텐츠를 갖고 있을 때 액션은 ACTION_SEND를 부가정보에는 컨텐츠를 담아 인텐트를 생성한다. 그리고 그 인텐트로 startActivity()를 호출하면 그 컨텐츠를 공유할 수 있는 앱들 중 하나를 사용자가 선택해서 실행시킬 수 있다.


Caution : 암시적 인텐트에 설정된 액션에 대응할 만한 앱이 없을 수도 있다. 이런 상황에서 startActivity()를 호출하게 되면 앱이 비정상적으로 종료된다. 해당 인텐트를 받아 처리할 수 있는 앱이 있는지 확인하려면 IntentresolveActivity()에 넘겨 결과를 확인한다. 만일 결과가 null이 아니고 적어도 하나 이상의 앱이 검색되었다면 startActivity()를 안전하게 호출할 수 있다. 하지만 결과가 null이면 그 인텐트는 사용하면 안된다. 가능하다면 해당 인텐트로 문제가 발생하는 기능은 동작하지 않도록 미리 손을 써두는 것이 좋다.


1
2
3
4
5
6
7
8
9
10
// Create the text message with a string
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType(HTTP.PLAIN_TEXT_TYPE); // "text/plain" MIME type

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

Note : 이 경우 URI는 사용되지 않았지만 데이터 타입은 부가정보에 있는 컨텐츠의 데이터 타입으로 선언되었다.


startActivity()가 호출되면 시스템은 설치된 모든 앱 중에서 해당 인텐트를 처리할 수 있는 앱은 어떤 것이 있는지 결정을 한다.(인텐트는 ACTION_SEND 액션을 가지고 있으며 'text/plain'데이터를 가지고 있다.) 만일, 이 인텐트를 다룰 수 있는 앱이 하나 뿐이라면 그 앱은 바로 실행이 되고 인텐트를 받게 된다. 반면, 여러 개의 앱이 검색되면 시스템은 해당 앱들의 목록을 대화상자에 보여주고 사용자가 그 중에 사용한 앱을 선택하도록 한다.


앱 선택기 적용하기

암시적 앱에 대응하는 앱이 하나 이상이면 사용자는 그 중 하나의 앱을 선택하여 기본 앱으로 만들 수도 있다. 이것은 앞으로 동일한 액션에 대해 같은 앱이 실행되도록 하는 아주 유용한 방법이다. 예를 들어 웹페이지를 보여주는 앱은 여러 개가 있지만 사용자는 종종 웹페이지를 볼 때 하나의 앱만을 사용하는 경향이 있다. 이런 경우에 기본앱으로 설정하면 매번 사용자가 실행될 앱을 선택하지 않고 기본앱이 바로 실행되도록 할 수 있다.


 

 그림 2. 앱 선택기 대화상자


그러나 인텐트에 대응하는 앱이 여러 개 있고 사용자가 매번 다른 앱을 사용하고 싶어한다면 선택기 대화상자를 확실하게 보여줘야 한다. 앱 선택기 대화상자는 사용자에게 매번 액션을 실행할 앱을 선택하도록 한다. (이 때 사용자는 해당 액션에 대해 기본 앱을 지정할 수 없다.) 예를 들어, ACTION_SEND 액션으로 무언가를 공유한다고 할 때 사용자는 매번 상황에 따라 사용하고자 하는 앱이 달라질 것이다. 이럴 때는 (그림 2)처럼 매번 앱 선택기 대화상자를 보여줘야 한다.


앱 선택기 대화상자를 표시하려면 createChooser()를 사용하여 Intent를 생성하고 생성된 인텐트를 startActivity()로 넘겨준다. 다음은 그 예제코드다.


1
2
3
4
5
6
7
8
9
10
11
12
13
Intent intent = new Intent(Intent.ACTION_SEND);
...

// Always use string resources for UI text.
// This says something like "Share this photo with"
String title = getResources().getString(R.string.chooser_title);
// Create intent to show chooser
Intent chooser = Intent.createChooser(intent, title);

// Verify the intent will resolve to at least one activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(sendIntent);
}

이 예제는 createChooser() 메서드에 전달된 인텐트에 대응할 수 있는 앱의 리스트를 보여주며 대화상자의 타이틀도 주어진 텍스트로 표시된다.


'인텐트와 인텐트 필터'는 내용이 좀 기네요.

웬만하면 하나의 포스트에 다 담아보려 했는데 스압때문에 Part.1 과 Part.2로 나누어 게재합니다.


다음 글 >> 인텐트와 인텐트 필터(Intents and Intent Filters) (2)

이전 글 >> 시스템 퍼미션(System Permissions)





posted by 리치크루