API GUIDE/2. App Components
2014. 2. 28. 07:34


 


안드로이드 앱 독학으로 개발하기 - 일반적인 인텐트(Common Intents) (1)


지금까지 앞서 진행되어 왔던 글들을 보면 항상 "인텐트"를 연발해 왔습니다.


수도 없이 들어온 탓에 인텐트라는 것이 뭘 하는 건지는 대충 알겠는데 막상 사용법은 어떻게 되고 또 어떤 것들이 있는지는 확실히 알지 못했습니다.


이번 글은 그런 궁금증을 어느정도 해결해 주는 글이 아닐까합니다. 안드로이드 개발자 사이트에 있는 도움말이 개발자들을 참 세심하게 배려했다는 생각을 갖게 되는 글입니다.


물론 안드로이드 API에 존재하는 인텐트가 이 글에 나오는 것들이 전부는 아닙니다. 하지만 많이 사용되는 일부를 추려서 설명한 만큼 이 글을 독파하고 나면 다른 인텐트들도 어떻게 사용해야할 지 쉽게 접근할 수 있으리라 생각합니다.


이후 제 블로그에서도 지금의 번역글을 기반으로 하여 개발하는 과정을 다뤄볼까 생각중입니다.(물론, 지금은 실력이 안되서 곤란하지만 ^^;) 그 때까지 기초를 다진다고 생각하시고 읽어주시기 바랍니다.


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


 

저작권 표시 : 

 

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-common.html이며 안드로이드 4.4 Kitkat 기준으로 설명되었습니다.


일반적인 인텐트(Common Intents)


인텐트는 Intent오브젝트 안에 단순한 액션("지도를 본다." 또는 "사진을 찍는다."처럼)을 기술함으로써 다른 앱의 액티비티를 실행시킬 수 있다. 이런 종류의 인텐트는 실행시킬 컴포넌트를 지정하지 않기 때문에 암시적 인텐트라고 부른다. 하지만 수행할 액션과 함께 액션을 수행하는데 필요한 정보를 정해준다.


startActivity()startActivityForResult()를 호출할 때 암시적 인텐트를 넘겨주면 시스템은 인텐트를 다룰 수 있는 앱을 결정하고 해당하는 Activity를 실행시킨다. 만일 인텐트를 다룰 수 있는 앱이 여러 개 존재한다면 시스템은 사용자가 그 중에서 하나를 고를 수 있도록 다이얼로그를 띄운다.


이 글은 일반적인 액션으로 사용될 수 있는 몇몇 암시적 인텐트를 소개한다. 각 센션에서는 같은 액션을 수행할 수 있는 능력을 외부로 알리는 intent filter의 생성방법에 대해서도 함께 보여준다.


Caution : 만일 암시적 인텐트를 처리할 수 있는 앱이 단말에 설치되어 있지 않다면 startActivity() 메서드를 호출하는 순간 앱은 크래시되고 말 것이다. 먼저 인텐트를 처리할 수 있는 앱이 존재하는지를 확인하기 위하여 Intent 오브젝트를 사용해 resolveActivity() 메서드를 호출한다. 결과가 null이 아니면 인텐트를 처리할 수 있는 앱이 최소 하나는 존재한다는 뜻이며 startActivity()를 호출해도 안전하다. 반면에 결과가 null이면 해당 인텐트를 사용해서는 안되며 가능하다면 인텐트를 사용하게 되는 기능을 비활성화시키는 것이 좋다.


만약, 인텐트나 인텐트 필터를 생성하는 법을 잘 모른다면 인텐트와 인텐트 필터(Intents and Intent Filters)를 먼저 읽기 바란다.


카메라(Camera)



사진이나 동영상을 찍고 결과물 돌려받기
(Capture a picture or video and return it)

카메라 앱을 열고 사진이나 동영상 결과물을 돌려받기 위해서는 ACTION_IMAGE_CAPTUREACTION_VIDEO_CAPTURE액션을 사용한다. 또한 EXTRA_OUTPUT 부가정보 키에 카메라가 사진이나 동영상을 저장할 URI위치를 지정한다.


액션(Action)

ACTION_IMAGE_CAPTURE 또는 ACTION_VIDEO_CAPTURE


데이터 URI 스키마(Data URI Scheme)

필요없음


MIME Type

필요없음


부가정보(Extras)

EXTRA_OUTPUT

카메라 앱에서 사진이나 동영상 파일을 저장하기 위한 URI위치(Uri 오브젝트 사용)


카메라 앱이 정상종료되고 호출자의 액티비티로 포커스가 돌아오면(호출한 앱은 onActivityResult() 콜백을 받게 된다.) EXTRA_OUTPUT으로 지정한 URI위치에 있는 사진이나 동영상을 액세스할 수 있다.


Note : ACTION_IMAGE_CAPTURE를 사용해 사진을 촬영하며 카메라 앱은 결과 Intent내에 "data"라는 필드명으로 Bitmap형태의 썸네일을 제공하기도 한다.


인텐트 예제 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static final int REQUEST_IMAGE_CAPTURE = 1;
static final Uri mLocationForPhotos;

public void capturePhoto(String targetFilename) {
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    intent.putExtra(MediaStore.EXTRA_OUTPUT,
            Uri.withAppendedPath(mLocationForPhotos, targetFilename);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(intent, REQUEST_IMAGE_CAPTURE);
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
        Bitmap thumbnail = data.getParcelable("data");
        // Do other work with full size photo saved in mLocationForPhotos
        ...
    }
}


이 인텐트로 어떻게 촬영하고 저장위치를 위한 올바른 Uri를 생성하는 법에 대한 보다 자세한 설명은 Taking Photos SimplyTaking Videos Simply를 읽어보기 바란다.


인텐트 필터 예제 :

1
2
3
4
5
6
<activity ...>
    <intent-filter>
        <action android:name="android.media.action.IMAGE_CAPTURE" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>


연락처 앱(Contacts/People App)



연락처 선택하기(Select a contact)

사용자가 연락처를 선택하도록 하고 연락처의 정보를 완전히 액세스하려면 ACTION_PICK 액션을 사용하고 MIME 타입은 Contacts.CONTENT_TYPE을 사용해야 한다.


onActivityResult() 콜백함수로 돌려받은 Intent에는 선택된 연락처가 content:의 스키마(scheme)를 갖는 URI로 포함되어 있다. 앱이 READ_CONTACTS 권한을 갖고 있지 않더라도 반환된 결과는 Contacts Provider API를 통해 연락처의 내용을 읽을 수 있는 임시권한을 부여해준다.


Tip : 만일 전화번호나 이메일 주소와 같은 연락처 정보의 일부분만 액세스하려고 한다면 다음 섹션(연락처의 특정 데이터 선택하기; select specific contact data)을 참고 바란다.


액션(Action)

ACTION_PICK


데이터 URI 스키마(Data URI Scheme)

필요없음


MIME Type

Contacts.CONTENT_TYPE


인텐트 예제 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static final int REQUEST_SELECT_CONTACT = 1;

public void selectContact() {
    Intent intent = new Intent(Intent.ACTION_PICK);
    intent.setType(ContactsContract.Contacts.CONTENT_TYPE);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(intent, REQUEST_SELECT_CONTACT);
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_SELECT_CONTACT && resultCode == RESULT_OK) {
        Uri contactUri = data.getData();
        // Do something with the selected contact at contactUri
        ...
    }
}


연락처 URI의 상세 정보를 읽는 법은 Retrieving Details for a Contact를 참고하기 바란다. 다시한번 말하자면 위에서 설명한 인텐트로 획득한 연락처 URI의 상세정보를 읽을 때는 READ_CONTACTS 권한이 필요없다.


연락처의 특정 데이터 선택하기(Select specific contact data)

사용자가 연락처를 선택하도록 하고 선택된 연락처의 내용 중 전화번호, 이메일 주소등의 일부 정보를 획득하려면 액션은 ACTION_PICK를 사용하고 MIME 타입은 아래 목록에 표시된 컨텐트 타입 중 하나를 사용한다. 예를 들어, 연락처의 전화번호를 가져오고 싶으면 MIME 타입에 CommonDataKinds.Phone.CONTENT_TYPE을 사용하면 된다.


만일 연락처의 내용 중에서 한 가지 데이터만 필요하다면 ContactsContract.CommonDataKinds 클래스의 CONTENT_TYPE을 사용하는 것이 Contacts.CONTENT_TYPE(앞에서 설명 되었다.)을 사용하는 것보다 더 효과적인다. 왜냐하면 ContactsContract.CommonDataKinds 클래스의 CONTENT_TYPE을 사용하여 얻은 결과는 ContactsProvider에 복잡한 쿼리를 하지 않고도 원하는 정보에 직접 액세스 할 수 있기 때문이다.


onActivityResult() 콜백을 통해 전달되는 결과 Intent는 선택된 연락처 정보를 가리키는 content: URI를 포함하고 있다. 응답된 결과는 앱이 READ_CONTACTS 퍼미션을 갖고 있지 않아도 연락처 정보를 읽어낼 수 있는 임시 퍼미션을 부여해 준다.


액션(Action)

ACTION_PICK


데이터 URI 스키마(Data URI Scheme)

필요없음


MIME Type

CommonDataKinds.Phone.CONTENT_TYPE

연락처의 전화번호를 추출한다.

CommonDataKinds.Email.CONTENT_TYPE

연락처의 이메일 주소를 추출한다.

CommonDataKinds.StructuredPostal.CONTENT_TYPE

연락처의 우편번호를 추출한다.

ContactsContract에는 더 많은 CONTENT_TYPE이 있다.


인텐트 예제 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
static final int REQUEST_SELECT_PHONE_NUMBER = 1;

public void selectContact() {
    // Start an activity for the user to pick a phone number from contacts
    Intent intent = new Intent(Intent.ACTION_PICK);
    intent.setType(CommonDataKinds.Phone.CONTENT_TYPE);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(intent, REQUEST_SELECT_PHONE_NUMBER);
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_SELECT_PHONE_NUMBER && resultCode == RESULT_OK) {
        // Get the URI and query the content provider for the phone number
        Uri contactUri = data.getData();
        String[] projection = new String[]{CommonDataKinds.Phone.NUMBER};
        Cursor cursor = getContentResolver().query(contactUri, projection,
                nullnullnull);
        // If the cursor returned is valid, get the phone number
        if (cursor != null && cursor.moveToFirst()) {
            int numberIndex = cursor.getColumnIndex(CommonDataKinds.Phone.NUMBER);
            String number = cursor.getString(numberIndex);
            // Do something with the phone number
            ...
        }
    }
}


연락처 보기(View a contact)

저장된 연락처의 상세정보를 표시하려면 액션은 ACTION_VIEW를 사용하고 데이터에는 표시하려는 연락처를 content: URI 형태로 지정해 준다.


연락처 URI를 얻기 위해서는 주로 두 가지 방법을 사용한다.

  • 앞에서 설명했던 ACTION_PICK을 사용하여 얻어진 연락처 URI를 사용한다.(이 방법을 사용하기 위해서 필요한 퍼미션은 따로 없다.)
  • Retrieving a List of Contacts에 기술된 방법으로 모든 연락처의 목록에 직접 액세스한다.(이 방법을 사용하기 위해서는 READ_CONTACTS 퍼미션이 필요하다.)


액션(Action)

ACTION_VIEW


데이터 URI 스키마(Data URI Scheme)

content:<URI>


MIME Type

필요없음. 타입은 연락처 URI로 부터 유추가 가능한다.


인텐트 예제 :

1
2
3
4
5
6
public void viewContact(Uri contactUri) {
    Intent intent = new Intent(Intent.ACTION_VIEW, contactUri);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}


저장된 연락처 수정(Edit an existing contact)

저장된 연락처를 수정하려면 액션은 ACTION_EDIT, 데이터에는 content: URI의 형태로 연락처를 지정해 준다. 그리고 부가정보에 수정될 항목과 수정될 내용을 입력해 준다. 수정될 항목은 ContactsContract.Intents.Insert에 정의된 상수로써 정해줄 수 있다.


연락처 URI를 얻기 위해서는 주로 두 가지 방법을 사용한다.

  • 앞에서 설명했던 ACTION_PICK을 사용하여 얻어진 연락처 URI를 사용한다.(이 방법을 사용하기 위해서 필요한 퍼미션은 따로 없다.)
  • Retrieving a List of Contacts에 기술된 방법으로 모든 연락처의 목록에 직접 액세스한다.(이 방법을 사용하기 위해서는 READ_CONTACTS 퍼미션이 필요하다.)


액션(Action)

