2009년 12월 5일 토요일

5. 안드로이드 사용자 인터페이스

5.1 사용자 인터페이스 개요

 

안드로이드 어플리케이션은 사용자 인터페이스를 만들 때 View와 ViewGroup 객체를 사용합니다. 뷰나 뷰 그룹은 여러 가지가 있으나 모두 View 클래스를 상속받은 것들입니다. 안드로이드 플랫폼에서 사용자 인터페이스를 만드는 기본 빌딩 블럭이 바로 이 View 객체입니다. View 클래스는 "위젯(widget)" 하위 클래스들의 기반이 됩니다. 실제로 위젯은 완전하게 구현된 UI 객체입니다. 예를 들면, 텍스트 필드나 버튼 같은 것들이 위젯입니다. ViewGroup 클래스는 "레이아웃(layout)" 하위 클래스의 기반이 됩니다. 레이아웃을 이용하여 화면 배치를 다양하게 만들 수 있습니다. 예를 들면, 일렬로 나열하거나 테이블로 표현하거나, 서로 상대적인 관계를 표현하거나 할 수 있습니다.

 

View 객체는 레이아웃 매개변수나 화면의 특정 사각 영역에 대한 정보를 담고 있는 자료 구조입니다. View 객체는 자신이 놓여 있는 화면 영역의 위치나 크기 정보와 배치, 화면 영역 안에 그리기, 포커스 변경 처리, 스크롤, 키 또는 제스처 상호 작용 처리 등을 담당하고 있습니다. 다시 말해서 사용자 인터페이스 객체로서 사용자와 상호작용을 위한 창구 및 이벤트 리스너(listener) 역할을 합니다.

 

(1) 뷰 계층 구조

 

안드로이드 플랫폼에서 액티비티의 UI를 정의할 때는 아래 그림과 같이 뷰나 뷰 그룹의 계층 구조로 정의합니다. 이런 계층 구조는 개발하려는 UI의 복잡도에 따라서 달라지겠지요. UI 계층 구조는 안드로이드에 미리 정의된 위젯이나 레이아웃을 이용할 수도 있고, 자신의 용도에 맞게 맞춤식 뷰를 만들 수도 있습니다.

 

 

이런 뷰 계층 구조를 화면에 표시하기 위해서는 액티비티에서 setContentView() 메소드를 부르면 됩니다. 호출 시 인자로 뷰 계층 구조 트리의 루트 노드에 대한 참조를 전달해야 합니다. 안드로이드 시스템은 이 참조를 받아서 뷰 트리에 해당하는 화면 영역이 어디인지 측정하고, 해당 화면 영역을 지우고(invalidate), 이 화면 영역에 다시 인자로 받은 뷰 트리의 루트 노드를 그립니다. 물론 뷰 계층 구조 트리의 루트 노드는 다시 자기 자식 노드들에게 뭔가를 그리라고 요청할 수 있습니다. 이렇게 차례로 계층 구조 트리를 따라 내려 가면서 각각의 노드를 그리게 됩니다. 각 뷰 그룹 노드는 자신은 그릴 필요가 없고 자식 노드들에게 뭔가를 그리라고 요청만 하면 되겠지요. 자식 노드는 부모 노드의 어느 위치에 어떤 크기로 그려야 할 지를 부모 노드에게 물어 볼 수 있습니다. 부모 객체가 자식 객체를 어디에 어떤 크기로 그릴 지를 최종 결정합니다. 안드로이드는 레이아웃 구성 요소들을 파싱할 때 계층 구조의 꼭대기에서부터 in-order로 따라 가면서 파싱합니다. in-order로 파싱한다는 의미는 뷰들을 인스턴스화 한 후에 이것들을 부모 노드에 추가하는 식으로 동작한다는 뜻입니다. 그릴 때도 in-order로 그려지기 때문에 만일 두 구성 요소의 위치가 겹치게 되면 마지막에 그려지는 구성 요소가 이전에 그려진 구성 요소 위에 그려지게 됩니다. 뷰 계층 구조가 어떻게 측정되고 그려지는지는 다음에 다시 자세히 설명하겠습니다.

 

(2) 레이아웃

 

XML 파일로 레이아웃을 정의하고 뷰 계층 구조를 표현하는 것이 가장 일반적인 방법입니다. XML은 HTML 처럼 사람이 읽을 수 있도록 구조를 표현합니다. XML에 있는 각 구성 요소는 뷰 객체이거나 뷰 그룹 객체 중 하나입니다. 앞의 그림에서 설명했듯이 뷰 객체들은 트리의 끝에 있는 잎이고, 뷰 그룹 객체는 트리의 가지입니다.

 

XML 구성 요소의 이름은 해당 자바 클래스의 이름을 쓰면 됩니다. <TextView> 요소는 TextView를 생성합니다. <LinearLayout> 요소는 LinearLayout 뷰 그룹을 생성합니다. 레이아웃 리소스를 로드할 때, 안드로이드 시스템이 레이아웃에 있는 각 구성 요소에 대응되는 이런 런타임 객체를 초기화해 줍니다. 아래 소스 코드는 텍스트 뷰와 버튼을 가진 간단한 수직 레이아웃의 예입니다.

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:orientation="vertical" >
    <TextView android:id="@+id/text"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="Hello, I am a TextView" />
    <Button android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello, I am a Button" />
</LinearLayout>

 

위 코드를 보면 LinearLayout 요소 안에 TextView와 Button이 있습니다. 위 뷰 계층 구조를 더 확장하여 더 복잡한 레이아웃을 만들려면 여기에다 다른 LinearLayout을 중첩시키면 됩니다. UI 레이아웃을 만드는 방법은 추후 더 자세히 설명드리겠습니다.

 

뷰를 배치하는 방법은 여러 가지가 있습니다. 더 다양한 종류의 뷰 그룹을 사용하면 무한히 많은 방법으로 자식 뷰와 뷰 그룹을 구조화할 수 있겠죠. 안드로이드가 제공하는 미리 정의된 뷰 그룹(레이아웃)에는 LinearLayout, RelativeLayout, TableLayout, GridLayout 등이 있습니다. 각각은 각기 다른 파라미터로 자식 뷰의 위치나 레이아웃 구조를 정의하고 있습니다.

 

 

(3) 위젯

 

위젯은 사용자와 상호 작용하기 위한 인터페이스로 뷰 객체의 일종입니다. 안드로이드는 버튼, 체크 박스, 텍스트 입력 필드 등과 같이 완전히 구현된 위젯들을 제공해 줍니다. 이 위젯들을 이용하여 UI를 신속하게 만들 수 있습니다. 안드로이드는 날짜 지정, 줌 컨트롤과 같은 더 복잡한 위젯도 제공합니다. 그렇다고 안드로이드 플랫폼에서 제공 가능한 위젯의 종류가 제한되어 있는 것은 아닙니다. 만약 맞춤식 위젯을 원한다면 기존 위젯을 확장하거나 조립하여 새로운 뷰 객체를 정의할 수 있습니다.

 

(4) UI 이벤트

 

일단 UI에 뷰나 위젯을 추가했다면, 이것들과 상호작용하는 방법을 알아야 원하는 동작을 수행할 수 있습니다. UI 이벤트를 받으려면 다음 2 가지 중 하나를 해야 합니다.

 

  • 이벤트 리스너(listener)를 하나 정의하고 뷰에 등록
    이벤트를 받지 않을 경우보다는 받을 일이 더 많겠죠. 이럴 때는 특정 이벤트를 수신하는 리스너를 정의하고 뷰에 등록해야 합니다. View 클래스에는 On<이벤트 이름>Listener 형식의 인터페이스가 여러 개 정의되어 있습니다. 이런 각각의 리스너에는 On<이벤트 이름>() 형식의 콜백 함수가 대응됩니다. 예를 들면, 뷰의 클릭 이벤트를 수신하기 위한 View.OnClickListener가 있고, 뷰의 터치 스크린 이벤트를 수신하기 위한 View.OnTouchListener가 있고, 뷰의 키 입력 이벤트를 수신하기 위한 View.OnKeyListener가 있습니다. 버튼이 선택되었을 때 즉, 클릭이 되었을 때 뷰가 이런 이벤트를 수신하려면 OnClickListener를 정의하고 setOnClickListener() 메소드로 뷰에 등록을 해야 합니다. 그리고 이런 이벤트를 받았을 때 뭔가를 수행하려면 onClick() 콜백 메소드를 구현해야 합니다.
  • 뷰에 있는 기존 콜백 메소드의 재정의
    View
    클래스를 하나 구현하고 나서 특정 이벤트의 동작을 재정의하고자 할 때 사용합니다. 이런 이벤트로는 스크린이 터치되었을 때 onTouchEvent(), 트랙볼이 움직였을 때 onTrackballEvent(), 장치에 있는 키가 눌렸을 때 onKeyDown() 등이 있습니다. 자신의 맞춤형 뷰에서 각 이벤트의 기본 동작을 재정의하거나 자식 뷰에게 이런 이벤트를 전달하고 할 때 사용합니다. 이런 함수들은 뷰 클래스의 콜백 함수들입니다. 따라서 이런 콜백 함수를 재정의하는 경우는 맞춤형 컴포넌트를 만들었을 때만 하면 됩니다.

 

(5) 메뉴

 

어플리케이션 메뉴는 UI의 중요한 부분 중 하나죠. 이런 메뉴는 어플리케이션의 기능이나 설정을 드러낼 때 주로 사용됩니다. 장치의 MENU 키를 눌렀을 때 나오는 메뉴가 가장 대표적인 것입니다. 이 외에도 여러 가지 메뉴가 있습니다. 예를 들면 문맥(context) 메뉴를 추가하여 사용자가 어떤 항목을 누르고 있으면 튀어 나오게 할 수 있습니다.

 

메뉴도 뷰 계층 구조를 사용하여 구조화됩니다. 그러나 사용자가 이런 구조를 일일이 정의할 필요는 없습니다. 대신에 액티비티에 onCreateOptionsMenu()나 onCreateContextMenu() 콜백 함수만 정의하면 됩니다. 그리고 메뉴에 포함되길 원하는 항목을 액티비티에 선언하세요. 그러면 적당한 시점에 안드로이드가 자동으로 메뉴를 구성하여 뷰 계층 구조를 만들고 각 메뉴 항목을 화면에 그려 줍니다.

 

메뉴 관련 이벤트를 처리할 필요가 있을 때도 각 메뉴 항목에 대한 이벤트 리스너를 등록할 필요가 없습니다. 메뉴에 있는 항목이 하나 선택되면 onOptionsItemSelected()나 onContextItemSelected() 메소드가 프레임웍에 의해서 호출됩니다. 레이아웃처럼 메뉴도 XML 파일에 각 항목이 선언되어 있습니다.
 

(6) 어뎁터

 

외부 데이터를 가지고 뷰를 구성하거나 하드 코딩되지 않은 정보를 가진 뷰 그룹을 만들고 싶을 때가 있겠죠. 이렇게 하려면 뷰 그룹으로 AdapterView를 사용하고, 각 자식 뷰를 어뎁터에서 초기화하고 데이터를 집어 넣어 주면 됩니다.

 

AdapterView 객체는 주어진 Adapter 객체에 기반하여 자신의 자식 뷰들을 결정하도록 ViewGroup을 구현한 것입니다. Adapter는 데이터 원천(아마도 외부 문자열 배열이 되겠죠)과 이 데이터를 디스플레이하는 AdapterView 사이에서 전달자처럼 동작합니다. Adapter 클래스를 구현한 예는 많이 있습니다. Cursor로부터 오는 데이터베이스 데이터를 읽는 CursorAdapter도 있고, 임의의 배열로부터 데이터를 읽는 ArrayAdapter도 있습니다.

 

(7) 스타일과 테마

 

표준 위젯의 모양이 마음에 안 드는 분도 있겠죠. 이런 모양을 고치고 싶다면 자기 자신만의 스타일이나 테마를 만들 수도 있습니다.

 

  • 스타일은 하나 이상의 포맷팅 속성들의 집합입니다. 스타일 속성들은 레이아웃의 각각의 구성 요소에 스타일 단위로 적용될 수 있습니다. 예를 들면, 어떤 텍스트의 크기나 색깔을 규정하는 스타일을 하나 정의하고, 이 스타일을 특정 뷰 구성 요소에 적용할 수 있습니다.
  • 테마도 하나 이상의 포맷팅 속성들의 집합입니다. 테마 속성들은 어떤 어플리케이션의 모든 액티비티에 테마 단위로 적용될 수도 있고, 하나의 액티비티에만 적용될 수도 있습니다. 예를 들면, 윈도 프레임의 색깔이나 패널의 배경색을 특정 색으로 바꾼다든지, 메뉴의 텍스트 크기나 색깔을 바꿀 수 있습니다. 이렇게 테마는 특정 액티비티나 전체 어플리케이션에 적용될 수 있습니다.

 

스타일이나 테마는 리소스입니다. 안드로이드는 기본 스타일이나 테마 리소스를 몇 개 제공합니다. 이것들을 그대로 사용하거나 새로운 맞춤형 스타일이나 테마 리소스를 새로 선언할 수 있습니다.

 

5.2 레이아웃 선언하기

 

