[C] 댕글링 포인터(Dangling Pointer)

댕글링 포인터 정의

  • 포인터가 여전히 해제된 메모리 영역을 가르키고 있다면, 이러한 포인터를 댕글링 포인터(Dangling Pointers)라고 부른다. ⇒ 댕글링 포인터가 가리키는 메모리는 유효하지 않다.
  • 댕글링 포인터는 종종 너무 빠른 해제(premature free)라고 불린다.

댕글링 포인터 문제점

  • 메모리 접근 시 예측 불가능한 동작
  • 메모리 접근 불가 시 세그멘테이션 오류(Segmentation fault) 발생
  • 잠재적인 보안 위험

댕글링 포인터 발생 원인

  • 메모리 해제 후, 해제된 메모리에 접근하는 경우
  • 함수 호출에서 자동 변수를 가리키는 포인터를 반환
카테고리: C, Programming | 댓글 남기기

[C] 메모리 누수(Memory Leak)

메모리 누수 정의

메모리 누수는 할당된 메모리가 더는 사용되지 않지만, 해제되지 않을 때 발생한다. 이를 테면 다음과 같은 상황에서 발생한다.

  • 메모리의 주소를 잃어버린 경우
  • free 함수가 호출되어야 하는 상황에 호출되지 않은 경우

메모리 누수의 예

  • 메모리 누수가 누적되면 힙 관리자가 사용할 수 있는 메모리 양이 감소하게 된다.
  • 메모리 누수가 반복되면, 추가적인 메모리가 필요할 때 malloc 함수가 OOM(Out of Memory) 오류를 발생시키며 추가적인 메모리를 할당하지 못해 프로그램이 종료된다.

아래의 코드는 메모리 누수를 발생시키는 코드의 예이다. 변수 chunk는 힙으로 부터 메모리를 할당받지만, 다른 메모리가 할당 되기전에 해제되지 않는다. 이 코드는 결국 OOM 오류를 발생시키고 비정상 종료된다.

메모리 주소 손실

  • pi 포인터는 해제되지 않은 상태에서 새로운 메모리 주소가 재할당된다.
  • pi 변수에 두 번째 메모리 주소가 할당될 때, 첫 번째 메모리의 주소는 손실된다.

아래의 그림에서 메모리 주소 Ox0E는 여전히 해제되지 않은 상태이며 프로그램 어디에서도 이 주소를 알지 못한다.

메모리 주소 손실

 
  • 아래의 예제 코드는 name 변수를 루프의 반복마다 1씩 증가시킨다.
  • 루프의 마지막에 name 변수는 문자열의 NUL 종료 문자를 가리킨다. ⇒ 할당된 메모리의 시작 주소를 잃어버림.

동적으로 할당된 메모리 주소 손실

 

숨겨진 메모리 누수

숨겨진 메모리 누수는 보통 프로그래머의 부주의에 때문에 발생한다.

  • 대표적인 예로는 struct 키워드를 사용하여 생성한 구조체를 내부에 멤버 변수로 포인터 변수가 존재하는 경우이다.
  • 만약 구조체 내부에 동적으로 생성된 메모리의 포인터를 포함하고 있다면, 이 포인터는 구조체를 해제하기 전에 반드시 해제되어야 한다.
카테고리: C, Programming | 댓글 남기기

[C] 포인터와 메모리

C 프로그램에서의 메모리

컴파일된 C 프로그램은 다음 세 종류의 메모리를 사용한다.

정적(Static)/전역(Global)

  • 정적(Static)/전역(Global)으로 선언된 변수들은 정적/전역 메모리에 할당된다.
  • 정적/전역 변수들은 프로그램이 시작될 때 할당되며, 프로그램이 종료될 때까지 메모리 공간에 남아 있다.
  • 전역 변수는 파일 전체가 접근 범위(scope)이며, 정적 변수의 접근 범위는 해당 변수를 선언한 함수로 제한된다.

