본문 바로가기

Android/현업에 투입되기위한 STUDY

App Components > Intents and Intent Filters

안드로이드 프로그래밍을 하려면 쓰레드 단위의, 프로세스 단위의 일들을 3차원으로 생각할 수 있어야 한다.


안드로이드의 (시스템 측면의) 가장 기본인 App Components를 읽고나면 프로그래밍의 측면이 달라질 것이다.

내부 시스템을 모르고 프레임워크가 편하게 가이드하는 대로만 프로그래밍을 하면 얻는게 없다.



Intro

프로그래머가 어떻게 컴포넌트들을 빌드할 수 있는지 설명하고 있는 부분이다.

예를들어 인텐트를 사용해서 어떻게 연결시키는지와 같은 것들을.






Intents and Intent Filters


Intent(이하 인텐트)는 다른 앱 컴포넌트로부터 액션을 요청하기위해 사용할 수 있는 메세지 객체이다.

비록 인텐트가 몇가지 방법으로 컴포넌트 간에 통신을 용이하게 해준다.

그 세 가지 기본적인 사례이다.


  • 액티비티를 시작하기 위해
    startActivity()에 인텐트를 전달해서 새로운 액티비티 객체를 시작할 수 있다.
    만약 그 액티비티를 마칠 때 결과를 받고싶으면, startActivityForResult()를 호출하면 된다.
    그러면 onActivityResult() 콜백으로 결과를 받을 수 있다.
  • 서비스를 시작하기 위해
    서비스는 UI 없이 백그라운드에서 수행하는 컴포넌트이다.
    startService()에 인텐트를 전달해서 단 한 번의 수행을 하는 서비스를 시작할 수 있다. (ex 파일 다운로드)
    클라이언트-서버 인터페이스로 설계된 서비스라면, bindService()에 인텐트를 전달해서 다른 컴포넌트로부터 서비스에 bind할 수 있다.
  • 브로드캐스트를 전달하기 위해
    브로드캐스트는 어떤 앱이라도 수신할 수 있는 메시지이다.
    시스템은 시스템 이벤트를 위해 다양한 브로드캐스트를 전달한다.
    sendBroadcast() / sendOrderedBroadcast() / sendStickyBroadcast() 에 인텐트를 전달해서 다른 앱에 브로드캐스트를 전달할 수 있다.



Intent Types


다음은 인텐트의 두 종류이다.


  • 명시적 인텐트
    이름으로 실행하는 컴포넌트를 지정한다.
    당신은 실행하고자하는 액티비티나 서비스의 클래스 이름을 알고 있습니다.
  • 암시적 인텐트
    특정 컴포넌트의 이름을 정하지 않는다. 대신에 수행하기위한 일반적인 액션을 선언한다.

 
- 액티비티나 서비스를 시작하고자 명시적 인텐트를 생성할 때, 시스템은 그 즉시 인텐트 객체에 명시된 앱 컴포넌트를 시작한다.
- 암시적 인텐트를 생성할 때, 안드로이드 시스템은 장치에 있는 다른 앱들의 매니페스트에 선언된 인텐트 필터에
인텐트의 컨텐츠를 비교하여 시작하는 적당한 컴포넌트를 찾는다.
- 인텐트가 인텐트 필터와 일치하면 시스템은 컴포넌트를 시작하고, 컴포넌트에게 인텐트 객체를 전달한다.
- 멀티 인테트 필터들이 호환될경우에 시스템은 다이얼로그를 띄워 유저가 어떤 앱을 사용할 것인지 선택하게 한다.

- 인텐트 필터는 컴포넌트가 수신하고자하는 인텐트 타입을 명시한 앱의 매니페스트 안에서의 표현법이다.
예를들어 액티비티에 대한 인텐트 필터를 선언함으로써, 다른 앱이 직접 나의 액티비티를 인텐트의 특정 종류로 시작할 수 있다.
마찬가지로 어떤 인텐트 필터도 선언하지 않으면, 명시적 인텐트로만 시작될 수 있다.






암시적 인텐트가 다른 액티비티를 시작하기위해서는 시스템을 통해 어떻게 전달되어지는지 나타내었다.

[1] 액티비티 A가 액션 코드와 함께 인텐트를 생성한다.
                    startActivity( )에 인텐트를 넘긴다.
[2] 안드로이드 시스템이 인텐트와 일치하는 인텐트 필터를 위해 모든 앱을 탐색한다.
[3] 안드로이드 시스템이 일치하는 액티비티(B)를 onCreate( )로 깨우며, 인텐트를 넘기며 시작한다.



주의사항:

앱이 안전하다는 것을 보장하기위해서 서비스를 실행할 때는 항상 명시적 인텐트를 사용하자. 그리고 서비스에 인텐트 필터를 선언하지 말자.