레이아웃은 액티비티 안의 사용자 인터페이스를 정의하기 위한 아키텍처입니다. 레이아웃은 사용자에게 보여지는 모든 구성 요소들을 가지고 있으며, 이것들의 배치 구조를 정의합니다. 다음과 같이 2 가지 방법으로 레이아웃을 선언합니다.

 

  • XML로 UI 구성 요소 선언
    안드로이드는 뷰 클래스와 그 하위 클래스에 각각 직접 대응되는 XML 어휘(예를 들면, 위젯이나 레이아웃 등)를 제공합니다.
  • 실행 시 레이아웃 구성 요소의 인스턴스화
    프로그램으로 뷰나 뷰 그룹 객체를 생성할 수 있습니다. 물론 속성 값들도 프로그램으로 지정하면 됩니다.

 

안드로이드 프레임웍은 어플리케이션의 UI를 선언하고 관리하는 방법으로 위 2 가지 중 하나 또는 2 가지 방법 모두를 사용할 수 있도록 유연성을 제공합니다. 예를 들면, 어플리케이션의 기본 레이아웃(화면에 표시될 스크린 구성 요소와 그 속성들)은 XML로 선언하고, 실행시 XML로 선언된 화면 객체들의 상태를 변화시킬 수 있는 것들은 어플리케이션의 코드에 추가할 수 있습니다.

 

XML로 UI를 선언하는 장점은 어플리케이션의 동작을 제어하는 코드로부터 겉모습(presentation)을 더 잘 분리할 수 있다는 점입니다. UI 서술 내역이 어플리케이션 코드의 외부에 있기 때문에 소스 코드를 수정하여 재컴파일 하지 않고도 UI를 수정하여 적용할 수 있습니다. 예를 들면, 여러 가지 다른 화면 방향(orientation)이나 여러 가지 다른 장치 화면 크기, 다양한 언어에 따른 XML 레이아웃을 만들어 놓을 수 있습니다. 또한 XML에 레이아웃을 선언하면 UI 구조를 더 쉽게 가시화시킬 수 있어서 디버깅도 쉬워집니다. 따라서 이 장에서는 XML에 레이아웃을 선언하는 방법에 대한 설명에만 집중합니다.

 

  • 이클립스 ADT 플러그인은 XML 레이아웃의 미리보기 기능을 제공합니다. XML 파일을 연 후에 Layout 탭을 선택하면 레이아웃을 볼 수 있습니다.
  • 레이아웃 디버깅을 하려면 계층 구조 뷰어 도구를 꼭 사용하세요. 이 도구를 이용하면 레이아웃 속성 값을 볼 수 있고, 패딩(padding)/마진(margin) 인디케이터를 가지고 윤곽을 그려 볼 수 있고, 에뮬레이터나 장치에서 디버깅할 때 모두 그려진 뷰들을 볼 수 있습니다.
  • layoutopt 도구를 사용하면 비효율적이거나 다른 문제가 있는 레이아웃이나 계층 구조를 신속히 분석할 수 있습니다.

 

일반적으로 XML 어휘는 XML 요소의 경우 클래스나 메소드의 구조나 명명법을 그대로 따릅니다. 요소의 이름은 클래스 이름에 대응되고, 속성의 이름은 메소드에 대응됩니다. 사실 이런 대응 관계가 너무 직관적이라서 어떤 XML 속성이 어떤 클래스 메소드에 대응되는지, 어떤 클래스가 어떤 XML 요소에 대응되는지 추측할 수 있죠. 그러나 모든 어휘가 이런 것은 아닙니다. 어떤 경우는 미세하게 명명법이 달라집니다. 예를 들면, EditText 요소는 EditText.setText()에 대응되는 setText 속성이 아닌 text 속성을 가지고 있습니다.

 

(1) XML 작성

 

안드로이드의 XML 어휘를 사용하여 UI 레이아웃과 이 레이아웃 안에 포함되는 스크린 구성 요소들을 신속히 설계할 수 있습니다. HTML로 일련의 중첩 요소를 가진 웹 페이지를 만드는 것처럼 말이죠. 각 레이아웃 파일에는 뷰 또는 뷰 그룹 객체인 루트 요소를 하나만 포함해야 합니다. 일단 루트 요소를 정의하면 추가적인 레이아웃 객체나 위젯들을 자식 요소로 추가하여 점진적으로 레이아웃을 구성하는 뷰 계층 구조를 만듭니다. 예를 들면, 앞에서 본 것과 같은 다음 XML 레이아웃은  TextView와 Button을 가지는 수직 LinearLayout입니다.

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:orientation="vertical" >
    <TextView android:id="@+id/text"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="Hello, I am a TextView" />
    <Button android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello, I am a Button" />
</LinearLayout>

 

펼쳐두기..

 

XML로 레이아웃을 선언한 후에는 이 파일을 .xml 확장자로 안드로이드 프로젝트의 res/layout/ 디렉토리에 저장하여야 올바로 컴파일됩니다.

 

(2) XML 리소스 로드

 

어플리케이션을 컴파일할 때, 각 XML 레이아웃 파일도 뷰 리소스로 컴파일됩니다. 어플리케이션의 Activity.onCreate() 콜백 함수에서 이 레이아웃 리소스를 로드하는 코드를 구현해야 합니다.  R.layout.layout_file_name 형식의 레이아웃 리소스에 대한 참조를 인자로 전달하여 setContentView()를 호출하면 됩니다. 예를 들어 XML 레이아웃 파일이 main_layout.xml로 저장되어 있다면 다음 코드와 같이 액티비티에서 레이아웃 리소스를 로드할 수 있습니다.

 

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView.(R.layout.main_layout);
}

 

위 액티비티가 론치될 때 안드로이드 프레임웍에 의해서 onCreate() 콜백 메소드가 호출됩니다. 이에 대해서는 이미 4 장의 안드로이드 어플리케이션 기초의 액티비티 수명 부분에서 설명하였습니다.

 

(3) 속성

 

모든 뷰와 뷰 그룹 객체는 자신들의 다양한 XML 속성들을 지원합니다. 어떤 속성들은 특정 뷰 객체에만 해당됩니다. 예를 들면, TextView는 textSize 속성을 지원합니다. 그러나 이런 속성들은 이 클래스를 확장하는 다른 뷰 객체들에게도 상속됩니다. 어떤 속성들은 모든 뷰 객체들에 공통됩니다. id 속성과 같은 것들은 루트 뷰 클래스로부터 상속되기 때문입니다. 그리고 다른 속성들은 "레이아웃 파라미터"로 간주됩니다. 레이아웃 파라미터는 뷰 객체의 레이아웃 방향을 기술하는 속성입니다. 이런 속성은 해당 객체의 부모인 뷰 그룹 객체에 의해 정의됩니다.

 

ID

 

뷰 객체는 정수 ID를 가집니다. 이 ID는 트리 내에서 뷰를 식별하는 유일한 값입니다. 어플리케이션이 컴파일될 때 이 ID는 정수 값으로 참조가 됩니다 그러나 레이아웃 XML 파일에는 id 속성의 문자열 값으로 기술되어 있습니다. 이 ID는 View 클래스에 의해 정의되며 모든 뷰 객체에 공통된 속성입니다. 이 값을 사용할 일이 자주 있을 것입니다. XML 태그 내에서 ID를 사용할 때의 구문은 다음과 같습니다.

 

android:id="@+id/my_button"

 

문자열의 첫 번째 문자인 엣 심볼(@)은 XML 파서가 이 ID 문자열의 나머지 부분을 파싱한 후에 ID 리소스로 식별하도록 지시하는 것입니다. 플러스 심볼(+)은 R.java 리소스 파일에 새로운 이름을 생성하여 추가하라는 의미입니다. 안드로이드 프레임웍에서 사용되는 많은 ID 리소스들이 있습니다. 안드로이드 리소스 ID를 참조할 때는 플러스 심볼은 필요하지 않습니다. 다음과 같이 android 패키지 네임스페이스를 플러스 대신 추가해야 합니다.

 

android:id="@android:id/empty"

 

android 패키지 네임스페이스를 가지고 있으면 이제는 로컬 리소스 클래스가 아닌 android.R 리소스 클래스에서 ID를 참조합니다. 어플리케이션에서 뷰를 생성하고 참조하는 일반적인 패턴은 다음과 같습니다.

 

  1. 레이아웃 파일에 뷰/위젯을 정의하고 유일한 ID를 할당합니다.
    <Button android:id="@+id/my_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/my_button_text"/>
    
  2. 그리고 뷰 객체의 인스턴스를 생성하고, (보통은 onCreate() 메소스에서) 다음과 같이 레이아웃에서 이것을 가져 옵니다.
    Button myButton = (Button) findViewById(R.id.my_button);
    

 

RelativeLayout을 생성할 때는 뷰 객체에 ID를 정의하는 것이 중요합니다. 상대 레이아웃에서는 형제 뷰의 배치가 다른 형제 뷰와의 상대적인 관계로 정의됩니다. 이럴 때 유일한 ID로 다른 형제 뷰를 참조합니다. 전체 트리를 통틀어서 ID가 유일할 필요는 없지만 검색 중인 부분에서는 유일해야 합니다. (물론 전체 트리에서 검색할 수도 있으므로 가능한 한 전체 트리에서 완벽히 유일하게 ID를 만드는 것이 제일 좋습니다.)

 

레이아웃 파라미터

 

layout_something으로 이름이 지어진 XML 레이아웃 속성은 뷰에 대한 레이아웃 파라미터를 정의합니다. 이 레이아웃 파라미터는 뷰가 어떤 뷰 그룹에 속하면 적합한 지를 나타냅니다. 모든 뷰 그룹 클래스는 ViewGroup.LayoutParams를 확장하여 중첩 클래스로 구현해야 합니다. 이런 하위 클래스는 뷰 그룹에 적합하도록 자식 뷰의 크기와 위치를 정의하는 속성을 가지고 있습니다. 아래 그림에서 부모 뷰 그룹은 자식 뷰(자식 뷰 그룹도 포함)의 레이아웃 파라미터를 정의하고 있습니다.

 

 

모든 LayoutParams 하위 클래스는 값을 설정하는 자신 만의 구문이 있습니다. 자식은 부모에 적합하도록 LayoutParams이 정의되어야 합니다. 물론 손자도 그 부모에 적합하도록 LayoutParams이 정의되어야 합니다.

 

모든 뷰 그룹은 폭과 높이(layout_width와 layout_height)를 가집니다. 각 뷰도 폭과 높이를 정의해야 합니다. LayoutParams는 마진과 테두리를 옵션으로 포함합니다. 폭과 높이를 정확히 측정하여 명시할 수도 있습니다. 비록 이렇게 자주 하고 싶지는 않겠죠. 종종 뷰에게 자신의 내용을 표시하기 위해서 필요한 폭과 높이를 알아서 지정하도록 할 수도 있고, 부모 뷰의 크기와 똑같게 맞출 수도 있습니다. 이럴 경우 각각 wrap_content와 fill_parent 값을 사용하면 됩니다. 허용되는 치수 타입은 이용가능한 리소스 문서에 정의되어 있습니다.

 

(4) 레이아웃 위치

 

뷰의 모양은 사각형입니다. 뷰의 위치는 left와 top 좌표로, 차원은 폭과 높이로 표현됩니다. 위치와 차원의 단위는 픽셀입니다.

 

getLeft()와 getTop() 메소드를 호출하여 뷰의 위치를 알 수 있습니다. 앞의 메소드는 뷰를 나타내는 사각형의 왼쪽 x 좌표를 반환하고, 뒤의 메소드는 위쪽 y 좌표를 반환합니다. 반환 값은 뷰의 부모의 위치에 상대적입니다. 즉, getLeft()가 20을 반환하면 현 뷰가 부모 뷰의 왼쪽 모서리에서 오른쪽으로 20 픽셀 떨어져서 위치하고 있다는 뜻입니다.

 

불필요한 계산을 피하도록 여러 가지 편리한 메소드들이 많이 제공됩니다. 예를 들면 뷰의 사작형 오른쪽 끝이나 아래쪽 끝의 좌표를 알아내는 getRight()나 getBottom() 등도 있습니다. getRight()는 getLeft() + getWidth()를 계산한 것과 같습니다.

 

(5) 크기, 패딩, 마진

 

뷰의 크기는 폭과 높이로 표현됩니다. 뷰는 실제로 폭과 높이를 두 쌍을 가지고 있습니다. 첫 번째 쌍은 measured width와 measured height입니다.  이 값은 뷰가 부모 안에서 얼마나 크게 표시되기를 원하는지 정의합니다. 이 차원 값은 getMeasuredWidth()와 getMeasuredHeight()를 호출하여 얻을 수 있습니다.

 

두번째 쌍은 width와 height입니다. 이 값은 때로는 각각 drawing width와 drawing height로 불립니다. 이 차원 값은 화면에 표시된 뷰의 실제 크기를 정의합니다. 이 값들은 measured width, measured height와 다를 수도 있습니다. (그렇다고 꼭 달라야 하는 것은 아닙니다.) 이 값들은 getWidth()와 getHeight()를 호출하여 얻을 수 있습니다. getPaddingLeft(), getPaddingTop(), getPaddingRight()와 getPaddingBottom() 메소드를 불러서 각각의 값을 얻을 수 있습니다.

비록 뷰가 패딩을 정의할 수 있지만 마진을 지원하지 않습니다. 뷰 그룹이 마진을 지원합니다. 더 자세한 사항은 ViewGroup과 ViewGroup.MarginLayoutParams을 참조하세요.

 

끝.

4. 안드로이드 어플리케이션 기초

4.1 안드로이드 아키텍처

 

(1) 안드로이드 어플리케이션의 특징

 