자동(Automatic)/지역(Local)

  • 지역 변수(종종 Automatic 으로 언급)는 함수 안에서 선언되고 함수가 호출될 때 생성된다.
  • 지역 변수의 접근 범위는 선언된 함수로 제한되며, 함수가 호출되는 동안에만 메모리에 존재한다.

동적(Dynamic)

  • 동적 메모리는 힙(Heap) 메모리 영역에 할당되고 필요한 경우 해제된다.
  • 포인터를 사용하여 할당된 메모리 영역을 참조하며, 포인터에 의해 접근이 제한된다.
  • 메모리를 해제하지 않는 한 메모리에 계속 존재한다.

접근 범위(scope)와 수명(Lifetime)

메모리의 종류 접근 범위 수명
전역 전체 파일 프로그램 실행 동안 유지
정적 선언된 함수 프로그램 실행 동안 유지
로컬(자동) 선언된 함수 함수 실행 동안 유지
동적 참조하는 포인터 메모리 해제 전까지 유지

포인터 변수는 다른 변수 및 객체(Object) 또는 함수의 메모리상 주소를 포함하고 있다. 여기서 객체란, malloc 함수 같은 메모리 할당 함수를 사용하여 할당된 메모리를 말한다.

포인터는 가르키는 대상에 따라 특정 타입을 가지도록 선언된다. (정수, 문자, 문자열 혹은 구조체 등)

그러나 포인터 자체는 포인터 자체가 가르키는 데이터 타입의 속성을 가지지 않는다. 포인터는 단지 주소만을 가지고 있다.

포인터의 용도

  • 빠르고 효율적인 코드 작성
  • 다양한 문제에 대한 효과적인 해결 방법 제공
  • 동적 메모리 할당 지원
  • 작고 간결한 표현의 사용
  • 큰 오버헤드 없이 데이터 구조를 포인터로 전달
  • 함수의 매개변수로 전달된 데이터 보호

포인터 사용 시 주의점

  • 배열이나 데이터 구조의 경계를 넘는 접근
  • 소멸한 자동/로컬 변수의 참조
  • 힐당 해제된 힙 메모리의 참조
  • 아직 할당되지 않은 포인터에 대한 역참조
 

표준 문서에서의 포인터 동작 정의

C99(ISO/IEC 9899:TC3)

구현 방법에 따라 정의된 행동(Implementation-defined behavior)

  • 동작에 대한 문서화된 구현을 제공한다. ⇒ 정수에 대한 오른쪽 시프트 연산에서 상위 비트의 확장 방법

명시되지 않은 행동(Unspecified behavior)

  • 동작에 대한 구현을 제공하지만 문서화하지는 않는다. ⇒ malloc 함수에 인자를 0으로 주고, 실행할 때 메모리가 얼마나 할당되는가에 대한 문제
  • 명시되지 않은 행동의 목록 : CERT Secure Coding Appendix DD

정의되지 않은 행동(Undefined behavior)

  • 포인터의 동작에 대한 어떤한 것도 강요하지 않으므로 어떠한 동작도 발생할 수 있다. ⇒ free 함수에 의해 해제된 포인터의 값
  • 정의되지 않은 행동의 목록 : CERT Secure Coding Appendix CC

 

카테고리: C, Programming | 댓글 남기기

[Android] Kotlin을 활용한 android.os.properties 접근

해당 글에서 언급하는 프로퍼티는 안드로이드 시스템에서 관리되는android.os.SystemProperties를 의미합니다. (APK 내부에서 관리되는 System.getProperty, System.setProperty 와는 다릅니다.)

안드로이드 시스템에 시스템 속성(System Property)를 추가하거나 API로 구현하는 방법에 대해서는 아래의 안드로이드 문서에 잘 정리되어 있습니다.

시스템 속성 추가
시스템 속성을 API로 구현

다만, 위에 소개된 안드로이드 문서대로 ‘시스템 속성(System Property)’를 추가하기 위해서는 아래와 같은 사항이 동반되어야 합니다.

  • 플랫폼 빌드시 같이 빌드되는 시스템 app일 경우 android.os.SystemProperties 사용 가능
  • 플랫폼 빌드시 사용되는 key를 가지고 app을 signing 하여 app을 빌드하면 사용가능

