-
Android Native App 개발 - ②Android 2023. 4. 17. 11:43
들어가기 전에
안드로이드 개발에 앞서 공식문서에서 안내하는 App 아키텍처 가이드를 확인 해보려고한다.
모바일 앱 사용자 환경
대부분의 경우 데스크톱 앱에는 데스크톱 또는 프로그램 런처의 단일 진입점이 있으며 하나의 모놀리식 프로세스로 실행된다. 반면에 Android 앱의 구조는 훨씬 복잡히다. 일반적인 Android 앱에는 활동, 프래그먼트, 서비스, 콘텐츠 제공업체, broadcast receiver 를 비롯하여 여러 앱 구성요소가 포함된다.
개발자는 앱 매니페스트에서 이러한 앱 구성요소 대부분을 선언하며, Android OS 에서 이 파일을 사용하여 기기의 전반적인 사용자 환경에 앱을 통합하는 방법을 결정한다. 올바르게 작성된 Android App은 여러 구성요소를 포함하며, 사용자는 짧은 시간 내에 여러 앱과 상호작용할 때도 많다는 점을 고려하면, 앱은 사용자 중심의 다양한 워크플로 및 작업에 맞게 조정될 수 있어야 한다.
예를 들어 좋아하는 소셜 네트워크 앱에서 사진을 공유하면 어떻게 되는지 생각해 보자.
- 앱이 카메라 인텐트를 트리거한다. 그러면 Android OS 에서 요청을 처리하기 위해 카메라 앱을 실행한다. 이 시점에 사용자는 소셜 네트워크 앱에서 나간 상황이지만 사용 환경은 끊김 없이 연결된다.
- 카메라 앱은 파일 선택기 실행처럼 다른 인텐트를 트리거하여 다른 앱을 실행할 수도 있다.
- 이후에 사용자가 다시 소셜 네트워크 앱으로 돌아와서 사진을 공유한다.
이 과정에서 언제든지 전화나 알림에 의해 사용 환경이 중단될 수 있다. 사용자는 이 중단에 대응하고 난 후에 사진 공유 프로세스로 돌아가서 작업을 계속할 수 있기를 기대한다. 휴대기기에서는 이렇게 앱을 바꾸는 동작이 일반적이므로, 앱에서 이러한 흐름을 올바르게 처리해야 한다.
또한 휴대기기는 리소스가 제한되어 있으므로, 운영체제에서 새로운 앱을 위한 공간을 확보하도록 언제든지 일부 앱 프로세스를 종료할 수도 있다.
이러한 환경 조건을 고려해 볼 때 앱 구성요소는 개별적이고 비순차적으로 실행될 수 있으며, 운영체제나 사용자가 언제든지 앱 구성요소를 제거할 수 있다. 이러한 이벤트는 직접 제어할 수 없기 때문에 앱 구성요소에 앱 데이터나 상태를 저장해서는 안 되며 앱 구성요소가 서로 종속되면 안 된다.
일반 아키텍처 원칙
앱 데이터와 상태를 저장하는 데 앱 구성요소를 사용할 수 없다면 앱을 어떻게 설계해야 할까요?
관심사 분리
가장 중요한 원칙은 관심사 분리이다. Activity 또는 Fragment 에 모든 코드를 작성하는 실수는 흔히 일어난다. 이러한 UI 기반의 클래스는 UI 및 운영체제 상호작용을 처리하는 로직만 포함해야 한다. 이러한 클래스를 최대한 가볍게 유지하여 많은 수명 주기 관련 문제를 피할 수 있다.
Activity 및 Fragment 구현은 소유의 대상이 아니며 Android OS와 앱 사이의 계약을 나타내도록 이어주는 클래스일 뿐이다. OS는 사용자 상호작용을 기반으로 또는 메모리 부족과 같은 시스템 조건으로 인해 언제든지 클래스를 제거할 수 있다. 만족스러운 사용자 환경과 더욱더 수월한 앱 관리 환경을 제공하려면 이러한 클래스에 대한 의존성을 최소화하는 것이 좋다.
모델에서 UI 도출하기
또 하나의 중요한 원칙은 모델에서 UI를 도출해야 한다는 것이다. 가급적 지속적인 모델을 권장한다. 모델은 앱의 데이터 처리를 담당하는 구성요소로, 앱의 View 객체 및 앱 구성요소와 독립되어 있으므로 앱의 수명 주기 및 관련 문제의 영향을 받지 않는다.
지속 모델이 이상적인 이유는 다음과 같다.
- Android OS에서 리소스를 확보하기 위해 앱을 제거해도 사용자 데이터가 삭제되지 않는다.
- 네트워크 연결이 취약하거나 연결되어 있지 않아도 앱이 계속 작동한다.
데이터 관리 책임이 잘 정의된 모델 클래스를 기반으로 앱을 만들면 쉽게 테스트하고 일관성을 유지할 수 있다.
권장 앱 아키텍처
모든 시나리오에 최적화된 하나의 앱 작성 방법은 없다. 하지만 이 권장 아키텍처는 대부분의 상황 및 워크플로에 유용한 출발점이 될 것이다. 일반 아키텍처 원칙을 준수하는 Android 앱 작성 방법을 사용하고 있다면 방법을 변경할 필요가 없다.
사용자 프로필을 표시하는 UI를 제작한다고 가정해보자. 이 경우 비공개 백엔드 및 REST API를 사용하여 특정 프로필의 데이터를 가져온다.
개요
먼저 다음 다이어그램은 앱을 설계한 이후 모든 모듈이 서로 어떻게 상호작용해야 하는지 보여준다.
각 구성요소가 한 수준 아래의 구성요소에만 종속됨을 볼 수 있다. 예를 들어 활동 및 프래그먼트는 뷰 모델에만 종속된다. 저장소는 여러 개의 다른 클래스에 종속되는 유일한 클래스이다. 이 예에서 저장소는 지속 데이터 모델과 원격 백엔드 데이터 소스에 종속된다.
이 설계는 일관되고 즐거운 사용자 환경을 제공한다. 사용자가 앱을 마지막으로 닫은지 몇 분 후 또는 며칠 후에 다시 사용하는지와 관계없이 앱이 로컬에 보존하는 사용자의 정보가 바로 표시된다. 이 데이터가 오래된 경우 앱의 저장소 모듈이 백그라운드에서 데이터 업데이트를 시작한다.
권장 아키텍처 다이어그램에서 보면 위에서 아래로 작업이 진행되고 현재 모듈은 이전 모듈을 관여하지 않는것을 알 수 있다. 위에서 설명한 관심사 분리를 통해 각 모듈이 해야하는 로직만을 작성하는 방향을 제사한다.
Android 애플리케이션 기본 항목
Android 앱은 Kotlin, Java, C++ 언어를 사용하여 작성할 수 있다. Android SDK 도구는 모든 데이터 및 리소스 파일과 함께 코드를 컴파일하여 하나의 APK를 만든다. Android 패키지는 접미사가 .apk인 아카이브 파일이다. 한 개의 APK 파일에는 Android 앱의 모든 콘텐츠가 들어 있으며, Android로 구동하는 기기가 앱을 설치할 때 바로 이 파일을 사용한다.
Android 앱은 다음과 같은 Android 보안 기능으로 보호된다
- Android 운영체제는 멀티유저 Linux 시스템으로, 여기서 각 앱은 각기 다른 사용자와 같다.
- 기본적으로 시스템이 각 앱에 고유한 Linux ID를 할당한다(이 ID는 시스템만 사용할 수 있으며 앱에서는 인식하지 못함). 시스템은 앱 안의 모든 파일에 대해 권한을 설정하여 해당 앱에 할당된 사용자 ID만 이에 액세스할수 있도록 한다.
- 각 프로세스에는 자체적인 가상 머신(VM)이 있고, 그렇기 때문에 한 앱의 코드가 다른 앱과는 격리된 상태로 실행된다.
- 기본적으로 모든 앱이 앱 자체의 Linux 프로세스에서 실행된다. Android 시스템은 앱의 구성 요소 중 어느 하나라도 실행해야 하는 경우 프로세스를 시작하고, 더 이상 필요 없거나 시스템이 다른 앱을 위해 메모리를 복구해야 하는 경우 해당 프로세스를 종료한다.
Android 시스템은 이런 방식으로 최소 권한의 원리를 구현한다. 다시 말해, 각 앱은 기본적으로 자신의 작업을 수행하기 위해 필요한 구성 요소에만 액세스 권한을 가지고 그 이상은 혀용되지 않는다.
그러나 앱이 다른 앱과 데이터를 공유하고 시스템 서비스에 액세스하는 방법은 여러 가지가 있다.
- 두 개의 앱이 같은 Linux 사용자 ID를 공유하도록 설정할 수 있다. 이 경우 두 앱은 서로 파일에 액세스할 수 있게 된다. 시스템 리소스를 절약하기 위해 사용자 ID가 동일한 앱들이 같은 Linux 프로세스에서 실행되고 같은 VM을 공유하도록 설정할 수도 있다. 또한 이러한 앱은 같은 인증서로 서명해야 한다.
- 앱은 사용자의 연락처, SMS 메시지, 마운트 가능한 저장소(SD 카드), 카메라, 블루투스를 비롯한 여러 가지 기기 데이터에 액세스할 권한을 요청할 수 있다. 사용자는 이러한 권한을 명시적으로 부여해야 한다.
앱 구성 요소
앱 구성 요소는 Android 앱의 필수적인 기본 구성 요소이다. 각 구성 요소는 시스템이나 사용자가 앱에 들어올 수 있는 진입점이다. 다른 구성 요소에 종속되는 구성 요소도 있다.
앱 구성 요소에는 네 가지 유형이 있다.
- 액티비티
- 서비스
- Broadcast Receiver
- 콘텐츠 제공자
각 유형은 뚜렷한 목적을 수행하고 각자 나름의 수명 주기가 있어 구성 요소의 생성 및 소멸 방식을 정의한다.
액티비티
액티비티는 사용자와 상호작용하기 위한 진입점이다. 이것은 사용자 인터페이스를 포함한 화면 하나를 나타낸다.
- 사용자가 현재 관심을 가지고 있는 사항(화면에 표시된 것)을 추적하여 액티비티를 호스팅하는 프로세스를 시스템에서 계속 실행하도록 합니다.
- 이전에 사용한 프로세스에 사용자가 다시 찾을 만한 액티비티(중단된 액티비티)가 있다는 것을 알고, 해당 프로세스를 유지하는 데 더 높은 우선순위를 부여한다.
- 앱이 프로세스를 종료하도록 도와서 이전 상태가 복원되는 동시에 사용자가 액티비티로 돌아갈 수 있게 한다.
- 앱이 서로 사용자 플로우를 구현하고 시스템이 이러한 플로우를 조정하기 위한 수단을 제공한다.
서비스
서비스는 여러 가지 이유로 백그라운드에서 앱을 계속 실행하기 위한 다목적 진입점이다. 이는 백그라운드에서 실행되는 구성 요소로, 사용자가 다른 앱에 있는 동안에 백그라운드에서 음악을 재생하거나, 사용자와 액티비티간의 상호작용을 차단하지 않고 네트워크를 통해 데이터를 가져올 수 도 있다.
- 음악 재생은 사용자가 바로 인식할 수 있는 작업이다. 따라서 앱은 사용자에게 이와 관련된 알림을 보내고 음악 재생을 포그라운드로 옮기라고 시스템에 지시한다. 이 경우, 시스템은 이 서비스의 프로세스가 계속 실행되도록 많은 노력을 기울여야 한다. 이 서비스가 사라지면 사용자가 불만을 느낄 것이기 때문이다.
- 정기적인 백그라운드 서비스는 사용자가 실행되고 있다고 직접 인식할 수 없는 작업이므로 시스템은 좀 더 자유롭게 프로세스를 관리할 수 있다. 사용자와 좀 더 직접적인 관련이 있는 작업에 RAM이 필요할 경우 이 서비스를 종료할 수도 있다.
바인딩된 서비스는 다른 앱(또는 서비스)에서 서비스를 사용하고 싶다는 의향을 표현했기 때문에 실행된다. 이는 기본적으로 서비스가 다른 프로세스에 API를 제공하는 것이다.
Broadcast Receiver
브로드캐스트 리시버는 시스템이 정기적인 사용자 플로우 밖에서 이벤트를 앱에 전달하도록 지원하는 구성 요소로, 앱이 시스템 전체의 브로드캐스트 알림에 응답할 수 있게 한다.
대표적인 브로드캐스트로는,
- 화면이 꺼졌거나
- 배터리가 부족하거나
- 사진을 캡처했다고 알리는 것
앱으로 들어갈 수 있는 또 다른 명확한 진입점이기 때문에 현재 실행되지 않은 앱에도 시스템이 브로드캐스트를 전달할 수 있다.
콘텐츠 제공자
콘텐츠 제공자는 파일 시스템, SQLite 데이터베이스, 웹상이나 앱이 액세스할 수 있는 다른 모든 영구 저장 위치에 저장 가능한 앱 데이터의 공유형 집합을 관리한다. 콘텐츠 제공자를 데이터베이스에 대한 추상화로 생각하기 쉽다. 하지만 시스템 설계 관점에서 볼 때 핵심 목적이 서로 다르다.
시스템의 경우 콘텐츠 제공자는 URI 구성표로 식별되고 이름이 지정된 데이터 항목을 게시할 목적으로 앱에 진입하기 위한 입구이다. 따라서 앱에서 URI 네임스페이스에 넣을 데이터를 매핑할 방식을 결정하고, 해당 URI를 다른 엔터티에 전달할 수 있다. 이를 전달받은 엔터티는 URI를 사용하여 데이터에 액세스한다. 시스템이 이렇게 할 수 있는 데에는 앱 관리에 몇 가지 특별한 점이 있기 때문이다.
- URI를 할당하더라도 앱을 계속 실행할 필요가 없으므로 URI를 소유한 앱이 종료된 후에도 URI를 유지할 수 있다.
시스템은 URI를 소유한 앱이 해당 URI에서 앱의 데이터를 검색할 때만 실행되도록 하면 된다. - 이 URI는 중요하고 조밀한 보안 모델을 제공한다. 예를 들어 앱은 클립보드에 있는 이미지에 URI를 할당하고 콘텐츠 제공자가 검색하도록 하여, 다른 앱이 자유롭게 이미지에 액세스하지 못하게 막을 수 있다. 두 번째 앱이 클립보드에서 해당 URI에 액세스하려고 시도하면 시스템에서는 임시 URI 권한을 부여하여 그 앱이 데이터에 액세스하도록 허용할 수 있다. 따라서 두 번째 앱에서는 URI 뒤에 있는 데이터 외에 다른 것에는 액세스할 수 없다.
콘텐츠 제공자는 앱 전용이어서 공유되지 않는 데이터를 읽고 쓰는 데도 유용하다.
구성 요소 활성화
구성 요소 유형 네 가지 중 세 가지(액티비티, 서비스, Broadcast Receiver)는 인텐트라는 비동기식 메시지로 활성화된다. 인텐트는 런타임에서 각 구성 요소를 서로 바인딩한다. 이것은 일종의 메신저라고 생각하면 된다. 즉 구성 요소가 어느 앱에 속하든 관계없이 다른 구성 요소로부터 작업을 요청하는 역할을 한다.
액티비티와 서비스의 경우, 인텐트는 액티비티에 이미지를 표시하거나 웹 페이지를 열라는 요청을 전달할 수 있다.
경우에 따라 결과를 수신하기 위해 액티비티를 시작할 수 있다. 이 경우, 액티비티도 Intent 에서 결과를 반환한다.
브로드캐스트 리시버의 경우, 기기 배터리 잔량이 낮다는 것을 나타내는 브로드캐스트에는 배터리 부족을 나타내는 작업 문자열만 포함된다.
콘텐츠 제공자는 인텐트로 활성화되지 않는다. ContentResolver 가 보낸 요청의 대상으로 지정되면 활성화된다. 콘텐츠 확인자는 모든 트랜잭션을 처리하여 콘텐츠 제공자와의 트랜잭션을 수행하는 구성 요소가 그런 처리를 하지 않는 대신 ContentResolver 객체에서 메서드를 호출한다. 다시말해, Content provider 에는 Intent 를 직접 날리는게 아니라, ContentResolver 를 사용해 한 계층 거쳐서 활성화한다. 중간에 Resolver 를 두고 Resolver 와만 일처리를 하기 때문에 보안이 강화된다는 장점이 있다.
각 유형의 구성 요소를 활성화하는 데는 별도의 메서드가 있다.
- 액티비티를 시작하거나 새로운 작업을 새로운 작업을 배정하려면 Intent 를 startActivity() 또는 startActivityForResult()에 전달하면 된다(액티비티가 결과를 반환하기를 원하는 경우).
- Android 5.0(API 레벨 21) 이상에서는 JobScheduler 클래스를 사용하여 작업을 예약할 수 있다. 초기 Android 버전의 경우 Intent 를 startService() 에 전달하여 서비스를 시작할 수 있다(또는 진행 중인 서비스에 새로운 지침을 전달할 수 있다). Intent 를 bindService() 에 전달하여 서비스에 바인딩할 수도 있다.
- sendBroadcast(), sendOrderedBroadcast(), 또는 sendStickyBroadcast() 와 같은 메서드에 Intent 를 전달하면 브로드캐스트를 시작할 수 있다.
- 콘텐츠 제공자에 쿼리를 수행하려면 ContentResolver 에서 query() 를 호출하면 된다.
매니페스트 파일
Android 시스템이 앱 구성 요소를 시작하려면 시스템은 우선 앱의 매니페스트 파일, AndroidManifest.xml 을 읽어서 해당 구성요소가 존재하는지 확인한다. 앱은 이 파일 안에 모든 구성 요소를 선언해야 하며, 이 파일은 앱 프로젝트 디렉토리의 루트에 있어야 한다.
매니페스트의 또 다른 역할
- 앱이 요구하는 모든 사용자 권한(예: 인터넷 액세스, 사용자 연락처 읽기 액세스)을 식별한다.
- 앱이 어느 API를 사용하는지를 근거로 앱에서 요구하는 최소 API 레벨을 선언한다.
- 앱에서 사용하거나 요구하는 하드웨어 및 소프트웨어 기능(예: 카메라, 블루투스 서비스, 멀티터치 화면)을 선언한다.
- 앱이 링크되어야 하는 API 라이브러리(Android 프레임워크 API 제외)(예: Google Maps 라이브러리)를 선언한다.
구성 요소 선언
매니페스트의 주요 작업은 시스템에 앱의 구성 요소에 대해 알리는 것이다. 예를 들어 매니페스트 파일은 액티비티를 다음과 같이 선언할 수 있다.
<?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 특성이 완전히 정규화된 클래스 이름을 나타내며 android:label 특성은 액티비티 사용자에게 표시되는 레이블로 사용할 문자열을 나타낸다.
모든 앱 구성 요소를 선언하는 방법은,
- 액티비티의 경우 <activity> 요소
- 서비스의 경우 <service> 요소
- 브로드캐스트 리시버의 경우 <receiver> 요소
- 콘텐츠 제공자의 경우 <provider> 요소
코드에는 작성되지만 매니페스트에는 선언하지 않는 액티비티, 서비스, 콘텐츠 제공자는 시스템에 표시되지 않으며, 따라서 실행될 수 없다. 그러나 브로드캐스트 리시버는 매니페스트에서 선언해도 되고 코드를 사용해(BroadcastReceiver 객체) 동적으로 생성한 다음 시스템에 등록해도 된다. 이때 registerReceiver() 를 호출하는 방법을 사용한다.
구성 요소 기능 선언
Intent 를 사용하여 액티비티, 서비스 및 브로드캐스트 리시버를 시작할 수 있다. 그렇게 하려면 Intent 를 사용하여 대상 구성 요소를 인텐트 내에서 명시적으로 명명하면 된다(구성 요소 클래스 이름을 사용). 또한 암시적 인텐트를 사용할 수도 있다. 암시적 인텐트는 수행할 작업의 유형을 설명하고, 원한다면 작업을 수행할 대상인 데이터를 설명할 수도 있다.
암시적 인텐트를 사용하면 시스템이 작업을 수행할 수 있는 기기에서 구성 요소를 찾아서 작업을 시작할 수 있다. 인텐트가 설명한 작업을 수행할 수 있는 구성 요소가 여러 개인 경우, 어느 것을 사용할지는 사용자가 선택한다.
❗ 주의: 인텐트를 사용하여 Service 에 바인딩할 경우에는 명시적 인텐트를 사용하여 앱을 보호하세요. 암시적 인텐트를 사용하여 서비스를 시작하면 보안 위험을 초래한다. 인텐트에 어느 서비스가 응답할 것인지 확신할 수 없고, 사용자는 어느 서비스가 시작되는지 볼 수 없기 때문이다. Android 5.0(API 레벨 21)부터 시스템은 개발자가 암시적 인텐트로 bindService()를 호출하면 예외를 발생시킨다. 서비스에 대해 인텐트 필터를 선언하지 마세요.
시스템은 기기에 설치된 다른 앱의 매니페스트 파일에 제공된 인텐트 필터와 수신된 인텐트를 비교하는 방법으로 인텐트에 응답할 수 있는 구성 요소를 식별한다.
예를 들어, 새 이메일을 작성하는 액티비티가 포함된 이메일 앱을 빌드한다고 가정해보자. 이때 (새 이메일을 전송하기 위해)"send" 인텐트에 응답하는 인텐트 필터를 선언하려면 다음과 같이 하면 된다.
<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>
다른 앱이 ACTION_SEND 작업이 포함된 인텐트를 생성하고 이를 startActivity() 로 전달하면 사용자가 이메일 초안을 작성하고 이를 전송할 수 있는 액티비티를 시작할 수 있다.
앱 요구사항 선언
Android로 구동되는 기기는 수없이 많지만 모두 특징이 같고, 똑같은 기능을 제공하는 것은 아니다. 앱에 필요한 기능이 없는 기기에 앱을 설치하는 불상사를 방지하려면, 앱이 지원하는 기기 유형에 대한 프로필을 명확하게 정의하는 것이 중요하다. 그러려면 매니페스트 파일에 기기와 소프트웨어 요구사항을 선언하면 된다. 이와 같은 선언은 대부분 정보성일 뿐이며 시스템은 이를 읽지 않는 것이 일반적이지만, Google Play와 같은 외부 서비스는 사용자가 본인의 기기에서 앱을 검색할 때 필터링을 제공하기 위해 이와 같은 선언도 읽는다.
예를 들어, 앱에 카메라가 필요하고 Android 2.1(API 레벨 7)에 도입된 API를 사용하는 경우, 이와 같은 내용을 매니페스트 파일에 요구사항으로 선언하려면 다음과 같이 한다.
<manifest ... > <uses-feature android:name="android.hardware.camera.any" android:required="true" /> <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" /> ... </manifest>
위와 같은 선언을 한 다음, 카메라가 없고 Android 버전이 2.1 이하인 기기는 Google Play 에서 앱을 설치할 수 없다. 그러나 앱이 카메라를 사용하기는 하지만 필수는 아니라고 선언할 수도 있다. 이 경우에는 앱이 required 특성을 false 에 설정하고 런타임에 확인하여 해당 기기에 카메라가 있는지, 경우에 따라서는 모든 카메라 기능을 비활성화할 수 있는지 알아본다.
앱 리소스
리소스는 코드에서 사용하는 추가 파일과 정적인 콘텐츠이다. 예를 들어 비트맵, 레이아웃 정의, 사용자 인터페이스 문자열, 애니메이션 지침 등이 있다.
Android 앱은 코드만으로 이루어지지 않는다. 소스 코드와 별도로 이미지, 오디오 파일, 그리고 앱의 시각적 표현과 관련된 것 등의 여러 리소스가 필요하다.
Android 프로젝트에 포함하는 리소스마다 SDK 빌드 도구가 고유한 정수 ID를 정의하므로, 이를 사용하여 앱 코드에서의 리소스나 XML로 정의된 다른 리소스에서 참조할 수 있다. 예를 들어, 앱에 logo.png 라는 이름의 이미지 파일이 들어 있다면 (res/drawable/ 디렉토리에 저장), SDK 도구가 R.drawable.logo 라는 리소스 ID를 생성한다. 이 ID는 각 앱의 정수로 매핑되며, 이것을 사용하여 이미지를 참조할 수도 있고 이것을 사용자 인터페이스에 삽입할 수도 있다.
소스 코드와는 별개로 리소스를 제공하는 것의 가장 중요한 측면 중 하나는 여러 가지 기기 구성에 맞게 대체 리소스를 제공할 능력을 갖추게 된다는 점이다. 예를 들어, UI 문자열을 XML로 정의하면 이러한 문자열을 다른 언어로 번역한 뒤 해당 문자열을 별개의 파일에 저장할 수 있다. 그러면 Android 가 리소스 디렉토리 이름에 추가하는 언어 한정자(예를들어, 프라스어 문자열 값의 경우 res/values-fr/)및 사용자의 언어 설정을 기반으로 적절한 언어 문자열을 UI에 적용한다.
Android 는 대체 리소스에 대해 다양한 한정자를 지원한다. 한정자란 리소스 디렉토리의 이름에 포함하는 짧은 문자열로, 이를 사용해 해당 리소스를 사용할 기기 구성을 정의한다. 예를 들어, 기기의 화면 방향과 크기에 따라 액티비티에 여러 가지 레이아웃을 생성해야 할 때가 많다. 기기 화면이 세로 방향(세로로 길 때) 또는 가로 방향(가로로 넓을 때)에 따라 레이아웃을 변경하려면 서로 다른 두 가지 레이아웃을 정의하여 적절한 한정자를 각 레이아웃의 디렉토리 이름에 적용하면 된다.
그러면 시스템이 현재 기기 방향에 따라 적절한 레이아웃을 자동으로 적용한다.
리소스 유형 그룹화
프로젝트의 res/ 디렉토리에 속한 특정 하위 디렉토리에 각 유형의 리소스를 배치해야 한다. 간단한 프로젝트에서 볼 수 있는 파일 계층 구조는 다음과 같다.
res/ 디렉토리가 모든 리소스(이미지 리소스 한 개, 레이아웃 리소스 두 개, 런처 아이콘용 mipmap/ 디렉토리, 문자열 리소스 파일 한 개)를 하위 디렉토리에 포함한다.
프로젝트 res/ 디렉토리 내부에서 지원되는 리소스 디렉토리
디렉토리 리소스 유형 animator/ 속성 애니메이션을 정의하는 XML 파일이다. anim/ 트윈 애니메이션을 정의하는 XML 파일이다. color/ 색상의 상태 목록을 정의하는 XML 파일이다. drawable/ 다음 드로어블 리소스 하위 유형으로 컴파일되는 비트맵 파일(.png, .9.png, .jpg, .gif) 또는 XML 파일이다.
☞ 비트맵 파일
: .png 또는 .webp, .jpg, .gif. BitmapDrawable을 만든다.
☞ 나인-패치(크기 조정이 가능한 비트맵)
: PNG 파일(.9.png). NinePatchDrawable을 만든다.
☞ 상태 목록
: 상태별로 다른 비트맵 그래픽. StateListDrawable을 만든다.
☞ 도형
: 색상과 그라데이션을 포함하여 기하학적 도형 정의. GradientDrawable을 만든다.
☞ 애니메이션 드로어블
: AnimationDrawable을 만든다.
☞ 기타 드로어블mipmap/ 다양한 런처 아이콘 밀도에 관한 드로어블 파일이다.
mipmap/ 디렉토리 앱 아이콘 배치layout/ 사용자 인터페이스 레이아웃을 정의하는 XML 파일이다. menu/ 옵션 메뉴, 컨텍스트 메뉴 또는 하위 메뉴 등의 앱 메뉴를 정의하는 XML 파일이다. 메뉴 리소스 raw/ 원시 형태로 저장하기 위한 임의의 파일이다. 원시 InputStream으로 이러한 리소스를 열려면 리소스 ID(R.raw.filename)를 사용하여 Resources.openRawResource()를 호출한다.
그러나 원본 파일 이름과 파일 계층 구조에 액세스해야 한다면 res/raw/ 대신 assets/ 디렉토리에 일부 리소스를 저장하는 것이 좋다. assets/ 의 파일에는 리소스 ID가 부여되지 않으므로 AssetManager 를 통해서만 읽을 수 있다.values/ 문자열과 정수, 색상 등 간단한 값을 포함하는 XML 파일이다.
다른 res/ 하위 디렉토리에 있는 XML 리소스 파일은 XML 파일 이름을 근거로 하나의 리소스를 정의하는 반면, values/ 디렉토리에 있는 파일은 여러 개의 리소스를 설명한다.
파일 이름 규칙은 다음과 같다.
☞ 리소스 배열: arrays.xml
☞ 색상 값: colors.xml
☞ 크기 값: dimens.xml
☞ 문자열 값: strings.xml
☞ 스타일: styles.xmlxml/ Resource.getXML() 을 호출하여 런타임에 읽을 수 있는 임의의 XML 파일이다. 검색 가능한 설정 font/ .ttf, .otf, .ttc 확장자가 붙은 글꼴 파일이나 <font-family> 요소를 포함한 XML 파일이다. XML의 글꼴 'Android' 카테고리의 다른 글
Jetpack compose Scaffold 와 Snackbar (0) 2023.05.03 Android Native App 개발 - ⑤ (0) 2023.04.25 Android Native App 개발 - ④ (0) 2023.04.21 Android Native App 개발 - ③ (0) 2023.04.17 Android Native App 개발 - ① (0) 2023.04.11