안드로이드 어플리케이션은 다음과 같은 특징을 가지고 있습니다.

 

  • 경계가 없는 어플리케이션 : 어플리케이션은 표준 API(Application Programming Interface)를 통해 핵심(core) 모바일 기능에 접근 가능합니다. 원하는 경우 어플리케이션은 자신의 기능을 다른 어플리케이션에게 공개할 수 있습니다.
  • 평등한 어플리케이션 : 모바일 기기에 있는 어플리케이션은 다이얼러(dialer)나 홈(home) 같은 핵심 구성 요소인 경우에도 대체 또는 확장이 가능합니다.
  • 웹 제어가 쉬운 어플리케이션 : 어플리케이션은 HTML, 자바스크립트, 스타일시트 등을 쉽게 추가할 수 있습니다. 또한 WebView를 통해 웹 컨텐츠를 쉽게 보여줄 수 있습니다.
  • 동시에 실행될 수 있는 어플리케이션 : 어플리케이션을 동시에 여러 개 실행할 수 있는 완벽한 멀티태스킹 환경을 제공합니다. 어플리케이션은 백그라운드에서 실행되면서 필요한 경우 알림 정보를 보낼 수 있습니다.

 

(2) 안드로이드 아키텍처

 

안드로이드는 아래 그림과 같은 아키텍처로 구성되어 있습니다. 아래 그림을 보면 4 개의 계층이 있습니다. 위에서부터 어플리케이션, 어플리케이션 프레임웍, 라이브러리/안드로이드 런타임이 있고, 제일 아래애 리눅스 커널이 있습니다.

 

 

어플리케이션 계층

 

제일 위의 어플리케이션 계층에는 Home, Contacts, Phone, Browser 같은 내장 어플리케이션 뿐만 아니고, 다른 어플리케이션들이 존재합니다. 내장 어플리케이션은 다음과 같은 것들이 있습니다.

 

  • Alarm Clock
  • Browser
  • Calculator
  • Camcorder
  • Camera
  • Contacts
  • Custom Locale (developer app)
  • Dev Tools (developer app)
  • Dialer
  • Email
  • Gallery
  • Gestures Builder
  • IME for Japanese text input
  • Messaging
  • Music
  • Settings
  • Spare Parts (developer app)

 

어플리케이션 프레임웍 계층

 

그 아래 계층이 어플리케이션 프레임웍 계층입니다. 이 계층에서는 뷰 시스템, 컨텐츠 제공자, 리소스 관리자, 알림 관리자, 액티비티 관리자 등의 프레임웍 API를 제공합니다. 이 API들은 내장 어플리케이션들이 사용하는 것과 동일합니다. 어플리케이션 아키텍처는 컴포넌트 재사용이 쉽게 설계되어 있습니다. 어플리케이션이 자신의 기능을 다른 어플리케이션에 노출(export)할 수 있으며, 이런 기능들은 다른 어플리케이션에서 사용이 가능합니다.

 

  • 뷰 시스템
    윈도 시스템의 컨트롤과 같은 것으로 리스트, 그리드(grid), 텍스트 박스, 버튼, 내장 브라우저 등 풍부하고 확장 가능한 사용자 인터페이스를 제공합니다.
  • 컨텐츠 제공자
    다른 어플리케이션이 데이터에 접근할 수 있도록 지원합니다.
  • 리소스 관리자
    현지화된 문자열, 그래픽, 레이아웃 파일 등과 같은 非코드 리소스에 접근할 수 있도록 합니다.
  • 알림 관리자
    상태 바에 각 어플리케이션의 용도에 따른 알림 정보를 표시할 수 있도록 합니다.
  • 액티비티 관리자
    어플리케이션의 수명을 관리합니다.

 

라이브러리와 안드로이드 런타임 계층

 

그 아래 계층에는 라이브러리와 안드로이드 런타임이 있습니다. 라이브라리 계층에는 표준 C 시스템 라이브러리인 libc와 PacketVideo 사의 OpenCORE에 기반을 둔 미디어(이미지나 음악 및 동영상) 라이브러리, 디스플레이 하부 시스템에 대한 접근을 제어하는 표면(surface) 관리자, 웹 브라우저 엔진인 LibWebCore, 기본 2D 그래픽 엔진인 SGL, OpenGL ES 1.0 API를 구현한 3D 라이브러리, 비트맵이나 벡터 폰트를 그려주는 FreeType, 가볍고 강력한 관계형 DBMS인 SQLite가 있습니다.

 

안드로이드 런타임은 자바 언어로 코어 라이브러리 기능을 사용할 수 있도록 하는 것입니다. 여기서 핵심은 Dalvik VM입니다. 이것은 모바일용으로 최적화된 가상 머신으로 전통적으로 사용되던 Java ME(Mobile Edition)와 같은 것을 사용하지 않고, 모든 어플리케이션이 자신의 프로세스 안에서 동작하고, 자신의 VM 인스턴스를 가지도록 만들어져 있습니다. 따라서 여러 개의 VM이 하나의 장치 안에서 동작이 가능하게 됩니다. 이 계층이 있으므로 해서 개발자는 특정 하드웨어 구현에 신경쓰지 않고 어플리케이션을 개발할 수 있게 된 것입니다.

 

리눅스 커널 계층

 

제일 아래 계층에는 리눅스 커널이 있습니다. 안드로이드 커널은 리눅스 2.6에 기반을 두고 만들어졌습니다. 이 계층에서는 보안, 스레딩, 프로세스, 메모리 관리 등의 하위 수준의 기능을 처리합니다.

 

4.2 어플리케이션 컴포넌트

 

(1) 안드로이드 패키지

 

컴파일된 자바 코드와 어플리케이션에 필요한 각종 데이터 및 리소스는 안드로이드 패키지(Android Package)에 번들이 되어 모바일 장치에 배포나 설치가 됩니다. 안드로이드 패키지는 확장자가 .apk로 아카이브된(archive)된 파일입니다. 하나의 .apk 파일은 하나의 어플리케이션으로 간주됩니다.

 

안드로이드 어플리케이션이 동작하는 양상은 3 가지 모습으로 표현할 수 있습니다. 첫 번째 모습은 모든 어플리케이션은 자신의 리눅스 프로세스 상에서 동작합니다. 어플리케이션을 실행시키면 안드로이드가 새로운 프로세스를 생성합니다. 그리고 어플리케이션이 더 이상 필요하지 않을 경우 프로세스를 종료합니다. 그 외에 다른 어플리케이션이 시스템 리소스를 요구할 때 다른 프로세스를 종료해야만 가능한 경우라면 안드로이드를 다른 프로세스를 강제로 종료할 수 있습니다.

 

안드로이드 어플리케이션 동작의 또 다른 모습은 각 프로세스는 자신의 자바 VM을 가진다는 것입니다. 이렇게 동작을 하기 때문에 각 어플리케이션은 다른 어플리케이션과 격리되어 동작합니다.

 

마지막 모습은 각 어플리케이션은 유일한 리눅스 사용자 ID를 할당 받는다는 것입니다. 어떤 어플리케이션의 파일들은 단지 그 어플리케이션의 사용자에게만 보이도록 권한(permission)이 설정됩니다. 두 개 이상의 어플리케이션이 같은 사용자 ID를 가지게 하는 예외적인 경우가 있는데, 이런 경우는 서로 다른 어플리케이션의 파일들을 볼 수 있게 됩니다.

 

(2) 어플리케이션 컴포넌트

 

안드로이드의 핵심 특징 중 하나는 다른 어플리케이션의 컴포넌트를 재사용할 수 있다는 것입니다. 만약 내 어플리케이션이 다른 어플리케이션의 기능을 이용하고 싶다면 다른 어플리케이션의 소스 코드도 필요 없고, 링크도 필요 없이 사용이 가능합니다. 물론 다른 어플리케이션의 자신의 기능을 노출해 줘야 합니다. 이렇게 되면 다른 어플리케이션의 기능 중 일부를 단순 호출하기만 하면 됩니다. 이런 특징 때문에 안드로이드 어플리케이션은 main() 함수와 같은 하나의 진입점(entry point)만 가지지 않습니다. main()함수 자체가 없습니다.

 

이런 재사용을 가능하게 하는 구조가 바로 안드로이드 컴포넌트입니다. 안드로이드 컴포넌트는 다음과 같은 4 가지 종류가 있습니다.

 

  • 액티비티(activity)
    가시적인(visible) 사용자 인터페이스를 가지는 것들을 액티비티라고 합니다. 각 액티비티는 Activity 기본 클래스(base class)를 상속받아서 구현합니다. 하나의 어플리케이션은 여러 개의 액티비티로 구성이 될 수 있습니다. 각 액티비티는 전체 화면 또는 화면의 일부인 윈도(window)로 구성됩니다. 윈도는 뷰(view)를 가지고, 이 뷰는 컨트롤(control)과 같은 것으로 버튼, 텍스트, 스크롤 바, 메뉴 등을 가집니다.
  • 서비스(service)
    가시적인 사용자 인터페이스를 가지지 않고 백그라운드(background)에서 동작하는 것들을 서비스라고 합니다. 각 서비스는 Service 기본 클래스를 상속받아서 구현합니다. 서비스의 예로는 음악을 연주하는 서비스가 있습니다. 음악 연주 어플리케이션은 사용자 인터페이스가 필요하겠지만, 사용자가 음악 연주를 시작한 후에는 중지나 종료를 하기 전까지는 특별히 사용자 인터페이스가 필요하지 않습니다. 이런 부분은 서비스로 구현할 수 있습니다.
  • 브로드캐스트 수신자(broadcast receiver)
    널리 공지사항을 알리고자 하는 경우 특정 알림에 반응하는 것들을 브로드캐스트 수신자라고 합니다. 각 브로드캐스트 수신자는 BroadcastReceiver 기본 클래스를 상속받아 구현합니다. 반대로 사용자에게 뭔가를 알리고자 할 때는 NotificationManager를 이용합니다. 예를 들어, 타임존(timezone)이 바뀌었거나, 배터리가 부족하거나, 어떤 그림이 선택이 되었거나, 언어가 바뀌었거나 하는 다양한 상황이 있겠지요. 이럴 때 정보를 알리고자 하면 NotificationManager를 사용하고, 알림을 받고자 하면 BroadcastReceiver 기본 클래스를 상속받아 구현하면 됩니다.
  • 컨텐츠 공급자(content provider)
    어떤 어플리케이션의 데이터를 다른 어플리케이션에 제공하고자 할 때 사용하는 것들을 컨텐트 공급자라고 합니다. 각 컨텐츠 공급자는 ContentProvider 기본 클래스를 상속받아 구현합니다.

 

(3) 인텐트(intent)

 

앞에서 설명한 것과 같이 안드로이드 컴포넌트는 4 가지 종류가 있습니다. 안드로이드에서 각 컴포넌트를 활성화시키는 방법은 인턴트(intent)라는 비동기 메시지를 이용합니다. 인턴트는 메시지를 담고 있는 Intent 객체입니다.

 

인텐트 객체로 활성화될 수 있는 컴포넌트는 액티비티, 서비스, 브로드캐스트 수신자 3 가지입니다. 컨턴츠 공급자는 인턴트로 활성화되지 않고, ContentResolver로부터 요청을 받았을 때 활성화됩니다. 인턴트를 인자로 전달하여 이들 컴포넌트를 활성화하는 방법은 다음과 같습니다.

 

  • 액티비티 활성화
    액티비티를 호출하는 쪽에서 Context.startActivity()나 Activity.startActivityForResult()를 부르면 됩니다. 그러면 안드로이드가 받아서 호출되는 쪽의 onNewIntent() 메소드를 불러 줍니다. 물론 인자로 인텐트가 전달됩니다. 전달된 인텐트 인자를 보고 의도를 파악하면 됩니다. 이와 별도로 호출된 액티비티는 getIntent() 메소드를 호출하여 의도를 파악할 수도 있습니다.
  • 서비스 활성화
    서비스를 호출하는 쪽에서 Context.startService() 메소드를 부르면 됩니다. 그러면 안드로이드가 받아서 호출되는 쪽의 onStart() 메소드를 불러 줍니다. 물론 인자로 인텐트가 전달됩니다.
  • 브로드캐스트 수신자 활성화
    브로드캐스트 수신자를 호출하는 쪽에서 Context.sendBroadcast()나 Context.sendOrderedBroadcast() 또는 Context.sendStickyBroadcast()를 부르면 됩니다.

 

컴포넌트가 활성화된 후에는 자신이 스스로 종료(shut down)할 수도 있고, 다른 컴포넌트가 종료시킬 수도 있습니다. 각 컴포넌트를 종료하는 방법은 다음과 같습니다.

 

  • 액티비티 종료
    액티비티가 스스로 종료하고자 하는 경우는 finish() 메소드를 호출하면 됩니다. 반면에 다른 액티비티를 startActivityForResult() 메소드를 호출하여 활성화시킨 후에 종료하고자 할 때 finishActivity() 메소드를 호출하면 됩니다.
  • 서비스 종료
    서비스가 스스로 종료하고자 하는 경우는 stopSelf() 메소드를 호출하면 됩니다. 반면에 다른 서비스를 Centext.bindService()나 onBind(), bindService() 등의 함수로 호출하여 시작시킨 후에 종료하고자 하면 Context.stopService() 메소드를 호출하면 됩니다.
  • 컨텐츠 공급자 종료
    별도의 종료를 위한 메소드가 제공되지 않습니다. 컨턴츠 공급자는 ContentResolver에 반응하여 활성화되고 일을 처리한 후 종료됩니다.
  • 브로드캐스트 수신자 종료
    컨텐츠 공급자와 유사하게 별도의 종료를 위한 메소드가 제공되지 않습니다. 브로드캐스트 메시지에 반응하여 활성화되고 일을 처리한 후 종료됩니다.

 