만일, R&R에 따라 플랫폼 빌드 업무(패키징)와 app을 개발하는 업무가 분리되는 경우에 app 개발단에서 위와 같이 시스템 속성을 추가하는 것이 어렵습니다.

그런데 reflection을 이용하면 app 단에서도 안드로이드 시스템 프로퍼티를 get/set 할수 있습니다. 아래에 Kotlin 예제 코드를 첨부합니다.

카테고리: Android | 댓글 남기기

[Android] ADB를 이용한 파일 복사(adb pull / push)

개요

안드로이드 플랫폼에서 개발시에  MTP 를 통해 파일을 주고 받기 어려운 경우가 있습니다. 이런 경우 ADB pull / push 명령어를 통해 호스트 장치와 안드로이드 장치간 파일을 공유할 수 있습니다.

ADB 디버깅 설정

ADB를 사용하기 위해서는 안드로이드 장치에서 USB 디버깅 옵션이 활성화 되어 있어야 합니다.

  • 설정 > 기기 환경설정 > 정보 > 빌드 메뉴를 7번 이상 선택하면 개발자 옵션이 활성화 됩니다.
  • 개발자 옵션이 활성화되면 설정 > 기기 환경설정 > 개발자 옵션 > USB 디버깅 옵션을 활성화 합니다.

ADB push / pull 명령어 사용법

ADB push : 호스트(PC)에서 안드로이드 기기로 파일 복사

ADB pull : 안드로이드 기기에서 호스트(PC)로 파일 복사

 

카테고리: Android | 댓글 남기기

[Docker] Volume에 대한 이해와 활용

이 글에서는 Docker Volume에 대해서 설명하고 상황에 따라 Volume을 활용해 데이터를 관리하는 방법에 대해서 기술합니다.

개요

가장 기본적으로 Docker Container가 생성되면 Writable Layer에 데이터를 저장할 수 있습니다. 그런데 이 방법에는 몇가지 문제가 있습니다.

  • Container가 삭제되면 Container 내부에 데이터도 같이 삭제가 됩니다.
  • 현재 Container에 저장된 데이터를 다른 Container 혹은 프로세스와 공유하기 어렵습니다.
  • Container가 Writable Layer에 Data를 저장하기 위해서는 파일 시스템을 관리하는 스토리지 드라이버가 필요합니다. 그렇기 때문에 기존 호스트 파일 시스템 대비 데이터 읽기 쓰기 속도 성능이 떨어집니다.

Reference : About storage drivers

Docker 에서는 데이터 관리를 위해 volume, bind mount, tmpfs 의 총 3가지 방법을 제공하며, 이는 각각의 장점과 단점이 있기 때문에 상황에 맞게 활용해야 합니다.

도커에서 데이터를 관리하는 3가지 방법

아래 그림은 도커에서 데이터를 관리하는 방법에 대한 다이어그램입니다.

Reference : Use volumes

먼저 bind mount 를 살펴보면 Container 가 호스트 파일 시스템에 직접 접근하고 있습니다. 컨테이너에서 마운트한 경로의 호스트 파일과 디렉토리가 컨테이너 내부에 동일하게 표시되기 때문에 가장 간편한 방법일 수 있습니다. 하지만 호스트 파일 시스템 구조에 의존하기 때문에 호스트 파일 시스템과 컨테이너 내부의 파일 시스템 간의 파일/디렉토리 구조가 다른 경우 문제가 발생할 수 있습니다. (이에 대한 개인적인 경험은 뒤에서 자세하게 다룰 예정입니다.)

두번째로 volume 을 살펴보면 호스트 파일 시스템 내부에 Docker area 를 참조하고 있습니다. volume은 호스트 파일 시스템 내부에 Docker 에서 직접 관리하는 영역을 생성하여 사용하게 됩니다. volume은 Docker에 의해서 관리되기 때문에 호스트 파일 시스템과 완전 분리된다는 점이 있긴 하지만 사용방법은 bind mount 와 유사합니다.