서비스를 실행하기위해 암시적 인텐트를 사용하는 것은 보안 위험이 있다. 서비스가 인텐트에게 응답할 것이라는 보장이 없기 때문이다.

또 유저가 어떤 서비스가 실행된건지 알 수 없기 때문이다.

API 레벨 21(5.0) 부터는 암시적 인텐트로 bindService( )를 호출하면 예외가 발생하게끔 되어있다.




Building an Intent


인텐트 객체는 안드로이드 시스템이 사용하는 정보를 옮긴다.

인텐트에 포함된 주요 정보는 다음과 같다.


  • Component name
선택적 사항이지만, 인텐트를 명시적으로 만드는 주요한 정보의 부분이다.
즉, 인텐트는 컴포넌트 이름으로 정의되어 오직 앱 컴포넌트로 전달되어져야만 한다.
컴포넌트 이름 없이 인텐트는 암시적이고, 시스템은 어떤 컴포넌트가 (다른 인텐트 정보를 기반으로 한) 인텐트를 받아야 하는지 결정한다.
그래서 앱에 명시된 컴포넌트를 실행해야 한다면, 컴포넌트 이름을 명시해야만 한다.
:: 서비스를 실행할 때, 항상 컴포넌트 이름을 명시할 것.
:: 그렇지 않으면 서비스가 인텐트에 응답할 것이라는 확신을 할 수 없다.
:: 또한 유저는 어떤 서비스가 실행되었는지 알 수 없다.
이 인텐트 필드는 ComponentName 객체이다.
앱의 패키지 이름을 포함한 타겟 컴포넌트의 정규화된 클래스 이름을 이용하여 지정할 수 있는 ComponentName 객체이다.
예를 들어 com.example.MainActivity 가 있으면,
컴포넌트 이름을 setComponent(), setClass(), setClassName()이나 인텐트 생성자로 정할 수 있다.

  • Action
수행하려는 일반적인 액션을 지정하는 문자열이다.
방송수신자 인텐트의 경우에 발생하고 보고되는 액션이다.
액션은 주로 인텐트의 나머지가 어떻게 구조화되는지 결정한다.
앱 안에있는 인텐트를 사용함으로써 나만의 액션을 지정할 수 있지만, 통상 인텐트 클래스가 정의된 액션 상수를 사용해야만 한다.

다음은 액티비티를 실행하기위한 몇가지 공통 액션이다.
ACTION_VIEW
액티비티를 유저에게 보여줄 수 있는 정보를 가질 때, startActivity( )로 인텐트에서 이 액션을 사용해라.
사진첩 앱에서 사진을 보여준다던지, 지도 앱에서 주소를 보여준다던지.
ACTION_SEND
유저가 다른 앱을 통해 공유할 수 있는 데이타를 가질 때, startActivity( )로 인텐트에서 이 액션을 사용해라.
이메일 앱이나 페이스북 앱.

인텐트 클래스 레퍼런스에서 더많은 액션 상수들을 알아보자. (겁내 많음..........................)
setAction( )이나 인텐트 생성자로 인텐트를 위한 액션을 지정할 수 있다.
나만의 액션을 정의한다면, 선두에 내 앱의 패키지 이름을 꼭 포함해야한다.
static final String ACTION_STICKYNY = "com.android.action.STICKYNY";

  • Data
제공된 데이터의 타입은 일반적으로 인텐트의 액션에 따라 결정된다.
예를들어 ACTION_EDIT 액션인 경우에 데이터는 편집할 문서의 URI를 포함해야만 한다.

인텐트를 생성할 때 종종 데이터의 타입을 지정하는 것은 중요하다. 
예를들어 이미지를 나타낼 수 있는 액티비티는 아마도 음악 재생을 할 수 없을 것이다.
비록 URI 포맷이 유사하더라도 말이다.
그래서 데이터의 타입을 지정하는 것은 안드로이드 시스템이 인텐트를 받을 수 있는 최고의 컴포넌트를 찾을 수 있도록 도와준다.
그러나 이 타입은 가끔씩 URI로부터 추론될 수도 있다.

데이터 URI를 정하기위해서 setData( )를 호출한다.
타입을 정하기위해서 setType( )을 호출한다.
URI와 타입 모두를 정할 때에는 setDataAndType( )을 호출하자.
:: URI와 타입 모두를 정할 때에는 setData( )와 setType( )을 호출하지 말자.
:: 이 메소드들 각각으로 인해 서로의 값이 무효화된다.
:: 항상 setDataAnyType( )을 사용하자.

  • Category