(4) 매니패스트(manifest) 파일

 

안드로이드가 어플리케이션의 특정 컴포넌트를 시작하려면 안드로이드는 어플리케이션에 어떤 컴포넌트들이 있는지 알고 있어야 합니다. 이를 위해서 어플리케이션은 자신의 컴포넌트들을 매니패스트 파일에 선언을 해야 하며, 이 매니패스트 파일도 안드로이드 패키지인 .apk 파일 안에 번들되어야 합니다.

 

모든 어플리케이션은 항상 AndroidManifest.xml로 명명된 매니패스트 파일을 가지고 있어야 합니다. 매니패스트 파일은 구조화된 XML 파일입니다. 이 파일에는 컴포넌트(액티비티, 서비스, 브로드캐스트 수신자, 컨텐츠 제공자)들이 어떤 것들이 있는지와 링크 시에 필요한 라이브러리가 어떤 것들이 있는지와 어플리케이션의 권한(permission) 등이 기술되어 있습니다. 다음은 매니패스트 파일의 예입니다.

 

<?xml version="1.0" encoding="utf-8"?>
<manifest . . . >
    <application . . . >
        <activity android:name="com.example.project.FreneticActivity"
                  android:icon="@drawable/small_pic.png"
                  android:label="@string/freneticLabel"
                  . . .  >
        </activity>
        . . .
    </application>
</manifest>

 

<activity> 요소의 name 속성에 이 액티비티를 구현한 Acitivity 하위 클래스 이름을 적습니다. 위 예의 경우는 "com.example.project.FreneticActivity"입니다. 만약 액티비티의 라벨이나 아이콘이 사용자에게 표시될 필요가 있을 경우는 label과 icon도 기술합니다. 매니패스트 파일에는 <activity> 요소 외에 서비스를 기술하기 위한 <service>, 컨텐츠 제공자를 기술하기 위한 <provider>, 브로드캐스트 수신자를 기술하기 위한 <receiver> 요소가 있습니다.

 

인턴트 객체에는 타켓 컴포넌트를 명시적으로 적어 넣을 수 있지만, 타켓 컴포넌트를 명시적으로 적지 않을 수도 있습니다. 만약 타켓 컴포넌트를 명시적으로 적지 않으면 안드로이드는 인턴트 필터(intent filter)를 보고 어떤 컴포넌트를 활성화할 지 결정합니다. 다음은 매니패스트 파일에 기술된 인턴트 필터의 예입니다.

 

<?xml version="1.0" encoding="utf-8"?>
<manifest . . . >
    <application . . . >
        <activity android:name="com.example.project.FreneticActivity"
                  android:icon="@drawable/small_pic.png"
                  android:label="@string/freneticLabel"
                  . . .  >
            <intent-filter . . . >
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter . . . >
                <action android:name="com.example.project.BOUNCE" />
                <data android:mimeType="image/jpeg" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        . . .
    </application>
</manifest>

 

위 예는 앞의 매니패스트 파일에 인턴트 필터가 추가된 것입니다. 위 코드의 굵은 글씨로 표시된 부분이 추가된 부분입니다. 인텐트 필터에 <action>과 <category>, <data> 요소가 있습니다. 첫 번째 인턴트 필터는 어플리케이션 론처(launcher)에서 실행될 컴포넌트의 경우에 사용되는 일반적인 액션, 카테고리 조합입니다. 즉, 액션은 "android.intent.action.MAIN"을 사용하고, 카테고리는 "android.intent.category.LAUNCHER"를 사용합니다. 매니패스트 파일에 대한 자세한 내용은 별로도 설명할 예정입니다.

 

4.3 액티비티와 태스크

 

(1) 태스크(task)

 

어떤 어플리케이션에서 다른 어플리케이션의 기능을 이용하고자 할 경우에 다른 어플리케이션에 Intent 객체를 인자로 startActivity() 메소드를 호출하면 됩니다. 그러면 사용자에게는 타 어플리케이션이 현 어플리케이션의 일부인 것처럼 보이게 됩니다. 실제로 안드로이드는 호출한 액티비티와 호출된 액티비티를 같은 태스크(task) 안에 유지시킵니다.

 

태스크(task)는 사용자가 마치 하나의 어플리케이션인 것처럼 경험하게 하는 것들을 말합니다. 즉, 태스크는 "스택에 쌓여 있는 관련된 액티비티들의 그룹"이라고 말할 수 있습니다. 스택의 밑바닥에 있는 루트 액티비티(root activity)가 태스크를 시작한 것입니다. 보통은 어플리케이션 론처에서 사용자가 선택한 액티비티가 루트 액티비티가 됩니다. 스택의 꼭대기에 있는 액티비티는 현재 동작 중인 포커스된 액티비티입니다.

 

어떤 액티비티가 다른 액티비티를 동작시키면 새로운 액티비티가 스택에 쌓입니다. 휴대폰에서 사용자가 BACK 키를 누르면 이전 화면으로 돌아가는데, 이 때 현재 액티비티가 스택에서 팝(pop)되고 이전 액티비티가 실행됩니다. 따라서 태스크는 "액티비티들이 쌓여 있는 스택"으로 단순히 생각하는 것이 쉽게 이해가 됩니다. 태스크가 매니패스트 파일 안에 있는 어떤 클래스나 요소(element)가 아니므로 오해를 하지 않도록 주의하세요.

 

하나의 태스크 안에 있는 액티비티들은 태스크 단위로 움직입니다. 태스크가 포그라운드(foreground)나 백그라운드(background)로 전환될 때 관련 태스크가 통째로 옮겨집니다. 이런 동작을 바꾸려면 매니패스트 파일의 <activity> 요소의 플래그(flag)나 속성을 변경해야 합니다. 매니패스트 파일의 인텐트 플래그(flag)에는 다음과 같은 것들이 있습니다.

 

  • FLAG_ACTIVITY_NEW_TASK
  • FLAG_ACTIVITY_CLEAR_TOP
  • FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
  • FLAG_ACTIVITY_SINGLE_TOP

 

<activity> 요소의 속성에는 다음과 같은 것들이 있습니다.

 

  • taskAffinity
  • launchMode
  • allowTaskReparenting
  • clearTaskOnLaunch
  • alwaysRetainTaskState
  • finishOnTaskLaunch

 

위 플래그나 속성들에 대해서는 자세한 설명을 생략합니다. 후에 다시 설명할 기회가 있을 것입니다.

 

(2) 인척 관계(affinity)

 

기본적으로 한 어플리케이션 안의 모든 액티비티들은 서로 인척 관계(affinity)에 있습니다. 인척 관계라고 하는 것은 같은 태스크에 속할 수 있는지 여부를 나타냅니다. 이런 인척 관계는 다른 어플리케이션과도 맺어질 수가 있습니다. 또한 같은 어플리케이션 안의 액티비티들이 서로 다른 인척 관계를 가질 수도 있습니다. 이런 인척 관계를 설정하는 방법은 매니패스트 파일의 <activity> 요소의 taskAffinity 속성으로 제어를 할 수 있습니다. 인척 관계를 설정하는 2 가지 방법은 다음과 같습니다.

 

  • FLAG_ACTIVITY_NEW_TASK 플래그를 이용한 방법
    기본적으로 startActivity()로 호출된 액티비티는 호출한 액티비티의 태스크에 포함됩니다. 단, startActivity()를 부를 때 intent 객체 인자에 FLAG_ACTIVITY_NEW_TASK 플래그가 설정되어 있으면, 시스템이 새 태스크로 새 액티비티를 시작합니다. 기존 태스크에 포함된 경우는 해당 태스크에 포함시킵니다.
  • allowTaskReparenting 속성을 이용한 방법
    이 속성이 “true”인 경우, 어떤 태스크에 포함되어 있다가도 다른 인척 관계에 있는 태스크가 포그라운드가 될 때 이 태스크로 옮겨갈 수 있습니다.

 

(3) 론치 모드(launch mode)

 

<activity> 요소의 launchMode 속성에는 다음과 같은 4 가지가 있습니다.

 

  • "standard" (the default mode)
  • "singleTop”
  • "singleTask”
  • "singleInstance“

 

위 속성들을 해석하는 방법은 다음과 같이 4 가지 관점이 있습니다.

 

  • 인턴트에 반응한 액티비티를 어느 태스크가 소유하는가?
    "standard”나 "singleTop”의 경우는 startActivity()를 호출한 태스크가 호출된 액티비티를 소유합니다. (단, FLAG_ACTIVITY_NEW_TASK 플래그가 인턴트에 있는 경우는 예외입니다.) "singleTask”나 "singleInstance”의 경우는 항상 루트 액티비티가 됩니다. 즉, 항상 새 태스크가 됩니다.
  • 태스크가 여러 개의 인스턴스를 가질 수 있는가?
    "standard”나 "singleTop”의 경우는 여러 번 인스턴스화될 수 있습니다. 이 말은 하나의 액티비티가 여러 태스크에 속할 수 있다거나, 하나의 태스크에 여러 개의 같은 액티비티가 생길 수 있다는 뜻입니다. "singleTask”나 "singleInstance“의 경우는 단지 하나의 인스턴스만 가집니다.
  • 한 태스크 안에 같은 액티비티 인스턴스를 2 개 이상 가질 수 있는가?
    "singleInstance”인 경우는 단지 하나의 액티비티만 가지는 태스크를 만듭니다. 다른 액티비티를 부르면 불린 것은 마치 FLAG_ACTIVITY_NEW_TASK 플래그가 설정된 것처럼 새로운 태스크로 들어갑니다. "standard”나 "singleTop” 또는 "singleTask”의 경우는 하나의 태스크 안에 여러 개의 액티비티가 포함될 수 있습니다.
  • 새로운 인턴트를 처리할 때 항상 새로운 인스턴스가 생성되는가?
    "standard”
    인 경우는 신규 인텐트는 항상 신규 인스턴스로 반응합니다. "singleTop”인 경우는 신규 인텐트는 기존 인스턴스를 재사용하여 반응합니다. (단, 재사용된 액티비티는 타켓 태스크의 꼭대기에 있는 액티비티여야 합니다.) 꼭대기에 있는 액티비티가 아니면 재사용되지 않고 새 인스턴스가 생성되어 스택에 푸쉬(push)됩니다. 아래 그림을 참조 바랍니다.

 

(4) 스택 지우기

 

사용자가 오랫동안 태스크를 떠나 있으면, 시스템이 자동으로 그 태스크의 액티비티들을 지웁니다. 단 루트 액티비티는 남겨 놓습니다. 이런 경우 사용자가 다시 이 태스크로 돌아 왔을 때 루트 액티비티가 보여지게 됩니다. 이와 같은 동작이 기본 동작입니다. 만약 다른 동작을 하도록 하고 싶으면 앞에서 설명드린  <activity> 요소의 속성으로 제어할 수 있습니다. 다음의 3 가지 속성이 이를 제어하는 속성들입니다.

 

  • alwaysRetainTaskState 속성
    루트 액티비티의 속성이 “true”이면, 오랜 시간이 지나도 그대로 남아 있습니다.
  • clearTaskOnLaunch 속성
    루트 액티비티의 이 속성이 “true”이면, 사용자가 이 태스크를 떠났다가 돌아올 때마다 루트 액티비티만 남겨 놓고 나머지는 스택에서 삭제합니다.
  • finishOnTaskLaunch 속성
    사용자가 이 태스크를 떠났다가 돌아올 때마다 이 속성이 “true”인 액티비티만 스택에서 삭제합니다. 이 경우는 루트 액티비티도 삭제될 수 있습니다.

 

스택을 지우는 다른 방법으로 인턴트 객체의 FLAG_ACTIVITY_CLEAR_TOP 플래그를 사용하는 방법이 있습니다. 이 플래그를 사용하면 수신한 인턴트 객체를 처리할 수 없는 액티비티들이 타켓 태스크 스택에서 모두 삭제됩니다. 즉, 수신한 인턴트 객체를 처리할 수 있는 액티비티가 스택의 꼭대기에 나올 때까지 삭제가 일어납니다. 다만 처리 가능한 액티비티의 론치 모드가 "standard"인 경우는 이 액티비티도 스택에서 삭제되고, 새 액티비티가 생성되어 스택에 푸쉬됩니다. "standard"가 의미하는 것이 새 인턴트는 새 액티비티로 처리하라는 것이기 때문입니다.

 

(5) 진입점(entry point)

 

어떤 태스크의 진입점을 지정하는 방법은 앞에서도 설명하였듯이 매니패스트 파일의 액티비티의 <intent-filter>에 액션은 "android.intent.action.MAIN"을 사용하고, 카테고리는 "android.intent.category.LAUNCHER"를 사용하면 됩니다. 이런 진입점을 가지는 필터에는 아이콘과 라벨도 추가로 지정하여야 어플리케이션 론처에 표시가 됩니다.

 

이런 진입점을 가진 액티비티는 항상 "singleTask”나 "singleInstance”로 시작해야 합니다. 왜냐하면 어플리케이션 론처에서 실행 가능한 것이 "singleTask”나 "singleInstance”로 되어 있지 않으면, 실행 후 홈 키를 누른 후에는 다시 기존 태스크로 돌아갈 방법이 업습니다. 이와 비슷한 문제가 FLAG_ACTIVITY_CLEAR_TOP 플래그를 가진 액티비티에도 있습니다. 이런 것들은 활성화된 후에 다시 돌아올 다른 방법을 부가적으로 제공해 주어야 합니다. 만약 다시 돌아올 방법을 제공하지 못하는 경우는  <activity> 요소에 finishOnTaskLaunch를 "true"로 설정하여 종료가 되도록 해야 함에 주의하세요.

 