마지막으로 tmpfs 는 ramfs와 비슷하게 메모리에 마운트를 하는 방법입니다. 이는 여러가지로 활용될 수 있습니다. 데이터베이스 경로를 tmpfs로 마운트 시켜 In-memory 데이터베이스 형태로 사용하면 디스크에 직접 접근하는 방식보다 빠른 성능을 보장 받을 수 있습니다. 또한, Read-only 옵션을 추가하는 경우 외부의 접근으로 부터 컨테이너 내부를 Immutable 한 상태로 유지할 수 있습니다.

실제 사용예제

bind mount

volume

tmpfs

졸리당… 사용 방법은 내일 정리 하는 걸로다가…

카테고리: Docker | 댓글 남기기

[macOS, Big Sur] VirtualBox Kernel driver not installed (rc=-1908) 해결

macOS Big Sur 에서 Virtual Box를 이용해 Ubuntu 16.04 LTS를 설치하는 중에 다음과 같은 에러가 발생하였습니다.

Kernel driver not installed (rc=-1908)

Make sure the kernel module has been loaded successfully.

where: suplibOsInit what: 3 VERR_VM_DRIVER_NOT_INSTALLED (-1908) - The support driver is not installed. On linux, open returned ENOENT.

위 문제를 해결하기 위해서는 ‘시스템 환경 설정 > 보안 및 개인 정보 보호 > 일반 탭’ 으로 이동하여 ‘Oracle America, Inc’ 에 대한 허용을 선택하고 시스템을 재시작 해야 합니다.

다만, 허용 옵션을 활성화 하기 위해서는 Virtual Box의 재설치가 필요합니다.

https://download.virtualbox.org/virtualbox/6.1.18/VirtualBox-6.1.18-142142-OSX.dmg

Note: This option is available only for roughly 30 minutes after a fresh install of VirtualBox. If this message does not appear, uninstall VirtualBox by opening your “Applications” folder and then dragging the VirtualBox app to the Trash. Remove any leftover files, reinstall a fresh copy of VirtualBox, and immediately open the Security & Privacy menu to see this option.
Reference : How to Fix VirtualBox’s “Kernel Driver Not Installed (rc=-1908)” Error on a Mac

시스템 재시작이 완료되면 Virtual Box를 실행 후 가상 머신이 정상적으로 실행되는지 확인합니다.

카테고리: Memo | 댓글 남기기

[Android] Layout Mechanism 정리

View가 그려지는 과정

안드로이드의 View 들은 계층구조로 구성됩니다. 먼저 Activity가 포커스를 얻게되면 안드로이드 시스템은 Activity에게 루트 노드를 요청합니다.

보통 루트 노드는 MainActivity 클래스의 onCreate()메소드 내부에서 setContentView()메소드를 통해 지정되거나, XML 레이아웃의 최상위 레이아웃이 지정됩니다.

아래의 그림에서 좌측은 레이아웃 XML로 작성된 View 구조의 예시이며, 우측은 레이아웃이 구성되는 트리구조를 나타냅니다.

  • View는 포커스를 얻으면 레이아웃을 그리도록 요청하는데, 우측에 트리 구조를 기준으로 전위 순회 방식으로 그려집니다.
  • 부모 View는 자식 View가 그려지기전에 그려지고, 형제 View는 전위 순회 방식에 따라 순서대로 그려지게 됩니다.

전위 순회 순서로 레이아웃이 그려질때 다음과 같이 3가지 과정을 거치게 됩니다.

  • Measure : View의 크기를 정하는 과정
  • Layout : View의 위치를 정하는 과정
  • Draw : 최종적으로 View를 캔버스위에 실제 그리는 과정

measure(int widthMeasureSpec, int heightMeasureSpec)

