Primary/Android

[Android Fundamentals] Overview (2)

해스끼 2024. 7. 4. 23:43

Manifest

안드로이드에는 앱의 진입점 역할을 하는 4개의 주요 컴포넌트가 있다. 그런데 이 진입점들이 실제로 시스템과 상호작용하기 위해서는 시스템에게 컴포넌트의 존재를 알려야 한다.  그 역할을 하는 것이 바로 ``AndroidManifest.xml``이다.

 

Manifest는 컴포넌트를 정의하는 역할 외에도 여러 기능을 수행한다.

  • 앱에 필요한 사용자 권한을 정의한다. (인터넷, 연락처 접근 등)
  • 앱이 설치될 수 있는 API 최소 레벨을 정의한다. 다만 API 레벨은 manifest보다는 ``build.gradle`` 파일에서 선언하는 경우가 대다수이다.
  • 앱이 사용하는 하드웨어/소프트웨어 기능을 정의한다. (카메라, 블루투스 등)

주로 앱의 정적인 속성을 정의한다고 볼 수 있다. 권한이나 기능 등...

Manifest에 컴포넌트 정의

하지만 manifest의 가장 중요한 역할은 컴포넌트를 정의하는 것이다. 예를 들어 다음과 같이 activity를 정의할 수 있다.

<?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>``에서는 앱 아이콘, 이름 등 앱의 정적인 속성을 지정할 수 있다. 그런데 이 속성들은 api 레벨처럼 ``build.gradle``에서 지정하는 경우도 많다.

 

``<activity>``에서는 activity의 패키지 경로를 ``android:name`` 속성으로 지정하고, ``android:label`` 속성을 통해 사용자에게 보일 수 있는 라벨을 지정한다.

 

컴포넌트를 정의하는 방법은 다음과 같다.

  • Activity: ``<activity>``
  • Service: ``<service>``
  • Broadcast receiver: ``<receiver>``
  • Content provider: ``<provider>``

Activity, service, content provider를 manifest에 정의하지 않으면 시스템이 컴포넌트의 존재를 알 수 없고, 따라서 실행될 수 없다. 그러나 broadcast receiver는 manifest에 정의하지 않아도 ``Context.registerReceiver()``로 동적으로 정의할 수 있다.

컴포넌트의 범위 설정

Activity, service, broadcast receiver를 실행할 때 ``Intent``를 사용한다. 컴포넌트의 이름을 명시적으로 지정하면 explicit intent이고, 이름을 직접 지정하는 대신 컴포넌트가 수행할 작업의 종류만을 지정하면 implicit intent이다. Implicit intent를 보내면 시스템이 해당 intent가 지정하는 작업을 할 수 있는 앱을 찾고, 그러한 앱이 여러 개라면 사용자가 앱을 선택한다.

 

주의: Service를 실행할 때에는 explicit intent를 사용하자. 백그라운드로 실행되는 service 특성상 intent를 어떤 service가 처리하는지 확인하기 어렵기 때문에 보안상 문제가 생긴다. Android 5.0(API 21) 이상부터는 ``bindService()``에 implicit intent를 사용하면 exception이 발생한다. 마찬가지로 service에 intent filter를 선언해서는 안 된다(implicit intent를 받을 수 있기 때문).

 

시스템은 intent를 받으면 각 앱의 intent filter를 확인하며 해당 intent를 실행할 수 있는 앱을 찾는다. Intent filter는 컴포넌트가 처리할 수 있는 intent의 종류를 알려준다.

 

예를 들어 이메일 작성 activity가 있는 이메일 앱을 개발한다면, 이메일 작성 activity가 "send" intent를 처리할 수 있도록 intent filter를 선언할 수 있다.

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

``<intent-filter>``의 ``<action>``에서 intent의 종류를 지정할 수 있다.

앱의 요구사항 정의

앱이 요구하는 하드웨어/소프트웨어 기능을 manifest에 정의할 수 있다. 컴포넌트 부분과 다르게 이 부분은 시스템이 직접 참고하지는 않지만, Google Play에서 앱을 분류할 때 참고자료로 활용하는 등 외부 서비스에서 참조할 수 있다.

 

예를 들어, 앱이 카메라 기능을 사용한다는 것을 다음과 같이 나타낼 수 있다.

<manifest ... >
    <uses-feature android:name="android.hardware.camera.any"
                  android:required="true" />
    ...
</manifest>

Google Play에서는 ``<uses-feature>``에 선언된 기능을 지원하지 않는 기기에 앱을 설치할 수 없다. 그러나 해당 기능이 필수가 아니라면 ``android:required="false"``로 선언할 수 있고, 런타임에서 카메라가 있을 때와 없을 때를 처리하면 된다.

리소스

앱은 코드뿐만 아니라 이미지, 오디오, 애니메이션 등 다양한 리소스로 구성된다. 리소스 파일을 활용하면 화면 크기에 맞는 해상도의 이미지를 제공하고, 언어별 문구를 제공하는 등 앱을 기기별로 최적화할 수 있다.

 

각 리소스에는 유일한 정수 ID가 할당된다. 그러나 코드에서 ID 값을 직접 활용하기는 어려우므로, 대신 리소스와 ID를 매핑한 ``R`` 클래스를 활용한다. 예를 들어 ``res/drawable``에 저장된 ``logo.png``의 ID는 ``R.drawable.logo``로 접근할 수 있다.

 

리소스를 활용하면 기기별 설정에 맞는 서비스를 제공할 수 있다. 위에 말했던 것처럼 언어별로 다른 문구를 제공하는 것이 대표적. 기기별로 서로 다른 레이아웃을 사용할 수도 있다.

 

물론 Compose를 활용하면 레이아웃이나 애니메이션을 XML로 선언할 일은 거의 없어진다.

참고자료

 

애플리케이션 기본 항목  |  Android Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. 애플리케이션 기본 항목 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Android 앱은 Kotlin, Java 프로그

developer.android.com

다음 글부터는 activity를 시작으로 좀 더 자세하게 공부해 보자.