4.4 프로세스와 쓰레드

 

(1) 프로세스(process)

 

어떤 어플리케이션의 컴포넌트들 중 하나가 최초로 실행될 때, 안드로이드는 실행 쓰레드(thread) 하나를 가진 리눅스 프로세스(process)를 하나를 생성합니다. 기본적으로 이 어플리케이션의 모든 다른 컴포넌트들은 새로 생성된 이 프로세스와 쓰레드(메인 쓰레드라고 불림)에서 동작을 합니다. 컴포넌트가 다른 프로세스에서 실행되게 하려면 그 프로세스에 추가적인 쓰레드를 만들어서 실행해야 합니다.

 

어떤 컴포넌트가 어떤 프로세스에서 동작할 지는 매니패스트 파일에서 제어가 됩니다. <activity>, <service>, <receiver>, <provider> 요소의 process 속성으로 제어를 할 수 있습니다. process 속성으로 컴포넌트가 자신의 프로세스에서 동작할 지, 다른 프로세스에서 동작할 지 결정할 수 있습니다. 또한 리눅스 사용자  ID와 권한을 자신 것으로 할 지 다른 프로세스 것으로 할 지 결정할 수 있습니다. 각 컴포넌트에 개별적으로 속성을 지정하지 않고 <application> 요소의 process 속성으로 모든 컴포넌트에 적용되는 디폴트 값을 설정할 수도 있습니다.

모든 컴포넌트는 해당 프로세스의 메인 쓰레드에서 인스턴스화됩니다. 시스템 호출도 메인 쓰레드에서 동작합니다. 따라서 각 컴포넌트는 시스템에 의해서 불렸을 때는 시간이 오래 걸리거나 블럭킹(blocking)이 되는 연산(예, 네트웍 연산 또는 계산 루프)은 메인 쓰레드에서 수행하지 않도록 주의해야 합니다. 시간 오래 걸리는 것은 별도 쓰레드를 생성하는 것이 좋습니다.

 

안드로이드가 프로세스를 죽이는 경우는 메모리가 부족한 경우이거나 사용자에게 더 즉각적으로 서비스해야 하는 다른 프로세스의 요구가 있을 경우에 한합니다. 이런 상황이 닥쳤을 때 어떤 프로세스를 죽일지는 중요도에 따라 결정이 됩니다. 예를 들면 사용자에게 보이는 것과 보이지 않는 것이 있으면 보이지 않는 것이 중요도가 더 낮습니다. 안드로이드는 중요도가 낮은 보이지 않는 프로세스를 먼저 죽이게 됩니다. 중요도는 프로세스 상에서 동작하고 있는 컴포넌트의 상태에 따라서 달라집니다. 이에 대해서는 추후 다시 설명 드리겠습니다.

 

(2) 쓰레드(thread)

 

어플리케이션이 하나의 프로세스 상에서 동작하더라도 백그라운드 작업의 경우는 별도의 쓰레드를 만들어서 수행할 필요가 있다고 앞에서 설명하였습니다. 쓰레드를 만드는 방법은 표준 자바 Thread 객체를 사용하면 됩니다. 안드로이드는 쓰레드 관리를 쉽게 하도록 여러 클래스를 제공하고 있습니다. 예를 들면, 쓰레드 내에서 메시지 루프를 실행하기 위한 Looper, 메시지 처리를 위한 Handler, 메시지 루프를 가진 쓰레드를 만들기 위한 HandlerThread 등이 있습니다.

 

여러 쓰레드에서 동시에 실행되어도 문제가 없이 동작해야 하는 것을 쓰레드에 안전(thread-safe)하다고 합니다. 하나 이상의 쓰레드에서 호출될 수 있는 메소드는 쓰레드에 안전하게 작성되어야 합니다. 특히 원격에서 호출될 수 있는 메소드는 쓰레드에 안전하게 작성될 필요가 있습니다.

 

(3) RPC(Remote Procedure Call)

 

안드로이드는 경량의 RPC 메커니즘을 제공하고 있습니다. RPC는 지역 메소드 호출로 원격 프로세스의 메소드를 실행하여 그 결과를 반환받는 것입니다. 좀 더 자세히 설명하면, RPC는 운영체제가 이해할 수 있는 수준으로 메소드 호출과 관련 데이터를 쪼개서 다른 원격 프로세스에게 전송한 후에 다시 조립하여 원격에 있는 메소드를 호출하게 됩니다. 반환 값도 이런 식으로 쪼갠 후 조립하여 지역 메소드에 원격 메소드 호출 결과로 전달됩니다. 안드로이드가 이런 동작 메커니즘을 다 준비해 놓았으므로 개발자는 RPC 인터페이스 구현만 신경 쓰면 됩니다.

 

RPC 인터페이스는 메소드들로만 구성되어 있습니다. 여기에 정의된 모든 메소드는 동기화되어 실행이 됩니다. 메소드가 동기화되어 실행된다는 의미는 지역 메소드 호출은 원격 메소드의 실행이 종료될 때까지 블럭된다는 뜻입니다. 반환 값이 없더라도 동기화가 이루어집니다. RPC 인터페이스는 IDL(Interface Definition Language)로 선언됩니다. 안드로이드 SDK에서 제공되는 aidl 도구를 이용하여 IDL을 생성할 수 있습니다. RPC 인터페이스는 아래 그림과 같이 2 개의 내부 클래스로 정의됩니다.

 

 

 

위 그림에서 Stub 내부 클래스는 원격에서 제공해야 하는 서비스 기능입니다. 개발자가 구현해 주어야 하는 클래스이죠. 점선 박스로 표시된 내부 클래스는 IBinder를 구현한 것으로 안드로이드에 의해 사용되는 클래스입니다.

 

서비스와 클라이언트 간의 연결을 설정하는 방법에 대해 알아 보겠습니다. 우선 클라이언트에서는 원격에 있는 서비스와 연결이 성공하거나 실패하면 알림을 받을 수 있도록 onServiceConnected()와 onServiceDisconnected()를 구현해야 합니다. 서비스 연결에 성공했을 때는 onServiceConnected() 메소드가 호출되고, 서비스 연결이 실패했을 때는 onServiceDisconnected() 메소드가 호출됩니다. 서비스 연결에 성공하여 onServiceConnected() 메소드가 호출되면 이 메소드에서 bindService() 메소드를 호출하여 연결을 설정하면 됩니다. 반면에 서비스에서도 해 줘야 할 일이 있습니다. 서비스에서는 onBind() 메소드를 구현해 줘야 합니다. 이 메소드애서는 클라이언트에서 연결을 시도해 왔을 때 이를 수용할 지 거부할 지 결정할 수 있습니다. 이런 결정을 내릴 때 onBind() 메소드의 인자로 전달되는 인턴트를 보고 결정할 수 있습니다. 만약 클라이언트의 연결 시도가 받아들여지면 Stub 하위 클래스의 인스턴스가 반환됩니다. 서비스가 연결을 수용하면 안드로이드는 onServiceConnected() 메소드를 호출하여 IBinder 객체를 클라이언트에 전달합니다.  IBinder 객체는 서비스에 의해 관리되는 Stub 하위 클래스에 대한 프락시(proxy) 역할을 합니다. 이 프락시를 통해서 클라이언트는 원격 서비스를 호출할 수 있습니다. 앞에서도 설명했지만  IBinder 메소드들은 쓰레드에 안전하게 구현이 되어야 합니다.

 

4.5 컴포넌트의 수명

 

어플리케이션의 컴포넌트는 수명이 있습니다. 다시 말해서 컴포넌트는 태어나서 죽게 됩니다. 안드로이드가 인스턴스를 만들 때 생명이 시작됩니다. 그리고 안드로이드가 인스턴스를 삭제할 때 생명이 끝나게 됩니다. 컴포넌트는 태어나서 죽기까지 활성화되기도 하고 비활성화도기도 합니다. 다시 말해, 활성화될 때는 사용자에게 보이고 비활성화될 때는 사용자에게 안 보이게 됩니다.

 

(1) 액티비티의 수명

 

액티비티도 컴포넌트 중의 하나이므로 생명을 가지고 있고 수명도 있습니다. 액티비티가 살아 있는 동안 다음 3 가지 상태에 있을 수 있습니다.

 

  • 활성(active) 또는 실행 중(running) 상태
    액티비티가 포그라운드 상태에 있을 때입니다. 다시 말해서 현재 태스크 스택의 꼭대기에 위치해 있을 때입니다. 이 상태에서는 사용자의 동작에 대해서 포커스를 가지게 됩니다.
  • 일시 정지(paused) 상태
    포커스를 잃지만 여전히 보이는 상태에 있을 때입니다. 다른 액티비티가 이 액티비티 위에 일부 또는 투명하게 덮은 경우에 해당합니다. 일시 정지 상태에서는 안드로이드가 메모리가 부족하면 컴포넌트를 죽일 수 있습니다.
  • 멈춤(stopped) 상태
    다른 액티비티에 의해 완전히 가려진 때입니다. 이럴 때는 사용자에게 더 이상 보이지 않는 경우입니다. 멈춤 상태에서도 안드로이드가 메모리가 부족하면 컴포넌트를 죽일 수 있습니다.

 

액티비티 수명 관련 메소드

 

액티비티의 위 3 가지 상태 간의 전이가 일어나면 안드로이드는 다음과 같은 액티비티의 보호된(protected) 메소드를 불러서 상태 변경을 알려 줍니다.

 

  • void onCreate(Bundle savedInstanceState)
  • void onStart()
  • void onRestart()
  • void onResume()
  • void onPause()
  • void onStop()
  • void onDestroy()

 

위 메소드들은 재정의가 가능한 메소드들입니다. 따라서 상태 전이가 있을 때 무언가 일을 하려면 위 메소드들을 재정의하면 됩니다. 예를 들어서 액티비티가 인스턴스화 된 후에 초기화 코드를 구현해 주고 싶다면 onCrate() 메소드를 재정의하면 됩니다. 일시 정지 상태에서 시스템에 의해 죽는 경우에 복구를 할 수 있도록 영구적인 데이터를 스토리지에 저장할 필요가 있을 때는 onPause() 메소드를 재정의하면 됩니다.

 

액티비티 상태 전이 다이어그램

 

액티비티의 전체 수명은 onCreate()와 onDestroy() 사이의 시간입니다. 눈에 보이는 상태에 있는 경우는 onStart()와 onStop() 사이의 시간입니다. 포그라운드(foreground) 상태에 있는 경우는 onResume()과 onPause() 사이의 시간입니다. 다음 그림은 액티비티의 탄생에서부터 죽음에 이르는 과정과 이 사이의 여러 상태 전이 과정을 보이는 다이어그램입니다.

 

 

위 다이어그램에서 보면 액티비티가 탄생하여 인스턴스화가 되면 onCreate()가 불리고, 눈에 보이는 상태가 되면 onStart()가 불리고, 포그라운드로 되면서 onResume()이 불립니다. 이 상태가 활성화되어 실행 중인 상태입니다. 그리고 잠들어서 백그라운드가 되면서 일시 정지되어 onPause()가 불리고, 눈에 보이지 않게 되면서 멈추게 되어 onStop()이 불리고, 죽으면서 onDestroy()가 불립니다. onPause()나 onStop() 상태에서는 시스템에 의해서 죽음을 당할 수 있고 사용자가 이 액티비티로 되돌아 오면 부활을 하여 onCreate()가 다시 불립니다. 백그라운드에서 잠들어 있는 상태에서 포그라운드로 깨어난 상태가 되면서, 다시 말해 멈춤 상태에서 실행 상태로 바뀌면서 onRestart()와 onStart()가 연속으로 불립니다.

 

액티비티의 상태 저장


시스템이 액티비티를 죽을 수 있으므로, 사용자가 죽었던 액티비티로 되돌아 왔을 때 이전 상태를 복원할 수 있어야 합니다. 이전 상태를 복원하려면 onSaveInstance()라는 메소드를 구현하면 됩니다. 안드로이드는 액티비티를 죽이기 전에 이 메소드를 호출하여 나중에 부활 후 복원에 필요한 정보를 저장할 기회를 줍니다. onSaveInstance()에 인자로 Bundle 객체를 전달해 줍니다. 여기에 이름-값 쌍을 저장하면 됩니다. 액티비티가 다시 부활하여 다시 시작할 때 Bundle 객체가 onCreate() 메소드에 다시 전달됩니다. 또한 onStart()가 불린 후에 불리는 OnRestoreInstanceState() 함수에도 전달됩니다. onSaveInstance()라는 메소드는 액티비티가 죽을 때 항상 불리는 것이 아니고, 시스템에 의해서 죽을 때만 불리므로, 데이터를 영구적으로 저장할 목적으로 이 함수 이용하지는 말아야 합니다. 이런 목적을 위해서는 앞에서 설명했듯이 onPause() 함수에서 구현해야 합니다.


상태 전이 시 액티비티들의 호출 순서


어떤 액티비티가 다른 액티비티를 호출 시 두 액티비티는 상태가 변하게 됩니다. 상태가 변하면서 불리는 메서드의 순서를 잘 알고 있어야 합니다. 먼저, 현재 액티비티의 onPause() 메소드가 호출됩니다. 그리고 신규 액티비티의 onCreate(), onStart(), onResume() 메소드가 순서대로 불립니다. 마지막으로 신규 액티비티가 보이지 않게 되면, 이 액티비티의 onStop() 메소드가 호출됩니다.

 

