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