추가적인 정보를 담고있는 문자열이다. 인텐트가 다뤄야하는 컴포넌트의 종류와 같은.
어떤 카테고리의 숫자라도 인텐트에 자리할 수 있지만, 대부분의 인텐트는 카테고리를 요구하지 않는다.
addCategory( )로 카테고리를 만들 수 있다.

다음은 몇가지 공통 카테고리이다.
CATEGORY_BROWSABLE
타겟 액티비티는 데이터를 보여주기위해 웹 브라우저로써 자신이 시작되는 것을 허락한다.
이미지나 이메일 메시지같은.
CATEGORY_LAUNCHER
액티비티가 태스크의 최초 액티비티이고, 시스템의 앱 런처에 나열되어 있다.

  • Extras
요청된 액션을 완료하고 요청된 추가적인 정보를 나르는 키-값 짝꿍이다.

다양한 putExtra( ) 메소드로 extra 데이터를 추가할 수 있다.
또한 extra 데이터로 번들 객체를 생성할 수 있다. 그리고나서 putExtras( )로 인텐트에 번들을 넣을 수 있다.
예를들어 ACTION_SEND로 이메일을 보내는 인텐트를 생성할 때,
EXTRA_EMAIL 키로 받는사람을 "to"라고 정할 수 있고
EXTRA_SUBJECT 키로 "제목"을  정할 수 있다.

나만의 extra key를 선언하고 싶으면 선두에 나의 앱 패키지 이름을 포함해야 한다.
 static final String EXTRA_STICKYNY = "com.android.EXTRA_STICKYNY";


  • Flags

플래그는 인텐트를 위한 메타데이터로써 그 기능이 인텐트 클래스에 정의되어 있다.

플래그는 어떻게 액티비티를 실행하는지, 액티비티가 실행된 후에 어떻게 액티비티를 다루는지, 아마 안드로이드 시스템에게 지시한다.




Forcing an app chooser

  

암시적 인텐트에 응답할 앱이 두 개 이상일 때,

유저는 어떤 앱을 사용할지 고를 수 있고,

특정 액션을 위한 기본 선택값을 만들 수 있다.

그러나 만약 다수의 앱이 인텐트에 응답할 수 있고 유저가 다른 앱을 선택하기를 원한다면, 선택 다이얼로그를 띄워야한다.

선택 다이얼로그는 유저에게 어떤 앱을 사용할 것인지 묻는다.

chooser를 띄우기위해 createChooser( )를 사용하는 인텐트를 생성하고, startActivity( )로 인텐트를 넘긴다.


Intent sendIntent = new Intent(Intent.ACTION_SEND);
...

String title = getResources().getString(R.string.chooser_title);
// Create intent to show the chooser dialog
Intent chooser = Intent.createChooser(sendIntent, title);

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



Receiving an Implicit Intent


어떤 암시적 인텐트가 받을 수 있는지 광고하기위해, 각 앱 컴포넌트의 여러 인텐트 필터를 선언한다.

각 인텐트 필터는 인텐트의 액션, 데이터, 카테고리를 기반으로 받아들이는 인텐트의 타입을 지정한다.

인텐트가 인텐트 필터 중 하나를 통과할 수 있는 경우에만, 시스템은 암시적 인텐트를 나의 앱 컴포넌트에 전달할 것이다.

:: 암시적 인텐트는 타겟에 항상 전달되어진다. 컴포넌트가 선언한 어떤 인텐트 필터에 상관없이.

앱 컴포넌트는 분리된 필터를 각 고유의 작업마다 선언해야한다.

사진첩 앱의 한 액티비티는 아마 두 필터를 가졌을 것이다.

한 필터는 이미지를 보여주고, 다른 필터는 이미지를 편집한다.

이 액티비티가 시작할 때, 인텐트를 조사하고 인텐트의 정보를 기반으로 어떻게 동작할지를 결정한다.

 

<intent-filter> 안에서 인텐트 타입을 지정할 수 있다.

<action>

받아들일 인텐트 액션을 선언한다.

이 값은 반드시 액션의 문자열 값이어야 한다. 클래스 상수가 아니다.

<data>

받아들일 데이터 타입을 선언한다.

data URI와 타입의 다양한 측면을 지정하는 속성들을 이용하여 선언한다.

<category>

받아들일 인텐트 카테고리를 선언한다.

이 값은 반드시 액션의 문자열 값이어야 한다. 클래스 상수가 아니다.


:: 암시적 인텐트를 받기위해서 인텐트 필터에 CATEGORY_DEFAULT 카테고리를 반드시 포함해야 한다.

:: startActivity( )startActivityForResult( ) 메소드는 모든 인텐트를 다룬다. 

:: 마치 그들이 카테고리를 선언한 것 마냥.

:: 인텐트 필터에 이 카테고리를 선언하지 않으면 암시적 인텐트는 액티비티를 해결할 수 없다.


<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>