(2) 서비스의 수명

 

서비스를 시작하는 2 가지 방법

 

서비스는 2 가지 방법으로 시작하거나 중지될 수 있습니다. 서비스가 시작된 후에 다른 누군가가 중지하거나 스스로 중지할 수 있습니다. 다른 서비스를 시작할 때는 Context.startService() 메소드를 호출합니다. 다른 서비스를 중지시킬 때는 Context.stopService() 메소드를 호출합니다.

 

반면에 자기 스스로 서비스를 중지할 때는 Service.stopSelf() 또는 Service.stopSelfResult() 메소드를 호출하면 됩니다. startService()는 여러 서비스에서 여러 번 불릴 수 있습니다. stopService()를 불렀다고 해서 서비스가 중지되는 것은 아닙니다. 여러 서비스에서 서비스를 시작한 횟수만큼 stopService()가 불려야 중지가 됩니다. 그렇다고 stopService()를 여러 번 부르면 안 되고 startService()를 불렀다면 한 번만 부르면 됩니다.


서비스를 동작시키는 두 번째 방법은 외부 노출된 인터페이스를 이용하는 것입니다. 클라이언트가 Service 객체에 연결한 후에, 이 연결을 이용하여 서비스를 호출하는 방법이 있습니다. 서비스와 연결 맺기와 연결 끊기를 위해서 호출되는 메소드는 다음과 같습니다.

 

  • 연결 맺기 : Context.bindService() 호출
  • 연결 끊기 : Context.unbindService() 호출

 

이렇게 하여 여러 클라이언트가 하나의 서비스에 바인드(bind)될 수 있습니다. 서비스에 바인드되기 위해서는 다음과 같이 호출을 해야 합니다.

 

  • 서비스 바인드 : bindService() 호출

 

예를 들어, startService()에 인텐트 객체를 넘겨서 음악을 연주한 후에, 나중에 현재 연주 중인 곡에 대한 정보를 알고 싶다면 bindService()를 호출하여 액티비티가 서비스와 연결을 맺을 수 있습니다. 이런 경우는 stopService()를 불러도 실제 서비스가 중지되지 않을 수 있습니다. bindService()가 여러 번 불렸을 수 있기 때문이며 마지막 바인딩이 끊어질 때에야 비로소 서비스가 중지됩니다.

 

서비스의 수명 관련 메소드


서비스의 수명에 관련된 3 가지 메소드는 다음과 같습니다. 서비스는 액티비티와는 달리 아래 3 개 외에는 없습니다. 서비스에는 onStop()과 같은 메소드가 없는 것에 특히 주의바랍니다.

 

  • void onCreate()
  • void onStart(Intent intent)
  • void onDestroy()

 

서비스의 탄생에서 죽음까지의 수명은 onCreate()와 onDestroy() 사이입니다. Context.startService() 또는 Context.bindService()가 불려서 서비스가 시작될 때는 항상 이 2 개 메소드가 호출됩니다. 서비스는 onStart() 메소드가 호출되면 활성 상태가 되었다가 이 메소드가 끝나면 비활성 상태가 됩니다. 클라이언트가 startService() 메소드를 불러 서비스를 시작할 때 인자로 전달한 인텐트 객체가 서비스의 onStart() 메소드에 전달됩니다. 이 인자를 보고 어떤 서비스를 할 지 알 수 있습니다. 예를 들면, 인텐트 인자를 보고 연주할 음악이 무엇인지 알 수 있습니다. 이 메소드는 startService()로 시작된 서비스에서만 호출됩니다.

 

바인드된 서비스에서 구현해야 콜백 메소드

 

서비스에서 구현해야 할 3 개의 콜백(callback) 메소드가 있습니다.
 

  • IBinder onBind(Intent intent)
    이 메소드의 인자는 bindService()에 전달된 인텐트 객체입니다. 반환 값은 클라이언트가 서비스와 인터랙션을 하는데 사용할 통신 채널입니다.
  • boolean onUnbind(Intent intent)
    이 메소드의 인자는 unbindService()에 전달된 인텐트 객체입니다.
  • void onRebind(Intent intent)
    새로운 클라이언트가 서비스에 연결하는 경우 onUnbind()에서 onRebind()를 부릅니다.

 

다음 그림은 콜백 메소드 호출을 설명한 다이어그램입니다. 서비스가 startService()로 시작된 경우와 bindService()로 시작된 경우에 각각 호출되는 메소드나 콜백 함수가 다릅니다. 바인드된 서비스에는 onStart() 메소드가 없는 대신 위 3 개의 콜백 함수가 있습니다.

 

 

(3) 브로드캐스트 수신자의 수명

 

브로드캐스트 수신자는 다음과 같이 하나의 콜백 메소드만 제공합니다.

 

  • void onReceive(Context curContext, Intent broadcastMsg)

 

브로드캐스트 메시지가 수신자에게 도착하면 안드로이드가 onReceive() 메소드를 호출합니다. 이 때 메시지를 담고 있는 인텐트 객체를 인자로 전달합니다. 브로드캐스트 수신자는 onReceive() 함수가 호출될 때만 활성화되고, 이 함수가 끝나면 다시 비활성화됩니다. 활성화 상태의 브로드캐스트 수신자를 가지고 있는 프로세스는 시스템에 의해서 죽임을 당하지 않습니다.
 

(4) 컴포넌트의 중요도 순위

 

안드로이드는 메모리가 부족할 때 죽일 프로세스를 선택합니다. 어떤 프로세스를 죽일지는 컴포넌트의 “5 단계 중요도 계층 구조”로 결정합니다. 계층 구조에서 중요도 순위가 가장 낮은 것이 먼저 죽임을 당합니다. 1 순위부터 5 순위까지 중요도 계층 구조는 다음과 같습니다.

 

  • 1 순위 : 포그라운드 프로세스
    사용자가 현재 사용 중인 프로세스로 포그라운드 상태에 있는 것들이 중요도 1 순위입니다. 사용자가 상호작용 중인 액티비티를 가진 프로세스가 여기에 속합니다. 사용자가 상호작용 중인 액티비티에 바인드된 서비스를 가진 프로세스도 여기에 속합니다. 수명 관련된 콜백 함수들(onCreate(), onStart(), onDestroy()) 중 하나를 실행 중인 서비스 객체를 가진 프로세스도 여기에 속합니다. 그리고 onReceive() 메소드를 실행 중인 BroadcastReceiver 객체를 가진 프로세스도 여기에 속합니다.
  • 2 순위 : 보이는 프로세스
    포그라운드 컴포넌트를 가지고 있지 않으나 화면에 보이는 것들이 중요도 2 순위입니다. 포그라운드 상태는 아니나 사용자에게 여전히 보이는 상태의 액티비티를 가진 프로세스가 여기에 속합니다. onPause()가 불린 경우로, 이전 화면이 보이게 대화상자가 뜬 경우가 이런 상태입니다. 보이는 액티비티에 바인드된 서비스를 가진 프로세스도 여기에 속합니다.
  • 3 순위 : 서비스 프로세스
    startService()
    메소드로 시작된 서비스 중 실행 중인 프로세스들이 중요도 3 순위입니다. 단, 위 1~2 순위에 속하지 않는 것들만 해당됩니다. 예를 들면, 음악을 연주 중인 서비스, 네트웍에서 다운로들 하고 있는 서비스가 여기에 속합니다.
  • 4 순위 : 백그라운드 프로세스
    보이지 않는 액티비티를 가진 프로세스들이 중요도 4 순위입니다. onStop()으로 중지된 것들이 여기에 속합니다. 이런 액티비티는 특히 많기 때문에 가장 오래 전에 사용된 것들부터 즉, LRU(Least Recently Used)로 죽임을 당합니다.
  • 5 순위 : 텅빈 프로세스
    활성화된 어플케이션 컴포넌트가 없는 프로세스들이 중요도 5 순위입니다. 이런 프로세스들은 대부분 프로세스 시작 시간을 줄이기 위해 캐쉬된 프로세스들이 여기에 속합니다.

 

끝.

2009년 12월 3일 목요일

3. 이클립스 설치 및 헬로 안드로이드 샘플 제작

지금까지 안드로이드 SDK를 설치하고 에뮬레이터도 실행해 봤습니다. 안드로이드 에뮬레이터에는 Messaging, Phone, Contacts, Browser 등의 어플리케이션이 있는데 이것들을 실행해 보았는지요?  하단 중앙의 버튼을 누르면 다른 어플리케이션들을 볼 수 있습니다. 이런 것 좀 해 보고 나면 이제 직접 어플리케이션을 만들어서 올려 보고 싶은 욕구가 생길 것입니다. 이 장에서는 기본적인 어플리케이션인 "Hello World"를 제작해 보겠습니다.

 

3.1 이클립스 및 ADT 플러그인 설치하기

 

안드로이드 프로그래밍을 하기 위해서는 IDE(Integrated Development Environment)가 있어야 하고, 추천하는 IDE는 이클립스(Eclipse)입니다. 이클립스에서 안드로이드 프로그래밍을 돕는 ADT(Android Development Tools) 플러그인도 깔아야 합니다.

 

(1) 이클립스 설치하기

 

우선 이클립스부터 설치를 하도록 합시다. 이클립스는 자바 프로그래밍을 할 때 유용한 IDE입니다. 물론 다른 언어로 프로그래밍을 할 수 있는 플러그인도 많이 있습니다. 이클립스는 이클립스 재단(Eclipse Foundation) 홈 페이지에서 무료로 다운로드 받아서 설치할 수 있습니다. 물론 지원하는 하드웨어는 안드로이드 SDK를 지원하는 모든 개발 플랫폼(윈도우, 매킨토시, 리눅스)를 지원합니다. 다음 사이트로 가서 이클립스를 다운로드 합니다.

 

http://www.eclipse.org/downloads

 

위 화면으로 가면 다운로드 받을 수 있는 패키지가 10 개 정도 있습니다. 이 중에서 적합한 패키지를 골라야 합니다. 앞에서도 언급하였듯이 ADT는 이클립스 IDE 3.4 (Ganymede) 또는 3.5 (Galileo)가 있습니다. 당연히 숫자가 더 높은 것을 고르면 좋겠지요. 그런데 패키지 이름에는 이런 버전 표시가 없네요. 안드로이드 개발자 사이트의 문서에 보면 추천하는 이클립스 IDE 패키지로 Eclipse IDE for Java EE Developers, Eclipse IDE for Java Developers, Eclipse for RCP/Plug-in Developers를 권장하는군요. 하나만 권장하면 선택이 쉬운데 3 개씩이나 추천하니 어렵군요. 이럴 때는 제일 먼저 추천하는 것을 선택하는 것이 현명한 방법이겠죠.^^

 

 

"Eclipse IDE for Java EE Developers (189 MB)"를 클릭합니다. mirror selection 페이지가 나오네요. 제울 위해 있는 선택합시다.

 

 

그리고 "eclipse-jee-galileo-SR1-win32.zip" 파일을 원하는 곳에 저장을 합니다. 저장한 파일을 압축을 풉니다. 이것으로 설치가 완료되었습니다. 압축이 풀린 폴더 이름이 "eclipse-jee-galileo-SR1-win32"이군요. 혹시 임시 폴더에서 압축을 풀었다면 이 폴더를 적당한 곳에 옮겨 놓으면 더 좋겠지요. 저는 D:\Android 폴더에 옮겨 놓았습니다. 이 폴더 안에 들어가 보니 "eclipse"라는 폴더가 또 있군요. 더 들어가 보죠. "eclipse.exe" 파일이 있습니다. 이 파일을 실행시켜 봅시다. 로고 화면이 뜨면서 eclipse라는 글자 밑에 "GALILEO"라고 표시가 되어 있군요. 이클립스 IDE 3.5 (Galileo)가 맞군요.

 

로고가 사라지고 "Workspace Launcher"라는 대화상자가 뜨는군요. "Workspace" 입력 창에 "C:\Documents and Settings\강재우\workspace"라고 제 설정 폴더가 표시되는 군요. 한글 문제 때문에 한 번 고생을 하였으니, 바꾸고 싶네요. (사실 안 바꿔도 큰 문제는 없습니다.) 저는 다음 그림과 같이 안드로이드 폴더로 설정을 하고, 다시 물어보지 않게 체크도 하였습니다.

 

 

그러면 다음과 같은 화면이 뜹니다. 우상귀의 "Workbanch" 아이콘을 클릭합니다.

 

 

(2) 이클립스 ADT 플러그인 설치하기

 