부모노드에서 자식노드를 경유하며 실행되며, View의 크기를 알아내기 위해 호출됩니다.  실제 View의 크기 측정은 onMeasure(int, int)를 통해 이뤄집니다. measure(int, int)의 내부에서는 onMeasure(int, int)를 호출함으로써 View의 크기를 알아냅니다. 측정 과정에서는 부모 View와 자식 View간의 크기 정보를 전달하기 위해 다음의 2가지의 클래스를 사용합니다.

ViewGroup.LayoutParams

자식 View가 부모 View에게 자신이 어떻게 측정되고 위치를 정할지 요청하는데 사용됩니다. ViewGroup의 sub class에 따라 다른 ViewGroup.LayoutParams의 sub class가 존재할 수 있습니다. 예를 들어 ViewGroup의 sub class인 RelativeLayout 경우 자신만의 ViewGroup.LayoutParams의 sub class는 자식 View 를 수평적으로 또는 수직적으로 가운데정렬을 할 수 있습니다.

  • 정확한 수치 (ex. android:layout_width=”320dp”)
  • MATCH_PARENT (ex.android:layout_width=”match_parent”)
  • WRAP_CONTENT (ex.android:layout_width=”wrap_content”)

ViewGroup.MeasureSpec

부모 View가 자식 View에게 요구사항을 전달하는데 사용됩니다.

  • UNSPECIFIED – 부모 View는 자식 View가 원하는 치수대로 결정
  • EXACTLY – 부모 View가 자식 View에게 정확한 크기를 강요
  • AT MOST – 부모 View가 자식 View에게 최대 크기를 강요

layout(int l, int t, int r, int b)

부모노드에서 자식노드를 경유하며 실행되며, View와 자식 View 들의 크기와 위치를 할당할 때 사용됩니다. measure(int, int)에 의해 각 View에 저장된 크기를 사용하여 위치를 지정합니다. 내부적으로 onLayout() 를 호출하고 onLayout() 에서 실제 View의 위치를 할당하는 구조로 되어있습니다. View의 measure() 가 반환할 때, View의 getMeasureWidth() 와 getMeasureHeight() 값이 설정됩니다. 만약 자식 View 측정값의 합이 너무 크거나 작을 경우 다시 measure() 메서드를 호출하여 크기를 다시 측정합니다.

measure() 와 layout() 메서드는 내부적으로 각각 onMeasure() 와 onLayout() 메서드를 호출합니다. 이것은 final로 선언된 measure() 와 layout()  대신 onMeasure()onLayout()을 재구현(override)할 것을 장려하기 위함입니다.

View LifeCycle

Constructor

모든 View는 생성자에서 출발합니다. 생성자에서 초기화를 하고, default 값을 설정합니다. View는 초기설정을 쉽게 세팅하기 위해서 AttributeSet 이라는 인터페이스를 지원합니다. 먼저 attrs.xml 파일을 만들고 이것을 부름으로써 View의 설정값을 쉽게 설정할 수 있습니다.

onAttachedToWindow

부모 View가 addView(childView) 를 호출하고 나서 자식 View는 윈도우에 붙게 됩니다(attached). 이때부터 View의 id 를 통해 접근할 수 있습니다.

onMeasure

View의 크기를 측정하는 단계입니다. 대부분의 경우 레이아웃에 맞게 특정크기를 가져야합니다.

  • View가 원하는 사이즈를 계산합니다.
  • MeasureSpec 에 따라 크기와 mode를 가져옵니다.

  • MeasureSpec 의 mode를 체크하여 View의 크기를 적용합니다.

     

onLayout

이 단계에서 View의 크기와 위치가 할당됩니다.

onDraw

View를 실제로 그리는 단계입니다. Canvas 와 Paint 객체를 사용하여 요청된 내용을 그리게 됩니다. Canvas  객체는 onDraw 메서드의 파라미터로 제공되며, Canvas 을 이용하여 View의 모양을 그립니다. 또한 Paint 객체는 View의 색을 그립니다.

draw