ACTION_EDIT


데이터 URI 스키마(Data URI Scheme)

content:<URI>


MIME Type

필요없음. 타입은 연락처 URI로 부터 유추가 가능한다.


부가정보(Extras; 선택사항)

ContactsContract.Intents.Insert에 정의된 항목을 하나 이상 추가할 수 있다.


인텐트 예제 :

1
2
3
4
5
6
7
8
public void editContact(Uri contactUri, String email) {
    Intent intent = new Intent(Intent.ACTION_EDIT);
    intent.setDataAndType(contactUri, Contacts.CONTENT_TYPE);
    intent.putExtra(Intents.Insert.EMAIL, email);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

연락처를 수정하는 법에 대한 정보는 Modifying Contacts Using Intents에 더 많이 있다.


연락처 추가(Insert a contact)

새로운 연락처를 추가하려면 ACTION_INSERT 액션을 사용하고 MIME 타입은 Contacts.CONTENT_TYPE을 지정한다. 그리고 부가정보에 수정될 항목과 수정될 내용을 입력해 준다. 수정될 항목은 ContactsContract.Intents.Insert에 정의된 상수로써 정해줄 수 있다.


액션(Action)

ACTION_INSERT


데이터 URI 스키마(Data URI Scheme)

필요없음.


MIME Type

Contacts.CONTENT_TYPE


부가정보(Extras; 선택사항)

ContactsContract.Intents.Insert에 정의된 항목을 하나 이상 추가할 수 있다.


인텐트 예제 :

1
2
3
4
5
6
7
8
9
public void insertContact(String name, String email) {
    Intent intent = new Intent(Intent.ACTION_INSERT);
    intent.setType(Contacts.CONTENT_TYPE);
    intent.putExtra(Intents.Insert.NAME, name);
    intent.putExtra(Intents.Insert.EMAIL, email);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

연락처를 추가하는 법에 대한 더 많은 정보는 Modifying Contacts Using Intents를 참고하기 바란다.


이메일(Email)



이메일 쓰기(Compose an email with optional attachments)

이메일을 사용하기 위해서는 첨부가 있는지 여부에 따라 아래의 액션 중 하나를 사용하고 아래의 목록에 있는 부가정보 키에 맞춰 수신인이나 제목과 같은 세부사항을 이메일에 포함시킬 수 있다.


액션(Action)

ACTION_SENDTO (첨부가 없을 때) 또는

ACTION_SEND (하나만 첨부할 때) 또는

ACTION_SEND_MULTIPLE (여러 개를 첨부할 때)


데이터 URI 스키마(Data URI Scheme)

필요없음.


MIME Type

PLAIN_TEXT_TYPE("text/plain")

"*/*"


부가정보(Extras; 선택사항)

Intent.EXTRA_EMAIL

모든 수신인 이메일 주소의 문자열 배열

Intent.EXTRA_CC

모든 참조자 이메일 주소의 문자열 배열

Intent.EXTRA_BCC

모든 숨김 참조자 이메일 주소의 문자열 배열

Intent.EXTRA_SUBJECT

이메일 제목을 나타내는 문자열

Intent.EXTRA_TEXT

이메일의 본문을 나타내는 문자열

Intent.EXTRA_STREAM

첨부한 파일을 가리키는 Uri. 만일 ACTION_SEND_MULTIPLE 액션을 사용하였다면 Uri 오브젝트를 포함하는 ArrayList.


인텐트 예제 :

1
2
3
4
5
6
7
8
9
10
public void composeEmail(String[] addresses, String subject, Uri attachment) {
    Intent intent = new Intent(Intent.ACTION_SEND);
    intent.setType("*/*");
    intent.putExtra(Intent.EXTRA_EMAIL, addresses);
    intent.putExtra(Intent.EXTRA_SUBJECT, subject);
    intent.putExtra(Intent.EXTRA_STREAM, attachment);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}


문자 메세지, SNS 앱이 아닌 반드시 이메일로 인텐트를 처리하고 싶다면 ACTION_SENDTO 액션을 사용하고 데이터 스키마는 "mailto:"로 지정해 준다. 아래는 그 예제다.



1
2
3
4
5
6
7
8
9
public void composeEmail(String[] addresses, String subject) {
    Intent intent = new Intent(Intent.ACTION_SENDTO);
    intent.setData(Uri.parse("mailto:")); // only email apps should handle this
    intent.putExtra(Intent.EXTRA_EMAIL, addresses);
    intent.putExtra(Intent.EXTRA_SUBJECT, subject);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}



인텐트 필터 예제 :

1
2
3
4
5
6
7
8
9
10
11
12
<activity ...>
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <data android:type="*/*" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.SENDTO" />
        <data android:scheme="mailto" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>




다음 글에서 이 글(일반적인 인텐트)의 내용을 계속 이어갑니다.


긴 글들은 타이핑하는 것도 꽤 큰 일이지만 보는 사람도 지겨워질 수 있을 것 같아 한 번 쉬어갑니다.


다음 글 >> 일반적인 인텐트(Common Intents) (2)

이전 글 >> 인텐트와 인텐트 필터(Intents and Intent Filters) (2)


posted by 리치크루

API GUIDE/2. App Components
2014. 2. 25. 22:59

 

이전 글인 인텐트와 인텐트 필터(Intents and Intent Filters)에 이어지는 글입니다.


언제나 그렇듯 오역, 오타, 깨진 링크가 있으면 제보 부탁드립니다.


 

저작권 표시 : 

 

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 기준으로 설명되었습니다.

 

암시적 인텐트 수신하기



여러분의 앱이 암시적 인텐트를 다룰 수 있다는 것을 시스템에 알리려면 매니페스트 파일<intent-filter>엘리먼트를 사용하여 하나 이상의 인텐트 필터를 선언해야 한다. 각 인텐트 필터는 인텐트의 액션, 데이터, 카테고리에 기반하여 수용 가능한 인텐트의 타입을 지정한다. 인텐트 필터의 조건을 뚫고 통화할 수 있는 인텐트가 있다면 시스템은 암시적 인텐트를 여러분의 앱에 전달해 줄 것이다.


Note : 명시적 인텐트는 컴포넌트에 인텐트 필터가 있건 없건 상관없이 항상 지정된 컴포넌트에 배달된다.


앱 컴포넌트는 독립된 일 하나하나에 대해서 각각의 필터를 사용해야 한다. 예를 들어, 이미지 갤러리앱은 두 개의 필터를 갖게 될 것이다.; 하나의 필터는 이미지를 보여주기만 하고 나머지 하나는 이미지를 편집하는 필터로 말이다. 액티비티가 시작되면 Intent를 확인해서 Intent에 있는 정보에 대해 어떤 행동을 해야할 지 결정해야 한다.(편집 창을 보여줄지 말지를 결정해야 한다.)


Manifest 파일의 <intent-fillter>엘리먼트를 통해 선언된 인텐트 필터는 앱 컴포넌트(<activity>엘리먼트 같은) 안쪽에 네스트 되어야 한다. <intent-filter>안쪽에는 다음의 3개 엘리먼트 중 하나 이상을 사용하여 인텐트의 타입을 정해줘야 한다.


<action>

name속성에 수용하고자 하는 인텐트 액션을 선언한다. 값은 클래스 상수가 아닌 액션을 문자열로 써야한다.


<data>

URI데이터(scheme, host, port, path등)와 MIME타입의 다양한 점을 지정할 수 있도록 하나 이상은 속성을 사용하여 수용하고자 하는 데이터의 타입을 결정한다.


<category>

name속성을 사용하여 수용하고자 하는 인텐트 카테고리를 선언한다. 값은 클래스 상수가 아닌 카테고리를 문자열로 써야한다.


Note : 암시적 인텐트를 받기 위해서는 인텐트 필터에 CATEGORY_DEFAULT 카테고리를 반드시 포함시켜야 한다. startActivity()startActivityForResult()은 모든 인텐트가 CATEGORY_DEFAULT 카테고리를 선언했다고 여기기 때문이다. 만일 카테고리에 이 값을 선언하지 않았다면 암시적 인텐트는 받을 수 없다.


아래 예제는 ACTION_SEND의 액션과 텍스트 타입을 데이터를 갖는 인텐트를 수용하도록 인텐트 필터를 선언하는 법에 대한 것이다.


1
2
3
4
5
6
7
<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>

<action>, <data>, <category>엘리먼트 중 하나 이상을 포함하는 필터를 만드는 것이 좋다. 이렇게 하면 다룰 수 있는 인텐트를 필터에 선언된 엘리먼트들과 비교하여 그 조합된 조건에 맞도록 요구하는 컴포넌트가 되기 때문에 정확히 원하는 인텐트를 지정할 수 있게 된다.


특정 액션, 데이터, 카테고리 타입의 조합에 맞는 여러 종류의 인텐트를 다루도록 하고 싶다면 여러 개의 인텐트 필터를 선언해야 한다.


컴포넌트 액세스 제한하기(Restricting access to components)

인텐트 필터를 사용하는 것은 다른 앱이 여러분의 컴포넌트를 실행시키는 것을 제한하는 보안대책은 아니다. 인텐트 필터가 특정한 종류의 암시적 인텐트에 응답하도록 컴포넌트를 제한했다고 하더라도 개발자가 컴포넌트의 이름을 결정했다면 다른 앱이 명시적 인텐트를 사용하여 컴포넌트를 시작할 수 있는 잠재적 가능성은 있다. "오직 여러분의 앱에서만" 여러분의 컴포넌트를 실행시켜야 한다는 사실일 중요하다면 해당 컴포넌트의 exported 속성을 "false"로 설정해야 한다.


암시적 인텐트는 인텐트의 각각 세가지 엘리먼트를 필터와 비교하는 테스트를거치게 된다. 다시 말하자면 암시적 인텐트가 컴포넌트에 전달되려면 세 가지 테스트를 모두 통과해야만 하는 것이다. 각각의 엘리먼트를 테스트해서 하나라도 맞지 않는다면 안드로이드 시스템은 그 인텐트를 컴포넌트에 전달하지 않는다. 하지만 두스의 인텐트 필터를 갖고 있는 컴포넌트라면 또 다른 필터의 조건에 맞는 인텐트로 판단되어 인텐트가 전달될 수도 있다. 시스템이 어떻게 인텐트를 resolve하는지에 대한 추가 정보는 뒤에 나오는 Intent Resolution 섹션에서 제공된다.


Caution : 우연히 다른 앱이 Service를 실행시키는 일이 발생하지 않도록 하려면 여러분이 만든 서비스는 항상 명시적 인텐트를 사용하여 시작시키도록 서비스에 인텐트 필터를 선언하지 않도록 한다.


Note : 모든 액티비티의 인텐트 필터는 매니페스트 파일을 통해 선언되어야 한다. 하지만 브로드캐스트 리시버를 위한 필터는 registerReceiver() 메서드를 호출해서 동적으로 등록이 가능하다. 반대로 unregisterReceiver()를 통해 등록된 리시버를 해제시키는 것도 가능하다. 이런 메서드들을 사용하면 앱이 실행되는 동안 특정 브로드캐스트에 대해 일정 기간동안만 반응하도록 할 수가 있다.



필터 예제(Example filters)

인텐트 필터의 동작에 대한 이해를 돕기 위해 아래와 같은 예제를 준비했다. 이 예제는 소셜 공유앱의 매니페스트 파일을 일부 발췌한 것이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<activity android:name="MainActivity">
    <!-- 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">
    <!-- 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액션(리치크루 : 앞 필터 예제에서는 adnroid.intent.action.Main)은 앱의 주 진입점임을 알리며 다른 데이터는 필요로 하지 않는다.
  • CATEGORY_LAUNCHER(리치크루 : 앞 필터 예제에서는 android.intent.category.LAUNCHER)는 해당 액티비티의 아이콘이 시스템의 앱 런처에 추가되어 위치해야 함을 의미한다. 만일 <activity>엘리먼트에 icon으로 아이콘을 지정하지 않았다면 시스템은 <application>엘리머트에 지정된 아이콘을 대신 사용한다.

앱 럽처에 액티비티가 표시되도록 하기 위해서는 이 두 가지(ACTION_MAIN과 CATEGORY_LAUNCHER)가 함께 짝을 이루어야한다.


두 번째 액티비티인 ShareActivity는 텍스트와 미디어 컨텐츠의 공유가 가능하도록 하려고 한다. 사용자는 MainActivity에서 이 액티비티로 넘어오는 경우도 있겠지만 다른 앱을 사용하다가 발생하는 암시적 인텐트를 통해 MainActivity를 거치지 않고 바로 이 액티비티를 만날 수도 있다.


Note : application/vnd.google.panorama360+jpg라는 MIME 타입은 Google panorama API를 통해 다룰 수 있는 파노라마 사진의 특수한 데이터 타입이다.



펜딩 인텐트 사용하기(Using a Pending Intent)



PendingIntent 오브젝트는 Intent 오브젝트의 랩퍼 오브젝트다. PendingIntent의 주목적은 외부 어플리케이션이 Intent를 사용할 때 여러분의 앱이 동작하는 프로세스에서 실행되는 것처럼 사용권한을 부여하기 위해 사용된다.


펜딩 인텐트의 주용도는 다음과 같다. :

  • Notification에 맞는 액션을 사용자가 수행하려할 때 사용될 인텐트를 선언한다.(안드로이드 시스템의 NotificationManagerIntent를 실행한다.)
  • App Widget을 통해 사용자가 액션을 취할 때 사용될 인텐트를 선언한다.(홈 스크린앱은 Intent를 실행한다.)
  • 미래의 특정시간에 사용될 인텐트를 선언한다.(안드로이드 시스템의 AlarmManagerIntent를 실행한다.)


각 Intent 오브젝트는 특정타입의 앱 컴포넌트(액티비티, 서비스, 브로드캐스트 리시버)가 다룰 수 있도록 구성한다. 그래서 PendingIntent도 똑같이 특정 타입의 앱 컴포넌트에서 다뤄질 수 있도록 구성해야한다. 펜딩 인텐트를 이용할 때는 startActivity() 같은 호출을 하지 않는다. 대신 PendingIntent가 각 생성 메서드를 통해 생성될 때 의도하는 컴포넌트 타입을 선언해 주어야 한다. 각 생성 메서드는 다음과 같다.


다른 앱으로부터 펜딩 인텐트를 건네받지 않는 한 PenndingIntent를 생성하는 방법은 위의 메서드들을 사용하는 방법뿐이다.


각 메서드들은 파라메터로 (1) 현재 앱의 Context, (2) 래핑하려는 Intent, (3) 인텐트가 어떻게 사용되어야 하는지(예를 들어, 인텐트가 한 번 이상 사용될 수 있는지)를 나타내는 하나 이상의 플래그를 취한다.


펜딩 인텐트의 사용에 관한 더 많은 정보는 NotificationApp widgets API 가이드 같이 각 사용 케이스에 따른 문서에서 제공된다.


인텐트의 결정성(Intent Resolution)



시스템에서는 암시적 인텐트를 받아 액티비티를 실행시켜야 할 때 인텐트와 인텐트 필터를 다음 세가지 사항에 기초하여 비교함으로써 최적의 액티비티를 찾아낸다.

  • 인텐트 액션
  • 인텐트 데이터(URI와 데이터 타입)
  • 인텐트 카테고리


이후에 섹션에서는 인텐트가 어떤 인텐트 필터를 가진 컴포넌트와 어떻게 적절히 짝을 찾게 되는지 설명한다.


액션 테스트(Action test)

수용하고자 하는 인텐트의 액션을 지정하기 위한 것이 <action>엘리먼트다. 인텐트 필터 안에서 정의하게 되며 아예 선언을 안 할 수도 있고 하나 이상, 여러 개를 선언할 수도 있다. 예제는 다음과 같다.


1
2
3
4
5
<intent-filter>
    <action android:name="android.intent.action.EDIT" />
    <action android:name="android.intent.action.VIEW" />
    ...
</intent-filter>

이 필터를 통과하려면 Intent 내의 액션은 필터에 명기된 액션 중 하나와 일치해야만 한다.


만일 필터가 어떤 액션도 포함하지 않으면 인텐트와 매칭 시킬 게 없다. 때문에 모든 인텐트가 테스트를 통과하지 못한다. 반면에 액션을 지정하지 않은 Intent가 있다면 그 인텐트는 테스트를 통과할 수 있게 된다.(필터에 적어도 하나의 액션이 포함되어 있다면)


카테고리 테스트(Category test)

수용하려는 인텐트의 카테고리를 지정하려면 <category>엘리먼트를 사용하며 이 엘리먼트는 사용하지 않거나 하나 이상을 선언할 수 있다. 예제는 다음과 같다.


1
2
3
4
5
<intent-filter>
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    ...
</intent-filter>

인텐트가 카테고리 테스트를 통과하기 위해서는 인텐트의 모든 카테고리가 필터의 카테고리와 일치해야만 한다. 인텐트 필터 내의 카테고리가 인텐트에 지정된 것 이상으로 선언되어 있다면 그 인텐트는 테스트를 통과하게 되지만 그 반대의 경우는 통과할 수 없다. 그래서 인텐트에 지정된 카테고리가 없으면 필터에 선언된 카테고리가 있건 없건 무조건 테스트를 통과하게 된다.


Note : 안드로이드는 startActivity()startActivityForResult()를 통해 전달되는 모든 암시적 인텐트에 자동으로 CATEGORY_DEFAULT를 추가시킨다. 그렇게 때문에 암시적 인텐트를 받고자 하는 액티비티는 인텐트 필터에 "android.intent.category.DEFAULT"를 반드시 포함시켜야 한다.(앞의 <intent_filter>예제처럼 말이다.)


데이터 테스트(Data test)

수용하려는 인텐트 데이터를 지정하려면 <data>엘리먼트를 사용해야 하며 이 엘리먼트는 사용하지 않거나 하나 이상을 선언할 수 있다.


1
2
3
4
5
<intent-filter>
    <data android:mimeType="video/mpeg" android:scheme="http" ... />
    <data android:mimeType="audio/mpeg" android:scheme="http" ... />
    ...
</intent-filter>

<data>엘리먼트는 URI 스트럭쳐와 데이터 타입(MIME 미디어 타입)을 지정할 수 있다. URI를 표현할 때는 각각의 부분을 나타내는 별도의 속성들이 있다.(schem, host, port, path)


<scheme>://<host>:<port>/<path>


예를 들어, 다음과 같은 URI가 있다면


content://com.example.project:200/folder/subfolder/etc


이 URI에서 스킴(scheme)은 content, 호스트(host)는 com.example.project, 포트(port)는 200, 경로(path)는 folder/subfolder/etc가 된다.


이러한 각각의 속성들은 <data>엘리먼트 내에서 선택적으로 사용될 수는 있지만 선형적 의존성을 갖고 있다.

  • 스키마가 지정되지 않으면 호스트는 무시된다.
  • 홋트가 지정되지 않으면 포트는 무시된다.
  • 스킴과 호스트가 모두 지정되지 않으면 경로는 무시된다.


인텐트의 URI와 필터의 URI를 비교할 때는 다음과 같이 필터 URI의 부분, 부분만을 비교한다.

  • 필터에 스킴만 지정되었으면 그 스킴을 갖는 모든 URI는 필터와 일치하는 것으로 본다.
  • 필터에 경로는 없이 스킴과 권한만 지정되었으면 경로와 상관없이 스킴과 권한이 같은 URI는 필터를 통과하게 된다.
  • 필터에 스킴, 권한, 경로가 지정되어 있으면 오직 같은 스킴, 권한, 경로를 가진 URI만 필터를 통과할 수 있다.


Note : 경로 지정에는 경명의 일부에 무엇이 와도 매칭된 것으로 간주하는 와일드카드(* 표시)를 포함시킬 수 있다.


데이터 테스트는 필터와 인텐트의 URI와 MIME 타입을 비교하며 그 규칙은 다음과 같다.

  1. URI와 MIME 타입이 모두 지정되지 않은 인텐트는 URI와 MIME 타입 모두를 지정하지 않은 필터만 통과할 수 있다.
  2. URI는 포함하고 있지만 MIME 타입을 포함하지 않은 (타입이 명시적이지도 않고 URI에 의해 유추할 수도 없는) 인텐트는 URI 포맷은 지정하고 MIME 타입은 지정하지 않은 필터만 통과할 수 있다.
  3. MIME 타입은 포함하고 있지만 URI는 포함하지 않은 인텐트는 같은 MIME 타입이 지정되어 있고 URI 포맷은 지정하지 않은 필터만 통과할 수 있다.
  4. URI와 MIME 타입을 포함한 (타입이 명시적이거나 URI로 부터 유추가 가능한) 인텐트는 필터에서 지정한 타입과 매칭되면 일단 MIME 타입 부분만 테스트를 통과한다. URI 부분의 테스트는 필터에서 지정한 URI와 동일해도 통과할 수 있으며 필터에는 URI를 지정하지 않았지만 URI가 content: 또는 file: 형태라면 통과할 수 있다. 즉, 컴포넌트는 필터에 MIME 타입만 지정한 경우 content:와 file: 데이터는 수용할 수 있는 것으로 간주된다.


마지막의 (D)규칙은 컴포넌트가 파일이나 컨텐트 프로바이더라는 로컬 데이터를 수용할 수 있음을 의미한다. 그래서 컴포넌트들의 필터는 데이터 타입만 지정하고 content:와 file: 스킴은 명시적으로 지정하지 않아도 된다. 아래 예제는 이런 상황에 대한 전형적인 예시다. 예시에서는 컴포넌트가 컨텐트 프로바이더로 부터 이미지 데이터를 받아들이고 디스플레이 할 수 있다는 것을 보여주고 있다.


1
2
3
4
<intent-filter>
    <data android:mimeType="image/*" />
    ...
</intent-filter>

대부분의 쓸모있는 데이터는 컨텐트 프로바이더를 통해 제공받을 수 있기 때문에 필터는 데이터 타입만 지정하고 URI는 지정하지 않는 것이 대체로 일반적인다.


또다른 일반적인 구성은 필터에 스킴과 데이터 타입만 지정하는 경우다. 아래 예제의 <data>엘리먼트는 액션 수행을 위해 네트워크로부터 제공되는 비디오 데이터를 사용할 수 있음을 나타내고 있다.


1
2
3
4
<intent-filter>
    <data android:scheme="http" android:type="video/*" />
    ...
</intent-filter>

인텐트 매칭(Intent matching)

인텐트는 실행시킬 타겟 컴포넌트를 찾기 위해 사용되기도 하지만 기기의 컴포넌트 세트에 대한 무언가를 찾을 때도 사용된다. 예를 들어, 홈 앱은 ACTION_MAIN 액션과 CATEGORY_LAUNCHER 카테고리를 지정한 인텐트 필터를 가진 모든 액티비티를 찾아서 앱 런쳐에 채워넣는다.


여러분의 앱에서도 이와 비슷한 방법으로 사용할 수 있다. PackageManager는 특정 인텐트를 수용할 수 있는 모든 컴포넌트를 알려주는 query...() 메서드 세트를 가지고 있으며 인텐트에 응답하는 최적의 컴포넌트를 결정해주는 resolve...() 메서드 시리즈도 갖고 있다. 예를 들어, queryIntentActivities() 메서드는 인자로 넘겨주는 인텐트를 수행할 수 있는 모든 액티비티의 목록을 반환해주며 queryIntentServices()는 서비스 목록을 반환해 준다. 이 메서드들은 컴포넌트를 실행시키는 것이 아니다. 단순히 응답가능한 컴포넌트들의 목록만을 제공해 주는 것이다. 비슷한 메서드로 브로드캐스트 리시버를 찾아주는 queryBroadcastReceivers() 메서드가 있다.



오늘 포스트를 다 올리고 자야겠다는 생각에 밤 11시를 넘겨버렸네요.


예전에는 새벽 1시, 2시까지도 깨어 있다가 아침에는 지각할까 걱정하며 허겁지겁 문턱을 나섰습니다.


그런데 언제부터인가 '새벽 1, 2시까지 할 일이 있으면 차라리 아침 5시, 6시부터 시작하자'라는 쪽으로 바람직하게 바뀌었습니다.


바람직하게 바뀌기는 했는데... '나이들면 새벽잠이 없어진다더라....'는 말이 자꾸 머릿속에 멤도는 건...


젠장 그래서 그랬던건가?... ^^;


그래도 아침일찍 일어나는 게 효율적이긴 합니다.


다음 글 >> 일반적인 인텐트(Common Intents) (1)

이전 글 >> 인텐트와 인텐트 필터(Intents and Intent Filters) (1)





posted by 리치크루

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 리치크루

API GUIDE/1. Introduction
2014. 2. 16. 19:22

이번 글은 보안에 관련된 글입니다.


짧은 영어 실력 때문이기도 하지만 보안에 대해서 지식이 부족한 터라 번역하는데에 시간이 많이 소요되었고 이해가 안되는 부분도 많았습니다.


그래서 번역이 좀 부실하네요. ^^;


오역에 대한 제보는 저 뿐만 아니라 이곳을 방문하시는 분 모두에게 도움이 될 겁니다. 많은 제보 부탁드립니다.

 

 


저작권 표시 : 

 

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/topics/security/permissions.html이며 안드로이드 4.4 Kitkat 기준으로 설명되었습니다.

 


시스템 퍼미션(System Permissions)

 

안드로이드는 각 어플리케이션이 ID(리눅스 사용자 ID와 그룹 ID)로 구분되는 사용 권한이 분리되어 있는 운영체제다. 시스템의 각 부분들도 ID로 서로 분리되어 있다. 이러한 방식으로 리눅스는 어플리케이션을 또다른 어플리케이션이나 시스템과 분리시켜 동작시킨다.

 

여기에 추가로 세분화된 보안 기능을 통해 URI마다 임시 사용권한을 부여하여 특정 데이터에 액세스할 수 있도록 할 수도 있다.

 

이 문서는 안드로이드에 의한 보안 기능을 개발자가 어떻게 사용할 수 있는지에 대해서 설명한다. 좀 더 일반적인 안드로이드 보안의 개요는 안드로이드 오픈 소스 프로젝트에서 제공된다.

 

 

보안 아키텍쳐(보안시스템의 구조; Security Archtecture) 


 

안드로이드 보안 아키텍쳐의 주요 골자는 기본적으로 어떠한 어플리케이션도 또다른 어플리케이션이나 운영체제, 사용자와 충돌이 있을만한 동작을 할 권한을 주지 않는다는 것이다. 충돌이 있을만한 동작이라 함은 사용자의 개인정보(주소록이나 이메일 같은)나 또다른 어플리케이션의 파일의 액세스, 네트워크 사용, 기기가 계속 켜져있게 하는 등의 동작으로 의미한다.

 

각각의 안드로이드 어플리케이션은 별도의 프로세스 샌드박스 안에서 동작하기 때문에 공유된 자원이나 데이터를 사용하고 싶다면 확실히 의사를 밝혀야 한다. 이것은 공유된 자원이나 데이터를 사용할 수 있는 권한을 선언함으로써 이루어진다. 어플리케이션이 필요한 권한을 선언하면 안드로이드 시스템은 어플리케이션이 설치될 때 이를 사용자에게 알리고 승인을 받는다. 안드로이드는 런타임(실행 중)에 동적으로 권한을 부여하지는 않는다. 왜냐하면 이런 방법은 사용자를 혼란시켜 보안에 헛점이 발생할 소지가 있기 때문이다.

 

샌드박스(sandbox)

 

보호된 영역 내에서 프로그램을 동작시키는 것으로, 외부 요인에 의해 악영향이 미치는 것을 방지하는 보안 모델. ‘아이를 모래밭(샌드 박스)의 밖에서 놀리지 않는다’라고 하는 말이 어원이라고 알려져 있다. 이 모델에서는 외부로부터 받은 프로그램을 보호된 영역, 즉 ‘상자’ 안에 가두고 나서 동작시킨다. ‘상자’는 다른 파일이나 프로세스로부터는 격리되어 내부에서 외부를 조작하는 것은 금지되고 있다.

 

출처 - [네이버 지식백과] 샌드박스 [sandbox] (IT용어사전, 한국정보통신기술협회)

 

어플리케이션 샌드박스는 어플리케이션 빌드를 위한 기술에 의존하지는 않는다. Dalvik VM은 보안의 경계가 아니며 어떤 앱이더라도 네이티브 코드로 실행이 가능하다(the Android NDK 참조). 모든 형식의 어플리케이션(Java, Native, 하이브리드)은 같은 방식으로 샌드박스 안에서 동작하게 되며 서로에 대해 동일한 보안 수준을 갖게 된다.

 

어플리케이션 서명하기(Application Signing) 


 

모든 APK(.apk 파일)는 개발자를 나타내는 개인키로 확인 서명을 해 줘야 한다. 이 확인키는 어플리케이션의 제작자를 구분해준다. 이 확인키는 인증을 받을 필요도 없으며 안드로이드 어플리케이션을 위한 자기 스스로의 증명키다. 확인키의 목적은 어플리케이션의 저자를 구분하기 위한 것이다. 이것은 시스템이 어플리케이션에 signature-level 퍼미션에 액세스 할 수 있도록 하거나 다른 어플리케이션과 동일한 리눅스 ID를 받고자 하는 요청을 할 수 있도록 해준다.

 

사용자 ID와 파일 액세스(User IDs and File Access)


 

앱을 설치할 때 안드로이드는 각 패키지에 고유의 리눅스 사용자 ID를 부여한다. ID는 프로그램이 기기에 설치되어 있는 동안 상수로써 존재하게 된다. 다른 기기에서는 같은 패키지라고 하더라도 다른 사용자 ID를 가질 수도 있다. 중요한 것은 각각의 패키지가 하나의 기기내에서는 고유한 UID를 갖는다는 사실이다.

 

보안 시스템은 프로세스 레벨에서 실행되기 때문에 두 개의 패키지가 서로 다른 유저ID를 가지고 있다면 일반적으로는 같은 프로세스에서 실행되기는 어렵다. 각 패키지가 같은 사용자 ID를 갖도록 하려면 각 패키지의 AndroidManifest.xml파일에 있는 manifest 태그의 sharedUserID 속성을 사용해야 한다. 이렇게 하면 (같은 사용자 ID를 갖게 되면) ID와 권한이 동일한 어플리케이션으로 취급받게 된다. 보안을 유지하고 싶다면 오직 두 개의 어플리케이션에서만 같은 사용자 ID를 갖도록 해야한다. 같은 사용자 ID를 갖도록 하려면 두 개의 앱은 같은 서명으로 발행되어야 하며 같은 sharedUserID를 요청해야 한다.


어플리케이션에서 저장된 모든 데이터는 해당 어플리케이션의 사용자 ID에 귀속되며 일반적으로 다른 패키지에서는 접근이 제한된다. getSharedPreferences(String, int), openFileOutput(String, int), 또는 openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory)를 사용하여 새로운 파일을 생성할 때 다른 패키지에서 파일을 읽고 쓸 수 있도록 하기 위해서 MODE_WORLD_READABLE 과/또는 MODE_WORLD_WRITEABLE 플래그를 사용할 수 있다. 이 플래그를 설정하려면 이 파일은 여러분이 만든 어플리케이션에 속해있어야 하며 설정이 완료되면 다른 어플리케이션에서도 파일을 읽고 쓸 수 있도록 전역 권한이 부여된다. 


퍼미션 사용하기(Using Permissions)



안드로이드 어플리케이션을 처음 생성하면 기본적으로 아무런 퍼미션도 갖고 있지 않다. 이것은 어플리케이션이 기기의 어떤 데이터에도 영향을 끼칠 수 없다는 것을 의미한다. 기기에서 보호되고 있는 기능을 사용할 수 있도록 하려면 어플리케이션에서 필요로 하는 권한을 AndroidManifest.xml에 하나 이상의 <uses-permission>태그로 선언해줘야 한다.


아래 예제는 수신되는 SMS를 감시하기 위해 필요한 권한을 선언하는 방법이다.


1
2
3
4
5
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.app.myapp" >

    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    ...
</manifest>

 

어플리케이션에서 요청했던 퍼미션은 어플리케이션이 설치될 때 패키지 설치관리자에 의해 부여 된다. 이런 퍼미션은 그냥 부여되는 것이 아니라 퍼미션을 요청한 어플리케이션의 서명을 확인하고 사용자에게 의견을 물어본 후에 결정되는 것이다. 어플리케이션이 실행되고 있는 동안에 사용자에게 퍼미션을 요청하는 일은 없다. 앱은 설치할 때 받은 퍼미션이 있다면 원할 때 언제든 연관된 기능을 사용할 수 있지만 퍼미션이 없다면 연관된 기능을 사용하려할 때 사용자에게 다시 물어보는 일 없이 무조건 실패하게 된다.


가끔 권한의 부재 때문에 securityException이 발생하기도 한다. 그러나 이 예외가 항상 발생하는 것은 아니다. 예를 들어, sendBroadcast(Intent) 함수는 호출을 하고 리턴이 된 후에 각 리시버에 데이터가 전달이 되면 그 때서야 권한을 확인하게 된다. 그래서 만일 권한 부재로 인한 문제가 있어도 예외가 발생했는지 알 수가 없다. 하지만 거의 대부분의 경우 권한 부재로 인한 문제는 시스템 로그에 기록이 남게 된다.


하지만 일반적인 사용자의 경우(구글 플레이 스토에서 앱을 설치하는 경우), 앱에서 요청하는 권한을 사용자가 승인하지 않으면 앱을 설치되지 않는다. 그렇게 때문에 일반적으로 권한 부재에 따른 실행 중 오류는 크게 걱정하지 않아도 된다.


안드로이드 시스템에서 제공하는 권한은 Manifest.permission에서 확인할 수 있다. 어떤 어플리케이션은 자체적인 권한을 정의하고 요구할 수도 있게 때문에 사용 가능한 모든 권한의 목록은 아니다.


자체적인 권한은 프로그램이 동작하는 도중에 여러 곳에서 요청될 수 있다.


  • 시스템에서 호출하려고 하는 순간 어플리케이션이 특정 함수를 실행하는 것을 막기 위하여
  • 액티비티를 시작할 때 어플리케이션이 다른 어플리케이션의 액티비티를 실행하는 것을 막기 위하여
  • 브로드케스트를 보내거나 받을 때 브로드캐스트를 받는 쪽이나 브로드캐스트를 보내는 쪽을 제어하기 위해
  • 컨텐트 프로바이더에 접근하여 뭔가 작업을 수행하려고 할 때
  • 서비스에 바인딩하거나 서비스를 시작할 때


Caution : 시간이 지나면 어떤 API를 실행하기 위해 이전에는 요구하지 않았던 어떤 권한을 요구할 수도 있기때문에 새로운 제한 조건이 플랫폼에 추가될 지도 모른다. 기존의 앱은 사용중인 API를 자유로이 사용할 수 있다고 생각하기 때문에 안드로이드는 앱이 새로운 플랫폼에서 문제가 되는 것을 방지하기 위해 앱의 매니페스트에 새로운 권한 요구를 추가시킨다. 안드로이드는 targetSdkVersion 값을 기반으로 새로운 권한이 추가되어야 할 지 여부를 판단하게 된다. 만일 새로운 권한이 추가된 플랫폼의 버전보다 이 값이 낮다면 권한을 추가하게 된다.


예를 들어, WRITE_EXTERNAL_STORAGE 권한은 공유된 저장 공간에 접근하는 것을 제한하기 위해 API 레벨 4부터 추가되었다. 만일, 여러분의 앱에 설정된 targetSdkVersion이 3이하라면 그 이후의 안드로이드 버전에서는 여러분의 앱에 이 권한이 추가될 것이다.


만일 이런 상황이 벌어진다면 여러분의 앱이 실제로 그 권한을 필요로 하지 않더라도 구글 플레이에서 사용자가 앱을 설치하려고 하면 권한을 요구하게 된다는 사실을 명심하기 바란다.


여러분의 앱이 필요로 하지 않는 권한이 기본적으로 요구되지 않도록 하려면 항상 targetSdkVersion이 가능한한 최대한 높게 유지되도록 업데이트 해줘야 한다. 새로운 플랫폼에 어떤 권한이 새로 추가되었는지 확인하려면 Build.VERSION_CODES 문서를 참고할 수 있다.



권한을 선언하고 요청하기(Declaring and Enforcing Permissions)


 

권한을 요청하기 위해서는 먼저 AndroidManifest.xml<permission> 태그를 사용하여 권한을 선언해야만 한다.


예를 들어, 어떤 어플리케이션이 여러분의 앱에 있고 외부 실행이 가능한 액티비티 중에 하나를 시작하려고 한다면 아래와 같이 권한을 선언해야 한다.


1
2
3
4
5
6
7
8
9
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.me.app.myapp" >

    <permission android:name="com.me.app.myapp.permission.DEADLY_ACTIVITY"
        android:label="@string/permlab_deadlyActivity"
        android:description="@string/permdesc_deadlyActivity"
        android:permissionGroup="android.permission-group.COST_MONEY"
        android:protectionLevel="dangerous" />

    ...
</manifest>

<protectionLevel> 속성은 필수 사항이며 링크된 문서에 설명된 대로 어플리케이션이 요구하는 권한을 사용자에게 어떻게 알릴 것이며 누가 권한을 가질 수 있는지 시스템에 알려준다.


<permissionGroup>은 옵션 사항이며 시스템이 사용자에게 요구 권한을 표시하는 것을 돕기 위해서만 사용된다. 여러분은 통상적으로 이 속성을 표준 시스템 그룹(android.Manifest.permission_group에 있는 그룹)에 적용하거나 여러분이 정한 특별한 경우에 사용하게 되는데 이 속성은 사용자에게 보여주는 권한 요청 UI를 단순화시키기 위해 기존 그룹에 사용하는 것이 좋다.


필요한 권한을 선언할 때는 레이블과 설명이 함께 제공되어야 한다. 이것들은 문자열 리소스로써 사용자에게 권한의 리스트(android:label)를 보여주거나 하나의 권한에 대해 자세한 설명(android:description)을 보여줄 때 사용된다.  레이블은 권한을 주어 보호하려는 기능을 잘 표현할 수 있는 한두개 정도의 단어로 구성되도록 한다. 설명은 권한이 보호하려는 게 무엇인지에 대해 두 개의 문장으로 구성해야 한다. 첫번째 문장은 권한이 의미하는 바가 무엇인지, 두번째 문장은 사용자가 어플리케이션에 해당 권한을 부여했을 때 발생할 수 있는 나쁜 점은 무엇인지에 대해 쓰여져야 한다.


다음은 CALL_PHONE이라는 권한에 대한 레이블과 설명에 대한 예제다.


1
2
3
4
5
<string name="permlab_callPhone">directly call phone numbers</string>
    <string name="permdesc_callPhone">Allows the application to call
        phone numbers without your intervention. Malicious applications may
        cause unexpected calls on your phone bill. Note that this does not
        allow the application to call emergency numbers.</string>


현재 시스템에 정의된 권한들은 설치된 앱과 쉘커맨드 adb shell pm list permissions를 통해서 알아볼 수 있다. 설치된 앱을 통해 확인하는 방법은 설정 > 어플리케이션으로 이동한 후 하나의 앱을 선택하고 아래로 스크롤하면 된다. 개발자를 위한 방법으로는 adb에 '-s'옵션을 주면 사용자가 보는 것과 비슷하게 퍼미션들이 표시된다.


$ adb shell pm list permissions -s
All Permissions:

Network communication: view Wi-Fi state, create Bluetooth connections, full
Internet access, view network state

Your location: access extra location provider commands, fine (GPS) location,
mock location sources for testing, coarse (network-based) location

Services that cost you money: send SMS messages, directly call phone numbers

...


AndroidManifest.xml을 통해서 퍼미션 적용하기(Enforcing Permissions in AndroidManifest.xml)


시스템이나 어플리케이션의 전체 컴포넌트들에 대한 액세스를 제한하기 위한 고수준의 권한은 AndroidManifest.xml을 통해서 적용이 가능하다. 이러한 모든 요청은 제한하고자 하는 컴포넌트의 android:permission 속성을 설정하고 권한의 이름을 정해줘야 한다.


Activity 퍼미션(<activity>태그에 적용한다.)은 해당 액티비티의 시작 가능여부를 제한한다. 이 때 권한은 Context.startActivity()Activity.startActivityForResult()를 호출할 때 확인한다. 만일 호출자가 요구되는 권한을 갖고 있지 않으면 호출할 때 SecurityException이 발생한다.


Service 퍼미션(<service>태그에 적용한다.)은 해당 서비스를 시작시키거나 바인딩 할 수 있는지 여부를 제한한다. 권한은 Context.startService(), Context.stopService(), Context.bindService()를 하는 동안 확인한다. 만일 호출자가 요구된 권한을 갖고 있지 않으면 SecurityException이 발생한다.


BroadcastReceiver 퍼미션(<receiver>태그에 적용한다.)은 해당 리시버에 브로드캐스트를 보내는 것을 제한한다. 권한은 Context.sendBroadcast() 함수가 리턴(종료)을 하고 목표 리시버에 브로드캐스트 내용을 전달하려고 할 때 체크하게 된다. 그래서 권한 부족이라는 문제가 발생해도 호출자에 예외상황을 알리지 않는다. 그저 인텐트만 전달하지 못 할 뿐이다. 같은 이유로 브로드캐스트리시버를 프로그램 코드에서 등록하는 Context.registerReceiver()의 사용 여부를 결정짓는 권한도 마찬가지 상황에 처하게 된다. 정반대의 상황으로 Context.sendBroadcast()를 호출할 때 브로드캐스트를 수신하는 브로드캐스트 리시버를 제한하도록 퍼미션을 사용할 수도 있다.(이후 내용 참조)


ContentProvider의 퍼미션(<provider>태그로 적용시킬 수 있다.)은 ContentProvider의 데이터에 액세스하는 것을 제한한다. (컨텐트 프로바이더는 이후에 설명될 URI 퍼미션이라 불리는 중요한 보안장치를 추가로 가지고 있다.) 컨텐트 프로바이더는 다른 컴포넌트들과 달리 두 개의 퍼미션 속성을 설정할 수 있다. android:readPermission은 프로바이더로 부터 읽는 것을 제한하고 android:writePermission은 쓰는 것을 제한한다. 만일 읽기와 쓰기에 대해 퍼미션을 요구하는 프로바이더는 쓰기 권한을 가졌다고 해도 읽기를 수행할 수 없다는 것을 명심하자. 퍼미션 확인은 프로바이더를 처음 획득하게 될 때(읽기, 쓰기 권한이 둘 다 없다면 SecurityException이 발생한다.)와 프로바이더를 통해 작업을 수행할 때 이루어진다. ContentResolver.query() 함수를 사용하려면 읽기 권한이 필요하고 ContentResolver.insert(), ContentResolver.update(), ContentResolver().delete() 함수를 사용하려면 쓰기 권한이 필요하다. 이 모든 경우에 필요한 권한을 갖고 있지 않다면 SecurityException 예외가 발생한다.


브로드캐스팅 할 때 권한 적용하기(Enforcing Permissions when Sending Broadcasts)


등록된 BroadcastReceiver에 인텐트를 보낼 때 권한을 요구하는 것(앞서 설명되었다.) 외에도 브로드캐스트를 보낼 때 받는 쪽에도 권한을 요구할 수 있다. 퍼미션 문자열과 함께 Context.sendBroadcast()를 호출하게 되면 리시버가 속한 어플리케이션은 여러분이 보낸 브로드캐스트를 받기 위해서 요구된 퍼미션을 갖고 있어야만 한다.


리시버와 브로드캐스터 양쪽 모두 퍼미션을 요구할 수 있다는 사실을 명심하기 바란다. 이 경우 관련 대상에 전달되는 인텐트는 퍼미션 검사를 통과할 수 있어야 한다.


다른 사용권한 사용하기(Other Permission Enforcement)


세분화된 퍼미션은 서비스로의 어떠한 호출에도 적용할 수 있다. 그러면 어떻게 세분화되어 있는 많은 퍼미션을 가지고 있는지 알 수 있을까? 이것은 Context.checkCallingPermission() 메서드를 통해 해결할 수 있다. 필요한 퍼미션 문자열과 함께 호출하면 현재 호출하고 있는 프로세스가 퍼미션을 갖고 있는지를 나타내는 정수를 반환한다. 이 메서드는 일반적으로 서비스나 다른 프로세스로 부터 게시된 IDL 인터페이스를 통해 호출되는 경우에만 사용할 수 있다.(마지막 문장은 무슨 뜻인지 모르겠습니다. ^^;)


퍼미션을 확인하는 다른 유용한 방법도 많이 있다. 만일 여러분이 다른 프로세스의 PID를 알고 있다면 Context.checkPermission(String, int, int)메서드를 사용하여 해당 PID를 갖고 있는 프로세스가 적절한 퍼미션을 갖고 있는지 체크할 수 있다. 만일 다른 어플리케이션의 패키지 이름을 알고 있다면 패키지 매니저의 메서드인 PackageManager.checkPermission(String, String)을 통해 해당 패키지가 지정된 퍼미션을 갖고 있는지 알아낼 수 있다.


URI 사용권한(URI Permissions)



지금까지 설명한 표준 퍼미션 시스템은 컨텐트프로바이더를 사용할 때 종종 부족한 감이 있다. 컨텐트 프로바이더는 읽고 쓰기 퍼미션으로 자신을 지키고자 할 것이다. 반면 특정 URI를 다른 어플리케이션에 넘겨 그쪽에서 작업이 이루어지도록 해야할 수도 있다. 전형적인 예로 이메일에 첨부된 파일을 들 수 있다. 메일은 민감한 개인정보이기 때문에 퍼미션을 주어 액세스로부터 보호해야 한다. 그러나 만일 첨부된 파일이 이미지이고 그 이미지를 이미지 뷰어 앱으로 보려면 어떻게 해야할까. 이미지 뷰어 앱은 모든 이메일에 대해 퍼미션을 가질 필요가 없었기 때문에 십중팔구 첨부파일을 열어보기 위한 퍼미션을 갖고 있지 않을 것이다.


이 문제에 대한 해결은 URI 퍼미션이 답이다.; 액티비티를 시작하거나 액티비티에 결과를 반환할 때 호출자 쪽에서는 Intent.FLAG_GRAND_READ_URI_PERMISSION 과/또는 Intent.FLAG_GRANT_WRITE_URI_PERMISSION을 설정할 수 있다. 이 플래그들은 인텐트의 특정 URI를 수신하는 액티비티가 프로바이더의 데이터에 대해 퍼미션을 갖고 있건 말건 상관없이 특정 URI에 대해 접근할 수 있는 권한을 부여해준다.


이 메카니즘은 사용자와의 상호작용(첨부파일을 열거나 특정 연락처를 선택하는 등의 행동)을 통해 세분화된 퍼미션에 임시로 권한을 부여하는게 가능하도록 해준다.


그러나 이런 세분화된 URI 퍼미션을 부여하는 것은 URI를 보유하고 있는 컨텐트 프로바이더와 약간의 협력이 필요하다. 컨텐트 프로바이더는 이 기능을 구현하고 android:grantUriPermissions 속성이나 <grand-uri_permission>태그를 이용해 기능을 지원한다는 사실을 외부로 알려야 한다.


Context.grantUriPermission(), Context.revokeUriPermission(), Context.checkUriPermission() 메서드를 통해 좀 더 많은 정보를 구할 수 있다.



안드로이드 보안!!! 정말 어려웠습니다.


내용에 많은 오류가 있겠지만 잘 걸러서 읽어주세요.


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

이전 글 >> 기기 호환성(Device Compatibility)

 


posted by 리치크루

API GUIDE/1. Introduction
2014. 2. 16. 19:21

저작권 표시 : 

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/practices/compatibility.html이며 안드로이드 4.4 Kitkat 기준으로 설명되었습니다.


기기 호환성(Device Compatibility)


안드로이드는 전화, 태블릿, TV에 이르기까지 매우 다양한 기기에서 동작하도록 디자인되었다. 이것은 개발자로써 수많은 잠재 고객을 갖고 있음을 의미한다. 여러분의 앱이 이러한 모든 기기에서 성공을 거두길 바란다면 사양이 가지각색인 기기들에서도 잘 동작하고 다양한 해상도의 기기들에 대해서 유연하게 대응할 수 있는 UI를 제공해야한다.


이러한 것을 쉽게 하기 위해서 개발자가 다양한 환경에 맞는 앱 리소스를 제공하면 안드로이드가 환경에 따라 적절한 앱 리소스를 사용하는 동적인 앱을 제공하는 구조로 되어 있다. 예를 들어, 앱에서 "안녕하세요."라는 문자열을 사용한다고 하면 영어를 사용하는 기기에서는 "Hello"라는 문자열을, 한국어를 사용하는 기기에서는 "안녕하세요."가 표시될 수 있도록 두 개의 문자열 리소스를 하나의 앱에 포함시키는 것이다. 그러면 안드로이드가 기기에서 사용되는 언어가 영어이면 "Hello"를, 한국어이면 "안녕하세요."를 표시하게 된다. 그래서 여러분이 여러 개의 디자인과 리소스를 준비하면 다양한 기기에 최적화된 UI를 갖는 단일 애플리케이션 패키지(APK)를 만들어 낼 수 있다.


그러나 필요하다면 여러분의 앱이 요구하는 기기의 사양을 지정하여 구글 플레이 스토어에서 여러분의 앱이 설치될 수 있는 기기들을 선별할 수 있다. 이 글에서는 어떻게 하면 여러분의 앱을 설치할 수 있는 기기를 제어할 수 있는지 그 방법을 설명한다. 여러분의 앱이 다양한 사양의 기기들에서 동작하도록 제어하는 방법에 대해 더 많은 정보를 얻고 싶다면 Supporting Different Devices를 읽어보기 바란다.


"호환성"이란 무엇을 의미하는가?
(What Does "Compatibility" Mean?)



안드로이드 개발에 대한 글을 읽다보면 여러 곳에서 "호환성(compatibility)"이라는 용어를 맞닥뜨리게 된다. 호환성이라는 것은 크게 기기의 호환성과 앱의 호환성으로 생각해 볼 수 있다.


안드로이드는 오픈 소스 프로젝트이기 때문에 어느 하드웨어 제조사에서 만들어진 기기라도 안드로이드 운영체제를 탑재시킬 수 있다. 그러나 안드로이드 실행 환경에 맞추어 개발된 앱이 올바르게 실행될 수 있어야 그 기기를 "안드로이드 호환"이라고 할 수 있다. 안드로이드 실행환경에 대한 자세한 내용은 Android compatibility program에 정의되어 있으며 각 기기는 호환성 테스트 슈트(CTS; Compatibility Test Suite)를 통과해야만 한다.


앱 개발자 입장에서는 대상 기기가 안드로이드 호환 기종인지 아닌지에 대해서는 고민할 필요가 없다. 왜냐하면 안드로이드 호환 기종만이 구글 플레이 스토어에 접속할 수 있기 때문이다. 따라서 구글 플레이 스토어를 통해 앱을 설치하였다면 그 기기는 안드로이드 호환 기기인 것이다.


그러나 모든 기기의 구성에 대해서 여러분의 앱이 호환성을 가지고 있는지 확실히 할 필요가 있다. 왜냐하면 안드로이드는 매우 다양한 기기에 탑재되어 있으며 모든 기기의 사양이 다 같지는 않기 때문이다. 예를 들어, 어떤 기기는 나침반 센서가 없을 수 도 있다. 만일 여러분이 만든 앱의 핵심기능이 나침반 센서를 반드시 필요로 한다면 그 앱은 나침반 센서가 있는 기기에서만 호환이 되는 것이다.


기기에 대한 앱의 가용성 제어하기
(Controlling Your App's Availability to Devices)



안드로이드는 플랫폼 API를 통해 앱이 활용할 수 있는 다양한 기능을 제공한다. 일부 기능은 하드웨어를 기반(나침반 센서 같은)으로 한 것이고 일부 기능은 소프트웨어를 기반(앱 위젯 같은)으로 한 것이며 또 일부는 플랫폼 버전에 기반한 것이다. 모든 기기가 모든 기능을 지원하는 것은 아니다. 그래서 여러분의 앱이 필요로하는 기능에 따라 기기에 대한 앱의 가용성을 제어해야 할 수도 있다.


여러분의 앱을 최대한 많은 사람들이 사용할 수 있게 하려면 하나의 APK로 최대한 많은 종류의 기기에서 동작할 수 있도록 노력해야 한다. 대부분의 경우에는 실행 중에 특정 기능이 사용되지 않도록 하거나 다양한 기기의 환경에 맞춰 다른 앱 리소스를 제공함으로써 보다 많은 기기에서 동작하도록 할 수 있다. 다양한 기기의 환경이라하면 스크린의 해상도가 다르다거나 사용언어가 다르다거나 하는 것을 말한다. 하지만 필요하다면 다음의 조건들을 제약사항으로 하여 여러분이 작성한 앱이 설치될 기기를 제한할 수도 있다.


기기의 지원 기능(Device features)

기기의 지원 기능에 따라 앱의 가용성을 결정할 수 있도록 하기 위해서 안드로이드는 특정한 기능마다 고유의 기능 코드를 부여하고 있다. 예를 들어, 나침반 센서의 기능 코드는 FEATURE_SENSOR_COMPASS이며 앱 위젯을 위한 기능 코드는 FEATURE_APP_WIDGETS이다.


필요하다면 매니페스트 파일에 <uses-feature>엘리먼트를 선언하여 지정된 기능을 지원하지 않는 기기는 앱이 설치되지 않도록 할 수 있다.


예를 들어, 여러분의 앱이 나침반 센서 없이는 동작할 수 없는 구조라면 다음과 같이 매니페스트에 필수 요구사항으로 나침반 센서를 선언할 수도 있다.


1
2
3
4
5
<manifest ... >
    <uses-feature android:name="android.hardware.sensor.compass"
                  android:required="true" />

    ...
</manifest>
 


구글 플레이 스토어는 여러분의 앱에서 요청하는 기능과 사용자의 기기가 지원하는 기능들을 비교하여 앱이 기기에서 올바르게 동작할 수 있는지를 판단한다. 여러분의 앱이 요구하는 여러 기능을 하나라도 지원하지 않는 기기가 있다면 그 기기는 여러분이 만든 앱을 설치할 수 없다.


그러나 기기에 없는 기능이 여러분의 앱이 동작하는데 꼭 필요한 기능이 아니라면 required 속성을 "false"로 설정하고 실행 중에(runtime) 기기의 기능을 체크할 수도 있다. 앱의 특정 기능이 해당 기기에서 동작할 수 없는 상태라면 그 기기에서만 특정 기능이 동작하지 않도록 하면 된다. 이것은 hasSystemFeature()를 호출하여 기기가 해당 기능을 지원하는지 여부를 확인함으로써 구현이 가능하다.


1
2
3
4
5
PackageManager pm = getPackageManager();
if (!pm.hasSystemFeature(PackageManager.FEATURE_SENSOR_COMPASS)) {
    // This device does not have a compass, turn off the compass feature
    disableCompassFeature();
}


구글 플레이 스토어에서 특정 기능이 없는 기기를 필터링하는 방법에 대해 더 알고 싶다면 Filters on Google Play 문서를 읽어보기 바란다.


Note : 일부 시스템 퍼미션은 암시적으로 기기의 특정 기능을 요구하기도 한다. 예를 들어 여러분의 앱이 블루투스의 사용 권한을 요청하게 되면 이는 묵시적으로 기기의 FEATURE_BLUETOOTH를 필요로 하게 된다. 이런 경우 <uses-feature>태그의 required속성을 "false"로 설정하여 필터링에 걸리지 않도록 할 수 있다. 암시적으로 기기의 특정 기능을 요구하는 상황에 대해 좀 더 많은 정보가 필요하다면 Permissions that Imply Feature Requirements를 읽어보기 바란다.


플랫폼 버전(Platform version)

어떤 기기는 안드로이드 4.0이 설치되어 있고 또 어떤 기기는 안드로이드 4.4가 설치되어 있을 수 있다. 안드로이드 플랫폼 버전이 올라가면서 이전 버전에서는 존재하지 않아서 사용할 수 없는 새로운 API가 추가되기도 한다. 각 플랫폼 버전은 어떤 API 세트가 사용되는지 알기 쉽도록 하기 위해 API 레벨이라는 것을 갖고 있다. 안드로이드 1.0의 API 레벨은 1이고 안드로이드 4.4의 API 레벨은 19로 하는 것처럼 말이다.


매니페스트에 <uses-sdk>태그에 minSdkVersion 속성을 이용해 여러분의 앱이 호환되는 최소 API 레벨을 지정할 수 있다.


예를 들어, Calendar Provider API는 안드로이드 4.0(API 레벨 14)에서 추가됐다. 만일 여러분의 앱이 이 API가 없이는 동작할 수 없다면 다음과 같이 앱이 요구하는 최소 플랫폼 버전을 API 레벨 14라고 선언해야 한다.


1
2
3
4
<manifest ... >
    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" />
    ...
</manifest>


minSdkVersion 속성을 앱이 호환되는 최소 버전을 선언하는 데 쓰이며 targetSdkVersion 속성을 앱이 최적화 되어 있는 최상위 버전을 선언하는 데 쓰인다.


새로운 안드로이드 버전은 이전 버전의 API를 사용한 앱에 대해 호환성을 갖도록 만들어진다. 그렇기 때문에 도큐먼트가 제공되는 안드로이드 API는 미래에 출시될 새로운 안드로이드 버전에서도 항상 호환성을 갖게 될 것이다.


Note : targetSdkVersion속성은 지정된 값 보다 더 높은 플랫폼 버전이 설치되어 있더라도 여러분의 앱을 설치하는 데는 문제가 없다. 하지만 이 속성은 중요하다. 왜냐하면 새로운 플랫폼 버전에서 변경된 내용을 적용할 지 말지를 결정하기 때문이다. 만일 여러분이 targetSdkVersion를 최신 버전으로 업데이트 하지 않았다면 최신 버전의 플랫폼이 동작하고 있더라도 여러분이 앱을 만들었던 이전 버전의 API가 하던 동작방식대로 실행되기 때문이다. 예를 들어, 안드로이드 4.4의 변경된 내용 중의 한 가지를 살펴보자. 안드로이드 4.4에서는 AlarmManager API로 생성된 알람은 기본적으로 정확하지 않다. 그래서 시스템에 등록된 앱의 알람들을 일괄적으로 처리하고 전력소비를 낮춘다. 하지만 여러분이 지정한 타겟 API 레벨이 19보다 낮다면 알람으로 인해 전력소비를 낮추는 잇점은 활용할 수 없게 된다.


하지만 여러분이 만든 앱의 주요 기능이 아닌 일부가 최신 버전의 API를 사용한다고 하자. 그렇다면 실행 중에 기기에 설치된 API 레벨을 확인하고 특정 기능을 구현하는데 요구되는 API 레벨보다 레벨이 낮다면 적절히 기능을 낮춰 줘야한다. 이런 경우 minSdkVersion은 주요 기능이 동작할 수 있는 최소한으로 지정하고 SDK_INT(시스템 버전)을 코드네임 상수인 Build.VERSION_CODES와 비교하여 여러분이 원하는 API 레벨인지 확인해야 한다. 아래는 그 예제다.


1
2
3
4
5
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
    // Running on something older than API level 11, so disable
    // the drag/drop features that use ClipboardManager APIs
    disableDragAndDrop();
}

스크린 구성(Screen configuration)

안드로이드는 전화기에서 태블릿, TV까지 다양한 크기의 기기에서 동작한다. 기기들을 스크린 종류에 따라 분류하기 위해 안드로이드는 각 기기들마다 두 가지 특징으로 한정짓는다. - 스크린의 사이즈(스크린의 물리적인 크기)와 스크린의 해상도(흔히 DPI라고 불리는 스크린에 있는 픽셀의 해상도.) 기기마다 서로 다은 이런 특징들을 단순히 하기 위해 안드로이드는 이러한 변수들을 쉽게 그룹화하기 위하여 다음과 같은 분류 기준을 사용한다.

  • 4종류 사이즈 : small, normal, large, xlarge
  • 몇몇 종류의 해상도 : mdpi(medium), hdpi(hdpi), xhdpi(extra high), xxhdpi(extra-extra high) 그리고 기타 종류들.

기본적으로 여러분의 앱은 모든 스크린 사이즈와 해상도에 대해 호환이 된다. 왜냐하면 시스템이 UI 레이아웃과 이미지 리소스를 각 스크린에 맞춰 적절히 조절해주기 때문이다. 하지만 사용자에게 보다 나은 환경을 제공하기 위해 다양한 스크린 사이즈에 특화된 레이아웃과 다양한 스크린 해상도에 최적화된 비트맵 이미지를 제공해야 한다.


다양한 스크린의 종류에 맞게 대체 리소스를 생성하는 법과 특정 사이즈의 스크린을 가진 기기에서 앱 설치가 제한되도록 하는 법에 대해 더 알고 싶다면 Supporting Different Screens를 읽어라.


업무적인 이유에 의한 앱의 가용성 제어
(Controlling Your App's Availability for Business Reasons)



기기의 특성 때문에 여러분이 만든 앱의 가용성을 제한하는 것 외에 업무적인 또는 법적인 문제로 앱의 가용성을 제한해야 할 때가 있다. 예를 들어 런던의 지하철 시간표를 제공하는 어떤 앱이 있다고 하자. 그렇다면 이 앱은 영국에 있는 사람이 아니라면 그다지 필요가 없다. 이런 경우 구글 플레이 스토어는 개발자 콘솔을 통해 기술적인 이유가 아닌 사용자의 로케일이나 무선통신의 상태에 따라 앱의 가용성을 제한하는 옵션을 제공한다.


하드웨어의 기능을 필요로하는 기술적인 호환성에 기인한 필터링은 앱의 APK 파일에 있는 정보에 의해 적용된다. 하지만 지리적 위치와 같은 비기술적인 문제로 인한 필터링은 구글 플레이의 개발자 콘솔을 통해 적용된다.


번역이 생각보다 쉬운 일이 아니네요.


원문을 읽을 때는 사전 찾아가며 떠듬떠듬 읽어도 무슨 뜻인지는 대충 알겠는데 우리말로 자연스럽게 만들기란 참 쉽지 않은 작업이네요.


앞으로 더 열심히 해야겠네요. 영어공부를...


다음 글 >> 시스템 퍼미션(System Permissions)

이전 글 >> 응용프로그램의 기초(Application Fundamentals)


posted by 리치크루

API GUIDE/1. Introduction
2014. 2. 16. 19:20

저작권 표시 :

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/fundamentals.html이며 안드로이드 4.4 Kitkat 기준으로 설명되었습니다.

 

응용프로그램의 기초
(Application Fundamentals)


안드로이드의 앱은 자바라는 언어로 작성된다. 안드로이드 SDK툴은 여러분이 작성한 코드와 데이터, 리소스 파일들을 안드로이드 패키지(확장자가 .apk인 파일)로 컴파일한다. 하나의 APK파일은 안드로이드 앱의 모든 내용을 포함하며 안드로이드가 설치된 기기에서 앱을 설치할 수 있도록 해 준다.


기기에 설치가 되면 각 안드로이드 앱은 그 앱만의 독립된 공간에 존재하게 된다.

  • 안드로이드 운영체제는 다수의 사용자를 지원하는 리눅스 시스템이며 각 앱은 안드로이드의 사용자가 된다.
  • 기본적으로 시스템은 각 앱마다 고유한 리눅스 사용자 ID를 부여한다.(ID는 시스템에 의해서만 사용되며 앱은 알 수 없다.) 시스템은 앱에 속한 모든 파일들에 권한 설정을 하여 사용자 ID를 부여받은 앱이 그 파일들을 접근할 수 있도록 한다.
  • 각 프로세스는 각각 별도의 전용 가상머신(VM)을 갖고 있기 때문에 앱들은 다른 앱과 독립된 공간에서 실행된다.
  • 기본적으로, 모든 앱은 전용 리눅스 프로세스 안에서만 실행된다. 안드로이드는 앱의 구성요소들 중 어느 것이라도 실행될 필요가 있으면 프로세스를 시작시킨다.

이러한 방법으로 안드로이드 시스템은 주요권한을 최소한(principle of least privilege)으로 배분한다. 이것은 각 앱이 기본적으로 작업에 필요한 권한만을 부여받게 되는 것이며 그 이상의 권한은 없다는 이야기다. 각 앱은 권한이 부여되지 않은 시스템 영역을 접근하지 못하게 되어 시스템 보안이 안전하게 유지된다.


그러나 앱끼리 데이터를 공유하거나 시스템의 서비스를 사용할 수 있는 방법은 있다.

  • 두 개의 앱이 리눅스 사용자 ID를 공유하도록 할 수 있으며 이러한 경우 각 앱은 서로의 파일들을 사용할 수 있다. 시스템의 자원을 보존하기 위해서 같은 ID를 갖는 앱들은 같은 리눅스 프로세스에서 실행되고 같은 가상머신(VM)을 사용하도록 할 수 있다.(이 때 앱들은 같은 권한을 가지고 있어야 한다.)
  • 앱은 연락처, 단문메세지, 저장장치, 카메라, 블루투스 등과 같은 장치를 사용하기 위해서 권한을 요청할 수 있다. 모든 앱은 설치할 때 이러한 권한 요청을 하고 사용자가 허락해야만 한다.

지금까지 안드로이드 앱이 시스템에서 어떻게 동작하는지에 대해서 다루었다. 이 문서의 나머지 내용은 다음과 같다.

  • 앱을 구성하는 핵심 프레임워크 컴포넌트들(요소들)
  • 앱에서 선언하는 컴포넌트들과 사용하고자 하는 장치에 대해서 기술하는 매니페스트 파일
  • 앱과 분리되어 있고 다양한 기기의 구성에 따라 간편하게 최적화할 수 있도록 하는 리소스


앱 컴포넌트(App Components)



앱 컴포넌트는 안드로이드 앱의 기본 요소들이다. 앱을 구성하는 컴포넌트가 어떤 것이냐에 따라서 시스템이 앱을 시작시키는 방법이나 시점이 달라진다. 일부 컴포넌트는 사용자나 다른 앱에 의해서 시작되지만 모든 컴포넌트가 그런 것은 아니다. 그러나 사용자나 다른 앱에 의해서 시작되지 않는 컴포넌트가 필요없는 것은 아니다. 각각의 컴포넌트는 모두 그들만의 존재 이유가 있으며 특정한 역할을 수행한다. – 각 컴포넌트는 앱의 전체적인 동작을 구성하는 그들만의 유일한 행동패턴을 가지고 있다.


앱 컴포넌트는 4개의 종류가 있다. 각 유형별로 서로 다른 사용 목적과 라이프사이클(lifecycle; 역주 – 생성되고 파괴되는 주기)을 갖고 있다.


4종류의 앱 컴포넌트는 다음과 같다.(역주 – 4종류는 액티비티, 서비스, 브로드캐스트 리시버, 컨텐트 프로바이더)


액티비티(Activities)

액티비티는 한 개의 화면을 말한다. 예를 들어, 이메일 앱은 새로운 이메일의 리스트를 보여주는 액티비티, 이메일을 작성하는 액티비티, 이메일을 읽을 수 있는 액티비티를 가질 수 있다. 액티비티들은 모두 합쳐져서 이메일 앱을 구성하고 있지만 각기 독립성을 가지고 있다. 무슨 말이냐하면 다른 앱이 이 중 하나의 액티비티를 실행시킬 수 있다는 말이다.(물론 이메일 앱에서 허락을 해줘야만 가능하겠지만) 예를 들어, 카메라 앱은 촬영한 사진을 공유하기 위해서 새로운 이메일을 작성하는 액티비티를 실행시킬 수 있다.


액티비티는 보통 Activity의 서브클래스를 구현한다. 이에 대해서 좀 더 자세히 알려면 Activities 개발자 가이드를 참고하기 바란다.


서비스(Services)

서비스 컴포넌트는 백그라운드에서 오랜시간 동작하거나 리모트 프로세스를 수행하는 컴포넌트다. 서비스는 사용자 인터페이스. 즉, 화면을 제공하지 않는다. 예를 들어, 사용자가 다른 앱을 사용 중 일 때 백그라운드에서 음악을 재생하거나 사용자가 액티비티를 동작시키는 행위에 방해를 하지 않으면서 네트워크로 데이터를 가져오는 일을 수행하는 것이 서비스 컴포넌트다. 액티비티 같은 다른 컴포넌트는 서비스를 시작하고 실행되도록 하거나 서비스와 상호작용하기 위해서 바인딩하기도 한다.


서비스는 Service클래스의 서브클래스를 만들어 구현하며 좀 더 자세한 내용은 Services 개발자 가이드를 참조하기 바란다.


컨텐트 프로바이더(Content providers)

컨텐트 프로바이더는 공유되는 앱의 데이터를 관리한다. 여러분은 데이터를 파일, SQLite 데이터베이스, 웹 등의 앱이 접근할 수 있는 영구 저장소에 저장할 수 있다. 컨텐트 프로바이더를 통해 다른 앱이 데이터에 쿼리를 날리거나 수정할 수도 있다.(물론 이런 경우 컨텐트 프로바이더가 허락을 해줘야 하겠지만) 예를 들어, 안드로이드 시스템은 사용자 연락처 정보를 관리하고 컨텐트 프로바이더를 제공한다. 그래서 어떤 앱이든 권한만 충분하다면 사람들의 연락처 정보를 읽고 쓸 수 있다.( ContactsContract.Data 같은 것을 통해서 할 수 있다.)


컨텐트 프로바이더는 공유되지 않는 사적인 데이터를 읽고 쓰는데도 유용하다. 예를 들어, Note Pad 샘플 같은 경우에는 노트내용을 저장하는데 컨텐트 프로바이더를 사용한다.


컨텐트 프로바이더는 ContentProvider를 상속받아 구현하며 다른 앱이 트랜젝션을 수행할 수 있도록 표준 API세트를 구현해 줘야만 한다. 좀 더 많은 것이 알고 싶다면 Content Providers 개발자 가이드를 참고하기 바란다.


브로드캐스트 리시버(Broadcast receivers)

브로드캐스트 리시버는 시스템 전체에 브로드캐스팅되는 알림에 응답하도록 하는 컴포넌트다. 많은 알림(예를 들어, 화면이 꺼지거나 배터리 잔량이 얼마 없거나 사진이 새로 촬영되었다거나 하는 등의)이 시스템에서 발생한다. 앱들도 브로드캐스팅을 할 수 있다. 예를 들어, 다른 앱에서도 쓸만한 데이터를 다운로드했을 때 알려주는 알림 같은 경우에 브로드캐스팅을 할 수 있다. 브로드 캐스트 리시버는 화면에 표시되는 UI를 가지고 있지는 않지만 브로트캐스팅 이벤트가 발생했을 때 사용자에게 알리기 위하여 상태바 알림을 생성할 수 있다. 그러나 일반적으로 브로드캐스트 리시버는 이벤트를 다른 컴포넌트에 전달해주는 관문같은 존재이며 최소의 작업만을 한다. 예를 들어, 이벤트가 발생하면 그 이벤트에 맞는 작업을 하는 서비스를 초기화 시켜주기만 하는 것이다.


브로드 캐스트 리시버는 BroadcastReceiver를 상속받아 구현하며 각 브로드캐스트 리시버는 Intent 객체를 전달한다. 좀 더 많은 정보는 BroadcastReceiver 클래스를 보면된다.


안드로이드 시스템의 큰 특징은 어떤 앱이라도 다른 앱의 컴포넌트를 시작시킬 수 있다는 것이다. 예를 들어 사용자가 기기에 장착된 카메라로 사진을 촬영하도록 하고 싶다면 기기에는 아마도 사진 촬영앱이 이미 존재할테고 여러분이 만든 앱에서는 사진촬영 기능을 새롭게 구현하지 않고 그 앱을 사용하면 된다. 이 때 앱을 프로젝트에 포함시키거나 링크를 걸거나 할 필요는 없다. 그냥 사진찍는 앱에 있는 액티비티를 시작하기만 하면 된다. 촬영이 완료되면 사진은 여러분이 만든 앱으로 반환되며 그냥 반환된 사진을 사용하기만 하면 된다. 그러면 사용자에게는 카메라 앱이 마치 여러분이 작성한 앱의 일부인 것 처럼 보여진다.


시스템이 컴포넌트를 실행시킬 때에는 먼저 그 컴포넌트가 속해있는 앱을 먼저 실행시키고(실행중이 아니라면) 컴포넌트의 실행을 위한 클래스의 인스턴스를 만든다. 예를 들어, 여러분의 앱이 사진을 찍는 앱의 액티비티를 실행시키려고 하면 그 액티비티는 여러분의 앱이 실행되는 프로세스가 아니라 카메라 앱의 프로세스에서 실행되는 것이다. 그래서 대부분의 시스템과는 달리 안드로이드 앱은 하나의 프로그램 진입점(Entry point)을 갖지 않는다(다른 시스템에서의 프로그램과 달리 main() 함수가 없다.).


시스템은 각 앱이 서로 다른 프로세스에서 실행되도록 하며 서로의 프로세스에 접근할 수 있는 권한을 제한하고 있기 때문에 여러분이 작성한 앱에서 다른 앱의 컴포넌트를 실행시킬 수가 없다. 그래서 다른 앱의 컴포넌트를 실행시키려면 그 컴포넌트에 맞는 인텐트를 시스템에 전달하여야 한다. 그러면 시스템은 여러분의 앱을 대신해서 그 컴포넌트를 실행시켜 준다.


컴포넌트 실행시키기(Activating Components)


4종류의 컴포넌트 중에서 3가지 - 액티비티, 서비스, 브로드캐스트 리시버 - 는 인텐트라고 하는 비동기 메세지에 의해서 실행시킬 수 있다. 인텐트는 컴포넌트가 여러분의 앱에 속해있건 아니건 서로 다른 컴포넌트를 묶어주는 역할을 한다.(인텐트를 컴포넌트간의 메신저라고 생각하면 되겠다.)


인텐트는 Intent오브젝트로 생성되며 특정 컴포넌트 또는 특정 타입의 컴포넌트를 실행시킬 수 있는 메세지를 담고 있다. 인텐트는 때에 따라 명시적일 수도 있으며 암시적일 수도 있다.


액티비티와 서비스를 위한 인텐트는 수행해야 하는 작업(예를 들어 "view" 또는 "send" 같은 작업)이 무엇인지에 대해 정의하고 있으며 더 세부적인 작업내용(URI)까지도 포함할 수 있다. 어떤 경우에는 결과를 돌려받기 위해 액티비티를 실행시키기도 하며 이런 경우 액티비티는 그 결과는 Intent에 담아 돌려준다.(예를 들어, 사용자가 연락처를 선택하고 그 결과를 돌려받는 경우가 그렇다. 이 때 반환된 인텐트는 선택된 연락처의 URI를 포함하게 된다.)


역주 :

URI(Uniform resource identifier)는 URL(Uniform resource locator)과 URN(Uniform resource name)을 포함하고 한 단계 더 큰 개념이다.


URL은 실제의 네트워크 경로를 가리키며 네트워크상의 리소스 접근에 사용된다.


URN은 자원에 대해 영속적이고 유일하며 위치에 독립적인 이름을 제공하기 위해 존재한다.


브로드캐스트 리시버를 위한 인텐트는 어떤 상황에 대한 내용만 포함하고 있으면 된다.(예를 들어, 기기의 배터리가 얼마남지 않았다면 "battery is low"라는 문자열만 포함시켜서 브로드캐스팅하면 된다.)


마지막으로 남은 컴포넌트 타입인 컨텐트 프로바이더는 인텐트가 아닌 컨텐트 리졸버(Content Resolver)를 통해 실행된다. 컨텐트 리졸버는 컨텐트 프로바이더와 모든 직접적인 소통을 하기 때문에 컴포넌트가 프로바이더에 지시할 어떤 작업이 있으면 프로바이더를 직접 실행시키지 말고 컨텐트 리졸버를 통해서 해야만 한다. 컴포넌트가 프로바이더를 직접적으로 실행시키 못하도록 이렇게 분리시켜 놓은 이유는 보안상의 문제 때문이다.


각 종류의 컴포넌트를 실행시킬 때에는 각기 다른 함수를 사용하여야 하며 그 함수들은 다음과 같다.


인텐트에 대해 보다 자세히 알고 싶으면 Intents and Intent Filters를 참고하고 다른 컴포넌트에 대해 더 알고 싶다면 Activities, Services, Broadcast Receiver, Content Providers를 참고하기 바란다.


매니페스트 파일(The Manifest File)



안드로이드 시스템이 앱 내의 컴포넌트를 실행시키는 것도 그냥 되는 것은 아니다. 어떤 앱에 어떤 컴포넌트가 존재하는지 알기 위해서는 각 앱의 "AndroidManifest.xml"이라는 매니페스트 파일을 읽어야만 한다. 그렇기 때문에 여러분이 앱을 작성할 때는 프로젝트의 루트에 있는 이 파일에 앱 내의 모든 컴포넌트에 대해 기록해 두어야만 한다.


매니페스트는 앱의 컴포넌트를 선언하는 것 뿐만아니라 여러가지 역할을 하게 되며 그 역할은 다음과 같다.

  • 앱이 필요로하는 퍼미션(권한)을 설정한다. 예를 들어, 인터넷 사용 또는 연락처 열람등과 같은 권한을 말한다.
  • 앱에 사용된 API가 요구하는 최소 API 레벨을 설정한다.
  • 앱이 사용을 요구하는 기기의 하드웨어 기능이나 소프트웨어 기능(카메라, 블루투스서비스, 멀티터치 스크린 등)을 선언한다.
  • Google Maps library 같은 외부 라이브러리에 연결해서 사용해야 하는 경우 외부 API 라이브러리를 선언한다.
  • 이외에도 더 많은 일을 한다.


컴포넌트 선언(Declaring components)

매니페스트가 하는 가장 주요한 일은 앱에서 사용되는 컴포넌트에 대해서 시스템에 알려주는 것이다. 예를 들어, 매니페스트 파일은 다음과 같이 액티비티를 선언할 수 있다.


1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application android:icon="@drawable/app_icon.png" ... >
        <activity android:name="com.example.project.ExampleActivity"
                  android:label="@string/example_label" ... >

        </activity>
        ...
    </application>
</manifest>

<application>엘리먼트 내의 android:icon 속성은 앱을 나타내는 아이콘 리소스를 지정한다.


<activity>엘리먼트 내의 android:name 속성은 Activity서브클래스의 전체 클레스네임을 지정하며 android:label 속성은 사용자에게 보여지는 액티비티의 라벨로 사용될 문자열을 지정한다.


모든 앱 컴포넌트는 다음과 같은 방법으로 선언해야만 한다.

  • 액티비티는 <activity>엘리먼트를 사용하여 선언한다.
  • 서비스는 <service>엘리먼트를 사용하여 선언한다.
  • 브로드캐스트 리시버는 <receiver>엘리먼트를 사용하여 선언한다.
  • 컨텐트 프로바이더는 <provider>엘리먼트를 사용하여 선언한다.

소스안에 포함은 되어 있지만 매니페스트에 선언되지 않은 액티비티, 서비스, 컨텐트 프로바이더는 시스템에서 알 수 있는 방법도 없고 절대로 실행될 수도 없다. 하지만 브로드캐스트 리시버는 매니페스트에 선언하거나 코드내에서 BroadcastReceiver 오브젝트를 사용하여 동적으로 생성하고 registerReceiver()를 호출하여 시스템에 등록시킬 수도 있다.


매니페스트 파일을 어떻게 구성하는지에 대해서 더 알고 싶으며 The AndroidManifest.xml File 문서를 참조한다.


컴포넌트의 수행능력 선언하기(Declaring component capabilities)

앞서 컴포넌트 실행시키기에서 언급한대로 액티비티, 서비스, 브로드캐스트는 Intent를 사용해서 시작시킬 수가 있다. 인텐트 내에 타겟 컴포넌트를 컴포넌트 클래스 이름을 사용하여 명시적으로 지정함으로써 그렇게 할 수 있었다. 그러나 인텐트의 진짜 힘은 암시적인 인텐트의 컨셉에 있다. 암시적인 인텐트는 단순히 수행할 액션이 무엇인지만 정하면 된다.(선택적으로 액션 수행을 위한 데이터를 더해 줄 수도 있다.) 그러면 시스템에서는 그 액션을 수행할 수 있는 컴포넌트를 찾아준다. 만일 인텐트의 액션을 수행할 수 있는 컴포넌트가 여러 개 있다면 사용자가 그 중 하나를 선택하도록 해 준다.


시스템이 인텐트에 응답할 수 있는 컴포넌트를 찾아내는 것은 기기내의 다른 앱이 매니페스트에서 제공하는 인텐트 필터가 있기 때문에 가능하다. 


매니페스트 내에 액티비티를 선언할 때는 선택적으로 앱이 어떤 일을 수행할 수 있는지를 정의하는 인텐트 필터를 포함시킬 수 있다. 매니페스트 파일에서 <intent-filter>엘리먼트를 사용해서 인텐트 필터를 정의할 수 있다.


예를 들어, 새로운 이메일을 작성할 수 있는 앱을 만들었다면 다음과 같이 "send"인텐트에 응답할 수 있는 인텐트 필터를 선언할 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
<manifest ... >
    ...
    <application ... >
        <activity android:name="com.example.project.ComposeEmailActivity">
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <data android:type="*/*" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>
</manifest>
 
  

이렇게 매니페스트를 작성한 후에, 다른 앱에서 startActivity()함수를 통해 ACTION_SEND를 보내면 사용자가 새로운 이메일을 작성하고 전송할 때 여러분이 만든 액티비티가 실행될 수도 있다.


인텐트 필터에 대해서 좀 더 알고 싶다면 인텐트와 인텐트 필터(Intents and Intent Filters) 문서를 참조하기 바란다.


앱의 요구사항 선언하기(Declaring app requirements)

안드로이드를 탑재한 기기는 매우 다양하다. 그리고 그 기기들이 모두 같은 사양과 기능을 가지고 있는 것은 아니다. 그래서 여러분이 만든 앱에서 꼭 필요한 사양을 만족하지 못하는 기기는 걸러내야 하는데 그런 작업은 매니페스트 파일을 통해서 가능하다. 대부분의 이러한 선언은 안드로이드 시스템이 참조하지는 않고 Google Play같은 외부 서비스에서 사용자가 본인들의 기기에 맞는 앱을 검색할 때 필터링용으로 사용된다.


예를 들어, 여러분의 앱이 카메라와 안드로이드 2.1(API Level 7) 이후 버전에서만 제공하는 API를 필요로한다면 그 요구사항을 다음과 같이 매니페스트 파일에 선언할 수 있다.


1
2
3
4
5
6
<manifest ... >
    <uses-feature android:name="android.hardware.camera.any"
                  android:required="true" />

    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" />
    ...
</manifest>


이제 카메라가 없거나 설치된 안드로이드 버전이 2.1보다 낮다면 구글 플레이 스토어를 통해서는 여러분의 앱을 설치할 수가 없다.


그러나 여러분의 앱이 카메라를 사용하기는 하지만 필수적인 것이 아니라면 매니페스트에 그 상태도 기록해 줄 수가 있다. 이런 상황에서는 required 속성을 "false"로 설정해야 한다. 대신에 앱이 실행되는 동안에는 기기에 카메라가 있는지 없는지를 코드상에서 체크하고 카메라 관련 기능을 적절히 제어하는 방법을 구현해 주어야 한다.


여러가지 다양한 기기에서 여러분의 앱이 안정적으로 동작할 수 있도록 관리하는 방법에 대해 더 많은 정보가 필요하다면

기기 호환성(Device Compatibility) 문서를 참고하기 바란다.


앱 리소스(App Resources)


안드로이드 앱은 단순히 코드외에도 이미지, 오디오 파일 그리고 앱의 시각적인 구성에 관련된 여러가지 리소스들로 구성된다. 예를 들어, XML파일에 애니메이션, 메뉴, 스타일, 컬러, 레이아웃과 같은 것들을 정의해야한다. 앱리소스를 이용하면 코드 수정 없이도 기기들의 다양한 구성(언어나 스크린 해상도 등과 같은)에 맞게 앱을 업데이트 할 수 있다.


안드로이드 프로젝트에 포함된 모든 리소스는 SDK빌드 도구로부터 정수로 된 유일 ID를 부여받는다. 여러분은 이 ID를 소스코드나 XML에서 사용할 수 있다. 예를 들어, res/drawable/ 디렉토리에 저장된 logo.png 파일이 있다면 R.drawable.logo라는 ID를 부여받게 될 것이고 이것을 통해 이미지를 참조하고 UI상에 표시할 수 있다.


소스코드와 분리된 리소스를 사용함으로써 누릴 수 있는 가장 큰 이점 중 하나는 기기마다 다른 상황에 맞는 리소스를 제공할 수 있다는 것이다. 예를 들어, XML파일에 UI표시용 문자열을 선언했다면 다른 언어로 번역해서 다른 파일로 저장해 둘 수 있다. 그리고나서 언어 구분자에 맞는 디렉토리를 만들고(불어의 문자열들은 res/values-fr/) 기기에 파일을 옮긴다. 그리고 기기의 언어설정을 맞추면 안드로이드 시스템이 UI에 맞는 적절한 언어를 적용해 준다.


안드로이드는 앱리소스를 위한 많은 다른 구분자를 지원한다. 앞서 설명한 언어를 위한 구분자외에도 기기의 회전방향이나 스크린 사이즈에 맞는 구분자도 있다. 세로로 긴 방향의 화면과 가로로 긴 화면은 당연히 화면 구성이 달라진다. 만일 두 개의 다른 레이아웃에 맞춰 리소스를 제공한다면 기기의 방향성에 맞춰 시스템이 적절한 레이아웃을 보여 줄 수 있게 된다.


앱 리소스에 대해 더 알고 싶다면 Providing Resources 문서를 참고하기 바란다.


드디어 한 개 끝냈네요.


우습게 보고 덤볐는데... 안드로이드 개발자 사이트를 다 번역한다면 두툼한 책이 한권 될지도 모르겠군요.


힘내서 다음 문서로 넘어가보도록 하겠습니다. 파이팅~~!


다음 글 >> 기기 호환성(Device Compatibility)

 

'API GUIDE > 1. Introduction' 카테고리의 다른 글

시스템 퍼미션(System Permissions)  (0) 2014.02.16
기기 호환성(Device Compatibility)  (0) 2014.02.16
posted by 리치크루