ADT는 안드로이드 에뮬레이터와 각종 안드로이드 개발 도구드을 IDE에 통합해 줍니다. 이것을 깔면 DOS 창에서 작업할 일이 거의 없다는 뜻이지요. 그래서 깔면 편합니다. 이 http://developer.android.com/sdk/eclipse-adt.html 페이지에 가면 ADT 플러그인 설치 방법이 나옵니다. Eclipse 3.4 (Ganymede)인 경우와 Eclipse 3.5 (Galileo)인 경우 2 가지가 모두 설명이 되어 있군요. Eclipse 3.5 (Galileo)의 설명을 따라 가면서 제가 설명을 하겠습니다. 다른 IDE 버전을 깐 분들은 직접 위 페이지를 보고 따라가면 되겠습니다.

 

  1. 이클립스를 실행합니다. (이미 실행을 해 놓았죠.^^) "Help > Install New Softare"를 실행합니다.
  2. "Available Software" 대화상자가 나옵니다. 여기서 우상귀 쪽에 있는 "Add...." 버튼을 클릭합니다.
  3. "Add Site" 대화상자가 뜨는군요. "Android Plugin"이라고 "Name" 필드에 입력합니다. (콜론은 빼고 텍스트만 넣으면 됩니다.) "Location" 필드에는 다음 URL을 입력합니다:
    https://dl-ssl.google.com/android/eclipse/

    주의: 만약 플러그인을 가져 오는데 문제가 생기면 URL의 "https" 대신에 "s"를 뺀 "http"로 바꿔서 다시 해 보세요. (https는 보안상의 이유로 더 선호되는 방법입니다.)


    그리고 OK를 누릅니다..

  4. 다시 "Available Software" 화면으로 돌아오게 되며, 화면 중앙 리스트에 "Developer Tools"라고 표시된 항목이 보이나요? 이게 보여야 합니다. 혹시 잘못된 것 같으면 위 3번을 다시 해 보세요. "Developer Tools"에 체크 박스를 체크를 해 줍니다. 이렇게 해 주면 그 하위에 있는 툴인 "Android DDMS"와 "Android Development Tools"이 자동으로 모두 선택됩니다. 그리고 화면 아래 쪽에 있는 Next 버튼을 눌러 주세요.
  5. 위 화면 하단에서 프로그레스 바가 쭉 지나간 후에 "Install Details" 대화상자가 뜹니다. "Android DDMS"과 "Android Development Tools"가 리스트 박스에 표시가 된 것이 보일 것입니다. 이것들을 설치하겠나고 물어 보는 화면입니다. 이 화면 하단의 Next 버튼을 누릅니다. 그러면 "Review Licenses" 화면이 뜹니다. 시간이 많으신 분들은 "License text"을 자세히 읽어 보세요. 그렇지 않은 분들은 바로 아래의 "I accept the terms of the license agreements" 라디오 버튼을 선택하하세요.  그러면 화면 하단에 "Finish" 버튼이 활성화될 것입니다. 그리고 Finish 버튼을 눌러 주세요.
  6. 그러면 "Android DDMS"와 "Android Development Tools"가 다운로드되면서 설치가 됩니다. 설치 중 "Security Warning"가 나오면 OK 버튼을 꾹~ 눌러 주세요. 그러면 이클립스를 재시작하겠냐고 물어 봅니다. Yes 버튼을 가볍게 눌러 주세요. 그러면 이큽리스 로고 화면이 뜨면서 이클립스가 재시작됩니다.

 

위 과정은 Eclipse 3.4 (Ganymede)인 경우와 Eclipse 3.5 (Galileo)인 경우가 조금 다릅니다. 이후 Preference 설정부터는 모두 동일합니다.

 

  1. "Window > Preferences..." 메뉴를 선택합니다. "Preferences" 화면이 뜨고, 왼쪽에 여러 가지 패널이 보입니다.
  2. 왼쪽 패널들 중에서 "Android" 패널을 선택합니다. 그러면 "Android SDK Location" 메시지 박스가 뜨면서 안드로이드 SDK 위치가 아직 설정이 안 되어 있다는 메시지가 나옵니다. OK를 누릅니다.
  3. 안드로이드 메인 패널의 "SDK Location" 입력 창에 안드로이드 SDK를 설치한 폴더 위치를 지정해야 합니다. Browse... 버튼을 클릭하여 안드로이드 SDK가 설치된 폴더를 지정하세요.  저의 경우는 D:\Android\android-sdk로 깔았습니다.
  4. 그리고 화면 하단의 Apply 버튼을 누른 후 OK 버튼을 누릅니다.

 

위 과정은 워낙 간단하기 때문에 별도의 이미지 덤프 화면을 첨부하지 않았습니다. 위 과정을 끝내고 나면 다시 ADT 플러그인을 업데이트해야 합니다. ADT 플러그인이 워낙 빨리 발전하고 있어서 최신 버전을 받아 놓는 것이 좋습니다. 이 부분도 위 과정은 Eclipse 3.4 (Ganymede)인 경우와 Eclipse 3.5 (Galileo)인 경우가 조금 다릅니다. 저는 Eclipse 3.5 (Galileo) 기준으로 설명을 합니다.

 

  1. Help > Check for Updates 메뉴를 선택합니다. "Connecting Software Sites" 화면이 뜨고 프로그래스 바가 진행이 됩니다. 그리고 저의 경우는 "Information" 메시지 창이 떠서 더 이상 최신 버전이 없다고 합니다. OK 버튼을 누릅니다.
  2. 혹시 "Available Updates" 대화상자가 뜨는 사람은 계속 따라와 주세요. 리스트 화면의 "Android DDMS"과 "Android Development Tools"를 체크합니다. 그리고 Next 버튼을 클릭합니다.

    만약 "Available Updates" 홤녀에 "Android DDMS"와 "Android Development Tools"가 표시되지 않으면 위 1번부터 10번 과정이 진행이 안 되었거나 실패한 것이므로 다시 진행을 해 주세요.

  3. "Update Details" 대화상자가 나오면 Next 버튼을 클릭합니다.
  4. "license agreement"를 읽고 accept를 체크합니다. 그리고 Finish 버튼을 누릅니다. 그러면 최신 버전의 "Android DDMS"과 "Android Development Tools"를 다운로드 받아서 업데이트를 합니다.
  5. 이클립스를 재실행합니다.

 

(3) Android SDK and AVD Manager

 

이제 ADT가 설치가 되었으니 "Window > Android SDK and AVD Manager" 메뉴가 보입니다. 그리고 IDE 상단 툴바의 4 번째 아이콘이 이 메뉴와 동일한 기능을 하는 아이콘입니다. 앞으로는 DOS 창에서 tools 폴더의 android.bat 파일을 실행하지 않아도 패키지 설치나 virtual devices 설정이 가능합니다. 혹시 Virtual Devices에 아무 것도 없다면 2 장의 설명을 다시 읽고 따라 하시기 바랍니다. AVD가 없다면 아래 부분을 따라 해 봐야 좌절만 하게 됩니다.

 

3.2 "Hello, Android" 샘플 프로그램 제작

 

지금까지 이클립스 및 ADT 플러그인을 설치한 이유는 내가 직접 만든 안드로이드 프로그램을 실행시켜 보기 위한 것이었습니다. 안드로이드 프로그래밍에 대한 기초도 모르는데 이런 것을 하는 이유는 이런 것을 함으로써 기초를 배우기 위함입니다. 안드로이드 아키텍처나 어플리케이션 컴포넌트 구조에 대해 알아 보기 전에 "Hello World"를 먼저 만들어 보는 것이 안드로이드 난이도 파악에 도움이 되겠지요. "Hello World"를 만들어 동작시키는 방법은 http://developer.android.com/guide/tutorials/hello-world.html에 잘 나와 있습니다. 그러나 여기서 AVD를 만들고 하는 방법은 이미 완료가 된 상태이므로 따라 할 필요가 없습니다. 이 방법대로 따라 하다가는 잘 되면 좋지만 안 되는 경우도 많이 있습니다. 엄청나게 고생하고 나서 좌절하여 포기하는 수도 있겠지요.

 

(1) 새 안드로이드 프로젝트 생성

 

1. 이클립스에서 File > New > Project... 메뉴를 선택합니다. ADT 플러그인이 잘 설치가 되었다면 "New Project" 대화장자에 "Select a wizard" 창이 뜨고 Wazards에 "Android"라고 표시된 항목이 보일 것입니다. 이것을 클릭하여 하위 항목을 열면 "Android Project"가 보일 것입니다.

 

 

2. "Android Project"를 선택하여 Next 버튼을 누르십시오. 그리고 Finish 버튼을 누르세요.

3. 그러면 다음과 같은 화면이 뜹니다. 다음과 같이 입력을 합니다.
    Project name: HelloAndroid

    Build Target : Android 2.0 체크
    Application name: Hello, Android
    Package name: com.example.helloandroid (또는 원하는 다른 패키지 이름)
    Create Activity: HelloAndroid
    Min SDK Version: 5

 

 

4. 그러면 이클립스 화면 좌측의 "Package Explorer"에 "HelloAndroid" 프로젝트가 생성됩니다. 트리 항목을 몇 개 열어 보면 다음 그림과 같습니다. 여기서 HelloAndroid.java를 더블클릭으로 선택합니다.

 

 

5. HelloAndroid.java 파일의 소스를 보면 다음과 같습니다.

 

package com.example.helloandroid;

import android.app.Activity;
import android.os.Bundle;

public class HelloAndroid extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

 

패키지 이름은 com.example.helloandroid이고 클래스 이름이 HelloAndroid이고 Activity 클래스를 상속 받아서 구현을 하고 있습니다. 그리고 onCreate() 공개 메소드가 재정의되어 있습니다. 코드에 대한 자세한 설명은 차후에 하고 일단 코드를 수정해 봅시다.

 

(2) UI 구성하기

 

우선 위 코드를 다음과 같이 수정합니다. 빨강색으로 표시된 코드는 추가하고 빨강색 코드는 수정하면 되겠습니다.

 

package com.example.helloandroid;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class HelloAndroid extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView tv = new TextView(this);
        tv.setText("Hello, Android");
        setContentView(tv);

    }
}

 

쉽게 수정하는 방법은 아래 쪽 코드만 수정한 후에 Ctrl-Shift-O를 누르면 이클립스에서 알아서 필요한 패키지를 추가로 임포트(import)를 해 줍니다.

 

TextView 클래스를 하나 인스턴스화 한 후에 setText() 함수를 호출하여 "Hello, Android" 문자열을 설정하였군요. 그리고 Activity 클래스에 있는 setContentView() 함수에 이 뷰를 인자로 넘겨 주었군요. 더 자세한 사항은 다음에 더 공부를 하기로 하고 일단 실행을 시켜 볼까요?

 

(3) 어플리케이션 실행하기

 

이클립스 메뉴 중 메뉴 Run > Run을 선택합니다. 또는 Ctrl+F11을 눌러도 됩니다. 그러면 다음과 같은 "Run As" 화면이 뜹니다. 여기서 "Android Application"을 선택합니다.

 

 

HelloAdroid.java 파일을 변경하고 저장을 안 했다면, "Save Resource" 대화상자가 떠서 변경 사항을 저장할 것인지 물어 봅니다. Yes 버튼을 눌러 줍니다. 에뮬레이터가 뜨면서 다음과 같은 화면이 나오면 성공한 것입니다.

 

 

혹시 실행을 했는데 에뮬레이터가 안 뜨는 분이 계시는지요? 이럴 가능성이 충분히 있습니다. 이럴 때는 여러 가지 원인이 있을 수 있습니다. 코딩를 잘못하여 에러가 있다든지, 아니면 AVD 설정이 잘못되어 있다든지 말입니다. 코딩이 잘못된 경우는 이클립스 하단의 "Problems" perspective에 표시가 됩니다. 에러가 없는 경우는 AVD 설정을 의심해 봐야 합니다. 2 장을 보고, 에뮬레이터 실행이 잘 되는지 확인을 해 보시기 바랍니다.  "Window > Android SDK and AVD Manager" 메뉴에서 확인이 가능합니다. 그렇지 않으면 Android SDK 패키지가 잘 안 깔려 있을 수도 있습니다. 이런 Troubleshooting을 인터넷을 검색하여 해결책을 찾아 보는 것도 방법입니다.

 

다음 장에서는 안드로이드 어플리케이션 기초로 안드로이드 아키텍처와 어플리케이션 컴포넌트, 액티비티와 태스크, 프로세스와 쓰레드, 컴포넌트 수명에 대해서 알아 보도록 하겠습니다.

 

끝.

2. 안드로이드 SDK 설치 및 에뮬레이터 실행

2.1 개발에 필요한 도구들

 

안드로이드 프로그래밍을 하기 위해서는 우선 어떤 플랫폼에서 안드로이드 개발이 가능한지 알아야 합니다. 안드로이드는 Windows XP, Vista 등의 윈도 플랫폼과 Mac OS X, Linux 상에서 개발이 가능합니다. 안드로이드 프로그래밍을 하기 위해 안드로이드 단말기가 꼭 필요하지는 않습니다. 안드로이드 SDK에 에뮬레이터를 제공하므로 에뮬레이터 상에서 개발이 가능합니다. 안드로이드 개발에 필요한 시스템 요구 사항은 http://developer.android.com/sdk/requirements.html를 참고하기 바랍니다.

 

우선 개발 플랫폼이 선정이 되고 나면 안드로이드 SDK(Software Development Kit)를 깔아야 합니다. 지금까지 안드로이드 SDK는 1.1, 1.5, 1.6, 2.0이 출시된 상태입니다. 2009년 11월 현재 2.0이 최신 버전입니다. 이 버전은 지난 달에 출시가 되었습니다. 안드로이드 SDK는 다국어를 지원합니다. 26개 국의 locale을 지원하고 있으며 다양한 에뮬레이터 스킨을 제공합니다. 안드로이드 SDK 다운로드 사이트는 http://developer.android.com/sdk/index.html입니다. Windows 상에서 안드로이드 SDK를 설치하여 에뮬레이터를 실행하는 과정은 별도의 섹션으로 자세히 설명하도록 하겠습니다.

 