onDraw 메서드를 호출시 많은 시간이 소요됩니다. Scroll 또는 Swipe 등을 할 경우 View는 다시 onDraw 와 onLayout 을 다시 호출하게 됩니다. 따라서 onDraw 메서드 내에서 객체할당을 피하고 한 번 할당한 객체를 재사용할 것을 권장합니다.

다시 말해 onDraw 메서드를 override 할때는 내부에서는 draw에 동작과 관련된 부분만 처리하고, 실제 이미지를 그릴때 사용되는Canvas, Drawable, Paint, Bitmap과 같은 객체는 별도의 메서드로 구분하여 구현하는 것을 권장합니다.

View Update

View Lifecycle을 보면 View를 다시 그리도록 하는 invalidate() requestLayout() 메서드를 볼 수 있습니다. 이것은 런타임에 View를 다시 그릴 수 있게 합니다. 각각의 사용 용도는 아래와 같습니다.

invalidate()

단순히 View를 다시 그릴때 사용됩니다. 예를 들어 View의 text 또는 color가 변경되거나 , touch interactivity가 발생할 때 onDraw() 메서드를 재호출하면서 View를 업데이트합니다.

requestLayout()

measure() 과정부터 다시 View를 그립니다. View의 사이즈가 변경될 때 View의 사이즈를 다시 측정해야하기 때문에 View Lifecycle에 따라 onMeasure() 단계에서부터 View를 다시 그립니다.

Animation

View의 animation은 frame단위의 프로세스입니다. 예를 들어 View가 점점 커지는 Animation을 갖는다고 가정하면 Frame 단위로 View가 점점 커지게 됩니다. 그리고 각 단계마다 invalidate()를 호출하여 View를 그립니다.

[label type=”info”]INFO[/label] 애니메이션에 사용되는 클래스는 대표적으로 ValueAnimator 입니다.

카테고리: Android | 댓글 남기기

[Ubuntu 16.04] VIM 8.2 설치

보통은 Ubuntu 16.04 LTS 에서 apt-get을 이용해 VIM을 설치하면 7.4 버전으로 설치됩니다.
VIM에서 YCM 등의 플러그인을 사용하기 위해서는 8.1 이상이 설치되어야 합니다.

다음과 같은 커맨드를 입력하여 VIM 8.2를 설치합니다.

삭제하는 경우 아래와 같은 커맨드를 입력합니다.

 

카테고리: Linux | 댓글 남기기

[Android] TV 레이아웃 구성

Layout 구성 예제

이번 포스트에서는 Android Studio 에서 실제 TV 레이아웃을 구성하고 편집하는 방법에 대해서 설명합니다.

Leanback 라이브러리를 활용한 레이아웃 구성

자세한 내용은 안드로이드 개발자 문서의 Android TV > TV 앱 빌드 란을 확인하세요. 또한 아래 Reference 란에 첨부한 Creating Layout and Activity 유튜브 동영상을 시청하시는 것도 TV 레이아웃을 이해하시는데 큰 도움이 됩니다.

Android의 Leanback 라이브러리는 Android TV 애플리케이션 개발을 위해 설계된 라이브러리로, TV 사용자 인터페이스를 쉽고 효율적으로 구축할 수 있도록 다양한 사전 정의된 클래스와 인터페이스를 제공합니다. 이 라이브러리는 Android TV 앱 개발 시 필수적인 요소로, 리모컨 입력과 같은 TV 특유의 사용자 상호작용을 쉽게 처리할 수 있게 해줍니다.