SDK가 설치되고 나면 프로그래밍을 위한 IDE(Integrated Development Environment)가 필요합니다. 안드로이드 프로그래밍을 하기 위해서는 자신이 주로 사용하는 IDE를 사용할 수 있습니다. 그러나 안드로이드 프로그래밍은 이클립스(Eclipse)를 추천합니다. 이클립스는 무료로 사용할 수 있는 IDE고 위 3 가지 개발 플랫폼을 모두 지원합니다. Eclipse IDE 3.4 (Ganymede) 또는 3.5 (Galileo)에서 프로그래밍이 가능하며, 추천 Eclipse IDE 패키지는 Eclipse IDE for Java EE Developers, Eclipse IDE for Java Developers, Eclipse for RCP/Plug-in Developers 중 하나입니다. 다운로드 사이트는 http://www.eclipse.org/downloads/입니다. 이클립스를 이용한 "Hello, Android" 프로그램을 만드는 것에 대해서는 별도의 섹션으로 자세히 설명하도록 하겠습니다.

 

이 외에 JDK 5 또는 6가 필요하며, Ant 빌드를 원하는 경우는 Apache Ant 1.6.5 이상을 지원합니다. Eclipse에서 안드로이드 프로그래밍을 쉽게 하도록 하는 ADT(Android Development Tools) 플러그인도 설치를 하여야 하며, 이 과정도 별도로 설명을 드리겠습니다.

 

2.2 안드로이드 SDK 설치하기

 

안드로이드 SDK에 대한 자세한 설명이나 안드로이드 아키텍처에 대한 설명을 하기 전에 먼저 안드로이드 에뮬레이터를 실행해 보도록 하겠습니다. 뭔가 실제로 돌려 본 후에 나머지를 자세히 알아가는 것도 좋은 방법이기 때문입니다.

 

(1) 안드로이드 SDK 다운로드

 

우선 웹 브라우저로 다운 사이트(http://developer.android.com/sdk/index.html)에 접속을 합니다. 다음 그림과 같이 개발 플랫폼에 따른 안드로이드 SDK를 다운로드 화면이 보일 것입니다. 아래 그림의 빨강색 테두리를 친 윈도용 ZIP 파일을 클릭합니다.

 

 

그러면 다음 화면이 나옵니다. 빨강색 테두리를 친 부분에 라이선스 동의를 체크를 하면 다운로드 버튼이 활성화가 됩니다. 다운로드 버튼을 눌러서 SDK를 자신이 원하는 임시 폴더에 저장을 합니다. 압축 파일의 이름은 android-sdk_r3-windows.zip로 나타납니다.

 

 

다음과 같이 다운로드가 됩니다.

 

 

(2) 안드로이드 SDK 설치

 

다운로드 받은 압축 파일(android-sdk_r3-windows.zip)을 풉니다. 압축 파일을 풀면 android-sdk_r3-windows라는 폴더가 생깁니다. 안드로이드 SDK 설치는 이것으로 끝납니다. 별도의 설치 과정이 필요 없습니다. 필요하다면 이 폴더를 원하는 곳으로 이동하거나 이름을 변경합니다. 저의 경우는 D:\Android로 폴더를 이동하고 이름을 변경하였습니다. 이 폴더 안에 보면  android-sdk-windows라는 폴더가 하나 더 있습니다. 이 안에 있는 폴더도 android-sdk로 변경하였습니다. 이 과정은 중요한 과정은 아니고 불필요하게 폴더가 많아져서 단순화한 것입니다. 안 해도 상관없는 작업입니다.

 

이 폴더 안에 보면 tools라는 폴더가 있고 그 밑에 android.bat이 있습니다.

 

 

DOS 창의 이 tools 폴더에서 여러 가지 도구를 사용할 일이 많습니다. 그래서 이 tools에 대한 경로를 시스템 환경 PATH 설정에 넣어 놓는 것이 좋습니다. 바탕 화면의 내 컴퓨터 아이콘을 오른쪽 클릭하여 속성을 실행하면 시스템 등록 정보 창이 뜹니다. 이 창의 고릅 탭을 눌러 아래 쪽의 "환경 변수" 버튼을 누릅니다. 그리고 아래 쪽의 시스템 변수의 Path를 더블 클릭하면 시스템 변수 편집이 나옵니다. 이 변수 값의 끝에 세미콜론(;)을 치고 위 tools 경로를 추가합니다. 저의 경우 D:\android\android-sdk\tools를 추가하였습니다. 그리고 확인 버튼을 누르고 다시 확인 버튼을 누릅니다. 그리고 다시 확인 버튼을 눌러서 시스템 등록 정보 화면을 닫습니다.

 

 

2.3 윈도에서 에뮬레이터 실행시키기

 

(1) 안드로이드 패키지 설치

 

안드로이드 책이나 개발자 사이트에는 이클립스 설치에 대해서 설명하고, ADT 플러그인 설치에 대해서 설명하지만 저는 먼저 이 상태에서 에뮬레이터를 띄우는 것을 설명하도록 하겠습니다. 다음 그림과 같이 이 android.bat 파일을 클릭하여 실행하세요.

 

 

그러면 다음 그림과 같이 "Android SDK and AVD Manager"가 실행됩니다.

 

 

우선 SDK 패키지를 설치해야 합니다. 왼쪽 리스트의 Settings를 선택하면 위와 같은 화면이 나옵니다. 이 화면의 가운데 Misc에 보면 "Force https://... Sources to be fetched using http://..."라고 표시된 것을 체크합니다. 그러면 "Save & Apply" 버튼이 활성화되며 이 버튼을 클릭합니다.

 

그리고 왼쪽 리스트의 Available Packages를 선택하고 오른쪽의 URL에 있는 체크 박스를 체크합니다.

 

 

그러면 설치 가능한 SDK 목록 리스트가 아래 그림과 같이 표시됩니다. 모두 선택된 상태로 표시되는데 아래 그림과 같이 "Google APIs by Google Incl, Android API"는 최신 버전 "5, revision 1"을 선택하고, "Android 2.0 API"는 "5, revision 1"을 선택하고, "Documentation for Android SDK, API 5, revision 1"과 "Usb Driver package, revision 2"를 선택합니다. 오른쪽 아래의 "Install Selected" 버튼을 클릭합니다.

 

 

(Troubleshooting) 위 https가 동작하지 않는 경우는 왼쪽 하단의 "Add Site..." 버튼을 눌러서 "https"의 "s"를 뺀 http://dl-ssl.google.com/android/repository/repository.xml을 입력합니다.

 

그러면 다음과 같이 설치할 패키지를 선택하라고 나오며 "Accept all"을 누르고 "Install Accepted"를 누릅니다.

 

 

그러면 다음 그림과 같이 다운로드가 시작됩니다. 약 10 ~30분 정도 오래 걸리는 작업이며 이 작업이 끝나면 "Close" 버튼을 눌러서 이전 창으로 돌아 옵니다.

 

 

(2) 안드로이드 가상 장치 설정

 

AVD는 Android Virtual Device의 약자이며, 에뮬레이터에서 가상의 장치를 설정하기 위한 용도로 사용됩니다. 에뮬레이터가 실행될 환경의 안드로이드 버전과 LCD 크기, SD 카드 크기 등을 설정해 주는 역할을 합니다.  앞 화면에서 이어서 계속됩니다. 왼쪽 리스트의 "Virtual Devices"를 선택하면 AVD를 생성할 수 있는 화면이 나옵니다.

 

 

위 화면의 "New" 버튼을 클릭하면 다음과 같은 화면이 나옵니다. "Name"은 AVD의 이름입니다. 여러 가지 virtual device를 만들 수 있으므로 적절한 이름을 지어주면 됩니다. 나중에 에뮬레이터를 실행시킬 때 어떤 virtual device에서 실행할 지 물어볼 때 이 이름이 나타납니다. 일단은 "android20"을 입력해 봅시다. 그리고 "Target"은 안드로이드 버전을 선택합니다. 앞의 안드로이드 패키지 설치에서 설치한 패키지가 보이며 "Android 2.0 - API Level 5"를 선택합니다. "SD Card"의 "Size"는 SD Card 용량을 나타냅니다. 임의로 입력을 하면 됩니다. 256을 입력해 보죠. "Skin"은 LCD 크기를 지정하거나 내장된 것을 선택할 수 있습니다. 일단 내장된 "Default (HVGA)"가 기본으로 선택되어 있습니다. 기타 "Hardware"는 장치에 부착된 하드웨어가 무엇이 있는지 선택하여 추가하는 것입니다. 기본으로 "Abstracted LCD density"이 추가되어 있습니다. 그리고 하단의 "Create AVD" 버튼을 클릭합니다.

 

 

위와 같이 하여 AVD가 하나 만들어졌다는 메시지 박스가 뜨면 "OK" 버튼을 누릅니다. 추가로 만들고 싶은 경우는 다시 "New" 버튼을 클릭하여 원하는대로 만들 수 있습니다. 일단은 에뮬레이터를 띄우기 위해서 하나만 있으면 충분하므로 우상귀의 x 버튼을 눌러서 "Android SDK and AVD Manager"를 종료합니다. 나중에 다시 추가/변경/삭제를 하고 싶으면 android.bat을 실행시키거나 이클립스의 Windows -> "Android SDK and AVD Manager..."를 실행하여 변경이 가능합니다.

 

 

(3) 안드로이드 에뮬레이터 실행

 

DOS 창을 실행합니다. 윈도 시작 버튼에서 "실행(R)..."을 눌러 "cmd"를 입력하여 확인을 누립니다. 여기서 에뮬레이터를 실행합니다. 다음과 같이 에뮬에이터를 실행합니다.

여기서 "android20"은 AVD를 만들 때 지정한 이름입니다. 만약 다른 이름으로 지정하였으면 그 이름을 쓰면 됩니다. 만약 환경 설정의 PATH에 tools 폴더를 넣지 않았다면 안드로이드 SDK의 tools 폴더로 이동을 한 후에 실행해 주세요. 그러면 다음 그림과 같이 에뮬레이터가 실행됩니다. 다음과 에뮬레이터가 보이면 '빙고~' 모든 과정이 성공적으로 끝난 것입니다. 혹시 아래 화면이 아니고 자물쇠가 보이면 이것을 오른쪽으로 돌리면 lock이 풀립니다. 이것 저것 에뮬레이터를 동작시켜 보세요. 안드로이드의 세계에 들어 오신 것을 환영합니다.

 

 

(Troubleshooting) 만약 위와 같은 화면이 안 나오고, 아래 그림과 같은 에러 메시지가 나오는 경우는 여러 가지 원인이 있을 수 있습니다.

 

 

그러나 대부분의 원인은 위 그림에서 보듯이 윈도 로그인 계정을 한글로 사용한 경우입니다. 안드로이드에서는 설정 정보가 C:\Documents and Settings\<사용자 계정>\.android 밑에 관리됩니다. 특히 이 폴더의 avd 폴더가 virtual device 설정 정보가 보관되는 곳입니다. 제가 방금 만든 AVD는 C:\Documents and Settings\강재우\.android\avd\android20.avd에 관리가 되네요.

 

이 문제의 해결책은 몇 가지가 있습니다. 우선 계정을 한글이 아닌 영어로 쓰고 다시 위 과정을 반복하면 쉽게 해결이 됩니다. 다른 해결책으로는 안드로이드에서 제공하는 android.bat 파일로 move avd를 하는 것입니다. 우선 안드로이드에서 기본 제공하는 기능으로 해결해 보겠습니다. 다음 그림과 같이 "android move avd -n android20 -p d:\android\avd"를 실행시켜 보겠습니다. 아래 그림과 같이 아직도 에러가 나네요. 원인이 무엇인지는 모르겠으나 안드로이드가 기존에 있던 폴더에 한글이 있어서 "move avd"도 실행이 제대로 되지 않는 것이 아닌가 추정됩니다. (원인을 정확히 아시는 분은 알려 주세요.) 혹, 계정을 영문으로 사용하시는 분은 AVD를 옮길 때 아래와 같이 하면 원하는 곳으로 AVD를 이동할 수 있으니 참고하시기 바랍니다.

 

 

그러면 다른 해결책은 무엇이 있을까요? 직접 C:\Documents and Settings\강재우\.android\avd 폴더에 가 보면 해결책이 보입니다. android20.ini 파일을 열면 다음 그림과 같이 path 변수에 새로 만든 AVD 파일의 경로가 보일 것입니다.

 

 

위 경로에 있는 android20.avd를 한글이 없는 곳으로 옮긴 후에 위 경로를 이동한 곳으로 변경해 주면 됩니다.

 

 

위와 같이 저는 변경을 하였고, android20.avd도 d:\android\avd 폴더로 옮겼습니다. 그러면 다시 에뮬레이터를 "emulator @android20"로 실행을 시켜 볼까요? 빙고~ 안드로이드 에뮬레이터가 뜨네요. 그런데 이상합니다. DOS 창이 하나 생겨서 "Error Laoding Prefrences"라는 메시지가 뜨네요. 화면에는 "ANDROID _"라는 문구가 보이고 커서가 깜빡이다가 ANDROID 로고가 바뀌고 글자가 깜빡이네요. 한 참 기다리면 위 안드로이드 홈 화면이 뜹니다. 에물레이터를 종료하고 다시 실행하니까 "Error Laoding Prefrences"가 다시 안 뜹니다. DOS 창에서 AVD를 만들고, 옮기고, 삭제하는 등의 방법에 대해서는 http://developer.android.com/guide/developing/tools/avd.html를 참고하시기 바랍니다.

 

3 장에서는 이클립스 IDE를 설치한 후에 "Hello, Android" 샘플 프로그램을 제작하여 에뮬레이터에서 띄워 보는 것을 설명하겠습니다.

 

끝.