프로젝트 생성

  • Android Studio를 실행 후 “Start a new Android Studio project”를 선택합니다.
  • Select a Project Template” 화면에서 TV 탭을 선택 후 “No Activity”를 선택합니다.

    기본값인 Android TV Activity 선택하면 샘플 TV 앱을 구동하기 위한 소스 코드와 메니페스트, 리소스(레이아웃 등)가 포함됩니다.
    이러한 세팅 값들은 TV 레이아웃을 이해하기 위한 기본 구성 이해에 방해가 되므로 No Activity 상태로 프로젝트를 생성합니다.

  • “Configure Your Project” 탭에서 “Name”을 지정합니다. 여기서는 TV-example로 하겠습니다. “Package name”과 “Save location”은 “Name”을 지정하면 자동으로 설정됩니다.
  • “Minimum SDK” 에서는 해당 앱에서 지원하는 안드로이드 SDK의 최소 버전을 선택합니다. 여기서는 API 25: Android 7.1.1 (Nougat)으로 지정하겠습니다. 설정이 완료되면 “Finish”를 클릭하여 프로젝트 생성을 완료합니다.
  • 프로젝트가 정상적으로 생성되면 아래와 같은 화면이 나타납니다.

    • 좌측에 “Project” 탭에 안드로이드 어플리케이션 개발을 위한 파일 구조가 보여집니다.
      • manifests : AndroidManifest.xml에 Activity 및 관련된 특정 속성을 선언해야 앱에서 Activity을 사용할 수 있습니다
      • java : 실제 개발자가 구현해야하는 클래스들이 위치합니다.
      • java (generated) : Gradle (빌드 자동화 툴)에 의해 생성된 자바 소스가 위치합니다.
      • res : resource의 약어로 앱에서 사용되는 문자열, 이미지 등이 위치합니다.
    • app 으로 표시되는 안드로이드 머리 우측에 X 표시가 되어 있습니다. 안드로이드 어플리케이션이 실행되기 위해서는 최소 1개 이상의 Activity가 있어야 합니다.
      현재 java/com/example/tv_example 자바 패스에 생성된 Activity가 없으므로 위와 같이 표시됩니다.

TV 어플리케이션을 위한 manifest 수정

  • TV에서 실행되도록 개발하는 애플리케이션은 manifest에서 TV 런처 Activity를 선언해야 합니다. 이 작업을 위해 CATEGORY_LEANBACK_LAUNCHER 인텐트 필터를 사용합니다.
    이 필터는 앱이 TV용으로 사용 설정되었음을 나타내며 Google Play에서 TV 앱으로 식별될 수 있도록 허용합니다. 사용자가 TV 홈 화면에서 앱을 선택하면 이 인텐트가 실행할 Activity를 식별합니다.
  • AndroidManifest.xml에  다음의 코드를 추가합니다.

    • android.software.leanback 값을 true로 세팅하여 이 어플리케이션은 오직 TV 장치에서만 실행되게 합니다.

    • android.hardware.touchscreen 값을 false로 세팅하여 터치 스크린을 사용하지 않게 합니다. (TV는 터치 스크린이 필요 없죠? ^^)

    • <application> XML 태그 내부에 <activity> 태그를 추가합니다. 앞서 설명하였듯이 안드로이드 앱은 최소 1개 이상의 Activity가 있어야 하며 보통 MainActivity로 지정합니다.

    • <activity> 태그 내부에 <intent-filter> 태그를 통해 해당 앱은 LEANBACK_LAUNCHER에 의해 분류되는 TV 앱 임을 표시합니다.

Layout 리소스 생성

  • 안드로이드 앱은 XML 레이아웃에 정의된 정보를 메모리 상에서 객체화 하는데 이를 Inflation 이라고 합니다. 최초에는 Layout 구성을 위한 XML 레이아웃 리소스 파일이 없으므로 생성합니다.
  • Project 탭 > app/res 오른쪽 버튼 클릭 > New > Android Resource File 을 클릭합니다.
    • “File name:” 은 기본적으로 activity_main으로 합니다. UI 구성에 따라 다수의 Activity를 생성할 수 있는데 기본적으로 가장 주요 레이아웃은 activity_main으로 명명합니다.
    • “Resource type:” 은 Layout을 선택합니다.
    • “Root element:” 에서는 위에서 설명한 ViewGroup의 Layout 형태를 선택할 수 있습니다. 여기서는 RelativeLayout을 선택하겠습니다.
    • 설정이 완료되면 “OK” 버튼을 클릭합니다.
  • XML 레이아웃 리소스 파일이 생성되면 아래와 같이 코드를 추가합니다.
    • tools:context=”.MainActivity” : 추후 생성될 MainActiviy.java 클래스가 해당 레이아웃 리소스와 연동된다는 것을 의미합니다.
    • xmlns:tools=”http://schemas.android.com/tools” : 위에서 tools 속성을 추가하였기 때문에 Android Studio는 해당 속성을 사용하기 위해서 해당 구문을 자동으로 추가합니다.
    • android:layout_width=”match_parent” / android:layout_height=”match_parent” : 해당 구문은 기본적으로 설정된 값입니다. width/height 값을 부모 객체(TV 레이아웃)과 일치시킨다는 뜻입니다.
    • android:orientation=”horizontal” : TV는 기본적으로 Landscape이기 때문에 horizontal로 설정합니다.

    • android:visibility=”visible” : 해당 레이아웃은 당연히 보여야 하기 때문에 visible로 설정합니다.

레이아웃(ViewGroup)에 View 추가

레이아웃은 XML 편집을 통해서 수정할 수 있지만 Design 탭에서 수정도 가능합니다. 본 예제에서는 Design 탭에서 View를 추가한 뒤에 세부적인 속성들을 XML에서 설정합니다.

  • Project 탭 > app/res/layout/activity_xml 더블 클릭 > 우측 상단에 “Design” 탭 클릭
  • Palette > Text 탭 > TextView 선택 후 레이아웃으로 Drag
  • 다음과 같이 “Component Tree”에 TextView가 추가되었으며 실제 레이아웃에도 배치된 것을 확인 할 수 있습니다.
    • 앞서 ViewGroup에서 설명드린대로 RelativeLayout 특성상 TextView가 0, 0 위치에 배치되었습니다.
    • 우측 상단의 “Code” 탭으로 이동하여 XML 속성값을 수정해 위치 및 기타 속성 값을 변경하겠습니다
  • 레이아웃 XML 수정

    • 위와 같이 레이아웃에 TextView를 추가하면 레이아웃 XML 파일에 다음과 같이 TextView에 대한 정보가 추가되어 있습니다.

    • android:textSize, android:text 속성을 다음과 변경하여 TextView에 표시되는 텍스트의 내용을 변경합니다.

MainActivity 클래스 추가 및 기본 구현

안드로이드 앱은 최소 1개의 Activity가 있어야 실행이 가능합니다. 보통 주요한 Activity 클래스명을 MainActivity로 명명합니다.

  • Project 탭 > java 오른쪽 클릭 > New > Java Class 클릭 > MainActivity 입력
  • MainActivity 클래스가 생성되면 다음과 같이 코드를 수정합니다.
    • Activity 클래스를 상속 받습니다.
    • onCreate() 메서드를 Override하여 재정의 합니다.
      • setContentView(R.layout.activity_main); : res/layout/activity_main.xml 레이아웃 리소스를 객체화하여 UI에 표시합니다.
      • TextView textView = findViewById(R.id.textView); 지정된 리소스 ID를 통해 View를 찾아 객체화합니다.
      • textView.setTextSize(50) : TextView 객체의 텍스트 크기의 50dp로 변경합니다.

에뮬레이터 실행

Android Studio 에서는 앱을 실행해 볼수 있는 에뮬레이터를 제공합니다. 에뮬레이터를 실행하기 위해서 다음의 과정을 수행합니다.

  • Tools > AVD Manager > + Create Virtual Device > TV 선택 > Android TV (1080p) 선택 후 “Next” 클릭
  • 앞서 프로젝트 생성시에 API 25: Android 7.1.1 (Nougat) 으로 API 버전을 선택했으므로 Android 7.1.1 (Android TV)을 다운로드 후 타겟으로 선택하고 “Next” 클릭
  • 다음 화면에서는 별다른 설정 없이 “Finish” 클릭
  • 상단탭에서 버튼을 클릭하면 에뮬레이터가 실행되고 결과 화면이 표시됩니다.

Reference

카테고리: Android | 댓글 남기기