안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

117

Post on 22-Jul-2016

270 views

Category:

Documents


0 download

DESCRIPTION

데이브 스미스, 제프 프리즌 지음 | 유윤선 옮김 | 임베디드 & 모바일 시리즈 _ 022 | ISBN: 9788992939690 | 30,000원 | 2012년 04월 25일 발행 | 600쪽

TRANSCRIPT

Page 1: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서
Page 2: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서
Page 3: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

안드로이드레시피

Page 4: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

iv

•목 차•

01장__안드로이드 시작하기 1안드로이드란? ....................................................................................................1

안드로이드의 역사 .............................................................................................2

안드로이드 아키텍처 .........................................................................................4

앱 아키텍처 .........................................................................................................8

컴포넌트_8; 인텐트_11; 매니페스트_13; 앱 패키지_16

액티비티 자세히 살펴보기 ............................................................................. 17

서비스 자세히 살펴보기 ................................................................................. 25

브로드캐스트 리시버 자세히 살펴보기 ........................................................ 33

콘텐츠 프로바이더 자세히 살펴보기 ............................................................ 34

1–1.안드로이드 SDK 설치 ............................................................................. 36

문제_36; 해결책_36; 문제 풀이_38

1-2.안드로이드 플랫폼의 설치 ...................................................................... 40

문제_40; 해결책_40; 문제 풀이_40

1–3.안드로이드 가상 기기의 생성 ................................................................. 45

문제_45; 해결책_45; 문제 풀이_45

1–4. AVD 시작하기 ........................................................................................ 48

문제_48; 해결책_48; 문제 풀이_48

1–5. UC 앱에 대한 소개 ................................................................................. 52

문제_52; 해결책_52; 문제 풀이_53

Page 5: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

v

1–6. UC 액티비티 만들기 .............................................................................. 69

문제_69; 해결책_69; 문제 풀이_70

1–7. UC의 설치와 실행 .................................................................................. 72

문제_72; 해결책_72; 문제 풀이_73

1–8. UC 앱의 배포 준비 ................................................................................. 76

문제_76; 해결책_76; 문제 풀이_76

1–9. 이클립스에서 작업하기.......................................................................... 82

문제_82; 해결책_82; 문제 풀이_82

1–10. 이클립스를 활용한 UC 앱 개발 .......................................................... 86

문제_86; 해결책_86; 문제 풀이_87

정리 ................................................................................................................... 92

02장__사용자 인터페이스 레시피 952–1.창 커스터마이징 ...................................................................................... 95

문제_95; 해결책_96; 문제 풀이_97

2–2.뷰의 생성과 표시 ................................................................................... 106

문제_106; 해결책_106; 문제 풀이_107

2–3.클릭 액션의 감지 ................................................................................... 109

문제_109; 해결책_109; 문제 풀이_109

Page 6: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

vi

2–4.해상도 독립적인 에셋 ........................................................................... 111

문제_111; 해결책_111; 문제 풀이_112

2–5.액티비티 화면 고정 ............................................................................... 114

문제_114; 해결책_114; 문제 풀이_114

2–6.동적인 방향 고정 ................................................................................... 115

문제_115; 해결책_116; 문제 풀이_116

2–7.직접적인 회전 처리 ............................................................................... 118

문제_118; 해결책_119; 문제 풀이_119

2–8.팝업 메뉴 액션의 생성 .......................................................................... 121

문제_121; 해결책_122; 문제 풀이_122

2–9.옵션 메뉴 커스터마이징........................................................................ 128

문제_128; 해결책_128; 문제 풀이_129

2–10.뒤로가기 버튼의 커스터마이징 .......................................................... 132

문제_132; 해결책_132; 문제 풀이_133

2–11.홈 버튼 흉내내기 ................................................................................. 134

문제_134; 해결책_134; 문제 풀이_134

2–12.TextView의 변화 감지 ......................................................................... 135

문제_135; 해결책_135; 문제 풀이_136

2–13.TextView 티커 스크롤 ......................................................................... 139

문제_139; 해결책_139; 문제 풀이_140

2–14.뷰 애니메이션 ...................................................................................... 141

문제_141; 해결책_141; 문제 풀이_141

2–15.배경 드로어블의 생성 ......................................................................... 152

문제_152; 해결책_152; 문제 풀이_153

Page 7: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

vii

2–16.커스텀 상태 드로어블의 생성............................................................. 157

문제_157; 해결책_157; 문제 풀이_157

2–17.이미지에 마스크 적용 ......................................................................... 160

문제_160; 해결책_160; 문제 풀이_161

2–18.상태 유지 대화상자의 생성 ................................................................ 166

문제_166; 해결책_166; 문제 풀이_167

2–19. 상황 관련 레이아웃의 구현 ............................................................... 168

문제_168; 해결책_169; 문제 풀이_169

2–20. 키보드 동작의 커스터마이징............................................................. 175

문제_175; 해결책_175; 문제 풀이_175

2–21. 소프트 키보드 사라지게 하기............................................................ 179

문제_179; 해결책_179; 문제 풀이_179

2–22. AdapterView의 비어 있는 뷰 처리 ................................................... 180

문제_180; 해결책_180; 문제 풀이_180

2–23. ListView 행 커스터마이징 ................................................................. 183

문제_183; 해결책_183; 문제 풀이_183

2–24. ListView 섹션 헤더 만들기 ................................................................ 188

문제_188; 해결책_189; 문제 풀이_189

2–25. 복합 컨트롤의 생성 ............................................................................ 193

문제_193; 해결책_193; 문제 풀이_193

알아두면 좋은 툴 - DroidDraw ................................................................... 198

DroidDraw의 설치와 실행_198;

DroidDraw UI 살펴보기_199;

간단한 화면 개발_201

정리 ................................................................................................................. 204

Page 8: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

viii

03장__통신과 네트워크 2073–1.웹 정보 보여주기 ................................................................................... 207

문제_207; 해결책_208; 문제 풀이_208

3–2. 웹뷰 이벤트 가로채기 .......................................................................... 213

문제_213; 해결책_213; 문제 풀이_213

3–3. 자바스크립트를 통한 웹뷰 접근 .......................................................... 215

문제_215; 해결책_215; 문제 풀이_216

3–4. 이미지 파일 내려받기 .......................................................................... 219

문제_219; 해결책_219; 문제 풀이_219

3–5. 백그라운드를 통한 완전한 다운로드 .................................................. 223

문제_223; 해결책_223; 문제 풀이_223

3–6. REST API 접근 ..................................................................................... 228

문제_228; 해결책_228; 문제 풀이_229

3–7. JSON 파싱 ............................................................................................. 237

문제_237; 해결책_237; 문제 풀이_237

3–8. XML 파싱 .............................................................................................. 242

문제_242; 해결책_242; 문제 풀이_242

3–8. SMS 수신 ............................................................................................... 248

문제_248; 해결책_248; 문제 풀이_248

3–9. SMS 메시지 전송 .................................................................................. 250

문제_250; 해결책_250; 문제 풀이_251

3–10. 블루투스 통신 ..................................................................................... 253

문제_253; 해결책_253; 문제 풀이_254

3–11. 네트워크 연결 상태 조회 ................................................................... 265

문제_265; 해결책_265; 문제 풀이_266

정리 ................................................................................................................. 268

Page 9: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

ix

04장__기기 하드웨어 및 미디어 활용 2714–1. 기기 위치 연동 ...................................................................................... 271

문제_271; 해결책_272; 문제 풀이_273

4–2.위치 매핑 ................................................................................................ 277

문제_277; 해결책_277; 문제 풀이_278

4–3.지도에 표시 남기기 ............................................................................... 283

문제_283; 해결책_283; 문제 풀이_283

4–4. 사진과 동영상 캡처 .............................................................................. 291

문제_291; 해결책_291; 문제 풀이_291

4–5. 커스텀 카메라 오버레이 만들기 .......................................................... 298

문제_298; 해결책_298; 문제 풀이_298

4–6. 오디오 녹음 ........................................................................................... 307

문제_307; 해결책_307; 문제 풀이_307

4–7. 음성 인식 기능의 추가 ......................................................................... 311

문제_311; 해결책_311; 문제 풀이_311

4–8. 오디오/동영상의 재생 .......................................................................... 314

문제_314; 해결책_314; 문제 풀이_314

4–9. 가속도계 모니터링 ............................................................................... 325

문제_325; 해결책_326; 문제 풀이_326

4–10 나침반 방향 모니터링 ......................................................................... 331

문제_331; 해결책_331; 문제 풀이_331

알아두면 좋은 툴 - 센서 시뮬레이터 ........................................................... 336

센서 시뮬레이터 내려받기_337;

센서 시뮬레이터 설정과 센서 시뮬레이터의 실행_337;

앱에서 센서 시뮬레이터 접근하기_342

정리 ................................................................................................................. 344

Page 10: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

x

05장__데이터 영속화 3475–1. 환경설정 화면 만들기 .......................................................................... 347

문제_347; 해결책_348; 문제 풀이_348

5–2. 단순 데이터의 영속화 .......................................................................... 354

문제_354; 해결책_354; 문제 풀이_354

5–3. 파일 읽고 쓰기 ...................................................................................... 360

문제_360; 해결책_361; 문제 풀이_361

5–4.파일을 리소스로 활용 ........................................................................... 368

문제_368; 해결책_368; 문제 풀이_368

5–5. 데이터베이스 관리하기........................................................................ 371

문제_371; 해결책_371; 문제 풀이_372

5–6. 데이터베이스 조회 ............................................................................... 379

문제_379; 해결책_379; 문제 풀이_380

5–7. 데이터 백업 ........................................................................................... 382

문제_382; 해결책_382; 문제 풀이_382

5–8. 데이터베이스의 공유 ........................................................................... 388

문제_388; 해결책_388; 문제 풀이_389

5–9. 기타 데이터의 공유 .............................................................................. 397

문제_397; 해결책_397; 문제 풀이_398

알아두면 좋은 툴 - SQLite3 ......................................................................... 405

SQLite3와 UC_408

정리 ................................................................................................................. 417

Page 11: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

xi

06장__시스템 연동 4196–1. 백그라운드로 알림 보내기 ................................................................... 419

문제_419; 해결책_420; 문제 풀이_420

6–2.예약 작업과 주기적인 작업의 생성 ...................................................... 424

문제_424; 해결책_424; 문제 풀이_425

6–3. 주기적인 작업의 예약 .......................................................................... 426

문제_426; 해결책_427; 문제 풀이_427

6–4.끝까지 완료되는 작업의 생성 ............................................................... 432

문제_432; 해결책_433; 문제 풀이_433

6–5.지속적인 백그라운드 작업의 실행 ....................................................... 439

문제_439; 해결책_440; 문제 풀이_440

6–6. 다른 애플리케이션의 실행 ................................................................... 448

문제_448; 해결책_448; 문제 풀이_449

6–7. 시스템 애플리케이션의 실행 ............................................................... 452

문제_452; 해결책_453; 문제 풀이_453

6–8.외부 애플리케이션에서 애플리케이션 실행 가능하게 하기 ............. 459

문제_459; 해결책_459; 문제 풀이_459

6–9. 주소록 활용 ........................................................................................... 462

문제_462; 해결책_463; 문제 풀이_463

6–10.기기 미디어 선택하기 ......................................................................... 471

문제_471; 해결책_472; 문제 풀이_472

6–11. MediaStore에 저장하기 ..................................................................... 475

문제_475; 해결책_475; 문제 풀이_475

정리 ................................................................................................................. 479

Page 12: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

xii

07장__라이브러리 활용 4817–1. 자바 JAR 라이브러리 만들기 .............................................................. 482

문제_482; 해결책_482; 문제 풀이_482

7–2. 자바 JAR 라이브러리의 활용 .............................................................. 485

문제_485; 해결책_485; 문제 풀이_485

7–3. 안드로이드 라이브러리 프로젝트의 생성 .......................................... 489

문제_489; 해결책_489; 문제 풀이_489

7–4.안드로이드 라이브러리 프로젝트의 활용 ........................................... 494

문제_494; 해결책_494; 문제 풀이_495

7–5. 차트 ........................................................................................................ 498

문제_498; 해결책_498; 문제 풀이_498

7–6. 현실적인 푸시 메시지 .......................................................................... 510

문제_510; 해결책_511; 문제 풀이_512

정리 ................................................................................................................. 522

부록A_안드로이드 스크립팅 레이어 525SL4A의 설치 .................................................................................................. 526

SL4A 살펴보기 .............................................................................................. 526

셸 스크립트 추가_528;

리눅스 셸 접근_529

파이썬 인터프리터 설치 ............................................................................... 530

파이썬을 활용한 스크립팅 ........................................................................... 535

Page 13: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

xiii

부록B_안드로이드 NDK 537NDK 설치 ...................................................................................................... 538

NDK 살펴보기 .............................................................................................. 540

NDK에서 온 안부 인사 ................................................................................ 542

안드로이드 SDK를 활용한 NDKGreetings의 빌드와 실행_545;

이클립스를 활용한 NDKGreetings 빌드와 실행_548

NDK 예제 ...................................................................................................... 550

부록C_앱 설계 가이드라인 555C–1. 앱 필터링 설계 ..................................................................................... 555

문제_555; 해결책_556

C–2. 고성능 앱 설계하기 .............................................................................. 558

문제_558; 해결책_558

C–3. 빠르게 반응하는 앱의 설계 ................................................................. 560

문제_560; 해결책_560

C–4. 다른 앱과 잘 호환되는 앱의 설계 ....................................................... 561

문제_561; 해결책_561

찾아보기...............................................................................567

Page 14: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

xiv

•옮긴이의 글•

예전에 켄트 벡(Kent Beck)은 트위터에서 'Sometimes the problem has to mature before the

solution can mature(때로는 해결책이 충분히 성숙하기 전에 문제가 먼저 성숙해야 한다)'는 글

을 남겼다. 사실 이 말은 테스트 주도 개발을 할 때 테스트 케이스를 제대로 작성하려면 먼저 문

제를 충분히 인식해야 한다는 의미였지만, 안드로이드 레시피를 소개하는 데 이 표현만큼 적절

한 표현도 없을 것 같다. 그만큼 안드로이드 세계에서는 해결해야 할 문제도 많고, 그 문제도 이

미 충분히 (어쩌면 골치가 아플 정도로) 성숙했다.

안드로이드 세계에는 크게 두 가지 문제가 있다. 안드로이드 입문자의 경우 다양한 API를 새

로 배워야 하는 게 첫 번째 문제이고, 다양한 파편화 이슈를 해결하는 법을 궁리해야 하는 게 두

번째 문제다. 사실 이런 문제는 서로 뒤엉켜 있어서 어떤 식으로 접근해야 할지 막막할 때가 많

다. 예를 들어 새로운 API가 출시돼 기존 문제를 해결해주는 일도 가끔 있지만, 이런 API가 하위

버전 호환성까지 해결해주지는 못하는 경우도 생긴다. 또 기기 간의 해상도, 하드웨어 유무, 프로

세서 성능, 탑재된 소프트웨어 라이브러리 버전(오픈지엘 등)까지 고려하면 파편화 이슈는 끝없

이 발생하는 듯하다.

이 책에서는 이런 여러 문제점들을 하나씩 살펴보고 다양한 해결책을 최소 API 레벨에서 제

공한다. 이 책은 안드로이드 개발자가 손쉽게 참고해 문제를 해결할 수 있게 문제를 명확히 규정

한 후, 재사용 가능한 코드 조각을 통해 가장 효과적으로 문제를 해결하는 법을 제공한다. 독자

들은 주제별로 분류된 이 책의 레시피를 통해 예제 코드를 실제 프로젝트에 바로 적용하고, 다양

한 문제를 동시에 해결할 수 있다.

이 책은 거의 모든 경우 최소 API 레벨(대부분 API 레벨 1)을 사용해 문제를 해결하므로 이 책

의 코드는 안드로이드 플랫폼 버전과 상관없이 모든 플랫폼에 안심하고 적용할 수 있다. 또 이 책

에서는 개발자들이 놓치기 쉬운 파편화 이슈를 꼼꼼히 지적하며 가장 효과적이고 안정적으로

Page 15: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

xv

안드로이드 개발을 할 때 자주 접하는 문제점(UI 이슈, 네트워크 활용, 시스템 연동, 하드웨어와

미디어 활용 등)을 빠짐 없이 다룬다.

이 책에서는 세부적인 API 사용법을 모르더라도 예제를 충분히 활용할 수 있게 기반 설명을

제공하고 바로 실전 예제 코드를 설명한다. 이 책의 예제 코드를 활용하면 손쉽게 시스템 연동

(시스템 서비스 연동, 외부 애플리케이션으로 애플리케이션 노출 등)을 할 수 있다. 이 책의 각 레

시피에 수록된 예제 코드는 대부분의 경우 외부 의존성이 거의 없는 독립적인 코드로 작성돼 있

으므로 이 책은 안드로이드 개발을 할 때 항상 참조할 수 있는 레퍼런스이자 쿡북이다.

이 책의 대상 독자

이 책은 안드로이드를 처음 접하는 개발자와 안드로이드에 익숙하지만 효과적인 문제 해결법을

고민하는 개발자 모두 볼 수 있는 책이다. 이 책에서는 기본 안드로이드 시스템에 대한 설명을 시

작으로 주제별로 필요한 설명과 API, 예제 코드를 적용하므로 안드로이드 개발을 담당하는 개

발자라면 누구든 상관없이 이 책을 추천한다.

감사의 글

먼저 이 책의 번역을 맡겨주신 위키북스 박찬규 사장님, 김윤래 팀장님께 감사드린다. 또 여러 사

정으로 인해 책의 번역이 지연된 점에 대해 이 자리를 빌어 죄송하다는 말씀을 전한다. 그리고 사

랑하는 가족과 항상 나와 함께 하시는 하나님께 감사를 드린다.

- 유윤선

Page 16: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

xvi

•추천의 글•

이 책은 데이브 스미스와 제프 프리즌의 많은 노력 끝에 세상에 나왔다. 모바일 개발 커뮤니티에

서 데이브를 오랫동안 알고 있는 사람으로서 필자는 데이브가 각 장의 내용에 얼마나 많은 공을

들이고, 독자들에게 가장 좋은 지식을 전달하기 위해 얼마나 노력했는지 잘 알고 있다. 필자는

현재 데이브와 함께 일하면서 데이브가 안드로이드 소프트웨어를 개발하는 과정에서 생기는 문

제들을 방법론적으로 신중하게 접근해 해결책을 찾아내는 것을 매일 지켜보고 있다.

안드로이드를 구동하는 기기들이 단기간에 폭발적으로 늘어나면서 모바일 컴퓨팅의 미래 지

형을 바꿀 수 있는 좋은 기회도 그만큼 늘어났다. 안드로이드는 폰, 태블릿, 산업 장비에 사용되

며 우리가 아직 상상하지 못한 미래 기기에서도 사용될 것이다. 이처럼 공통 플랫폼에서 수많은

기기들이 실행됨에 따라 이제 소프트웨어 개발자들은 한 번 작성해 모든 곳에서 실행할 수 있는

소프트웨어를 개발할 수 있게 됐다. 이런 여정에 독자들이 동참할 수 있도록 이 책에서는 데이브

와 제프가 실제 안드로이드 애플리케이션을 개발하면서 익힌 예제들을 설명하고 있다.

Page 17: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

xvii

독자들은 이 책의 내용을 기반으로 수준 높은 모바일 애플리케이션을 충분히 개발할 수 있으

리라 확신한다. 독자들이 자신만의 앱을 배포하면 이들 기기가 여러분 앱의 공략 대상이 된다. 현

재 수많은 모바일 기기와 더불어 모바일 소프트웨어도 쏟아져 나오고 있지만, 이런 소프트웨어

중 대부분은 품질이 좋지 못하다. 사용자 입장에서 생각하고 사용자들이 겪고 있는 문제를 해결

해줌으로써 사용자들이 남들에게 자랑할 수 있는 앱을 개발하길 바란다. 사소한 디테일조차도

소홀히 여기지 않는다면 사용자들은 이를 감사하게 생각할 것이다. 그리고 “진정한 예술가는 출

하한다(Real Artists Ship)” 1 라는 명구를 꼭 기억하길 바란다.

- 벤 루벤스타인 (@benr75)[email protected]

Xcellent Creations, Inc.

1   (옮긴이) 스티브 잡스가 한 말로, 진정한 예술가는 예술적인 영감과 아이디어도 중요하지만 그 아이디어를 실제로 구현해

사람들로부터 인정을 받는다는 의미다.

Page 18: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

xviii

•저자 소개•

■ 데이브 스미스 (Dave Smith)

데이브 스미스는 콜로라도 광업 대학에서 2006년에 전기 공학과 컴퓨

터 사인언스 학위를 받은 후로 임베디드 플랫폼용 하드웨어와 소프트

웨어를 개발하고 있다. 현재 데이브는 덴버에서 컨설턴트로 일하면서

모바일 개발에 전념하고 있다. 데이브는 2009년부터 SDK를 사용해 사

용자 애플리케이션을 개발하는 것부터 안드로이드 소스 코드를 커스

터마이징하는 데 이르기까지 안드로이드 플랫폼과 관련한 모든 개발

작업을 진행 중이다. 데이브는 커스텀 하드웨어와 연동한 안드로이드

프로젝트나 커스텀 임베디드 플랫폼용으로 안드로이드를 빌드하는 프

로젝트를 제일 좋아한다. 데이브는 개발 블로그(blog.wiresareobsolete.com)와 트위터 스트림(@

devunwired)을 통해 사람들과 소통하고 있다.

■ 제프 프리즌 (Jeff Friesen)

제프 프리즌은 자바(및 현재는 안드로이드)를 전문으로 하는 프리랜서

강사이자 소프트웨어 개발자다. 제프는 이 책 외에 자바와 다른 기술

에 대해 JavaWorld (www.javaworld.com), informIT (www.informit.

com), java.net, DevSource (www.devsource.com)에 다양한 기사를

기고했다. 제프에게 연락하려면 그의 웹사이트(tutortutor.ca)를 찾아

가면 된다.

Page 19: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

xix

•기술 감수자 소개•

■ 폴 코놀리 (Paul Connolly)

폴 코놀리는 Atypon Systems사의 RightSuite 생산 라인의 기술 책임

자다. RightSuite는 엔터프라이즈 접근 제어와 상업 솔루션으로, 전세

계 거대 출판 및 미디어 회사에서 사용하는 솔루션이다. 폴은 고성능,

엔터프라이즈 수준의 스프트웨어 시스템을 설계하고 구현하는 일을

좋아한다. 또 폴은 오픈소스 커뮤니티에서 소스 공헌자로도 활발하게

활동 중이다.

Atypon Systems에 입사하기 전 폴은 스탠다드&푸어스의 선임 소프트

웨어 엔지니어로 근무했다. 스탠다드&푸어스에서는 주요 커뮤니케이션 시스템을 설계하고 개발

하는 일을 담당했다. 폴은 썬 인증 자바 프로그래머, 썬 인증 비즈니스 컴포넌트 개발자, 썬 인증

웹 컴포넌트 개발자다. 폴은 뉴욕에서 아내 마리나와 딸 올리비아와 함께 살고 있다.

Page 20: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

xx

•감사의 글•

먼저 이 책의 예제를 컴파일하고 책의 내용을 구성하는 데 들인 오랜 시간을 참고 나를 도와준

아내 로리에게 감사하다는 말을 전하고 싶다. 다음으로 공저자인 제프 프리즌에게도 감사하다.

제프는 안드로이드 개발 플랫폼에 새로운 옵션이 추가될 때마다 이를 충실히 살펴보고 책에 반

영해줬고 이로 인해 이 책은 더욱 풍성한 내용으로 채워질 수 있었다. 또 친구이자 동료인 벤 루

벤스타인에게도 감사하다. 벤은 이 책의 추천사를 써줬고 내가 Apress 출판사와 연을 맺을 수 있

게 나를 소개해줬다. 끝으로 제프와 내가 이 책에 들인 노력을 최고의 결실로 맺어준 Apress 직

원들에게도 감사하다. 스티브 앵글린, 코빈 콜린스, 톰 웰시, 폴 코놀리, 그 외 모든 분께 감사하

다. 여러분들의 시간과 노력이 없었다면 이 책은 나올 수 없었을 것이다.

- 데이브 스미스

이 책을 집필할 수 있는 기회를 준 스티브 앵글린, 이 책의 집필 과정에서 다양한 가이드를 제공

해준 코빈 콜린스, 내가 맡은 장의 개발에 도움을 준 톰 웰시, 자칫 책에 오류로 남을 수 있는 부

분을 꼼꼼히 살펴보고 수정해준 폴 코놀리에게 감사하다. 또 이 책을 집필하는 데 환상적인 공헌

을 한 공저자 데이브 스미스에게 감사하다.

- 제프 프리즌

Page 21: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

xxi

서문

안드로이드 레시피의 세계에 온 것을 환영한다!

이 책을 읽고 있는 독자라면 모바일 기기가 소프트웨어 개발자와 사용자에게 제공하는 엄청난

기회에 대해 새삼스럽게 다시 얘기하지 않아도 될 것이다. 지난 몇 년 사이 안드로이드는 기기 사

용자들이 사용하는 최고의 모바일 플랫폼 중 하나가 됐다. 이 말은 개발자인 여러분이 이런 모바

일 플랫폼 시장에 참여해 시장의 잠재력을 활용하려면 안드로이드를 활용하는 법을 알고 있어야

한다는 뜻으로 해석할 수 있다. 하지만 새 플랫폼이 항상 그렇듯 처음 플랫폼을 접할 때는 모범 개

발 방식이라든가 특정 필요나 문제와 관련한 해결책을 몰라 헤매는 경우가 많다.

안드로이드 레시피는 이런 독자들이 해결하려는 문제와 직접 관련된 예제를 통해 안드로이드

플랫폼용 애플리케이션을 개발할 수 있는 도구를 갖출 수 있게 도와주는 책이다. 이 책에서는 안

드로이드 SDK, NDK, 기타 다른 툴에 대해서 깊이 살펴보지는 않는다. 이 책에서는 모든 세부 사

항과 내부 이론을 상세히 소개해 독자들에게 지레 겁을 주지 않는다. 물론 이런 세부 내용이 중요

하지 않은 것은 아니다. 이런 내용을 시간을 들여 배워두면 나중에 같은 실수를 되풀이하는 것을

막을 수도 있다. 하지만 당장 직면한 문제를 해결하려고 할 때 이런 세부 사항은 보통 문제 자체에

집중하는 것을 방해하는 장애물일 뿐이다.

이 책은 자바 프로그래밍이나 안드로이드 애플리케이션의 구성 요소를 개발하는 법을 다루는

책이 아니다. 이 책에서는 기본 레시피(예를 들어 TextView를 사용해 텍스트를 보여주는 법)를 많

이 다루지 않는다. 이런 내용은 한번 배워두고 나면 언제든 쉽게 기억할 수 있기 때문이다. 대신 이

책에서는 안드로이드에 익숙한 개발자가 자주 접하면서도 지나치게 복잡하지 않은 문제들을 단

몇 줄의 코드로 해결하는 법을 설명하고 있다.

안드로이드 레시피는 작업을 빠르게 효과적으로 끝내줄 수 있는 실전 가이드 쿡북으로 생각하

면 된다. 이 책은 언제든 필요할 때 참고해 바로 쓸 수 있는 레시피로 가득 채워져 있다.

Page 22: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

xxii

◈ 이 책에서 다루는 내용

이 책은 안드로이드 초보자용 가이드는 아니지만 책의 1장에서는 책의 나머지 내용을 이해하는

데 필요한 안드로이드 기본 구조에 대한 전반적인 설명을 제공한다. 또 1장에서는 안드로이드 앱

을 개발할 수 있게 개발 환경을 설정하는 법도 보여준다. 특히 안드로이드 SDK를 설치하는 법과

이클립스에서 ADT 플러그인을 설치하는 법을 살펴본다.

안드로이드 앱 개발에 조금씩 익숙해지다 보면 처음부터 모든 내용을 다시 구현하느라 들이는

시간을 조금이라도 줄이고 싶은 생각이 들기 마련이다. 이를 위해서는 재사용할 수 있는 코드로

이뤄진 커스텀 라이브러리를 만들어 사용하거나 다른 사람들이 만들어 놓은 라이브러리를 사용

하는 게 좋다. 7장에서는 JAR 기반의 커스텀 라이브러리와 안드로이드 라이브러리 프로젝트를 생

성하고 사용하는 법을 살펴본다. 더불어 커스텀 라이브러리를 생성하는 것 외에 애플리케이션에

서 활용할 수 있는 외부 자바 라이브러리를 한두 개 소개한다.

1장과 7장 사이의 장에서는 안드로이드 SDK를 활용해 실제 문제들을 해결한다. 이를 통해 모든

기기에서 잘 실행되는 UI를 효과적으로 개발하는 트릭을 배우고, 모바일 플랫폼에 독창적인 성격

을 부여하는 다양한 하드웨어(무선 통신 장치, 센서, 카메라)와 연동하는 법을 마스터한다. 또 시

스템과 연동해 구글 및 다양한 기기 제조사에서 제공하는 서비스와 애플리케이션을 활용하는 법

도 살펴본다. 더불어 애플리케이션의 개발과 테스트를 더 쉽게 진행할 수 있도록 커뮤니티에서 개

발한 일부 툴에 대해서도 소개한다.

독자들 중에 스크립팅 언어(파이썬이나 루비 등)에 관심이 있는 사람이 있다면 부록 A를 살펴

보자. 부록 A에서는 안드로이드용 스크립팅 레이어를 소개한다. 부록에서 소개한 특수 앱을 활용

하면 스크립트 언어의 인터프리터와 스크립트를 기기에 설치한 다음 이들 스크립트를 실행해 개

발을 더 빠르게 진행할 수 있다.

또 앱이 성공하려면 성능도 중요하다. (안드로이드 2.2 버전부터는) 안드로이드 달빅 가상 머신

에서 달빅 바이트 코드를 기기의 네이티브 코드로 컴파일하는 JIT 컴파일러를 제공하는 만큼 대

부분의 경우 성능은 크게 문제가 되지 않는다. 하지만 JIT 컴파일러로도 부족하다면 안드로이드

의 NDK를 활용해 성능을 더 올릴 수 있다. 부록 B에서는 NDK를 소개하고 오픈지엘 예제를 통해

이를 유용하게 활용하는 법을 살펴본다.

Page 23: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

xxiii

앱을 개발할 때는 앱이 최적화돼 있고, 빠르게 응답하고, 부드럽게 연동되는지 항상 확인해야

한다. 성능이 좋은 앱은 배터리도 그만큼 덜 소모하며, 응답이 빠른 앱은 무시무시한 '애플리케이

션 응답 없음' 대화상자가 나타나지 않게 해준다. 또 부드럽게 연동되는 앱은 다른 앱들과도 잘 상

호작용해 사용자를 방해하거나 혼란스럽게 하지 않는다. 추가적으로 구글의 안드로이드 마켓에

앱을 배포할 때는 앱이 호환되지 않는 기기에 노출되지 않게 해야 한다. 이 경우 안드로이드 마켓

필터를 활용해 호환되지 않는 기기의 사용자들이 앱을 내려받지 못하게(볼 수도 없게) 해야 한다.

부록 C에서는 성능이 우수하고 빠르게 반응하며, 부드럽게 연동하는 앱을 만드는 데 필요한 가이

드라인을 제시해 책의 내용을 마무리한다. 더불어 호환되는 기기 사용자만 앱을 (안드로이드 마

켓에서) 내려받을 수 있게 필터를 적용하는 법도 살펴본다.

◈ 지원 대상 API에 대한 확인

이 책의 내용을 살펴보면 거의 모든 레시피에 해당 기능을 지원하는 최소 API 레벨을 표시한 것

을 볼 수 있다. 이 책의 레시피 대부분은 API 레벨 1로 표시돼 있다. 이 말은 이 책의 코드 대부분

을 안드로이드 1.0 이상의 버전을 실행하는 애플리케이션에서 모두 사용할 수 있다는 뜻이다. 하

지만 API 레벨 1보다 높은 버전을 꼭 사용해야 하는 경우에는 이를 활용했다. 독자들은 지원할

안드로이드 버전과 일치하지 않는 코드를 사용하지 않도록 각 레시피에 표시된 API 레벨을 꼼꼼

히 확인해야 한다.

Page 24: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

And

roid

Rec

ipes

Page 25: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

1

01안드로이드 시작하기

안드로이드가 화제다. 그리고 많은 사람이 안드로이드 애플리케이션(줄여서 앱) 개발에 뛰어들

고 있다. 이 책을 읽고 있는 독자도 마찬가지로 앱을 만들고 싶지만 아마 어디서부터 시작해야

할지 잘 모르고 있을 것이다. 물론 구글의 온라인 안드로이드 개발자 가이드(http://developer.

android.com/guide/index.html)를 통해 필요한 지식을 익힐 수는 있지만, 이 가이드에 나와 있

는 방대한 지식에 엄두가 나지 않을 것이다. 이 장에서는 안드로이드의 기본 지식을 익히는 데 도

움되는 이론만 추려서 설명하고 있다. 이 장의 설명 뒤에는 앱을 개발하고 구글의 안드로이드 마

켓에 배포하는 데 필요한 레시피들이 이어진다.

안드로이드란?

안드로이드 개발자 가이드에서는 안드로이드를 모바일 기기용 소프트웨어 스택으로 정의한다.

소프트웨어 스택이란 여러 소프트웨어 하위 시스템이 모여서 전체 기능을 제공하는 솔루션을

말한다. 안드로이드의 소프트웨어 스택에는 운영체제(리눅스 커널 수정 버전), 일부 자바를 기

반으로 하는 미들웨어(저수준 운영체제를 고수준 앱으로 연결해주는 소프트웨어), 웹 브라우저

(폰에서의 명칭은 인터넷이다)와 연락처(폰에서의 명칭은 주소록) 같은 핵심 프로그램(자바로 작

성돼 있다)이 들어 있다.

Page 26: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

2 l 안드로이드 레시피

안드로이드는 다음과 같은 기능을 제공한다.

■ 앱 컴포넌트의 재사용 및 대체가 가능한 애플리케이션 프레임워크(이 장에서 나중에 설

명).

■ 블루투스, EDGE, 3G, 와이파이 지원(하드웨어 의존 기능)

■ 카메라, GPS, 나침반, 가속도계 지원(하드웨어 의존 기능)

■ 모바일 기기에 최적화된 달빅 가상 머신(DVM)

■ GSM 전화 기능(하드웨어 의존 기능)

■ 오픈소스 웹킷 엔진을 기반으로 한 통합 브라우저

■ 자주 사용하는 오디오, 비디오, 스틸 이미지 형식에 대한 미디어 지원 기능(MPEG4,

H.264, MP3, AAC, AMR, JPG, PNG, GIF)

■ 커스텀 2D 그래픽 라이브러리를 활용한 최적화된 그래픽, 오픈지엘 ES 1.0 명세 1 를 기반

으로 한 3D 그래픽(하드웨어 가속 기능은 하드웨어에 따라 다름)

■ 구조화된 데이터를 저장할 수 있는 SQLite

안드로이드 기기의 소프트웨어 스택에는 포함되지 않지만 안드로이드의 풍부한 개발 환경(기

기 에뮬레이터와 이클립스 IDE용 플러그인)도 안드로이드의 기능으로 생각할 수 있다.

안드로이드의 역사

독자들이 흔히 생각하는 것과 달리 안드로이드는 원래 구글에서 나온 게 아니다. 안드로이드는

Android, Inc라는 캘리포니아 팔로알토에 있는 작은 신생 개발업체에서 개발했다. 구글은 2005

년 7월에 이 회사를 인수했고 2007년 11월 안드로이드 SDK의 프리뷰 버전을 내놓았다.

1   (옮긴이) 안드로이드 2.2부터는 오픈지엘 ES 2.0 명세를 지원한다. 보다 자세한 사항은 http://developer.android.com/

guide/topics/graphics/opengl.html을 참고하자.

Page 27: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 3

2008년 8월 중순 구글은 안드로이드 0.9 SDK 베타를 출시했고 이어서 한 달 후 안드로이드

1.0 SDK를 선보였다. 표 1-1은 이후 출시된 SDK 업데이트 목록을 정리한 것이다(1.5 버전부터는

주요 배포 버전의 코드명으로 디저트 이름을 사용했다).

표 1–1. 안드로이드 업데이트 출시 목록

SDK 업데이트 출시일과 변경사항

1.1 구글은 2009년 2월 9일 SDK 1.1을 출시했다. 이 버전에서 달라진 점은 (안드로이드 마켓에서 구매한) 유료 앱과 '음성 검색' 지원 기능이다.

1.5 (컵케익) 리눅스 커널 2.6.27 기반

구글은 2009년 4월 30일 SDK 1.5를 배포했다. 이 버전에서 달라진 점은 캠코드 모드에서의 녹화 및 재생 기능, 유튜브로의 동영상 업로드 기능, Picasa로의 사진 업로드 기능, 홈 화면 위젯 추가 기능, 화면 애니메이션 전환 기능이다.

1.6 (도넛) 리눅스 커널 2.6.29 기반

구글은 2009년 9월 15일 SDK 1.6을 배포했다. 이 버전에서는 안드로이드 마켓 사용자 경험이 개선됐고, 카메라/캠코더/갤러리 인터페이스가 통합됐으며, 음성 검색 속도 및 성능이 개선됐고, 검색 사용자 경험이 좋아졌다.

2.0/2.1 (이클레어) 리눅스 커널 2.6.29 기반

구글은 2009년 10월 26일 SDK 2.0을 내놓았다. 이 버전에서는 사용자 인터페이스가 전면 개편되고, 주소록이 새롭게 바뀌었으며 마이크로소프트 익스체인지 지원 기능, 디지털 줌, 개선된 구글 맵(버전 3.1.2), 브라우저 앱에서의 HTML5 지원, 라이브 배경화면, 블루투스 2.1 지원이 포함됐다.

구글은 이어서 2.0.1 버전을 2009년 12월 3일날 출시했고, 2010년 1월 12일 SDK 2.1 업데이트를 선보였다.

2.2 (프로요) 리눅스 커널 2.6.32 기반

구글은 2009년 5월 20일 SDK 2.2를 출시했다. 이 버전에서는 크롬의 V8 자바스크립트 엔진이 브라우저 앱에 포함됐으며, 음성을 통한 전화걸기 기능, 블루투스를 통한 연락처 공유, 어도비 플래시 10.1 지원, JIT 구현체를 통한 앱 속도의 추가 개선, USB 테더링, 와이파이 핫스팟 기능이 추가됐다.

2.3 (진저브레드) 리눅스 커널 2.6.35.7 기반

구글은 2010년 12월 6일 SDK 2.3을 배포했다. 이 버전에서는 앱의 응답 속도를 빠르게 해주는 새로운 동시성 가비지 컬렉터가 추가됐고, 자이로스코프 센서 지원, WebM 동영상 재생 지원 및 기타 동영상 기능 개선, 근거리 통신 지원, 소셜 네트워크 연동 기능 개선 등이 추가됐다. 이 책은 안드로이드 2.3에 초점을 맞춘다.

구글은 이어서 버그가 수정된 SDK 2.3.1을 출시했고 안드로이드 2.3 플랫폼에 작은 기능을 추가하고 몇 가지 기능을 개선한 SDK 2.3.3을 출시했다.

3.0 (허니컴) 리눅스 커널 2.6.36 기반

구글은 2011년 2월 22일 SDK 3.0을 내놓았다. 이전 출시 버전과 달리 3.0 버전은 처음으로 출시된 안드로이드 태블릿(2011년 2월 24일 출시)인 모토롤라 줌 같은 태블릿만을 대상으로 한다. 이 버전에서는 개선된 UI와 더불어 멀티태스킹이 강화되고 멀티코어 프로세서 지원, 하드웨어 가속 지원 기능이 추가됐으며, 디자인이 전면 수정된 위젯을 활용한 바탕화면이 제공된다.

Page 28: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

4 l 안드로이드 레시피

안드로이드 아키텍처

안드로이드 스택은 최상위에 앱이 있고, 이어서 미들웨어(애플리케이션 프레임워크, 라이브러리,

안드로이드 런타임으로 구성)가 중간에 있으며 다양한 드라이버와 함께 리눅스 커널이 가장 아

래에 있다. 그림 1-1은 이런 계층화된 아키텍처를 보여준다.

플래시 메모리 드라이버 키패드 드라이버 전원 관리 와이파이 드라이버

애플리케이션

홈 인터넷 주소록 휴대전화 ...

애플리케이션 프레임워크

액티비티 관리자 콘텐츠 프로바이더 위치 관리자 알림 관리자

패키지 관리자 리소스 관리자 전화 관리자 뷰 시스템 창 관리자

라이브러리 안드로이드 런타임

코어 라이브러리

달빅 가상 머신

리눅스 커널

안드로이드 드라이버 바인더 (IPC) 드라이버 카메라 드라이버 디스플레이 드라이버

FreeType libc LibWebCore

Media Framework OpenGL | ES SGL

SQLite SSL Surface Manager

그림 1-1 다양한 주요 요소로 구성된 안드로이드의 계층화된 아키텍처

사용자들은 주로 앱에 관심이 있다. 안드로이드는 인터넷, 주소록, 휴대전화 같은 핵심 앱을 비

롯해 다양한 앱을 기본 제공한다. 이들 앱은 모두 자바 프로그래밍 언어로 작성돼 있다. 안드로이

드 아키텍처에서 앱은 최상위 계층을 형성한다.

앱 계층 바로 아래에는 애플리케이션 프레임워크가 있다. 애플리케이션 프레임워크는 앱을 개

발하는 데 필요한 고수준 컴포넌트들로 이루어져 있다. 애플리케이션 프레임워크는 안드로이드

기기에 미리 설치돼 있으며 다음과 같은 컴포넌트로 구성된다.

Page 29: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 5

■ 액티비티 관리자: 이 컴포넌트는 앱의 생명주기를 제공하며 앱과 앱 사이 또는 앱 내에서

이동할 때 필요한 공유 액티비티 스택을 관리한다. 두 주제 모두 이 장에서 나중에 자세히

살펴볼 것이다.

■ 콘텐츠 프로바이더: 이 컴포넌트는 데이터(브라우저 앱의 즐겨찾기 등)를 앱 사이에서 공

유할 수 있게 캡슐화한다.

■ 위치 관리자: 이 컴포넌트는 안드로이드 기기에서 물리적인 위치를 알 수 있게 해준다.

■ 알림 관리자: 이 컴포넌트는 사용자가 현재 하는 일을 방해하지 않고 중요한 이벤트(메시

지 도착 등)를 사용자에게 알려줄 수 있게 해준다.

■ 패키지 관리자: 이 컴포넌트는 현재 기기에 설치된 다른 앱 패키지에 대해 앱이 알 수 있게

해준다(앱 패키지는 이 장에서 나중에 자세히 살펴본다).

■ 리소스 관리자: 이 컴포넌트는 앱이 자원에 접근할 수 있게 해준다. 이 주제는 1-5 레시피

에서 간단히 다룬다.

■ 전화 관리자: 이 컴포넌트는 앱이 기기의 전화 서비스에 대해 알 수 있게 해준다. 또 전화

를 걸고 받는 일도 처리한다.

■ 뷰 시스템: 이 컴포넌트는 사용자 인터페이스 요소를 관리하고 사용자 인터페이스 중심

의 이벤트를 생성한다(이 주제는 레시피 1-5에서 간단히 살펴본다).

■ 창 관리자: 이 컴포넌트는 화면 공간을 창으로 조직화해 드로잉 영역을 할당하고 기타 창

과 관련한 작업을 수행한다.

애플리케이션 프레임워크의 컴포넌트는 C/C++ 라이브러리에 의존해 작업을 수행한다. 개발

자들은 프레임워크 API를 통해 다음 라이브러리와 연동할 수 있다.

■ FreeType: 이 라이브러리는 비트맵과 벡터 폰트 렌더링을 지원한다.

■ libc: 이 라이브러리는 임베디드 리눅스 기반 기기에 맞춰 표준 C 시스템 라이브러리를

BSD 파생 구현체로 구현한 것이다.

■ LibWebCore: 이 라이브러리는 현대적이고 빠른 웹 브라우저 엔진을 제공해 안드로이드

브라우저 및 임베디드 웹 뷰의 기반이 된다. 이 라이브러리는 웹킷(http://en.wikipedia.

org/wiki/WebKit) 기반이며 구글 크롬과 애플 사파리 브라우저에서도 사용 중이다.

Page 30: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

6 l 안드로이드 레시피

■ 미디어 프레임워크: 이 라이브러리들은 PacketVideo의 오픈코어를 기반으로 하며 인기

있는 다양한 오디오 및 동영상 형식의 녹음, 녹화 및 재생과 스틸 이미지 형식을 지원한

다. 지원하는 형식에는 MPEG4, H.264, MP3, AAC, AMR, JPEG, PNG가 있다.

■ 오픈지엘 / ES: 이들 3D 그래픽 라이브러리는 오픈지엘/ES 1.0 API를 기반으로 한 오픈지

엘(OpenGL) 구현체를 제공한다. 이 라이브러리는 3D 가속(사용 가능한 경우) 또는 포함

된(고도로 최적화된) 3D 소프트웨어 래스터라이저를 사용한다.

■ SGL: 이 라이브러리는 내부 2D 그래픽 엔진을 제공한다.

■ SQLite: 이 라이브러리는 모든 앱에서 사용할 수 있는 강력한 성능의 경량 관계형 데이터

베이스를 제공한다. 이 라이브러리는 모질라 파이어폭스와 애플의 아이폰에서도 영속성

데이터를 저장하는 데 사용한다.

■ SSL: 이 라이브러리는 네트워크 통신에 보안 소켓 계층 기반(SSL 기반)의 보안을 제공

한다.

■ 서피스 관리자(Surface Manager): 이 라이브러리는 모든 디스플레이 하위 시스템에 대

한 접근을 관리하고 여러 앱의 2D 및 3D 그래픽 계층이 자연스럽게 합성되도록 해준다.

안드로이드는 코어 라이브러리로 구성된 런타임 환경(아파치 하모니 자바 5 버전 구현체의 서

브셋을 구현)과 스택 기반이 아닌 프로세서 레지스터 기반의 비자바(non-Java) 가상 머신인 달

빅 가상 머신(DVM)을 제공한다.

알아두기

구글의 댄 본스테인은 달빅 가상 머신을 만들고 자신의 조상들이 살던 아이슬란드 어촌의 이

름을 따 이 가상 머신의 이름을 정했다.

기본적으로 각 안드로이드 앱은 한 개의 달빅 가상 머신을 갖춘 자체 리눅스 프로세스에서 실

행된다. 이 가상 머신은 기기에서 여러 가상 머신을 효과적으로 실행할 수 있게끔 설계됐다. 달빅

가상 머신이 이렇게 효과적으로 실행될 수 있는 이유는 상당 부분 달빅이 달빅 실행파일(DEX)

기반의 파일을 실행하는 데 따른 효과 때문이다. DEX 형식은 최소한의 메모리 족적을 남기도록

최적화된 형식이다.

Page 31: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 7

알아두기

안드로이드는 앱의 특정 부분에서 실행을 요청하면 새 프로세스를 시작하고 더 이상 실행되지

않거나 다른 앱에서 시스템 자원을 필요로 하는 경우 해당 프로세스를 종료한다.

아마 독자들은 비자바 가상 머신이 어떻게 자바 코드를 실행할 수 있는지 궁금할 것이다. 이에

대한 답은 달빅이 자바 코드를 실행하지 않는다는 것이다. 대신 안드로이드는 컴파일된 자바 클

래스 파일을 DEX 형식으로 변형하고, 이 결과 코드가 달빅에서 실행된다.

끝으로 이들 라이브러리와 안드로이드 런타임은 리눅스 커널(버전 2.6)에 의존해 스레딩, 저수

준 메모리 관리, 네트워크 스택, 프로세스 관리, 드라이버 모델 같은 내부 코어 서비스를 관리한

다. 더불어 이런 리눅스 커널은 하드웨어와 나머지 소프트웨어 스택 사이에서 추상 계층 역할을

한다.

안드로이드 보안 모델

안드로이드 아키텍처는 앱이 다른 앱이나 리눅스, 사용자에게 해를 끼칠 수 있는 행동을

하는 것을 막기 위한 보안 모델을 갖고 있다. 이 보안 모델은 표준 리눅스 기능(사용자와

그룹 ID)을 활용한 프로세스 레벨 강화를 주 기반으로 해 프로세스를 보안 샌드박스에 배

정한다.

기본적으로 샌드박스는 앱이 사용자의 개인 정보 데이터(예를 들어 연락처나 이메일)를 읽

고 쓰는 행위, 다른 앱의 파일을 읽고 쓰는 행위, 네트워크에 접근하는 행위, 기기가 깨어

있게 하는 행위, 카메라에 대한 접근 행위 등을 차단한다. 네트워크에 접근해야 하거나 기

타 민감한 작업을 수행해야 하는 앱은 먼저 이렇게 할 수 있는 퍼미션을 얻어야 한다.

안드로이드는 다양한 방식으로 퍼미션을 처리하는데, 보통 인증서를 기반으로 자동으로

요청을 승인 또는 거부하거나 사용자가 퍼미션을 승인 또는 거부할 수 있게 알림창을 띄운

다. 앱에서 필요한 퍼미션은 앱이 설치된 후 안드로이드 시스템에서 알 수 있게 앱의 매니

페스트 파일에 선언한다(메니페스트 파일은 이 장에서 나중에 다룬다). 이들 퍼미션은 앞

으로도 바뀌지 않을 것이다.

Page 32: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

8 l 안드로이드 레시피

앱 아키텍처

안드로이드 앱의 아키텍처는 데스크톱 응용프로그램의 아키텍처와는 다르다. 앱 아키텍처는 메

니페스트에서 선언한 인텐트를 활용해 앱 패키지 내에 들어 있는 다른 컴포넌트와 통신할 수 있

는 컴포넌트를 기반으로 한다.

컴포넌트

앱은 리눅스 프로세스에서 실행되고 안드로이드에서 관리하는 컴포넌트(액티비티, 서비스, 콘텐

츠 프로바이더, 브로드캐스트 리시버 등)의 집합이다. 이들 컴포넌트는 데이터베이스, 사용자 환

경설정, 파일시스템, 리눅스 프로세스 등 여러 리소스를 공유한다.

알아두기

이들 컴포넌트를 모두 앱에 사용할 필요는 없다. 예를 들어 어떤 앱은 액티비티로만 이뤄진 반

면 다른 앱은 액티비티와 서비스로 구성될 수도 있다.

이런 컴포넌트 중심의 아키텍처는 다른 앱에서 컴포넌트 사용을 허용한 경우 다른 앱의 컴포

넌트를 앱에서 재사용할 수 있게 해준다. 컴포넌트를 재사용하면 제한된 메모리 탓에 전체 메모

리 사용량이 매우 중요한 모바일 기기에서 메모리 사용량 자체를 크게 줄일 수 있다.

재사용 개념을 보다 분명히 이해하기 위해 사용자가 색상 팔레트에서 색상을 선택할 수 있는

드로잉 앱을 여러분이 만들었다고 가정하고, 또 다른 앱에서 쓸만한 색상 팔레트를 개발해 다른

앱이 사용할 수 있게 허용했다고 가정하자. 이 경우 드로잉 앱에서는 직접 색상 선택 팔레트를 제

공하는 대신 다른 앱의 색상 팔레트 선택 컴포넌트를 호출해 사용자가 색상을 선택하게 할 수 있

다. 이때 드로잉 앱에서는 색상 팔레트를 포함할 필요도 없으며 다른 앱에 대한 링크를 갖고 있을

필요도 없다. 그냥 필요할 때마다 다른 앱의 색상 선택 팔레트 컴포넌트를 요청하면 된다.

안드로이드는 어느 부분이 됐든 (앞에서 말한 색상 팔레트의 경우처럼) 앱의 기능이 필요할 때

마다 프로세스를 시작하고 이 부분에 대한 자바 객체를 생성한다. 바로 이런 점 때문에 안드로이

드 앱은 단일 시작점이 별도로 없다(이를테면 C 언어의 main() 함수 같은 게 없다). 대신 앱은 인

스턴스가 생성된 컴포넌트를 사용하며 필요할 때마다 실행된다.

Page 33: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 9

액티비티

액티비티는 사용자가 앱과 상호작용할 수 있는 UI를 제공하는 컴포넌트다. 예를 들어 안드로이

드의 주소록 앱은 새로운 연락처를 집어넣을 수 있는 액티비티를 포함하며, 휴대전화 앱은 전화

번호로 전화를 걸 수 있는 액티비티를 포함하고, 계산기 앱은 기본 계산을 수행할 수 있는 액티

비티를 포함한다(그림 1-2 참고).

그림 1-2. 안드로이드 계산기 앱의 메인 액티비티에서는 사용자가 기본 계산을 할 수 있다.

앱에는 한 개의 액티비티만 들어 있을 수도 있지만, 보통은 여러 액티비티가 들어 있다. 예를 들

어 계산기 앱에는 제곱근, 삼각함수 연산, 기타 고급 수학 연산을 할 수 있는 고급 패널 액티비티

가 추가로 들어 있다.

서비스

서비스는 백그라운드에서 무한히 실행되는 컴포넌트로, UI를 제공하지 않는다. 액티비티와 마찬

가지로 서비스도 프로세스의 메인 스레드에서 실행된다. 서비스에서 시간이 오래 걸리는 작업을

할 때는 별도의 스레드를 열어야 한다. 서비스는 로컬 서비스와 원격 서비스로 구분된다.

■ 로컬 서비스는 나머지 앱과 같은 프로세스에서 실행된다. 이런 서비스는 백그라운드 작

업을 구현하기 쉽게 해준다.

■ 원격 서비스는 별도의 프로세스에서 실행된다. 이런 서비스를 활용하면 프로세스 간 통

신을 수행할 수 있다.

Page 34: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

10 l 안드로이드 레시피

알아두기

서비스는 별도의 프로세스가 아니지만 별도 프로세스에서 실행되도록 지정할 수는 있다. 더불

어 서비스는 스레드가 아니다. 서비스는 (사용자가 앱을 직접 사용하지 않는 동안에도) 백그라

운드에서 한 일을 안드로이드 시스템에게 알려주는 일을 하거나 앱의 기능 일부를 다른 앱에

게 노출하는 일을 할 뿐이다.

사용자가 액티비티를 통해 선택한 음악에 따라 음악을 재생하는 서비스를 생각해 보자. 사용

자가 액티비티를 통해 노래를 선택하면, 사용자의 선택에 반응해 서비스가 시작된다. 그리고 이

서비스에서는 애플리케이션의 응답 없음 대화상자(부록 C에서 설명)가 나타나지 않게끔 다른

스레드를 통해 음악을 재생한다.

알아두기

여기서 서비스를 사용해 음악을 재생한 이유는 음악 재생을 시작한 액티비티 화면에서 사용자

가 나가더라도 사용자는 계속해서 음악을 듣고 싶어 하기 때문이다.

브로드캐스트 리시버

브로드캐스트 리시버는 브로드캐스트를 수신해 반응할 수 있는 컴포넌트다. 많은 브로드캐스

트는 시스템 코드에서 내보낸다. 예를 들어 타임존이 바뀌거나 배터리가 약할 때는 이를 알려주

기 위한 안내 브로드캐스트가 전송된다.

하지만 앱에서도 브로드캐스트를 보낼 수 있다. 예를 들어 네트워크에서 데이터 다운로드를

마치면 한 앱에서 다른 앱으로 브로드캐스트를 전송해 다른 앱에서 다운로드 기능을 사용하게

할 수 있다.

콘텐츠 프로바이더

콘텐츠 프로바이더는 앱의 특정 데이터를 다른 앱에서 사용할 수 있게 해주는 컴포넌트다. 이런

데이터는 안드로이드 파일 시스템이나 SQLite 데이터베이스, 기타 다른 적당한 방식으로 저장할

수 있다.

Page 35: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 11

콘텐츠 프로바이더는 로(raw) 데이터에 직접 접근하는 게 좋다. 이렇게 하면 컴포넌트 코드와

로 데이터 형식 사이의 결합이 느슨해지기 때문이다. 이런 느슨한 결합을 활용하면 형식이 바뀌

더라도 코드가 오작동하는 것을 막을 수 있다.

인텐트

인텐트는 수행할 작업을 설명하는 메시지(예를 들어 ‘이메일을 전송’ 또는 ‘사진을 선택’ 등)이면

서 브로드캐스트의 경우 발생한 외부 이벤트에 대한 설명(기기의 카메라가 활성화되는 등의 이

벤트)을 제공하는 역할도 한다.

안드로이드에서는 거의 모든 내용에 인텐트가 포함되므로 이를 활용해 기존 컴포넌트를 얼마

든지 커스텀 컴포넌트로 대체할 수 있다. 예를 들어 안드로이드에서는 이메일 전송과 관련한 인

텐트를 제공한다. 여러분의 앱에서는 표준 메일 앱을 활성화하도록 인텐트를 전달할 수도 있고

‘메일 전송’ 인텐트에 반응하도록 액티비티를 등록해 표준 메일 앱 대신 여러분의 액티비티를 사

용하게 할 수도 있다.

이들 메시지는 android.content.Intent 클래스 인스턴스로 구현한다. Intent 객체는 다음 항목

들을 조합해 메시지를 설명한다.

■ 액션: 수행할 액션을 나타내는 문자열 또는 브로드캐스트 인텐트의 경우 일어나고

있거나 현재 보고된 동작을 나타낸다. 액션은 ACTION_CALL(전화 걸기를 시작),

ACTION_EDIT(사용자가 편집할 데이터를 표시), ACTION_MAIN(새 액티비티를 시

작) 같은 Intent 클래스의 상수를 사용해 지정할 수 있다. 또 앱에서 컴포넌트를 활성화하

는 액션 문자열도 직접 지정할 수 있다. 이런 문자열은 앱 패키지를 접두어로 포함해야 한

다(예를 들어 com.example.project.SELECT_COLOR 등).

■ 카테고리: 인텐트를 처리할 컴포넌트의 종류에 대한 추가 정보를 제공하는 문자열이다.

예를 들어 CATEGORY_LAUNCHER는 호출하는 액티비티가 기기의 앱 론처에서 최고

레벨 앱으로 표시돼야 함을 나타난다(앱 론처는 레시피 1-4에서 간단히 살펴본다).

■ 컴포넌트 이름: 인텐트에 사용할 컴포넌트 클래스의 전체 클래스명(패키지명에 클래스명

을 합친 이름)을 지정한 문자열이다. 컴포넌트 이름은 선택 사항이다. 컴포넌트 이름을 설

정하면 Intent 객체가 지정된 클래스의 인스턴스로 전달된다. 컴포넌트 이름을 설정하지

않으면 안드로이드는 Intent 객체의 다른 정보를 사용해 적당한 타깃을 찾는다.

Page 36: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

12 l 안드로이드 레시피

■ 데이터: 처리할 데이터에 대한 공통 형식의 리소스 식별자이다(예를 들어 주소록 데이터

베이스의 사람 레코드).

■ Extras: 인텐트를 처리하는 컴포넌트에 전달할 추가 정보를 키-값 쌍 형태로 보관한다. 예

를 들어 이메일을 전송하는 액션의 경우 이 정보에는 메시지의 제목, 본문 등이 들어갈

수 있다.

■ 플래그: 액티비티를 어떻게 실행할지(예를 들어 액티비티가 어떤 작업(task)에 속하는

지 등. 작업(task)은 이 장에서 나중에 살펴본다) 또는 실행 후 어떻게 액티비티를 처리할

지(예를 들어 액티비티를 최근 실행한 액티비티로 간주할지 등) 안드로이드에게 알려주

는 비트 값이다. 플래그 값은 Intent 클래스의 상수를 통해 지정한다. 예를 들어 FLAG_

ACTIVITY_NEW_TASK 상수는 이 액티비티가 현재 액티비티 스택에서 새로운 작업을

시작함을 나타낸다. 액티비티 스택은 이 장에서 나중에 살펴본다.

■ 타입: 인텐트 데이터의 MIME 타입. 보통 안드로이드는 데이터를 통해 타입을 유추한다.

타입을 지정하면 이와 같은 안드로이드의 기본 타입 유추 기능이 비활성화된다.

인텐트는 명시적인 인텐트와 암시적인 인텐트로 나눌 수 있다. 명시적인 인텐트는 이름을 통해

대상 컴포넌트를 지정한다(앞에서 말한 컴포넌트 이름 항목에 값을 대입한다). 컴포넌트 이름은

보통 다른 앱의 개발자들이 알지 못하는 게 대부분이므로 명시적인 인텐트는 주로 앱 내부 메시

지에 사용된다(예를 들어 같은 앱에 들어 있는 다른 액티비티를 실행하는 등). 안드로이드는 지

정된 대상 클래스 인스턴스로 명시적인 인텐트를 전달한다. 어떤 컴포넌트가 인텐트를 처리할지

결정하는 과정에서는 Intent 객체의 컴포넌트 이름만이 판단 기준이 된다.

암시적인 인텐트는 대상을 지정하지 않는다(컴포넌트 이름에 값을 지정하지 않는다). 암시적인

인텐트는 주로 다른 앱의 컴포넌트를 시작하는 데 사용된다. 안드로이드는 가장 적합한 컴포넌

트(요청한 행동을 수행할 수 있는 단일 액티비티 또는 서비스)를 검색하거나 여러 컴포넌트를 찾

아(브로드캐스트에 반응할 브로드캐스트 리시버들) 암시적인 인텐트를 처리한다. 검색 과정에

서 안드로이드는 Intent 객체의 내용을 인텐트를 받을 수 있는 컴포넌트의 인텐트 필터 및 매니

페스트 정보와 비교한다.

필터는 컴포넌트의 기능을 외부에 알려주며 컴포넌트에서 처리할 수 있는 인텐트만을 식별할

수 있게 해준다. 필터를 통해 컴포넌트는 자신이 처리할 수 있다고 외부에 선언한 타입에 해당하

는 암시적인 인텐트를 받을 수 있게 된다. 컴포넌트에 인텐트 필터가 없는 경우 컴포넌트는 명시

Page 37: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 13

적인 인텐트만을 받을 수 있다. 하지만 컴포넌트에 필터를 적용하면 명시적 인텐트와 암시적 인텐

트를 모두 받을 수 있다. 안드로이드는 인텐트와 인텐트 필터의 내용을 서로 비교할 때 Intent 객

체의 액션, 카테고리, 데이터, 타입을 참고한다. 이 경우 extras와 플래그는 고려 대상이 아니다.

매니페스트

안드로이드는 앱의 XML 구조 매니페스트 파일인 AndroidManifest.xml을 통해 앱에 들어 있

는 다양한 컴포넌트에 대한 정보를 인식한다. 예를 들어 예제 1-1은 이런 매니페스트 파일을 사

용해 액티비티 컴포넌트를 선언하는 법을 보여준다.

예제 1-1 | 액티비티를 선언한 매니페스트 파일

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.example.project" android:versionCode="1"

android:versionName="1.0">

<application android:label="@string/app_name" android:icon="@drawable/icon">

<activity android:name=".MyActivity" android:label="@string/app_name">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

</application>

</manifest>

예제 1-1은 필수 프롤로그인 <?xml version="1.0" encoding="utf-8"?>로 시작한다. 이 태그는

이 파일의 XML 버전이 1.0이고 내용은 UTF-8 인코딩 표준을 따르고 있음을 나타낸다.

예제 1-1에서는 이어서 <manifest> 태그가 나온다. 이 태그는 XML 문서의 루트 요소다.

android는 안드로이드 네임스페이스를 나타내고, package는 앱의 자바 패키지, versionCode/

versionName은 버전 정보를 나타낸다.

<manifest> 태그 안에는 <application>이 들어 있다. 이 태그는 앱 컴포넌트 태그의 부모 태그

다. icon과 label 어트리뷰트는 안드로이드에서 앱을 나타낼 때 사용할 아이콘과 라벨 리소스를

가리킨다(리소스에 대한 내용은 레시피 1-5에서 간단히 살펴본다).

Page 38: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

14 l 안드로이드 레시피

알아두기

리소스는 @ 접두어 다음에 리소스 카테고리명(예를 들어 string 또는 drawable), /, 리소스

ID(예를 들어 app_name 또는 icon) 순으로 나타낸다.

<application> 태그의 icon과 label 어트리뷰트는 이들 어트리뷰트를 지정하지 않는 컴포넌

트 태그에서 자동으로 상속받을 기본값을 지정한다.

<application> 태그 안에는 액티비티 컴포넌트에 대해 기술한 <activity> 태그가 있다. 이 태

그의 name 어트리뷰트는 액티비티를 구현하는 클래스(MyActivity 등)를 나타낸다. 이 클래스

이름은 점으로 시작하는데 이는 클래스 경로가 com.example.project를 기준으로 함을 뜻한다.

알아두기

명령행에서 AndroidManifest.xml을 생성할 때는 점이 들어 있지 않다. 하지만 이클립스에

서 이 파일을 생성할 때는 점 문자가 자동 포함된다(레시피 1-10에서 설명). 하지만 이와 상

관없이 MyActivity는 <manifest>의 패키지 값(com.example.project)을 기준으로 한다.

<activity> 안에는 <intent-�lter> 태그가 들어 있다. 이 태그는 이 태그를 감싸고 있는 컴포넌

트의 기능을 선언하는 태그다. 예를 들어 이 태그는 안에 <action>과 <category> 태그를 사용해

액티비티 컴포넌트의 기능을 선언한다.

■ <action>은 수행할 액션을 나타낸다. 이 태그의 android:name 어트리뷰트를 android.

intent.action.MAIN으로 지정하면 액티비티가 앱의 시작점이 된다.

■ <category>는 컴포넌트 카테고리를 나타낸다. 여기서는 이 태그의 android:name 어트리

뷰트를 android.intent.category.LAUNCHER로 지정해 앱 론처에서 보여줄 액티비티를

지정했다.

Page 39: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 15

알아두기

다른 컴포넌트도 비슷하게 선언한다. 예를 들어 서비스는 <service> 태그를 사용해 선언하고,

브로드캐스트 리시버는 <receiver> 태그로, 콘텐츠 프로바이더는 <provider> 태그로 선언한

다. 런타임 시점에 생성될 수 있는 브로드캐스트 리시버를 제외하면 매니페스트에서 선언하지

않은 컴포넌트들은 안드로이드에서 생성하지 않는다.

매니페스트에는 앱에 필요한 <uses-permission> 태그를 포함시킬 수도 있다. 예를 들어 카메

라를 사용해야 하는 앱은 <uses-permission android:name="android.permission.CAMERA"

/> 태그를 지정하면 된다.

알아두기

<uses-permission> 태그는 <manifest> 태그 안에 있어야 한다. 이 태그는 <application>

태그와 같은 레벨에 위치한다.

앱 설치 시점에 (<uses-permission>을 통해) 앱에서 요청하는 퍼미션은 이들 퍼미션을 선언한

앱의 디지털 서명 검사 및/또는 사용자와의 상호작용을 거쳐 안드로이드의 패키지 인스톨러에

의해 승인된다.

앱이 실행되는 시점에는 사용자에게 확인을 거치지 않는다. 퍼미션은 설치 시점에 승인돼 해당

기능을 사용할 수 있게 되거나, 승인되지 않을 경우 사용자에게 다시 확인하는 절차 없이 해당

기능을 사용하지 못하게 된다.

알아두기

AndroidManifest.xml에는 앱에서 (기본 안드로이드 라이브러리 외에) 링크해야 할 라이브

러리 이름과 앱의 액티비티를 시작할 수 있는 대상을 제어하기 위해 다른 앱에 강제하는 퍼미

션(<permission> 태그)과 같은 추가 정보도 제공한다.

Page 40: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

16 l 안드로이드 레시피

앱 패키지

안드로이드 앱은 자바로 작성한다. 앱의 컴포넌트를 작성한 자바 코드가 컴파일되면, 이 결과는

다시 달빅의 DEX 형식으로 변형된다. 그 결과 코드 파일과 기타 필수 데이터 및 리소스는 이후

앱 패키지(APK) 번들로 묶인다. 이 파일은 .apk 확장자를 갖고 있다.

APK는 앱이 아니다. 다만 앱을 배포하고 모바일 기기에 설치하는 데 사용될 뿐이다. APK가

앱이 아닌 이유는 APK에 들어 있는 컴포넌트가 다른 APK의 컴포넌트를 재사용할 수도 있는데,

이 경우 앱의 내용이 하나의 APK 안에 모두 들어 있지 않기 때문이다. 하지만 보통은 APK와 앱

을 거의 같다고 본다.

APK는 (앱의 저작자를 인증하는) 인증서를 사용해 서명해야 한다. 이 서명의 비밀 키는 개발

자가 갖고 있다. 인증서는 인증 기관을 통해 서명받지 않아도 된다. 대신 안드로이드에서는 자기

서명 인증서를 통해 APK를 서명할 수 있게 해준다(APK 서명은 레시피 1-8에서 다룬다).

APK 파일, 사용자 ID, 보안

안드로이드 기기에 설치된 각 APK에는 고유 리눅스 사용자 ID가 주어진다. 이 ID는 APK

가 해당 기기에 설치돼 있는 한 바뀌지 않는다.

보안 제약은 프로세스 레벨에서 일어나므로 두 APK에 들어 있는 코드는 보통 같은 프로세

스에서 실행될 수 없다. 왜냐하면 각 APK의 코드가 서로 다른 리눅스 사용자로 실행되기

때문이다.

하지만 각 APK의 AndroidManifest.xml 파일에서 <manifest> 태그의 sharedUserId에

같은 사용자 ID를 지정하면 두 APK의 코드가 같은 프로세스에서 실행될 수 있다.

이와 같이 값을 지정하면 안드로이드에서는 두 패키지를 같은 사용자 ID와 파일 퍼미션을

갖고 있는 동일한 앱으로 처리한다.

보안을 유지하기 위해 같은 서명을 사용한(더불어 매니페스트에서 같은 sharedUserId 값

을 요청한) 두 개의 APK에 대해서만 동일한 사용자 ID가 부여된다.

Page 41: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 17

액티비티 자세히 살펴보기

액티비티는 추상 클래스인 android.content.Context 클래스의 간접 하위 클래스인 android.

app.Activity 클래스의 하위 클래스를 통해 지정한다.

알아두기

Context는 앱에서 자신의 환경에 대한 전역 정보(리소스와 파일시스템 등)에 접근할 수 있게

해주는 메서드를 갖고 있는 추상 클래스로서, 앱이 액티비티와 서비스를 시작하고, 인텐트를

전달하며, 내부 파일을 여는 등 현재 맥락에서 할 수 있는 작업들을 하게끔 해준다.

Activity 하위 클래스는 안드로이드에서 액티비티의 생명주기 동안 호출하는 다양한 액티비티

생명주기 콜백함수를 오버라이드한다. 예를 들어 예제 1-2의 SimpleActivity 클래스는 Activity

를 상속하고 void onCreate(Bundle bundle)와 void onDestroy() 생명주기 콜백 메서드를 오버

라이드한다.

예제 1-2 | 액티비티의 기본 예제

import android.app.Activity;

import android.os.Bundle;

public class SimpleActivity extends Activity

{

@Override

public void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState); // 항상 상위 클래스의 메서드를 먼저 호출

System.out.println("onCreate(Bundle) called");

}

@Override

public void onDestroy()

{

super.onDestroy(); // 항상 상위 클래스의 메서드를 먼저 호출.

System.out.println("onDestroy() called");

}

}

Page 42: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

18 l 안드로이드 레시피

예제 1-2에서 오버라이드하는 onCreate(Bundle)와 onDestroy() 메서드는 먼저 부모 클래스

의 해당 메서드를 호출한다. 이런 패턴은 void onStart(), void onRestart(), void onResume(),

void onPause(), void onStop() 같은 생명주기 콜백 메서드에서 항상 따라야 한다.

■ onCreate(Bundle)는 액티비티가 처음 생성될 때 호출된다. 이 메서드는 액티비티의 UI를

생성하고, 필요에 따라 백그라운드 스레드를 생성하며 기타 전역으로 사용할 초기화 작

업을 수행한다. onCreate() 메서드는 이전 상태가 있는 경우 액티비티의 이전 상태를 담

고 있는 android.os.Bundle 객체를 인자로 받는다. 이전 상태가 없을 때는 인자로 널 참

조가 전달된다. 안드로이드에서는 항상 onCreate(Bundle)를 호출한 후 onStart() 메서드

를 호출한다.

■ onStart()는 액티비티가 사용자에게 보이기 전에 호출된다. 안드로이드는 액티비티가 전

경으로 나갈 때 onStart()를 호출한 후 이어서 onResume() 메서드를 호출한다. 또 액티

비티가 숨겨질 때는 onStart()를 호출한 후 onStop()을 호출한다.

■ onRestart()는 액티비티가 중단된 후 다시 시작하기 전에 호출된다. 안드로이드는

onRestart()를 호출한 후 항상 onStart()를 호출한다.

■ onResume()은 액티비티가 사용자와 상호작용을 시작하기 전에 호출된다. 이 시점에

서 액티비티는 포커스를 갖고 있으며 사용자 입력은 액티비티로 전달된다. 안드로이드는

onResume() 다음에 onPause() 메서드를 호출하는데, 이 메서드는 액티비티가 일시 정지

할 때만 호출된다.

■ onPause()는 안드로이드가 다른 액티비티의 실행을 재개하려고 할 때 호출된다. 이 메서

드는 보통 저장되지 않은 변경사항을 저장하고, 프로세서에 부담을 줄 수 있는 애니메이

션을 중단하는 등의 일을 하는 데 사용된다. 다음 메서드는 이 메서드가 실행을 마치기

전까지는 실행을 재개하지 못하므로 이 메서드에서 수행하는 작업은 빠른 시간 내에 끝

나야 한다. 안드로이드는 onPause()에 이후 액티비티가 사용자와의 상호작용을 시작할

때 onResume()을 호출하며, 액티비티가 사용자에게 안 보일 때 onStop()을 호출한다.

■ onStop()은 액티비티가 더 이상 사용자에게 보이지 않을 때 호출된다. 이런 일은 액티비

티가 소멸되거나 다른 액티비티(기존 액티비티나 새 액티비티)가 실행을 재개해 현재 액티

비티보다 위에 올라올 때 일어날 수 있다. 안드로이드는 onStop() 이후 액티비티가 사용

자와의 상호작용을 하기 위해 다시 돌아올 때 onRestart()를 호출하고, 액티비티가 소멸

될 때는 onDestroy() 메서드를 호출한다.

Page 43: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 19

■ onDestroy()는 메모리가 부족해 안드로이드에서 액티비티의 프로세스를 강제로 죽여야

하는 경우를 제외하고 액티비티가 소멸되기 전에 호출된다. 액티비티 프로세스가 메모리

부족으로 인해 강제로 중단될 때는 onDestroy()가 호출되지 않는다. onDestroy()가 호

출되는 시점은 액티비티가 마지막으로 생명주기 메서드를 호출받는 시점이다.

알아두기

안드로이드는 onPause(), onStop(), onDestroy() 이후 언제든 액티비티를 실행하는 프로세

스를 죽일 수 있다. 액티비티는 onPause()가 실행을 마친 후부터 onResume()이 호출되기

전까지 언제든 죽을 수 있는 상태에 놓인다. onResume()이 호출된 후에는 다시 onPause()

가 실행을 마치기 전까지 액티비티는 죽을 수 있는 상태를 벗어난다.

앞의 7가지 메서드는 액티비티의 전체 생명주기를 주관하며 다음과 같은 세 가지 중첩 순환 주

기를 정의한다.

■ 액티비티의 전체 수명은 onCreate(Bundle)를 처음으로 호출한 시점부터 이 메서드에

대응되는 onDestroy()를 마지막으로 한 번 호출하는 때까지로 정의한다. 액티비티는

onCreate(Bundle)에서 ‘전역’ 상태의 초기 설정을 모두 수행하며 onDestroy()에서는 남

은 리소스를 모두 반환한다. 예를 들어 액티비티가 백그라운드에서 실행되는 스레드를

사용해 네트워크에서 데이터를 내려받는다면 onCreate(Bundle)에서 이 스레드를 생성

하고 onDestroy()에서는 이 스레드를 중단한다.

■ 가시적인 액티비티의 수명은 onStart()를 호출한 시점부터 이 메서드에 대응되는

onStop()을 호출하는 시점까지다. 이 시간 동안 사용자는 화면상에서 액티비티를 볼 수

있다(물론 사용자가 직접 전경의 액티비티와 상호작용하지 못할 수도 있다). 두 메서드 사

이에서 액티비티는 사용자에게 액티비티를 보여주는 데 필요한 리소스를 유지할 수 있다.

예를 들어 액티비티는 onStart()에서 브로드캐스트 리시버를 등록해 사용자 인터페이스

에 영향을 주는 변화를 모니터링할 수도 있고, 사용자가 액티비티를 더 이상 볼 수 없는

onStop()에서 이 객체의 등록을 해제할 수 있다. onStart()와 onStop() 메서드는 사용자

에게 액티비티가 나타나고 숨겨짐에 따라 여러 차례 호출될 수 있다.

■ 액티비티가 전경에 있는 시간은 onResume()을 호출한 시점부터 이 메서드에 대응되는

onPause() 메서드가 호출되는 시점까지다. 이 시간 동안 액티비티는 화면상에서 나머지

Page 44: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

20 l 안드로이드 레시피

액티비티보다 앞에 있으며 사용자와 상호작용하고 있다. 액티비티는 실행 재개 상태와

일시 정지 상태에서 자주 전환될 수 있다. 예를 들어 onPause()는 기기가 슬립 모드에 들

어가거나 새 액티비티가 시작할 때 호출되며, onResume() 메서드는 액티비티 결과나 새

액티비티가 전달될 때 호출된다. 두 메서드에 사용하는 코드는 매우 가벼운 코드이어야

한다.

알아두기

각 생명주기 콜백 메서드는 적절한 작업을 수행하기 위해 액티비티가 오버라이드할 수 있는

후크 메서드다. 모든 액티비티는 액티비티 객체가 처음 생성될 때 초기 설정을 수행할 수 있게

onCreate(Bundle)를 구현해야 한다. 또한 많은 액티비티들은 onPause()를 구현해 데이터

수정 사항을 저장하고 그 외 사용자와의 상호작용 중단에 대비하는 일을 한다.

그림 1–3은 이들 7가지 메서드의 관점에서 액티비티의 생명주기를 정리한 것이다.

startActivity()에 의해 액티비티 시작

다른 액티비티가 이 액티비티보다 앞으로 옴

액티비티가 더 이상 보이지 않음

액티비티가 전방으로 나옴

액티비티가 전방으로 나옴

액티비티가 닫힘

다른 액티비티에서 메모리가 필요함

사용자가 액티비티로 돌아옴

그림 1-3. 액티비티의 생명주기를 살펴보면 onDestroy()가 호출이 보장되지 않음을 알 수 있다.

Page 45: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 21

onDestroy() 메서드는 호출되지 않을 수도 있는 만큼 이 메서드에 의존해 데이터를 저장해서

는 안 된다. 예를 들어 액티비티에서 콘텐츠 프로바이더의 데이터를 수정했다면 이렇게 수정한

내용은 onPause() 내에서 반영해야 한다.

그에 반해 onDestroy()는 앱의 나머지 부분이 실행되는 동안 불필요한 리소스가 남아 있지 않

게끔 보통 (스레드 같은) 리소스를 반환하는 용도로 구현한다.

그림 1-3을 보면 startActivity()를 호출해 액티비티가 시작하는 것을 볼 수 있다. 이를 좀 더 자

세히 살펴보면 액티비티는 명시적 또는 암시적 인텐트를 기술한 Intent 객체를 생성하고 이 객체

를 Context의 void startActivity(Intent intent) 메서드(새 액티비티를 실행하는 메서드. 이 메서

드는 실행을 마친 후 아무 값도 반환하지 않는다)로 전달해 실행을 시작한다.

또는 Activity의 void startActivityForResult(Intent intent, int requestCode) 메서드를 호출

해 액티비티를 시작할 수도 있다. 이때 지정한 int 결과는 Activity의 void onActivityResult(int

requestCode, int resultCode, Intent data) 콜백 메서드에 인자로 돌아온다.

알아두기

이때 인텐트에 반응하는 액티비티는 Activity의 Intent getIntent() 메서드를 사용해 이 액

티비티가 실행되게 한 인텐트의 내용을 살펴볼 수 있다. 안드로이드에서는 액티비티의 void

onNewIntent(Intent intent) 메서드(이 메서드도 Activity 클래스에 들어 있다)를 호출해 이

후 인텐트를 액티비티로 전달한다.

예를 들어 여러분이 SimpleActivity라는 앱을 생성했고 이 앱이 SimpleActivity(예제 1-2)

와 SimpleActivity2로 이뤄져 있다고 가정하자. 또 SimpleActivity의 onCreate(Bundle) 메

서드에서 SimpleActivity2를 실행한다고 가정하자. 이 경우 다음과 같은 코드를 사용해

SimpleActivity2를 시작할 수 있다.

Intent intent = new Intent(SimpleActivity.this, SimpleActivity2.class);

SimpleActivity.this.startActivity(intent);

이 코드에서 첫 번째 줄은 명시적인 인텐트를 나타내는 Intent 객체를 생성한다. 이 코드에

서는 현재 SimpleActivity 인스턴스의 참조를 넘기고 SimpleActivity2의 Class 인스턴스를

Intent(Context packageContext, Class<?> cls) 생성자로 넘긴다.

Page 46: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

22 l 안드로이드 레시피

두 번째 줄에서는 Intent 객체를 startActiv ity(Intent)로 전달한다. 이 메서드가 바

로 SimpleActivity2 클래스를 통해 지정한 액티비티를 실행하는 책임을 지는 메서드다.

startActivity(Intent) 메서드가 지정된 액티비티를 찾지 못한 경우(이런 일은 일어나게 해서는

안 된다) 이 메서드는 android.content.ActivityNotFoundException 인스턴스 예외를 던진다.

액티비티는 앱의 AndroidManifest.xml 파일에 반드시 선언해야 하며, 이렇게 하지 않으면 (안

드로이드에서 액티비티를 찾을 수 없으므로) 액티비티를 시작할 수 없다. 예를 들어 예제 1-3의

AndroidManifest.xml 파일은 SimpleActivity와 SimpleActivity2를 선언하고 있다. 이 파일에

서 말줄임 표시는 이 내용과 관련 없는 나머지 내용을 감추기 위해 사용했다.

예제 1-3 | SimpleActivity의 매니페스트 파일

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.example.project" ...>

<application ...>

<activity android:name=".SimpleActivity" ...>

<intent-filter ...>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

<activity android:name=".SimpleActivity2" ...>

<intent-filter ...>

<action android:name="android.intent.action.VIEW" />

<data android:mimeType="image/jpeg" />

<category android:name="android.intent.category.DEFAULT" />

</intent-filter>

</activity>

...

</application>

</manifest>

Page 47: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 23

예제 1-3은 SimpleActivity와 SimpleActivity2가 <activity> 안에 들어 있는 <intent-�lter>

태그로 인해 인텐트 필터와 관련 있음을 보여준다. SimpleActivity2의 <intent-filter> 태그는

Intent 객체의 값이 다음 태그 값과 일치할 때 안드로이드에서 이 액티비티를 실행할 수 있도록

도와준다.

■ <action>의 android:name 어트리뷰트가 "android.intent.action.VIEW"로 지정된 경우.

■ <data>의 android:mimeType 어트리뷰트가 "image/jpeg" MIME 타입인 경우 – 이때

는 (android:path 같은) 추가 어트리뷰트를 지정해 볼 데이터의 위치를 지정하는 게 보

통이다.

■ <category>의 android:name 어트리뷰트가 "android.intent.category.DEFAULT"로 지

정돼 명시적으로 컴포넌트를 지정하지 않고 액티비티가 실행되도록 허용한 경우.

다음 코드는 암시적으로 SimpleActivity2를 시작하는 코드다.

Intent intent = new Intent();

intent.setAction("android.intent.action.VIEW");

intent.setType("image/jpeg");

intent.addCategory("android.intent.category.DEFAULT");

SimpleActivity.this.startActivity(intent);

처음 네 줄은 암시적인 인텐트를 기술한 Intent 객체를 생성한다. Intent의 Intent setAction(

String action), Intent setType(String type), Intent addCategory(String category) 메서드에 전

달된 값들은 인텐트의 액션, MIME 타입, 카테고리를 나타낸다. 이들 값은 안드로이드에서 실행

할 액티비티가 SimpleActivity2임을 알 수 있게 해준다.

Page 48: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

24 l 안드로이드 레시피

액티비티, 작업, 액티비티 스택

안드로이드는 관련 액티비티 시퀀스를 작업이라고 부르며 액티비티 스택(히스토리 스택

또는 백 스택이라고도 부른다)을 제공해 이 시퀀스를 기억한다. 작업이 시작되게 하는 액

티비티는 초기 액티비티로 스택에 쌓이며, 루트 액티비티라고 부른다. 이 액티비티는 보통

기기의 앱 론처를 통해 사용자가 선택한 액티비티가 된다. 현재 실행 중인 액티비티는 액티

비티 스택의 상단에 위치한다.

현재 액티비티가 다른 액티비티를 시작하면 새 액티비티가 스택 위로 올라오고 포커스를

갖는다(실행 액티비티가 된다). 이전 액티비티는 스택에 그대로 남아 있지만 실행은 정지

된다. 액티비티가 실행을 멈출 때 시스템은 액티비티 UI의 현재 상태를 그대로 유지한다.

사용자가 기기의 뒤로가기 키를 누르면 현재 액티비티가 스택에서 빠져나오고(액티비티가

소멸됨) 이전 액티비티가 실행 액티비티로서 실행을 재개한다(액티비티의 이전 UI 상태가

복원된다).

스택에 있는 액티비티는 다시 정렬되지 않으며 스택에 쌓이거나 스택에서 꺼내질 뿐이다.

현재 액티비티를 통해 액티비티를 시작하면 액티비티가 스택에 쌓이고, 사용자가 뒤로가

기 키를 눌러 액티비티를 벗어나면 액티비티가 스택에서 빠져 나온다. 따라서 액티비티 스

택은 가장 나중에 들어간 액티비티가 가장 먼저 나오는 객체 구조(LIFO)로 돼 있다.

사용자가 뒤로가기 키를 누를 때마다 스택의 액티비티가 빠져나와 이전 액티비티가 화면

에 보이게 한다. 계속해서 뒤로가기 키를 누르면 사용자는 결국 홈 화면 또는 작업이 시작

할 때 실행되던 액티비티로 돌아오게 된다. 스택에서 모든 액티비티가 제거되면 작업이 더

이상 존재하지 않게 된다.

액티비티와 작업에 대해 좀 더 자세한 내용을 알고 싶다면 구글의 온라인 안드로이드 문서

에서 ‘Tasks and Back Stack’ 절을 참고하자(tp://developer.android.com/guide/

topics/fundamentals/tasks-and-back-stack.html).

Page 49: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 25

서비스 자세히 살펴보기

서비스는 Context의 간접 하위 클래스인 android.app.Service 추상 클래스의 하위 클래스로 설

명할 수 있다.

Service 하위 클래스는 서비스의 생명주기 동안 안드로이드에서 호출하는 다양한 서비스 생명

주기 콜백 함수를 오버라이드한다. 예를 들어 예제 1-4의 SimpleService 클래스는 Service를 상

속하고 void onCreate()와 void onDestroy() 생명주기 콜백 메서드를 오버라이드한다.

예제 1-4 | Service 클래스의 기본 예제, 버전 1

import android.app.Service;

public class extends Service

{

@Override

public void onCreate()

{

System.out.println("onCreate() called");

}

@Override

public void onDestroy()

{

System.out.println("onDestroy() called");

}

@Override

public IBinder onBind(Intent intent)

{

System.out.println("onBind(Intent) never called");

return null;

}

}

onCreate()는 서비스가 처음 생성될 때 호출되고 onDestroy()는 서비스가 제거될 때 호출된

다. Service 클래스는 추상 클래스이므로 IBinder onBind(Intent intent) 생명주기 콜백 메서드

(이 절에서 나중에 설명)는 null을 반환해 이 메서드를 무시하는 한이 있더라도 항상 오버라이드

해야 한다.

Page 50: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

26 l 안드로이드 레시피

알아두기

Service 하위 클래스는 보통 onCreate()와 onDestroy()를 오버라이드해 초기화 작업과 정

리 작업을 수행한다. Activity의 onCreate(Bundle)와 onDestroy() 메서드와 달리 Service

의 onCreate() 메서드는 반복적으로 호출되지 않으며 onDestroy() 메서드는 항상 호출된다.

서비스의 수명은 onCreate()가 호출된 시점부터 onDestroy()가 실행을 마치는 시점까지다.

액티비티와 마찬가지로 서비스도 onCreate()에서 초기화되고 onDestroy()에서 정리된다.

예를 들어 음악을 재생하는 서비스는 음악을 재생하는 스레드를 onCreate()에서 생성하고

onDestroy()에서 멈출 수 있다.

로컬 서비스는 보통 Context의 컴포넌트명 startService(Intent intent) 메서드를 통해 시작한

다. 이 메서드는 시작된 서비스 컴포넌트를 나타내는 android.content.ComponentName 인스턴

스 또는 서비스가 존재하지 않을 경우 널 참조를 반환한다. startService(Intent) 메서드의 결과

는 그림 1-4 같은 생명주기를 일으킨다.

startService()에 의해 서비스가 시작

서비스 실행 중 서비스 중단됨(콜백 없음)

서비스가 종료됨

그림 1-4. startService(Intent)를 통해 시작된 서비스의 생명주기는 onStartCommand(Intent, int,

int)를 호출한다.

startService(Intent)를 호출하면 그 결과로 onCreate()이 호출되고 이어서 onStartCommand

(Intent intent, int �ags, int startId)가 호출된다. onStartCommand(Intent intent, int �ags, int

startId) 생명주기 콜백 메서드는 더 이상 권장하지 않는 void onStart(Intent intent, int startId)

메서드를 대체하는 메서드로서, 다음과 같은 인자를 받는다.

■ intent는 startService(Intent)를 호출하면서 넘겨준 Intent 객체다.

■ �ags는 시작 요청에 대한 추가 데이터를 제공할 수도 있지만 보통 0으로 설정돼 있다.

Page 51: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 27

■ startID는 정수 타입의 고유 값으로 시작 요청을 식별한다. 서비스는 이 값을 Service의

boolean stopSelfResult(int startId) 메서드로 전달해 자신의 실행을 멈출 수 있다.

onStartCommand(Intent, int, int)는 Intent 객체를 처리하고 명시적으로 서비스를 중단하기

전까지 서비스가 계속해서 실행되도록 보통 Service.START_STICKY 상수를 반환한다. 이 상

수가 반환되는 시점에서는 서비스가 실행 중이며 다음 이벤트 중 하나가 일어나기 전까지는 실행

을 계속한다.

■ 다른 컴포넌트에서 Context의 boolean stopService(Intent intent) 메서드를 호출해 서비

스를 중단한 경우. startService(Intent)를 몇 번 호출하든 상관없이 서비스를 중단할 때

는 stopService(Intent)를 한 번만 호출하면 된다.

■ 서비스가 오버로드된 stopSelf() 메서드 또는 Service의 stopSelfResult(int)를 호출해 스

스로 실행을 중단한 경우.

stopService(Intent), stopSelf(), stopSelfResult(int)가 호출된 후 안드로이드는 서비스가 정리

작업을 수행할 수 있게 onDestroy()를 호출해준다.

알아두기

startService(Intent)를 호출해 서비스를 시작하면 onBind(Intent)가 호출되지 않는다.

예제 1-5는 startService(Intent) 메서드를 사용해 실행할 수 있는 서비스 코드를 보여준다.

예제 1-5 | Service 클래스의 기본 예제, 버전 2

import android.app.Service;

public class SimpleService extends Service

{

@Override

public void onCreate()

{

System.out.println("onCreate() called");

Page 52: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

28 l 안드로이드 레시피

}

@Override

public int onStartCommand(Intent intent, int flags, int startId)

{

System.out.println("onStartCommand(Intent, int, int) called");

return START_STICKY;

}

@Override

public void onDestroy()

{

System.out.println("onDestroy() called");

}

@Override

public IBinder onBind(Intent intent)

{

System.out.println("onBind(Intent) never called");

return null;

}

}

아래 코드는 예제 1-2에 나온 SimpleActiv ity 클래스의 onCreate() 메서드 내에서

startService(Intent)를 사용해 명시적인 인텐트를 통해 예제 1-5에 나온 SimpleService 클래스

인스턴스를 시작하는 코드다.

Intent intent = new Intent(SimpleActivity.this, SimpleService.class);

SimpleActivity.this.startService(intent);

원격 서비스는 Context의 boolean bindService(Intent service, ServiceConnection conn,

int �ags) 메서드를 통해 시작할 수 있다. 이 메서드는 필요한 경우 서비스를 생성해 실행 중인

서비스에 연결하고, 성공적으로 서비스에 연결된 경우 ‘true’를 반환한다. bindService(Intent,

ServiceConnection, int)는 그림 1-5에 나온 것 같은 생명주기 결과를 낳는다.

Page 53: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 29

bindService()에 의해 서비스가 생성됨

클라이언트가 서비스와 상호작용함

서비스가 종료됨

그림 1-5. bindService(Intent, ServiceConnection, int)로 시작된 서비스의 생명주기에는

onStartCommand(Intent, int, int) 호출이 포함되지 않는다.

bindService(Intent, ServiceConnection, int)를 호출하면 onCreate()가 호출되고 이어서

onBind(Intent)가 호출된다. onBind(Intent)는 클라이언트에서 서비스와 연계할 수 있는 커뮤니

케이션 채널(android.os.IBinder 인터페이스를 구현하는 클래스 인스턴스)을 반환한다.

클라이언트는 다음과 같이 서비스와 연계할 수 있다.

1. 클라이언트는 android.content.ServiceConnection을 상속하고 서비스가 시작하고

멈추는 정보를 받기 위해 이 클래스의 void onServiceConnected(ComponentName

className, IBinder service)와 void onServiceDisconnected(ComponentName name)

메서드를 오버라이드한다. bindService(Intent, ServiceConnection, int) 메서드가 true

를 반환하면 서비스와의 연결이 맺어질 때 앞의 메서드(onServiceConnected(Compon

entName className, IBinder service))가 호출된다. 이때 이 메서드의 인자로 전달되는

IBinder는 onBind(Intent)에서 반환한 값과 같은 값이다. 두 번째 메서드는 서비스와의

연결이 끊어질 때 호출된다.

서비스 연결이 끊어지는 경우는 주로 서비스를 실행하던 프로세스가 강제 종료되거나 안

드로이드에 의해 죽었을 때 일어난다. 하지만 이때 ServiceConnection 인스턴스 자체는

제거되지 않는다. 서비스와의 바인딩은 그대로 유지되며 다음번에 서비스가 다시 실행되

Page 54: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

30 l 안드로이드 레시피

면 클라이언트는 onServiceConnected(ComponentName, IBinder) 호출을 다시 받게

된다.

2. 클라이언트는 ServiceConnection 하위 클래스를 bindService(Intent, ServiceConnection,

int)로 넘겨준다.

클라이언트는 Context의 void unbindService(ServiceConnection conn) 메서드를 호출해 서

비스와의 연결을 끊을 수 있다. 이 컴포넌트는 서비스가 재시작하더라도 다시 호출을 받지 않는

다. 서비스에 바인딩된 다른 컴포넌트가 없는 경우 서비스는 언제든 실행을 멈추게 할 수 있다.

서비스가 멈추기 전에 안드로이드는 unbindService(ServiceConnection) 메서드에 전달된

Intent 객체를 인자로 사용해 서비스의 boolean onUnbind(Intent intent) 생명주기 콜백 메서드

를 호출한다. onUnbind(Intent)에서 true를 반환하지 않을 경우 안드로이드는 이후 클라이언트

에서 서비스와 바인딩하려고 할 때마다 서비스의 void onRebind(Intent intent) 생명주기 콜백

메서드를 호출한다. 안드로이드는 서비스를 소멸시킬 때 onDestroy()를 호출한다.

예제 1-6은 bindService(Intent, ServiceConnection, int) 메서드를 통해 활용할 수 있는 기본

서비스 클래스를 보여준다.

예제 1-6 | 서비스 클래스의 기본 예제, 버전 3

import android.app.Service;

public class SimpleService extends Service

{

public class SimpleBinder extends Binder

{

SimpleService getService()

{

return SimpleService.this;

}

}

private final IBinder binder = new SimpleBinder();

@Override

public IBinder onBind(Intent intent)

{

return binder;

}

Page 55: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 31

@Override

public void onCreate()

{

System.out.println("onCreate() called");

}

@Override

public void onDestroy()

{

System.out.println("onDestroy() called");

}

}

예제 1-6에서는 먼저 android.os.Binder 클래스를 상속한 SimpleBinder 내부 클래스를 선

언한다. SimpleBinder는 SimpleService 하위 클래스 인스턴스를 반환하는 SimpleService

getService() 메서드 하나만 선언한다.

알아두기

Binder는 IBinder 인터페이스를 통해 프로세스 간 통신에 활용할 수 있는 원격 프로시저 호

출 메커니즘을 지원한다. 물론 이 예제에서는 서비스가 나머지 앱과 같은 프로세스에서 실행

된다고 가정했지만 이때도 Binder와 IBinder는 여전히 필요하다.

이어서 예제 1-6에서는 SimpleBinder 인스턴스를 생성하고 이 인스턴스의 참조를 private

binder 필드에 대입한다. 이 필드 값은 나중에 오버라이드한 onBind(Intent) 메서드를 통해 반환

된다.

이번에는 예제 1-2의 SimpleActivity 클래스가 SimpleService 타입의 ss라는 private 필드

(private SimpleService ss;)를 선언했다고 가정하자. 또 SimpleActivity의 onCreate(Bundle) 메

서드에 다음 코드가 들어 있다고 가정하자.

ServiceConnection sc = new ServiceConnection()

{

public void onServiceConnected(ComponentName className, IBinder service)

{

ss = ((SimpleService.SimpleBinder) service).getService();

System.out.println("Service connected");

Page 56: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

32 l 안드로이드 레시피

}

public void onServiceDisconnected(ComponentName className)

{

ss = null; System.out.println("Service disconnected");

}

};

bindService(new Intent(SimpleActivity.this, SimpleService.class), sc,

Context.BIND_AUTO_CREATE);

이 코드에서는 먼저 ServiceConnection 하위 클래스의 인스턴스를 생성한다. 오버라이

드한 onServiceConnected(ComponentName, IBinder) 메서드는 service 인자를 사용해

SimpleBinder’s getService() 메서드를 호출하고 결과를 저장하는 일만 한다.

오버라이드한 onServiceDisconnected(ComponentName) 메서드는 꼭 구현하기는 해야 하지

만 SimpleService가 SimpleActivity와 같은 프로세스에서 실행되므로 호출할 일이 전혀 없다.

이 코드에서는 이어서 ServiceConnection 하위 클래스 객체와 SimpleService를 인텐트

의 타깃으로 지정한 인텐트, Context.BIND_AUTO_CREATE(영속적인 커넥션을 생성)를

bindService(Intent, ServiceConnection, int) 메서드의 인자로 넘겨준다.

알아두기

서비스를 (startService(Intent)를 사용해) 시작할 수도 있고 연결이 (bindService(Intent,

ServiceConnection, int)를 통해) 바인딩되게 할 수도 있다. 안드로이드는 서비스를 시작했

거나 서비스에 대해 BIND_AUTO_CREATE 플래그를 사용한 연결이 맺어진 동안 계속해서 서

비스를 유지한다. 두 상황 모두에 해당하지 않는 경우 서비스의 onDestroy() 메서드가 호출

되고 서비스는 종료된다. 스레드를 종료하고 브로드캐스트 리시버를 등록 해제하는 등의 모든

정리 작업은 onDestroy() 메서드가 실행을 마치기 전에 모두 끝난다.

서비스를 어떻게 시작하든 상관없이 앱의 AndroidManifest.xml 파일에는 서비스 컴포넌트

에 대한 항목이 들어 있어야 한다. 다음 항목은 SimpleService를 선언한다.

<service android:name=".SimpleService">

</service>

Page 57: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 33

알아두기

앞의 예제에서는 bindService(Intent, ServiceConnection, int)를 사용해 로컬 서비스를 시

작했지만 보통 이 메서드는 원격 서비스를 시작하는 데 더 많이 쓰인다. 원격 서비스는 5장에

서 소개한다.

브로드캐스트 리시버 자세히 살펴보기

브로드캐스트 리시버는 android.content.BroadcastReceiver 추상 클래스를 상속하고

BroadcastReceiver의 추상 메서드인 void onReceive(Context context, Intent intent)를 오버라

이드한 클래스다. 예를 들어 예제 1-7의 SimpleBroadcastReceiver는 BroadcastReceiver를 상속

하고 이 메서드를 구현한다.

예제 1-7 | 브로드캐스트 리시버의 기본 예제

public class SimpleBroadcastReceiver extends BroadcastReceiver

{

@Override

public void onReceive(Context context, Intent intent)

{

System.out.println("onReceive(Context, Intent) called");

}

}

브로드캐스트 리시버를 시작할 때는 Intent 객체를 생성하고 이 객체를 Context의 브로드캐

스트 메서드(예를 들어 Context의 오버로드된 sendBroadcast() 메서드)로 전달해 메시지가 이

메세지를 수신하는 모든 브로드캐스트 리시버로 전달되게 하면 된다.

다음 예제에서는 코드가 예제 1-2의 SimpleActivity 클래스 내 onCreate() 메서드에 들어 있

고 예제 1-7의 SimpleBroadcastReceiver 클래스 인스턴스를 시작한다고 가정한다.

Intent intent = new Intent(SimpleActivity.this, SimpleBroadcastReceiver.class);

intent.putExtra("message", "Hello, broadcast receiver!");

SimpleActivity.this.sendBroadcast(intent);

Page 58: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

34 l 안드로이드 레시피

Intent의 Intent putExtra(String name, String value) 메서드는 키/값 쌍으로 메시지를 저장

하기 위해 호출한다. Intent의 다른 putExtra() 메서드와 마찬가지로 이 메서드는 메서드를 연쇄

적으로 호출할 수 있게끔 Intent 객체에 대한 참조를 반환한다.

브로드캐스트 리시버를 동적으로 생성하지 않는 경우에는 항상 AndroidManifest.xml에 이

컴포넌트에 대한 항목을 추가해야 한다. 다음 항목에서는 SimpleBroadcastReceiver를 선언한다.

<receiver android:name=".SimpleBroadcastReceiver">

</receiver>

콘텐츠 프로바이더 자세히 살펴보기

콘텐츠 프로바이더는 android.content.ContentProvider 추상 클래스의 하위 클래스로

ContentProvider의 추상 메서드(이를테면 String getType(Uri uri))를 오버라이드한 클래스를

말한다. 예를 들어 예제 1-8의 SimpleContentProvider 클래스는 ContentProvider를 상속하고

이들 추상 메서드를 오버라이드한다.

예제 1-8 | 콘텐츠 프로바이더의 기본 예제

public class SimpleContentProvider extends ContentProvider

{

@Override

public int delete(Uri uri, String selection, String[] selectionArgs)

{

System.out.println("delete(Uri, String, String[]) called");

return 0;

}

@Override

public String getType(Uri uri)

{

System.out.println("getType(Uri) called");

return null;

}

@Override

public Uri insert(Uri uri, ContentValues values)

Page 59: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 35

{

System.out.println("insert(Uri, ContentValues) called");

return null;

}

@Override

public boolean onCreate()

{

System.out.println("onCreate() called");

return false;

}

@Override

public Cursor query(Uri uri, String[] projection, String selection,

String[] selectionArgs, String sortOrder)

{

System.out.println("query(Uri, String[], String, String[], String) called");

return null;

}

@Override

public int update(Uri uri, ContentValues values, String selection,

String[] selectionArgs)

{

System.out.println("update(Uri, ContentValues, String, String[]) called");

return 0;

}

}

클라이언트에서는 SimpleContentProvider 인스턴스를 생성해 이들 메서드를 바로 호출하지

않는다. 대신 android.content.ContentResolver 추상 클래스의 하위 클래스 인스턴스를 생성

하고 하위 클래스의 메서드(예를 들어 public �nal Cursor query(Uri uri, String[] projection,

String selection, String[] selectionArgs, String sortOrder))를 호출한다.

알아두기

ContentResolver 인스턴스는 콘텐츠 프로바이더와 통신할 수 있다. 이 인스턴스는 콘텐츠

프로바이더와 연계해 프로세스간 통신을 관리할 수 있다.

Page 60: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

36 l 안드로이드 레시피

콘텐츠 프로바이더를 사용할 경우 AndroidManifest.xml에는 콘텐츠 프로바이더 컴포넌트에

대한 항목이 꼭 들어 있어야 한다. 다음 항목은 SimpleContentProvider를 선언한다.

<provider android:name=".SimpleContentProvider">

</provider>

1–1. 안드로이드 SDK 설치

◈ 문제

안드로이드에 대한 앞의 소개를 읽고 첫 번째 안드로이드 앱을 만들고 싶다는 생각이 들었다. 하

지만 앱을 개발하기 전에 먼저 안드로이드 SDK 2.3을 설치해야 한다. 2

◈ 해결책

구글은 윈도우, 인텔 기반의 맥 OS X, 리눅스 운영체제용 안드로이드 SDK 2.3 배포 파일을 제공

한다. 플랫폼에 맞는 배포 파일을 내려받아 압축을 풀고, 압축을 푼 홈 디렉터리를 편리한 위치

로 옮긴다. 이때 PATH 환경 변수를 수정하면 파일 시스템 어디에서든 SDK의 명령행 툴을 사용

할 수 있어서 더 편리하다.

이 파일을 내려받아 설치하기 전에 먼저 SDK의 요구 조건을 알아야 한다. 개발 플랫폼이 이 조

건에 맞지 않으면 SDK를 사용할 수 없기 때문이다.

안드로이드 SDK 2.3은 다음 운영체제를 지원한다.

■ 윈도우 XP(32비트), 비스타(32 또는 64비트), 윈도우 7(32 또는 64비트).

■ 맥 OS X 10.5.8 또는 이후 버전(x86전용).

2   (옮긴이) 이 책은 SDK 2.3을 기준으로 하지만 최신 안드로이드 SDK 버전을 내려받아 따라 하더라도 상관은 없다.

Page 61: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 37

■ 리눅스(우분투 리눅스, 루시드 링크스): GNU C라이브러리 (glibc) 2.11 또는 이후 버전

필요. 64비트 배포판은 32비트 애플리케이션을 실행할 수 있어야 한다. 32비트 애플리

케이션에 대한 지원 기능을 추가하는 법을 확인하려면 우분투 리눅스 설치 설명(http://

developer.android.com/sdk/installing.html#troubleshooting)을 참고하자.

SDK를 설치하고 나면 안드로이드 SDK 2.3이 다양한 컴포넌트로 이뤄져 있음을 쉽게 볼 수

있다. SDK 툴, SDK 플랫폼 툴, 각기 다른 버전의 안드로이드 플랫폼(안드로이드 소프트웨어 스

택이라고도 부름), SDK 애드온, 윈도우용 USB 드라이버, 예제, 오프라인 문서 등이 안드로이드

SDK에 모두 들어 있다. 이들 각 구성 요소는 최소한의 디스크 저장 공간을 필요로 한다. SDK가

디스크에서 차지하는 전체 공간은 설치하기로 한 SDK 구성 요소에 따라 달라질 수 있다.

■ SDK 툴: SDK 툴은 대략 35MB의 디스크 저장 공간을 차지하며 반드시 설치해야 한다.

■ SDK 플랫폼 툴: SDK 플랫폼 툴은 대략 6MB의 디스크 저장 공간을 차지하며 반드시 설

치해야 한다.

■ 안드로이드 플랫픔 : 각 안드로이드 플랫폼은 특정 안드로이드 버전을 따르며 대략

150MB의 디스크 저장 공간이 필요하다. 최소 한 개의 안드로이드 플랫폼은 반드시 설치

해야 한다.

■ SDK 애드온: 선택적으로 설치할 수 있는 SDK 애드온(예를 들어 구글 API 또는 서드파티

벤더의 API 라이브러리)은 대략 100MB의 디스크 저장 공간이 필요하다.

■ 윈도우용 USB 드라이버: 윈도우 플랫폼용 USB 드라이버는 선택 설치 사항으로, 10MB

정도의 저장 공간을 차지한다. 맥 OS X이나 리눅스에서 개발 중이라면 이 USB 드라이버

를 설치하지 않아도 된다.

■ 예제: 각 안드로이드 플랫폼에서 선택적으로 설치할 수 있는 예제는 약 10MB의 디스크

저장 공간을 차지한다.

■ 오프라인 문서: 이 문서를 내려받으면 안드로이드 문서를 참조하려고 할 때 인터넷에 연

결하지 않고 오프라인 상태에서도 문서를 확인할 수 있다. 오프라인 문서는 대략 250MB

의 디스크 저장 공간이 필요하다.

끝으로 다음 추가 소프트웨어가 설치돼 있어야 한다.

Page 62: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

38 l 안드로이드 레시피

■ JDK 5 또는 JDK 6: 이들 자바 개발 킷(JDK)은 자바 코드를 컴파일하는 데 필요하므로

둘 중 하나는 설치돼 있어야 한다. 안드로이드 개발을 할 때는 자바 런타임 환경(JRE)이

설치돼 있는 것만으로는 부족하다.

■ 아파치 앤트: 안드로이드 프로젝트를 빌드하려면 리눅스나 맥의 경우 앤트 버전 1.6.5 이

상을 설치해야 하고, 윈도우 운영체제의 경우 앤트 버전 1.7 이상을 설치해야 한다.

알아두기

개발 플랫폼에 JDK가 이미 설치돼 있다면 앞에서 설명한 버전 조건(5 또는 6)에 부합하는지

다시 한 번 확인하자. 일부 리눅스 배포판에는 안드로이드 개발 시에 지원되지 않는 JDK 1.4

가 포함돼 있을 수도 있다. 더불어 자바용 Gnu 컴파일러는 지원하지 않는다.

◈ 문제 풀이

브라우저에서 http://developer.android.com/sdk/index.html 3 로 이동해 android-sdk_r08-

windows.zip(윈도우), android-sdk_r08-mac_86.zip(맥 OS X), android-sdk_r08-linux_86.

tgz(리눅스) 중 하나를 내려받는다.

알아두기

윈도우 개발자는 installer_r08-windows.exe* 파일을 내려받아 실행할 수도 있다. 이 툴을

사용하면 자동으로 설치가 진행된다.

* 현재 최신 버전은 android-sdk_r16-windows.exe이다.

예를 들어 윈도우 XP를 사용 중이라면 android-sdk_r08-windows.zip을 내려받는다. 이 파

일의 압축을 푼 후에는 압축이 풀린 android-windows-sdk 홈 디렉터리를 파일시스템상에서

3   (옮긴이) 이 책을 번역한 현 시점 기준으로 최신 파일명은 android-sdk_r16-windows.zip, android-sdk_r16-macosx.zip

(맥 OS X), android-sdk_r16-linux.tgz이다.

Page 63: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 39

작업하기 편한 위치로 옮긴다. 예를 들어 압축을 푼 C:\unzipped\android-sdk_r08-windows\

android-sdk-windows 디렉터리를 C: 드라이브의 루트 디렉터리로 옮겨 C:\android-sdk-

windows를 설치 경로로 지정한다.

알아두기

설치를 마친 후에는 파일시스템상의 아무 위치에서나 SDK의 명령행 툴을 사용할 수 있게끔

PATH 환경변수에 tools 하위 디렉터리를 추가하는 게 좋다.

android-windows-sdk의 내용을 자세히 살펴보면 홈 디렉터리에 다음과 같은 하위 디렉터리

와 파일이 들어 있음을 알 수 있다.

■ add-ons: 이 디렉터리는 초기에는 비어 있으며 구글이나 다른 벤더의 애드온을 저장한

다. 예를 들어 구글 API 애드온은 이곳에 저장된다.

■ platforms: 이 디렉터리는 초기에는 비어 있으며 별도 하위 디렉터리를 통해 안드로이드

플랫폼을 저장한다. 예를 들어 안드로이드 2.3은 platforms 디렉터리의 하위 디렉터리 중

한 곳에 저장되며, 안드로이드 2.2는 또 다른 platforms 하위 디렉터리에 저장된다.

■ tools: 이 디렉터리는 플랫폼 독립적인 개발 툴과 프로파일링 툴을 보관한다. 이 디렉터리

에 있는 툴들은 안드로이드 플랫폼 출시와 상관없이 아무 때나 업데이트될 수 있다.

■ SDK Manager.exe: 안드로이드 SDK와 AVD 매니저 툴을 실행하는 특수 툴이다. AVD

매니저 툴은 SDK에 컴포넌트를 추가할 때 사용하는 툴이다.

■ SDK Readme.txt: 안드로이드 SDK와 AVD 매니저 툴을 각 플랫폼에서 어떻게 실행해

야 하는지 등 SDK의 초기 설정을 수행하는 법을 알려주는 파일이다.

tools 디렉터리에는 다음과 같은 툴을 비롯해 다양한 툴이 들어 있다.

■ android: 안드로이드 프로젝트를 생성하고 업데이트한다. 안드로이드 SDK를 새 플랫폼,

애드온, 문서로 업데이트한다. 또 안드로이드 가상 머신(레시피 1-3에서 설명)을 추가, 삭

제하거나 내용을 보는 데도 사용한다.

Page 64: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

40 l 안드로이드 레시피

■ emulator: 커널 레벨까지 이르는 전체 안드로이드 소프트웨어 스택을 실행하며, 에뮬레

이터에서 사용할 수 있는 사전 설치 앱(예를 들어 브라우저 등)들을 포함한다.

■ sqlite3: 안드로이드 앱에서 생성한 SQLite 데이터베이스를 관리한다.

■ zipalign: APK 파일에 대한 아카이브 정렬 최적화를 수행한다.

1-2. 안드로이드 플랫폼의 설치

◈ 문제

안드로이드 SDK를 설치하는 것만으로는 안드로이드 앱을 만드는 데 부족하다. 안드로이드 플랫

폼을 적어도 한 개는 설치해야 한다.

◈ 해결책

SDK Manager 툴을 사용해 안드로이드 플랫폼을 설치한다.

◈ 문제 풀이

SDK Manager를 실행한다. 이 툴은 안드로이드 SDK와 AVD 매니저 대화상자를 보여준 후

Refresh Sources와 Choose Packages to Install 대화상자를 보여준다.

안드로이드 SDK와 AVD 매니저는 가상 머신, 설치된 패키지, 추가 설치 가능한 패키지를 알려

준다. 또 프록시 서버와 다른 설정을 수정할 수 있게도 해준다.

Page 65: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 41

이 대화상자가 나타나면 대화상자 오른쪽에 있는 목록에서 설치된 패키지 항목이 하이라이트

되고, 이 목록의 오른쪽 창에는 이미 설치된 패키지가 나온다. 안드로이드를 처음 설치한다면 이

창에 Android SDK(리비전 8) 컴포넌트만 설치된 것으로 보일 것이다.

알아두기

android 툴을 사용해 안드로이드 SDK와 AVD 매니저 대화상자가 나타나게 할 수도 있다. 이

때는 명령행에서 android를 입력하면 된다. 이 방식으로 안드로이드 SDK와 AVD 매니저를

띄울 때는 설치된 패키지 대신 가상 기기가 하이라이트된다.

이 대화상자가 나타난 후 SDK 매니저는 구글의 서버를 검사해 설치할 수 있는 컴포넌트 패키

지가 있는지 찾는다. Refresh Sources 대화상자는 이런 진행 상황을 보여준다.

SDK Manager가 검사를 모두 마치면 Choose Packages to Install 대화상자(그림 1-6 참고)가

나타나 설치할 컴포넌트를 지정할 수 있게 해준다.

그림 1-6. 패키지의 목록에는 설치할 수 있는 패키지가 나온다.

Page 66: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

42 l 안드로이드 레시피

알아두기

구글은 SDK를 설치하는 도중에는 백신 소프트웨어를 비활성화할 것을 권장한다. 이렇게 하

지 않으면 SDK Manager: failed to install 대화상자가 나타나 폴더 이름을 변경하거나 폴

더를 옮기지 못했으므로 대화상자의 YES 버튼을 클릭해 설치를 다시 시도하기 전에 백신 소

프트웨어를 잠시 비활성화하라고 알려줄 것이다.

Choose Packages to Install 대화상자는 설치할 수 있는 패키지 목록을 보여준다. 이때 설치하

기로 한 패키지 옆에는 체크 표시가 나타나고, 설치하지 않기로 한 패키지에는 x 표시가 패키지

옆에 나타난다.

하이라이트된 패키지에 대해서는 Package Description & License에서 패키지 설명, 설치되는

패키지에 의존하는 다른 패키지 목록, 이 패키지가 들어 있는 아카이브에 대한 정보 및 추가 정

보를 제공한다. 또 이 영역에서 라디오 버튼을 선택하면 패키지를 승인 또는 거부할 수 있다.

알아두기

때로는 SDK에서 다른 컴포넌트의 최소 리비전 또는 특정 SDK 툴을 요구하는 경우도 있다.

Package Description & License에서 이런 의존성에 대해 설명하는 것 외에 개발 툴에서도

해결해야 할 의존성이 있는 경우 디버그 경고를 통해 이를 알려준다.

이 책은 안드로이드 2.3에 초점을 맞추고 있으므로 여기서 설치해야 할 패키지는 Android

SDK Platform-tools, revision 1과 SDK Platform Android 2.3, API 9, revision 1뿐이다. 체크한

다른 패키지 항목들은 해당 창에서 Reject 라디오 버튼을 클릭해 선택을 해제할 수 있다.

알아두기

이전 안드로이드 버전이 들어 있는 기기에서 실행할 앱을 개발한다면 이들 버전 옆에 있는 체

크 표시를 그대로 두어도 무방하다. 하지만 현시점에서는 굳이 그렇게 하지 않아도 된다. SDK

Manager를 사용하면 언제든 다시 돌아와 이들 버전을 추가할 수 있기 때문이다.

이제 이들 패키지만 선택된 것을 확인한 후 Install 버튼을 클릭해 설치를 시작한다. 그림 1-7은

이 버튼을 눌렀을 때 나오는 Installing Archives 대화상자를 보여준다.

Page 67: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 43

그림 1-7. Installing Archives 대화상자는 선택한 패키지 아카이브를 내려받아서 설치하는

과정을 보여준다.

설치를 마치고 나면 ADB와 관련한 패키지가 업데이트됐으므로 ADB를 지금 다시 시작할지

묻는 ADB Restart 대화상자가 나타날 것이다. YES를 클릭해 ADB Restart 대화상자를 닫고

Installing Archives 대화상자에서 Close를 클릭한다.

이제 안드로이드 SDK와 AVD 매니저의 Installed packages 창에 Android SDK Tools,

revision 8과 더불어 Android SDK Platform-tools, revision 1, SDK Platform Android 2.3, API

9, revision 1이 보일 것이다. 그리고 이제 다음과 같은 하위 디렉터리가 생긴 것을 볼 수 있다.

■ platform-tools (android-sdk-windows 디렉터리 내)

■ android-9 (android-sdk-windows/platforms 디렉터리 내)

platform-tools는 각 플랫폼이 출시될 때마다 업데이트될 수 있다. 이 툴에는 aapt(안드로이드

에셋 패키징 툴 - ZIP 호환 아카이브(.zip, .jar, .apk)를 보고, 생성하고 업데이트하며 리소스를

바이너리 에셋으로 컴파일하는 툴), adb(안드로이드 디버그 브리지 - 에뮬레이터 인스턴스 또는

안드로이드가 설치된 기기의 상태를 관리), dx(달빅 실행파일 - 자바 .class 파일로부터 안드로이

드 바이트코드를 생성)가 들어 있다. android-9 폴더에는 안드로이드 2.3 데이터와 사용자 인터

페이스 관련 파일들이 들어 있다.

Page 68: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

44 l 안드로이드 레시피

platform-tools 디렉터리를 PATH 환경 변수에 추가하면 파일 시스템 어디에서든 툴에 바로

접근할 수 있어 편리하다.

사용 가능한 패키지와 컴포넌트 업데이트 감지

Available packages 창에서는 설치할 수 있는 패키지를 보여준다. 기본적으로 이 창에는

구글의 안드로이드 저장소에서 제공하는 패키지와 서드파티 애드온(구글 및 삼성 등)을 보

여주지만 다른 커스텀 안드로이드 SDK 애드온을 제공하는 웹 사이트를 추가한 후 이들 사

이트에서 제공하는 SDK 애드온을 내려받을 수도 있다.

예를 들어 모바일 통신사나 기기 제조사에서 자신들의 안드로이드 기기에서 지원하는 추

가 API 라이브러를 제공하는 경우를 가정하자. 이때 개발 중인 앱에서 이들 라이브러리를

지원하려면 통신사/기기 제조사의 안드로이드 SDK 애드온을 설치해야 한다.

통신사나 기기 제조사가 웹사이트를 통해 SDK 애드온 저장소 파일을 호스팅한다면 다음

절차를 통해 이런 사이트를 SDK 매니저에 추가할 수 있다.

1. 목록에서 Available packages를 선택한다.

2. 결과창에서 Add Add-on Site 버튼을 클릭하고 대화상자 텍스트 필드에 repository.

xml 파일을 호스팅하는 웹사이트의 URL을 입력한다. OK를 클릭한다.

그럼 해당 웹사이트에서 내려받을 수 있는 SDK 컴포넌트가 Available Packages 아래 표

시될 것이다.

기존 SDK 컴포넌트의 업데이트 버전이 출시되면 SDK 저장소를 통해 이를 내려받을 수 있

다. 대부분의 경우, 이들 컴포넌트가 현재 개발 환경에 설치돼 있다면 가능한 한 빨리 새 버

전을 내려받은 게 좋다.

가장 쉽게 컴포넌트 업데이트를 확인하려면 Available Packages 창을 살펴보면 된다. 이

창에서 새 버전을 내려받을 수 있는 게 확인되면 앞서 안드로이드 2.3 플랫폼을 설치할 때

와 같은 방식으로 SDK 매니저를 사용해 내려받고 이를 개발 환경에 설치하면 된다. 새 버

전을 설치하면 기존 컴포넌트를 새 컴포넌트가 대신하게 되는데, 이 경우 기존 앱에는 별도

로 영향을 미치지 않는다.

Page 69: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 45

1–3. 안드로이드 가상 기기의 생성

◈ 문제

안드로이드 SDK와 안드로이드 플랫폼을 설치하고 이제 안드로이드 앱을 만들 준비를 모두 마쳤

다. 하지만 안드로이드 가상 기기(AVD, 안드로이드 기기를 나타내는 기기 설정)를 생성하기 전

까지는 emulator를 통해 앱을 실행할 수 없다.

◈ 해결책

SDK Manager 툴을 사용해 AVD를 생성한다.

◈ 문제 풀이

필요하다면 SDK Manager를 실행한다. Android SDK and AVD Manager 대화상자의 왼쪽 목

록에서 Virtual devices 항목을 클릭한다. 그럼 그림 1-8 같은 창이 보일 것이다.

그림 1-8. 초기에는 AVD가 전혀 설치돼 있지 않다.

Page 70: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

46 l 안드로이드 레시피

New 버튼을 클릭한다. 그림 1-9는 이 버튼을 클릭할 때 나타나는 Create new Android

Virtual Device (AVD) 대화상자를 보여준다.

그림 1-9. AVD는 이름, 대상 플랫폼, SD 카드, 스킨, 하드웨어 속성으로 이뤄진다.

그림 1–9는 AVD가 이름과 특정 안드로 플랫폼 타깃을 갖고 있으며 SD 카드를 에뮬레이트

할 수 있고 특정 화면 해상도에 따른 스킨을 제공함을 보여준다. 이름으로는 test_AVD를 입력

하고 대상 플랫폼으로는 Android 2.3 – API Level 9, SD 카드의 Size 필드에는 100 값을 입력한

다. Android 2.3 – API Level 9를 선택하면 Abstracted LCD density 속성이 인치당 160개의 점

(dpi)으로 설정된 Default (HVGA)가 기본 스킨으로 선택된다.

알아두기

안드로이드 2.3.1을 설치한 경우 Android 2.3.1 - API Level 9를 선택하면 Abstracted

LCD density 속성이 240 dpi로 설정된 Default (WVGA800)가 기본 스킨으로 선택된다.

더불어 Max VM application heap size 속성도 24메가바이트로 설정된다.

Page 71: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 47

앞의 값들을 입력하고 화면은 기본 설정으로 둔 채 Create AVD를 클릭해 AVD 생성을 마무

리한다. 그럼 이제 그림 1-8의 AVD 창에 test_AVD 항목이 추가돼 있을 것이다.

주의

컴파일된 앱을 테스트하기 위해 AVD를 생성하는 경우 앱에서 필요로 하는 API 레벨보다 크거

나 같도록 타깃 플랫폼을 설정해야 한다. 다시 말해 AVD에서 앱을 테스트할 때 앱에서는 AVD

의 API 레벨에서 지원하는 것보다 더 최신 버전의 플랫폼 API에 접근할 수 없다.

SDK Manager를 사용해 AVD를 만드는 방식이 더 쉽기는 하지만 android create avd -n

name -t targetID [-option value]....처럼 지정해 android 툴을 통해서도 AVD를 만들 수 있다.

이 구문에서 name은 기기 설정(target_AVD 같은 이름)을 나타내고, targetID는 대상으로 하는

안드로이드 플랫폼을 식별하는 정수 ID(이 정수 ID 값은 android list targets를 실행해 알 수 있

다)이며, [-option value]...는 이어지는 다양한 옵션(SD 카드 크기 등)을 나타낸다.

충분한 옵션을 지정하지 않으면 android는 커스텀 하드웨어 프로필을 생성하는 창을 띄운다.

이때 커스텀 하드웨어 프로필을 사용하지 않고 기본 하드웨어 에뮬레이션 옵션을 사용하려면

엔터키를 누르면 된다. 예를 들어 android create avd -n test_AVD -t 1 명령을 실행하면 test_

AVD라는 이름의 AVD가 생성된다. 이 명령에서는 1이 안드로이드 2.3 플랫폼을 나타낸다고 가

정하며, 이 명령을 실행하면 커스텀 하드웨어 프로필을 작성하는 창이 나타난다.

알아두기

개별 AVD는 다른 기기와 무관하게 동작하며 자체 사용자 데이터 저장을 위한 공간과 자체 SD

카드 등을 갖고 있다. AVD를 사용해 emulator 툴을 실행하면 이 툴에서는 AVD 디렉터리로

부터 사용자 데이터와 SD 카드를 로드한다. 기본적으로 emulator는 사용자 데이터, SD 카드

데이터, 캐시를 AVD에 할당된 디렉터리에 저장한다.

Page 72: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

48 l 안드로이드 레시피

1–4. AVD 시작하기

◈ 문제

AVD에 앱을 설치하고 실행하려면 먼저 AVD를 실행해야 한다. AVD가 실행되기까지는 몇 분이

걸린다. 이제 AVD를 실행하는 법을 배우려고 한다.

◈ 해결책

SDK Manager 툴을 사용해 AVD를 시작한다. 또는 emulator 툴을 사용해 AVD를 시작할 수도

있다.

◈ 문제 풀이

그림 1-8을 살펴보면 Start 버튼이 비활성화된 것을 볼 수 있다. 이 버튼은 AVD 항목을 생성하면

바로 활성화된다. Start를 클릭해 현재 선택된 AVD 항목을 에뮬레이터의 기기 설정으로 사용해

emulator 툴을 실행하자.

그럼 Launch Options 대화상자가 나타날 것이다. 이 대화상자에서는 AVD의 스킨과 화면 밀

도를 보여준다. 또 물리적 기기의 화면 크기에 맞게끔 에뮬레이터의 디스플레이 해상도의 스케

일을 조정하는 체크박스와 Wipe user data 체크박스가 선택 해제된 것을 보여준다.

알아두기

앱을 수정하는 동안 에뮬레이터에 앱을 패키징하고 설치하는 일을 자주 하게 된다. 에뮬레이

터에서는 AVD가 재시작하더라도 앱과 앱의 상태 데이터를 사용자 데이터 디스크 파티션에 그

대로 유지한다. 간혹 앱을 수정했을 때 앱이 제대로 실행되게 하려면 에뮬레이터의 사용자 데

이터 파티션을 삭제해야 할 수도 있다. 이 데이터를 삭제하려면 Wipe user data 체크박스를

선택하면 된다.

Page 73: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 49

Launch 버튼을 클릭해 AVD를 사용해 에뮬레이터를 실행한다. 그럼 SDK Manager는 잠시

동안 Starting Android Emulator 대화상자를 보여주고, 이어서 명령창(윈도우 운영체제의 경우)

을 보여준 후, 최종적으로 에뮬레이터 창을 표시한다.

에뮬레이터 창은 안드로이드 로고를 검은색 배경과 홈 화면 위에 보여주는 왼쪽 영역과 전화

기 버튼과 키보드를 보여주는 오른쪽 영역으로 나뉜다. 그림 1-10은 test_AVD 기기의 에뮬레이

터 영역을 보여준다.

그림 1-10. 에뮬레이터 창은 왼쪽에 홈 화면을 보여주고 오른쪽에 전화기 버튼과 키보드를 보여준다.

기존에 안드로이드 기기를 사용해 봤다면 이런 홈 화면 전화기 버튼, 키보드가 익숙할 것이다.

기존에 안드로이드 기기를 사용해본 적이 없다면 다음 사항들을 참고로 알아두자.

■ 홈 화면은 안드로이드 기기를 사용할 때 시작점 기능을 하는 특수 앱이다.

■ 상태바는 홈 화면 위에 나타난다(더불어 모든 앱 화면 위에). 상태바는 현재 시각, 남은 배

터리 양, 기타 정보를 보여준다. 또 알림을 확인할 수 있게도 해준다.

■ 홈 화면에서는 배경화면을 보여준다. 전화기 버튼에서 MENU를 클릭하고 팝업 메뉴에

서 Wallpaper를 클릭하면 배경화면을 바꿀 수 있다.

Page 74: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

50 l 안드로이드 레시피

■ 홈 화면은 상단 부근에 구글 검색 위젯을 보여준다. 이 위젯은 홈 화면과 다른 앱에서 임

베드할 수 있는 작은 앱 뷰이며 주기적으로 업데이트된다.

■ 홈 화면은 하단 부근에 앱 론처를 보여준다. 앱 론처는 휴대전화, 인터넷 앱처럼 자주 사

용하는 앱을 실행할 수 있는 아이콘을 보여주며 사각형 그리드를 통해 설치된 전체 앱을

보여준다. 이들 앱 아이콘 중 하나를 더블클릭하면 해당 앱을 실행할 수 있다.

■ 홈 화면은 여러 화면으로 구성된다. 앱 론처 옆에 있는 좌우측 점을 클릭하면 현재 화면

의 좌우측에 있는 화면으로 이동할 수 있다. 이들 점의 개수는 왼쪽이나 오른쪽에서 볼

수 있는 화면의 개수를 나타낸다. 또는 앱 론처의 중간에 있는 아이콘 위에 마우스를 올

리고 마우스를 누른 채로 가만히 있으면 작은 화면 아이콘 목록이 나타난다. 이들 아이

콘 중 하나를 클릭하면 해당 홈 화면을 볼 수 있다.

■ 전화기 버튼 중 집 모양의 버튼은 어느 위치에서든 바로 홈 화면으로 이동할 수 있게 해

준다.

■ MENU 버튼은 현재 실행 중인 앱과 관련한 메뉴 선택항목을 보여준다.

■ 휘어진 화살표 아이콘 버튼은 액티비티 스택에 있는 이전 액티비티로 돌아가게 해준다.

AVD가 실행되는 동안은 마우스를 사용해 터치 화면과 상호작용할 수 있고 키보드를 사용해

AVD 키를 누를 수 있다. 표 1-2는 AVD 키와 키보드 키 사이의 매핑 관계를 보여준다.

표 1–2. AVD 키와 키보드 키 사이의 매핑관계

AVD 키 키보드 키

홈 HOME

메뉴 (왼쪽 소프트 키) F2 또는 Page Up

시작 (오른쪽 소프트 키) Shift-F2 또는 Page Down

뒤로 가기 ESC

전화/다이얼 버튼 F3

끊기/전화 종료 버튼 F4

검색 F5

전원 버튼 F7

오디오 볼륨 올림 버튼 KEYPAD_PLUS, Ctrl-5

오디오 볼륨 내림 버튼 KEYPAD_MINUS, Ctrl-F6

Page 75: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 51

AVD 키 키보드 키

카메라 버튼 Ctrl-KEYPAD_5, Ctrl-F3

이전 레이아웃 방향(세로 또는가로)으로 전환 KEYPAD_7, Ctrl-F11

다음 레이아웃 방향으로 전환 KEYPAD_9, Ctrl-F12

셀 네트워크 온/오프 F8

코드 프로파일링 토글 F9 (-trace 시작 옵션을 사용한 경우만)

전체 화면 모드 토글 Alt-Enter

트랙볼 모드 토글 F6

임시로 트랙볼 모드에 들어감(키를 누르는 동안) Delete

DPad 왼쪽/위쪽/오른쪽/아래쪽 KEYPAD_4/8/6/2

DPad 가운데 클릭 KEYPAD_5

오니온(Onion) 알파 증가/감소 KEYPAD_MULTIPLY(*) / KEYPAD_DIVIDE(/)

키패드 키를 사용하려면 먼저 개발 컴퓨터상에서 NumLock을 비활성화해야 한다.

표 1-2에서는 코드 프로파일링 토글과 관련해 -trace 시작 옵션을 설명하고 있다. 이 옵션은

emulator 툴을 통해 AVD를 시작할 때 파일에 프로파일링 결과를 저장하게 해주는 옵션이다.

예를 들어 emulator -avd test_AVD -trace results.txt 명령을 사용하면 test_AVD 기기 설정

을 사용해 에뮬레이터가 시작되고 F9를 누를 때 프로파일링 결과가 results.txt에 저장된다. F9

를 다시 누르면 코드 프로파일링을 중단할 수 있다.

그림 1-10에서는 제목에 5554:test_AVD가 나와 있다. 5554 값은 동적으로 쿼리를 사용하고

기타 다른 방식으로 AVD 환경을 제어하는 콘솔 포트를 나타낸다.

알아두기

안드로이드에서는 16개까지 동시 실행되는 AVD를 지원한다. 각 AVD에는 5554로 시작하는

짝수 번호의 콘솔 포트 번호가 지정된다.

Page 76: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

52 l 안드로이드 레시피

telnet localhost console-port를 지정하면 AVD의 콘솔에 연결할 수 있다. 예를 들어 telnet

localhost 5554를 지정하면 test_AVD의 콘솔에 연결할 수 있다. 그림 1-11은 윈도우 XP에서 이

명령을 실행한 결과창을 보여준다.

그림 1-11. 명령과 관련한 도움말을 확인하려면 명령 이름만 입력하면 된다.

1–5. UC 앱에 대한 소개

◈ 문제

이제 안드로이드 SDK와 안드로이드 플랫폼을 설치했고 표준 AVD까지 만들었으니 앱을 개발

해 AVD에 설치하고 실행할 준비가 다 끝났다. 물론 예제 1-2의 SimpleActivity 클래스를 기반으

로 앱을 만들 수도 있지만, 이 레시피의 UC 앱을 참고하면 더 도움이 될 것이다.

◈ 해결책

UC (Units Converter(단위 변환기)의 약자) 앱은 단위 타입을 변환해주는 앱이다. 예를 들어 섭

씨온도를 같은 값의 화씨온도로 변환하거나 파운드 값을 킬로그램 단위로 변환하는 등의 일을

한다.

Page 77: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 53

◈ 문제 풀이

UC 앱은 변환할 숫자 단위를 입력하고 변환 결과를 보여줄 입/출력 텍스트 필드와 변환할 대상

을 선택하는 선택 화면, 텍스트 필드의 내용을 지우거나, 변환을 수행, 앱을 닫는 등의 일을 하는

버튼으로 구성된 UI를 보여주는 단일 액티비티(액티비티 이름도 UC다)로 이뤄져 있다.

예제 1-9는 UC 액티비티의 소스 코드를 보여준다.

예제 1-9 | 단위 변환을 수행하는 액티비티

// UC.java

package com.apress.uc;

import android.app.Activity;

import android.os.Bundle;

import android.text.Editable;

import android.text.TextWatcher;

import android.view.View;

import android.widget.AdapterView;

import android.widget.ArrayAdapter;

import android.widget.Button;

import android.widget.EditText;

import android.widget.Spinner;

public class UC extends Activity

{

private int position = 0;

private double[] multipliers =

{

0.0015625, // 에이커를 제곱 마일로

101325.0, // 대기 압력을 파스칼로

100000.0, // 바를 파스칼로

0, // 섭씨를 화씨 온도로(임시 지정값)

0, // 화씨를 섭씨 온도로(임시 지정값)

Page 78: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

54 l 안드로이드 레시피

0.00001, // 다인을 뉴톤으로

0.3048, // 피트/초를 미터/초로

0.0284130625, // 액량온스 (영국식)를 리터로

0.0295735295625, // 액량온스(미국식)를 리터로

746.0, // 마력 (전기)을 와트로

735.499, // 마력 (미터법)을 와트로

1/1016.0469088, // 킬로그램을 톤으로(영국식 또는 롱 톤)

1/907.18474, // 킬로그램을 톤으로 (미국식 또는 쇼트 톤)

1/0.0284130625, // 리터를 액량온스로(영국식)

1/0.0295735295625, // 리터를 액량온스로 (미국식)

331.5, // 마하 숫자를 미터/초로

1/0.3048, // 미터/초를 피트/초로

1/331.5, // 미터/초를 마하 숫자로

0.833, // 마일/갤론 (영국식)을 마일/갤론(미국식)으로

1/0.833, // 마일/갤론(미국식)을 마일/갤론(영국식)으로

100000.0, // 뉴톤을 다인으로

1/101325.0, // 파스칼을 대기 압력으로

0.00001, // 파스칼을 바로

640.0, // 제곱 마일을 에이커로

1016.0469088, // 톤(영국식 또는 롱 톤)을 킬로그램으로

907.18474, // 톤(미국식 또는 쇼트 톤)을 킬로그램으로

1/746.0, // 와트를 마력으로(전기)

1/735.499 // 와트를 마력으로(미터법)

};

@Override

public void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

final EditText etUnits = (EditText) findViewById(R.id.units);

final Spinner spnConversions = (Spinner) findViewById(R.id.conversions);

ArrayAdapter<CharSequence> aa;

aa = ArrayAdapter.

createFromResource(this, R.array.conversions,

android.R.layout.simple_spinner_item);

aa.setDropDownViewResource(android.R.layout.simple_spinner_item);

spnConversions.setAdapter(aa);

Page 79: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 55

AdapterView.OnItemSelectedListener oisl;

oisl = new AdapterView.OnItemSelectedListener()

{

@Override

public void onItemSelected(AdapterView<?> parent, View view,

int position, long id)

{

UC.this.position = position;

}

@Override

public void onNothingSelected(AdapterView<?> parent)

{

System.out.println("nothing");

}

};

spnConversions.setOnItemSelectedListener(oisl);

final Button btnClear = (Button) findViewById(R.id.clear);

AdapterView.OnClickListener ocl;

ocl = new AdapterView.OnClickListener()

{

@Override

public void onClick(View v)

{

etUnits.setText("");

}

};

btnClear.setOnClickListener(ocl);

btnClear.setEnabled(false);

final Button btnConvert = (Button) findViewById(R.id.convert);

ocl = new AdapterView.OnClickListener()

{

@Override

public void onClick(View v)

{

String text = etUnits.getText().toString();

double input = Double.parseDouble(text);

double result = 0;

if (position == 3)

Page 80: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

56 l 안드로이드 레시피

result = input*9.0/5.0+32; // 섭씨를 화씨로

else

if (position == 4)

result = (input-32)*5.0/9.0; // 화씨를 섭씨로

else

result = input*multipliers[position];

etUnits.setText(""+result);

}

};

btnConvert.setOnClickListener(ocl);

btnConvert.setEnabled(false);

Button btnClose = (Button) findViewById(R.id.close);

ocl = new AdapterView.OnClickListener()

{

@Override

public void onClick(View v)

{

finish();

}

};

btnClose.setOnClickListener(ocl);

TextWatcher tw;

tw = new TextWatcher()

{

@Override

public void afterTextChanged(Editable s)

{

}

@Override

public void beforeTextChanged(CharSequence s, int start, int count,

int after)

{

}

@Override

public void onTextChanged(CharSequence s, int start, int before,

int count)

{

Page 81: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 57

if (etUnits.getText().length() == 0)

{

btnClear.setEnabled(false);

btnConvert.setEnabled(false);

}

else

{

btnClear.setEnabled(true);

btnConvert.setEnabled(true);

}

}

};

etUnits.addTextChangedListener(tw);

}

}

예제 1-9는 액티비티에 해당하는 소스 파일(UC.java)을 쉽게 인식할 수 있게 해주는 주석으로

내용을 시작한다. 그런 다음 소스 파일인 UC 클래스가 저장된 패키지명(com.apress.uc)을 지정

한 패키지 명령문을 선언하고, 이어서 다양한 안드로이드 API 타입을 임포트하는 import 명령

문이 나온다.

구글의 안드로이드 API 레퍼런스(http://developer.android.com/reference/packages.

html)에서 API 타입을 쉽게 찾으려면 안드로이드 API의 패키지 구조에 익숙해지는 게 좋다.

이 문서는 안드로이드 앱 개발에 더 깊이 들어감에 따라 이들 API 타입을 찾을 때마다 자주 찾

아보게 될 것이다.

이어서 예제 1-9는 Activity를 상속한 UC 클래스를 선언한다. 이 클래스에서는 먼저 position

과 multipliers 필드를 선언한다.

■ position은 스피너에서 선택한 변환 종류에 대한 0 기반(zero-based) 인덱스로 기본값은

0이다(스피너에서 제일 먼저 보여주는 변환 종류). 이 필드에 스피너의 선택 위치를 저장

해 두면 수행할 변형 작업을 고르는 게 더 간단해진다.

Page 82: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

58 l 안드로이드 레시피

■ multipliers는 곱셈 인자 값을 배열로 저장한다. 이 배열에 들어 있는 각 항목값은 스피너

값에 대응된다. 변환은 사용자가 입력한 값에 multipliers[position]를 곱해 이뤄진다. 하

지만 두 가지 예외가 있는데, 바로 섭씨-화씨 변환과 화씨-섭씨 변환이다. 이들 변환은 값

을 추가하거나 빼는 작업이 별도로 필요하므로 따로 처리한다.

앱의 모든 기능은 onCreate(Bundle) 메서드 내에서 이뤄진다. 이 액티비티에서는 다른 메서드

가 필요 없으므로 이 앱의 코드는 상당히 간단하다.

onCreate(Bundle)는 먼저 상위 클래스에서 같은 이름의 메서드를 호출한다. 이 원칙은 오버라

이드하는 액티비티 메서드에서 항상 따라야 하는 원칙이다.

그런 다음 이 메서드는 setContentView(R.layout.main)를 실행해 앱의 UI를 설정한다.

R.layout.main은 앱의 코드에서 필요로 하는 데이터 중 하나인 리소스를 식별하는데, 이처럼

리소스를 개별 파일로 저장하면 코드와 상관없이 리소스를 유지할 수 있다.

알아두기

리소스는 UI를 다른 화면 크기로 쉽게 바꿀 수 있게 해주고 다른 언어에 대한 지원을 쉽게 해

앱의 관리를 편하게 해준다.

리소스의 ID는 다음과 같이 해석한다.

■ R은 앱이 빌드될 때 (aapt 툴에 의해) 생성되는 클래스의 이름이다. 이 클래스의 이름이 R

인 이유는 이 클래스의 콘텐츠가 다양한 리소스(레이아웃, 이미지, 문자열, 색상 등)를 나

타내기 때문이다.

■ layout은 R 안에 들어 있는 클래스명을 나타낸다. ID가 이 클래스에 저장된 모든 리소스

는 특정 레이아웃 리소스를 나타낸다. 각 리소스 종류는 리소스의 이름과 유사한 이름의

클래스와 관련이 있다. 예를 들어 string은 문자열 리소스를 나타낸다.

■ main은 layout 내에 선언된 int 상수의 이름이다. 이 리소스 ID는 main 레이아웃 리소스

를 나타낸다. 좀 더 구체적으로 설명하자면 main은 메인 화면의 레이아웃 정보를 저장하

는 main.xml을 나타낸다. main은 UC 클래스에서 유일한 레이아웃 리소스다.

Page 83: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 59

R.layout.main은 Activity의 void setContentView(int layoutResID) 메서드에 인자로 전달돼,

안드로이드에서 main.xml에 저장된 레이아웃 정보를 사용해 UI 화면을 생성하게 한다. 이때 내

부적으로 안드로이드는 main.xml에 기술된 UI 컴포넌트들을 생성한 후 main.xml의 레이아웃

데이터에서 지정한 대로 이를 화면에 배치한다.

사용자 인터페이스는 뷰(UI 컴포넌트의 추상화)와 뷰 그룹(관련 UI 컴포넌트를 그룹으로 묶

는 뷰)을 기반으로 한다. 뷰는 android.view.View 클래스의 하위 클래스 인스턴스로서 자바 컴

포넌트와 유사하다. 뷰 그룹은 android.view.ViewGroup 추상 클래스의 하위 클래스 인스턴스

로서 자바 컨테이너와 유사하다. 안드로이드에서는 이런 뷰(버튼 또는 스피너 등)를 위젯이라고

부른다.

알아두기

여기서 말한 위젯을 안드로이드 홈 화면에 있는 위젯과 혼동하지 말자. 물론 같은 용어를 사용

하기는 하지만 UI 위젯과 홈 화면 위젯은 서로 다르다.

계속해서 onCreate(Bundle) 메서드는 �nal EditText etUnits = (EditText) �ndViewById(R.

id.units);를 실행한다. 이 명령문에서는 먼저 View의 View �ndViewById(int id) 메서드를 호출

해 main.xml에 units로 선언된 EditText 뷰를 찾고 android.widget.EditText 인스턴스를 생성

한 후, 이 인스턴스를 뷰의 선언 정보로 초기화하고, 객체의 참조를 지역 변수 etUnits에 저장한

다. 이 변수는 나중에 익명 내부 클래스에서 접근할 수 있게끔 �nal로 선언했다.

마찬가지 방식으로 f inal Spinner spnConversions = (Spinner) f indViewById(R.

id.conversions);은 main.xml에 저장된 선언 정보를 사용해 android.widget.Spinner 클래스의

인스턴스를 생성하고 생성된 객체 참조를 나중에 접근할 수 있게 변수에 저장한다.

알아두기

유지보수 관점에서는 UI 화면을 레이아웃 리소스를 통해 선언하고 안드로이드에서 위젯을 생

성해 여러분 대신 레이아웃에 추가하게 하는 게 더 좋지만, 안드로이드에서는 필요할 경우 프

로그래밍적으로 위젯을 생성해 레이아웃을 지정할 수 있는 옵션도 제공한다.

Page 84: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

60 l 안드로이드 레시피

이어서 onCreate(Bundle) 메서드는 보여줄 텍스트를 갖고 있지 않은 스피너 객체에 사

용하기 위해 android.widget.ArrayAdapter 클래스의 ArrayAdapter<CharSequence>

createFromResource(Context context, int textArrayResId, int textViewResId) 메서드를 호출

한다. 이 메서드는 텍스트 메시지를 제공하는 배열 어댑터를 스피너에게 반환하는 메서드다.

■ context에는 현재 앱 컴포넌트를 나타내는 Context 인스턴스가 필요하다. 여기서는 이 인

스턴스로 this 키워드를 지정해 현재 액티비티를 넘겨줬다.

■ textArrayResId에는 문자열(예를 들어 Degrees Celsius to Degrees Fahrenheit 같은 문

자열)을 저장한 배열 리소스의 ID가 필요하다. 여기서는 이 값으로 다양한 변환 종류를

지정한다. 이 파라미터에 전달된 R.array.conversions는 변환 문자들을 보관하는 array

리소스의 이름으로 arrays.xml 파일(이 레시피에서 나중에 설명)에 저장된 conversions

를 지정한다.

■ textViewResId는 스피너의 모양을 만드는 데 사용할 layout 리소스의 ID를 필요로 한다.

이 파라미터에 넘겨준 android.R.layout.simple_spinner_item 인자는 android 패키지

의 R 클래스에 들어 있는 layout 클래스에서 미리 지정한 ID다. simple_spinner_item은

자바 스윙 콤보박스 같은 모습의 스피너를 보여준다.

createFromResource(Context, int, int), onCreate(Bundle)를 호출한 후에는 android.

R.layout.simple_spinner_item을 인자로 사용해 ArrayAdapter의 void setDropDownView

Resource(int resource) 메서드를 호출한다. 이 메서드를 호출하면 스피너의 드롭다운 뷰 영역이

생성된다.

이제 배열 어댑터를 생성했고 적당한 단위 변환 문자열과 레이아웃 정보로 이를 초기화했으

므로 onCreate(Bundle)에서 spnConversions.setAdapter(aa);를 호출해 이 정보를 스피너에 설

정해준다. 이 메서드를 호출하면 스피너 위젯이 앞에서 설정한 정보에 접근해 사용자에게 변환

목록을 보여줄 수 있다.

Page 85: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 61

알아두기

Spinner는 android.widget.AdapterView<T extends Adapter> 조상 클래스로부터 void

setAdapter(T) 메서드를 상속한다.

적절한 변환을 수행하려면 현재 선택된 스피너 항목을 UC 앱에서 계속 추적해야 한다.

onCreate(Bundle) 메서드는 이를 위해 스피너의 위치를 (앞에서 말한) position 변수에 대입해

아이템 선택 이벤트에 스피너가 반응할 수 있도록 리스너를 등록한다.

onCreate(Bundle) 메서드는 먼저 ArrayAdapter의 OnItemSelectedListener 인터페이스를 구

현하는 익명 클래스 인스턴스를 생성한 후 AdapterView의 void setOnItemSelectedListener(A

dapterView.OnItemSelectedListener listener)를 호출해 이 인스턴스를 스피너에 등록한다.

OnItemSelectedListener의 void onItemSelected(AdapterView<?> parent, View view, int

position, long id) 메서드는 사용자가 새 항목을 선택할 때마다 호출되므로 선택 위치를 저장하

기에 안성맞춤이다. 이때 사용하지 않더라도 void onNothingSelected(AdapterView<?> parent)

메서드도 함께 구현해줘야 한다.

이제 스피너에 대한 작업을 마무리했으니 onCreate(Bundle) 메서드는 이어서 Clear, Convert,

Close 버튼을 만드는 일을 한다. 각 버튼에 대해 �ndByViewId(int)를 호출해 main.xml로부터

버튼 정보를 가져온 후 android.widget.Button 클래스의 인스턴스를 생성한다.

그런 다음 AdapterView에 들어 있는 onClickListener 인터페이스를 사용해 리스너 객

체를 생성한다. 이들 리스너 객체의 onClick(View v) 메서드는 사용자가 버튼을 클릭할 때

마다 호출된다. 각 리스너는 AdapterView의 void setOnItemClickListener(AdapterView.

OnItemClickListener listener) 메서드를 호출해 버튼별로 등록한다.

Clear 버튼의 클릭 리스너에서는 etUnits.setText("")를 실행해 사용자 입력값을 없애고

etUnits 텍스트 필드의 변환 결과를 지우는 일을 한다. Close 버튼의 클릭 리스너도 마찬가지

로 간단한다. 이 리스너는 finish()를 호출해 현재 액티비티, 즉 UC 앱을 종료한다. 그에 반해

Convert 버튼의 클릭 리스너는 좀 더 많은 일을 한다.

Page 86: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

62 l 안드로이드 레시피

1. etUnits 텍스트 필드의 내용을 String 객체로 가져온다: String text = etUnits.getText().

toString();.

2. 이 String 객체를 Double 타입의 부동 소수로 파싱한다: double input = Double.

parseDouble(text);

3. 변환을 수행하고 position의 값에 근거해 결과를 저장한다: result = input*9.0/5.0+32;,

result = (input-32)*5.0/9.0;, 또는 result = input*multipliers[position];.

4. result: etUnits.setText(""+result);를 사용해 etUnits를 업데이트한다.

onCreate(Bundle)에서 할 일이 아직 하나 더 있다. 바로 etUnits가 비어 있을 때는 Clear와

Convert 버튼을 비활성화하는 것이다. 사실 텍스트 필드가 비어 있을 때는 내용을 지운다는 게

의미가 없을뿐더러 parseDouble()에서도 빈 텍스트 필드를 파싱하려고 하면 예외가 발생하기

때문이다.

onCreate(Bundle)는 android.widget.TextView의 void addTextChangedListener(TextWatc

her watcher) 메서드를 호출해 etUnits 텍스트 필드에 텍스트와처(android.text.TextWatcher 인

터페이스를 구현한 클래스 객체)를 등록함으로써 이 작업을 처리한다. TextView는 EditText의

상위 클래스다.

TextWatcher 인터페이스는 void afterTextChanged(Editable s), void beforeTextChanged(

CharSequence s, int start, int count, int after), void onTextChanged(CharSequence s, int

start, int before, int count) 메서드를 선언하고 있다. 이 중 마지막 메서드만 오버라이드하면

Clear와 Convert 버튼을 활성 또는 비활성화할 수 있다.

onTextChanged(s, int, int, int) 메서드에서는 먼저 텍스트 필드의 길이를 반환하는 etUnits.

getText().length() 결과 값을 검사한다. 이 길이가 0이면(텍스트 필드가 비어 있는 경우)

btnClear.setEnabled(false);와 btnConvert.setEnabled(false);를 호출해 버튼을 비활성화한다.

이 길이가 0이 아니면 btnClear.setEnabled(true);와 btnConvert.setEnabled(true);를 호출해 버

튼을 활성화한다.

UC 액티비티에서 대부분의 리소스는 XML 파일로 저장돼 있다. 예를 들어 UC 액티비티의 위

젯과 레이아웃 정보는 예제 1-10에 나온 main.xml에 들어 있다.

Page 87: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 63

예제 1-10 | 위젯과 레이아웃 정보를 담고 있는 main.xml 파일

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:gravity="center_vertical"

android:background="@drawable/gradientbg"

android:padding="5dip">

<LinearLayout android:layout_width="fill_parent"

android:layout_height="wrap_content">

<TextView android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginRight="10dip"

android:text="@string/units"

android:textColor="#000000"

android:textSize="15sp"

android:textStyle="bold"/>

<EditText android:id="@+id/units"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:hint="type a number"

android:inputType="numberDecimal|numberSigned"

android:maxLines="1"/>

</LinearLayout>

<Spinner android:id="@+id/conversions"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:prompt="@string/prompt"/>

<LinearLayout android:layout_width="fill_parent"

android:layout_height="wrap_content">

<Button android:id="@+id/clear"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:layout_weight="1"

android:text="@string/clear"/>

<Button android:id="@+id/convert"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:layout_weight="1"

Page 88: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

64 l 안드로이드 레시피

android:text="@string/convert"/>

<Button android:id="@+id/close"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:layout_weight="1"

android:text="@string/close"/>

</LinearLayout>

</LinearLayout>

예제 1–10은 먼저 <LinearLayout> 태그를 선언하는 것부터 시작한다. 이 태그는 화면상에서

가로 또는 세로로 안에 들어 있는 위젯과 자식 레이아웃을 정렬하는 레이아웃(안드로이드 기기

의 화면상에 포함된 뷰를 특정 방식으로 정렬하는 뷰 그룹)이다.

<LinearLayout> 태그에서는 선형 레이아웃을 제어하는 데 사용할 몇 가지 어트리뷰트를 지정

하고 있다. 이들 어트리뷰트에는 다음과 같은 내용이 포함된다.

■ orientation은 가로 레이아웃이나 세로 레이아웃을 지정한다. 기본 방향은 가로다. 이 어

트리뷰트에 지정할 수 있는 값은 "horizontal"과 "vertical"밖에 없다.

■ layout_width는 레이아웃의 너비를 나타낸다. 사용할 수 있는 값에는 "�ll_parent"(전체

너비를 차지)와 "wrap_content"(뷰에서 필요한 너비만 차지) 등이 있다. �ll_parent는 안

드로이드 2.2에서 match_parent로 이름이 바뀌었지만 여전히 지원하며 폭넓게 사용되

고 있다.

■ layout_height는 레이아웃의 높이를 나타낸다. 사용할 수 있는 값에는 "�ll_parent"(전

체 높이를 차지)와 "wrap_content"(뷰에서 필요한 높이만 차지) 등이 있다. �ll_parent는

안드로이드 2.2에서 match_parent로 이름이 바뀌었지만 여전히 지원하며 폭넓게 사용

되고 있다.

■ gravity는 화면을 기준으로 레이아웃 위치를 어떻게 정할지 지정한다. 예를 들어

"center_vertical"은 레이아웃을 화면의 세로 중심에 배치하도록 지정한다.

■ background는 리소스 참조(@ 문자로 시작하는 특수 구문)를 통한 배경 이미지 또는 그

라디언트 등을 나타낸다. 예를 들어 "@drawable/gradientbg"는 gradientbg라는 이름의

드로어블 리소스(이미지 또는 그래픽)를 참조한다.

■ padding은 레이아웃과 화면 모서리 사이의 경계를 지정하기 위해 레이아웃에 추가할 공

Page 89: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 65

간을 나타낸다. "5dip"는 5만큼의 밀도 독립적인 픽셀(dip)을 나타낸다. dip는 가상의 픽

셀 단위로 앱에서는 이 단위를 사용해 화면 밀도와 상관없이 크기/위치 레이아웃을 표현

할 수 있다.

알아두기

밀도 독립적 픽셀은 안드로이드에서 기본 밀도로 가정하는 160 dip 화면에서 물리적 1픽셀

과 동일하다. 런타임 시점에 안드로이드는 사용 중인 화면의 실제 밀도를 기반으로 필요한

dip 단위의 변환을 투명하게 처리한다. Dip 단위는 픽셀 = dip * (밀도 / 160) 공식에 의해

화면 픽셀로 변환된다. 예를 들어 240dpi 화면에서 1 dip는 1.5 물리 픽셀과 같다. 구글에서

는 각기 다른 화면에서 UI가 제대로 보일 수 있게끔 dip 단위를 사용해 앱의 UI를 정의할 것을

권장한다.

첫 번째 선형 레이아웃 안에는 두 번째 선형 레이아웃이 들어 있다. 이 레이아웃에서는

orientation 어트리뷰트를 지정하지 않았으므로 이 레이아웃은 위젯을 가로로 배치한다. 부모

레이아웃과 마찬가지로 이 레이아웃의 layout_width 값은 "fill_parent"로 지정했다. 하지만

layout_height 값은 "wrap_content"로 지정해 내부 레이아웃이 전체 화면을 차지하지 못하게

했다.

이 선형 레이아웃에는 텍스트뷰와 에디트텍스트 엘리먼트가 들어 있다. 텍스트뷰 엘리먼트는

에디트텍스트 엘리먼트에서 지정한 위젯의 라벨을 표시하는 위젯을 나타낸다. <TextView> 태

그는 layout_width 및 layout_height과 더불어 다음 어트리뷰트를 보여준다.

■ layout_marginRight은 텍스트뷰 위젯의 오른쪽에 남겨둘 여백을 지정한다. 여기서는 이

여백으로 10 dip를 지정했다.

■ text는 위젯에서 보여줄 텍스트를 나타낸다. 이 텍스트는 표준 strings.xml 리소스 파일

에 들어 있는 units 항목에 대한 문자열 참조(예제 1-12 참고)에 해당하는 @string/units

를 통해 지정했다. 이 항목의 값이 바로 이 텍스트에 사용된다.

■ textColor는 텍스트 색상을 나타낸다. 색상은 #RRGGBB 형식으로 지정한다. #00000은

검은색을 나타낸다.

Page 90: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

66 l 안드로이드 레시피

■ textSize는 텍스트의 크기를 나타낸다. 여기서 이 크기는 "15sp"로 지정했는데, 이 값은

15만큼의 스케일 독립 픽셀로 해석된다(사용자는 기기 설정을 통해 스케일을 선택한다).

구글에서는 (사용자가 텍스트 스케일을 정할 수 있게) 스케일 독립 픽셀을 사용하거나

(사용자가 텍스트 스케일을 정할 수 없게) 기기 독립적인 픽셀을 사용할 것을 권장한다.

■ textStyle은 볼드나 이탤릭 같은 텍스트 스타일을 나타낸다. 여기서는 스타일을 "bold"로

설정해 화면에서 텍스트가 강조되게 했다.

<EditText> 태그는 다음 어트리뷰트를 보여준다.

■ id는 코드에서 참조할 수 있게 이 위젯 엘리먼트를 식별해준다. 이 리소스 식별자는 @+id

접두어로 시작하는 특수 구문을 사용해 지정한다. 예를 들어 "@+id/units"를 사용하면

units라는 리소스 ID로 이 에디트텍스트 위젯을 식별할 수 있다. 이 위젯의 리소스를 코

드에서 참조할 때는 R.id.units를 사용한다.

■ hint는 아무 내용도 입력하지 않았을 때 텍스트 필드에 보여줄 문자열을 나타낸다. 이 어

트리뷰트 값은 이 텍스트 필드에 입력할 데이터의 종류를 사용자에게 알려준다. 이 어트

리뷰트에는 문자열 리소스를 지정하는 대신 "type a number" 리터럴 문자열을 바로 지정

했다. 물론 이렇게 리터럴 문자 값을 리소스(또는 코드)에 임베드할 수도 있지만 앱을 프

랑스어나 독일어 등 다른 언어로 쉽게 지역화하려면 별도 strings.xml 리소스 파일에 문

자열 값을 저장해야 한다는 점을 주의하자.

■ inputType은 사용자가 입력할 데이터 종류를 나타낸다. 기본값을 사용하면 사용자는 모

든 문자를 입력할 수 있다. 하지만 숫자 값이 필요할 때 사용자가 아무 문자나 입력하면

안되므로 여기서는 inputType에 "numberDecimal|numberSigned"를 지정했다. 이 문

자열은 10진수만 입력할 수 있게 지정한다. 더불어 음수 값도 입력할 수 있다.

■ maxLines은 텍스트 필드에 입력할 수 있는 텍스트 줄 수를 제한한다. "1"을 이 어트리뷰

트에 대입하면 한 줄 텍스트만 입력할 수 있게 된다.

이 선형 레이아웃 엘리먼트 아래에는 conversions라는 이름의 스피너 엘리먼트가 있다. 이 엘

리먼트는 화면 너비는 모두 채우지만 화면 높이는 모두 채우지 않도록 선언했다. 더불어 사용자

가 변환할 내용을 (그림 1-15에 보이는 드롭다운 뷰를 통해) 선택하도록 이 엘리먼트의 prompt

어트리뷰트에는 "@string/prompt" 값을 지정했다.

Page 91: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 67

스피너 엘리먼트 아래에는 또 다른 선형 레이아웃이 있다. 이 레이아웃 안에는 Clear, Convert,

Close 버튼이 들어 있다. 각 버튼에는 코드에서 참조할 수 있는 고유 ID가 지정돼 있다. 버튼의

layout_weight 어트리뷰트에는 다른 버튼의 layout_weight와 같은 값을 지정해 각 버튼이 동일

한 크기를 갖게 했다(이렇게 하는 게 더 보기 좋다).

안드로이드는 셰이프(사각형 또는 타원) 리소스를 XML 파일로 선언할 수 있게 해준다. 이들

셰이프는 직각 또는 둥근 모서리로 선언할 수 있으며 그라디언트 배경과 기타 속성을 사용해 선

언할 수 있다. 예를 들어 예제 1-11에는 그라디언트 배경을 사용한 사각형 셰이프가 나와 있다.

예제 1-11 | 액티비티의 배경을 칠할 그라디언트 셰이프를 갖고 있는 gradientbg.xml 파일

<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android">

<gradient android:startColor="#fccb06"

android:endColor="#fd6006"

android:angle="270"/>

<corners android:radius="10dp"/>

</shape>

<shape> 태그는 shape 어트리뷰트를 통해 셰이프를 지정한다. 이 어트리뷰트가 없는 경우 셰

이프는 기본적으로 사각형이 된다.

안에 들어 있는 <gradient> 태그는 그라디언트를 사용해 셰이프의 색상을 지정한다. 그라디언

트는 startColor, endColor, angle 어트리뷰트를 지정해 선언할 수 있다. 이 중 angle 어트리뷰트

는 그라디언트가 사각형 영역에서 진행되는 방향을 나타낸다. angle 어트리뷰트가 없는 경우 그

라디언트 각도는 기본값인 0도가 된다.

안에 들어 있는 <corners> 태그는 사각형 셰이프가 모서리를 갖는지 여부를 결정한다. 이 태

그가 있는 경우 이 태그의 어트리뷰트가 각 모서리 또는 전체 모서리의 둥글기 각도를 지정한다.

예를 들어 예제 1-11에서 radius 어트리뷰트는 각 모서리가 10만큼의 밀도 독립적 픽셀을 갖게

했다(dp는 dip와 같은 말이다).

텍스트 지역화를 쉽게 하려면 문자열을 별도로 보관해야 한다. 안드로이드에서는 예제 1-12에

보이는 strings.xml이라는 파일에 문자열을 보관하도록 강제한다.

Page 92: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

68 l 안드로이드 레시피

예제 1-12 | 앱의 문자열을 보관하는 strings.xml 파일

<?xml version="1.0" encoding="utf-8"?>

<resources>

<string name="app_name">Units Converter</string>

<string name="clear">Clear</string>

<string name="close">Close</string>

<string name="convert">Convert</string>

<string name="prompt">Select a conversion</string>

<string name="units">Units</string>

</resources>

strings.xml 파일은 resources 엘리먼트 내에 들어 있는 연속 string 엘리먼트를 통해 문자열

을 보관한다. 각 <string> 태그에는 문자열에 해당하는 내용을 알 수 있게 해주는 고유 name 어

트리뷰트가 필요하다. 이 어트리뷰트 값은 코드나 다른 리소스에서 참조한다. 문자열 텍스트는

<string>과 </string> 태그 사이에 집어넣는다.

끝으로 arrays.xml에는 변환 문자열 배열이 들어 있다. 예제 1-13은 이 표준 파일에 들어 있는

내용을 보여준다.

예제 1-13 | 변환 문자 배열이 들어 있는 arrays.xml 파일

<?xml version="1.0" encoding="utf-8"?>

<resources>

<string-array name="conversions">

<item>Acres to Square Miles</item>

<item>Atmospheres to Pascals</item>

<item>Bars to Pascals</item>

<item>Degrees Celsius to Degrees Fahrenheit</item>

<item>Degrees Fahrenheit to Degrees Celsius</item>

<item>Dynes to Newtons</item>

<item>Feet/Second to Metres/Second</item>

<item>Fluid Ounces (UK) to Litres</item>

<item>Fluid Ounces (US) to Litres</item>

<item>Horsepower (electric) to Watts</item>

<item>Horsepower (metric) to Watts</item>

<item>Kilograms to Tons (UK or long)</item>

<item>Kilograms to Tons (US or short)</item>

<item>Litres to Fluid ounces (UK)</item>

Page 93: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 69

<item>Litres to Fluid ounces (US)</item>

<item>Mach Number to Metres/Second</item>

<item>Metres/Second to Feet/Second</item>

<item>Metres/Second to Mach Number</item>

<item>Miles/Gallon (UK) to Miles/Gallon (US)</item>

<item>Miles/Gallon (US) to Miles/Gallon (UK)</item>

<item>Newtons to Dynes</item>

<item>Pascals to Atmospheres</item>

<item>Pascals to Bars</item>

<item>Square Miles to Acres</item>

<item>Tons (UK or long) to Kilograms</item>

<item>Tons (US or short) to Kilograms</item>

<item>Watts to Horsepower (electric)</item>

<item>Watts to Horsepower (metric)</item>

</string-array>

</resources>

안드로이드에서는 각기 다른 타입의 데이터를 arrays.xml에 배열로 저장할 수 있게 해준다. 예

를 들어 <string-array>는 배열에 문자열이 들어 있음을 뜻한다. 이 태그에는 이 배열을 고유 식

별할 수 있는 name 어트리뷰트가 필요하다. 각 배열 항목은 <item>과 </item> 태그 사이에 내

용을 집어넣어 지정한다.

1–6. UC 액티비티 만들기

◈ 문제

안드로이드 SDK의 명령행 툴을 사용해 UC 앱을 만드는 법을 배우려고 하는데 어떻게 해야 할

지 모르겠다.

◈ 해결책

android 툴을 사용해 UC를 생성하고 ant를 사용해 프로젝트를 빌드한다.

Page 94: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

70 l 안드로이드 레시피

◈ 문제 풀이

UC 앱을 만들려면 android 툴을 사용해 프로젝트를 생성해야 한다. 이 방식을 사용할 경우

android 툴은 다음과 같은 구문을 필요로 한다(여기서는 읽기 쉽게 몇 줄에 나눠서 적었다).

android create project --target target_ID

--name your_project_name

--path /path/to/your/project/project_name

--activity your_activity_name

--package your_package_namespace

프로젝트 명(이 이름을 지정하면 앱을 빌드한 결과물 .apk 파일에서도 이 이름이 사용된다)을

지정하는 --name (또는 –n)을 제외한 다음 항목들은 모두 필수 항목이다.

■ --target (또는 -t) 옵션은 앱의 빌드 대상을 나타낸다. target_ID 값은 안드로이드 플랫폼

을 나타내는 정수 값이다. 이 값은 android list targets를 호출해 알 수 있다. 안드로이드

2.3 플랫폼만을 설치했다면 이 명령을 실행할 경우 플랫폼 대상이 안드로이드 2.3 플랫폼

하나뿐이므로 정수 ID 1이 나온다.

■ --path (또는 -p) 옵션은 프로젝트의 디렉터리 위치를 지정한다. 존재하지 않는 디렉터리

를 지정하면 디렉터리가 생성된다.

■ --activity (또는 -a) 옵션은 기본 액티비티 클래스의 이름을 지정한다. 결과 클래스 파일

은 /path/to/your/project/project_name/src/your_package_namespace/ 내에 생성되며

--name (또는 -n) 옵션을 지정하지 않으면 이 파일명이 .apk 파일의 이름이 된다.

■ --package (또는 -k) 옵션은 프로젝트의 패키지 네임스페이스를 지정한다. 이 네임스페이

스는 자바 언어에서 규정하는 패키지 명명 규칙을 따라야 한다.

윈도우 XP 플랫폼을 사용하고 C:\prj\dev 디렉터리 내에 C:\prj\dev\UC처럼 UC 프로젝트가

저장된다고 가정하면 파일 시스템상의 아무 위치에서나 다음 명령을 호출해 UC 프로젝트를 생

성할 수 있다.

android create project -t 1 -p C:\prj\dev\UC -a UC -k com.apress.uc

이 명령은 다양한 디렉터리를 생성하고 이들 디렉터리에 파일을 추가한다. 이 중 특별히 다음

과 같은 파일 및 디렉터리 구조를 C:\prj\dev\UC 내에 생성한다.

Page 95: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 71

■ AndroidManifest.xml은 빌드되는 앱에서 사용할 매니페스트 파일이다. 이 파일은 앞서

--activity 또는 -a 옵션을 통해 지정한 Activity 하위 클래스와 동기화된다.

■ bin은 아파치 앤트 빌드 스크립트에서 사용하는 결과물 디렉터리다.

■ build.properties는 빌드 환경에 맞춰 수정할 수 있는 속성 파일이다. 이 파일을 수정하면

아파치 앤트에서 사용하는 기본 빌드 설정을 수정하고 배포 모드로 앱을 빌드하려고 할

때 빌드 툴에서 앱을 서명할 수 있게 키 저장소와 키 별칭에 대한 포인터를 제공할 수 있

다(레시피 1-8에서 설명).

■ build.xml은 이 프로젝트에서 사용하는 아파치 앤트 빌드 스크립트다.

■ default.properties는 빌드 시스템의 기본 속성 파일이다. 이 파일은 수정하지 말아야 한다.

■ libs에는 필요한 경우 개인 라이브러리를 집어넣을 수 있다.

■ local.properties에는 안드로이드 SDK 홈 디렉터리의 위치가 들어 있다.

■ proguard.cfg에는 개발자들이 배포 빌드를 할 때 코드를 (코드를 리버스 엔지니어링을

통해 풀지 못하게) 암호화해주는 SDK 툴인 프로가드에 대한 설정 데이터가 들어 있다.

■ res에는 프로젝트 리소스가 들어 있다.

■ src에는 프로젝트의 소스 코드가 들어 있다.

res에는 다음 디렉터리들이 들어 있다.

■ drawable-hdpi는 고밀도 화면에 사용할 드로어블 리소스(아이콘 등)를 포함한다.

■ drawable-ldpi는 저밀도 화면에 사용할 드로어블 리소스를 포함한다.

■ drawable-mdpi는 중간 밀도 화면에 사용할 드로어블 리소스를 포함한다. 예제 1-11의

gradientbg.xml 파일은 이 디렉터리에 들어 있다.

■ layout에는 레이아웃 파일들이 들어 있다. 예제 1-1의 main.xml 파일은 이 디렉터리에 들

어 있다.

■ values에는 값 파일들이 들어 있다. 예제 1-12의 strings.xml과 예제 1-13의 arrays.xml

파일은 이 디렉터리에 들어 있다.

또 src 폴더에는 com\apress\uc 디렉터리 구조가 들어 있으며 최하위 디렉터리인 uc에는

UC.java 소스 파일이 들어 있다. 이 파일의 내용은 예제 1-9로 대체된다.

Page 96: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

72 l 안드로이드 레시피

C:\prj\dev\UC가 현재 디렉터리라고 가정하고 기본적으로 이 디렉터리의 build.xml 파일을

처리하는 아파치의 ant 툴을 사용해 이 앱을 빌드해 보자. 명령행에서는 ant 다음에 debug나

release를 사용해 빌드 모드를 지정할 수 있다.

■ 디버그 모드: 앱을 테스트 및 디버그용으로 빌드한다. 빌드 툴에서는 디버그 키를 사용해

결과 APK를 서명하고 zipalign을 사용해 APK를 최적화한다. 디버그 모드를 사용하려면

ant debug를 입력한다.

■ 배포 모드: 사용자에게 배포하기 위해 앱을 빌드한다. 이때는 개인 키를 사용해 결과

APK를 서명해야 하며, 이때도 zipalign을 통해 APK를 최적화한다(이 작업은 이 장에서

나중에 다시 설명한다). 배포 모드를 사용하려면 ant release를 입력한다.

C:\prj\dev\UC 디렉터리에서 ant debug를 호출해 디버그 모드로 UC 앱을 빌드해 보자. 이 명

령은 ant에서 생성한 R.java 파일이 포함된 gen이라는 하위 디렉터리를 생성(생성 경로는 com\

apress\uc 디렉터리 계층구조 내부)하고 UC-debug.apk 파일을 bin 하위 디렉터리에 생성한다.

1–7. UC의 설치와 실행

◈ 문제

방금 생성한 UC-debug.apk 패키지 파일을 앞에서 만든 AVD에 설치해 앱을 실행하려고 한다.

◈ 해결책

adb 툴을 사용해 UC를 설치한다. 앱 론처 화면에서 이 앱으로 이동해 UC를 실행한다.

Page 97: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 73

◈ 문제 풀이

AVD가 아직 실행 중이라 가정하고 adb install C:\prj\dev\UC\bin\UC-debug.apk를 실행해

UC-debug.apk를 AVD에 설치한다. 그럼 잠시 후 다음과 같은 메시지를 볼 수 있을 것이다.

411 KB/s (19770 bytes in 0.046s)

pkg: /data/local/tmp/UC-debug.apk

Success

홈 화면에서 앱 론처 아이콘(홈 화면의 하단 중심에 있는 사각형 그리드 아이콘)을 클릭하고

앱 아이콘이 나와 있는 화면 목록에서 스크롤을 내려 이동한다. 그림 1-12는 Units Converter 앱

항목이 들어 있는 모습을 보여준다.

그림 1-12. 하이라이트 표시된 Units Converter 앱 항목은 커스텀 아이콘을 보여준다(이 아이콘은 책의

코드에 들어 있는 icon.png 파일이다). 이 아이콘도 drawable-mdpi에 들어 있다.

Units Converter 아이콘을 클릭하면 그림 1-13과 같은 화면을 볼 수 있다.

Page 98: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

74 l 안드로이드 레시피

그림 1-13. 사용자에게 숫자를 입력하라고 알려주는 Units 텍스트 필드

Units 텍스트 필드에 37을 입력하면 그림 1-14와 같은 화면을 볼 수 있다.

그림 1-14. 이제 Clear와 Convert 버튼이 더 이상 비활성화돼 있지 않다.

스피너를 클릭하면 그림 1-15 같은 화면을 볼 수 있다.

Page 99: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 75

그림 1-15. 스피너는 변환 종류가 들어 있는 드롭다운 리스트의 상단에서 프롬프트를 보여준다.

“Degrees Celsius to Degrees Fahrenheit”를 선택하면 그림 1-16 같은 화면을 볼 수 있다.

그림 1-16. Convert를 누르고 나면 Units 텍스트필드에 변환 결과가 나온다.

Close를 눌러 앱을 종료하고 그림 1-12에 보이는 론처 화면으로 다시 돌아간다.

Page 100: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

76 l 안드로이드 레시피

알아두기

UC 앱은 현재 제대로 실행되는 것처럼 보이지만 앱을 배포하기 전에는 코드가 올바른지 확인

하기 위해 이 앱(을 포함해 다른 앱도 마찬가지)의 코드를 단위 테스트해야 한다. 구글의 온라

인 안드로이드 개발자 가이드는 http://developer.android.com/guide/topics/testing/

index.html에 있는 'Testing' 절에서 이 주제를 상세히 다루고 있다.

1–8. UC 앱의 배포 준비

◈ 문제

UC 앱이 제대로 동작하는 데 만족한 여러분은 구글의 안드로이드 마켓이나 다른 배포 서비스

로 앱을 배포할 준비를 하려고 한다.

◈ 해결책

UC 앱을 배포하기 전에 먼저 앱의 버전을 지정해야 한다. 그런 다음 배포 모드로 앱을 빌드한 후

앱 패키지를 서명하고 정렬한다.

◈ 문제 풀이

구글의 온라인 안드로이드 개발자 가이드(http://developer.android.com/guide/index.html)는

앱 배포에 대한 방대한 정보를 제공한다. 이 가이드의 내용을 그대로 반복하는 대신 이 레시피에

서는 UC를 배포하는 데 필요한 단계를 설명하려고 한다.

Page 101: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 77

UC 버전 지정

안드로이드에서는 Android Mani fest .x ml의 <mani fest> 태그에서 versionCode와

versionName 어트리뷰트를 사용해 앱의 버전 정보를 추가할 수 있게 해준다.

versionCode에는 앱의 코드 버전을 나타내는 정수 값을 지정한다. 이 값은 정수이므로 앱에

서는 이 값을 프로그래밍적으로 해석해 업그레이드나 다운그레이드 여부를 판단할 수 있다. 이

값은 원하는 정수를 얼마든지 지정할 수 있지만 앱의 업그레이드 버전을 배포할 때마다 항상 더

높은 값을 사용해야 한다. 안드로이드에서는 이런 행동을 강제하지는 않지만 이후 배포 버전에

서 값을 올리는 게 표준 규범에 부합된다.

versionName에는 앱 코드의 배포 버전을 나타내는 문자열 값을 지정하며, 이 값은 (앱

을 통해) 사용자가 볼 수 있다. 이 값이 문자열인 이유는 <major>.<minor>.<point> 문자열

이나 다른 절대 또는 상대 버전 식별자를 사용해 앱의 버전을 식별할 수 있게 하기 위해서다.

android:versionCode와 마찬가지로 안드로이드에서는 이 값을 내부 용도로 사용하지 않는다.

하지만 배포 서비스에서는 versionName 값을 추출해 사용자에게 보여줄 수도 있다.

UC의 AndroidManifest.xml 파일에 들어 있는 <manifest> 태그에는 versionCode 어트리뷰

트가 "1"로 초기화돼 있고 versionName 어트리뷰트가 "1.0"으로 초기화돼 있다.

배포 모드로 UC 배포하기

윈도우 XP를 사용한다고 가정하고 C:\prj\dev\UC 디렉터가 현재 디렉터리라면 다음 명령을 실

행한다.

ant release

이 명령은 UC-unsigned.apk를 생성하고 이 파일을 bin 디렉터리에 저장한다. 더불어 APK를

서명해야 하고 zipalign을 사용해 정렬해야 한다는 메시지도 출력해준다.

UC 앱 패키지의 서명

안드로이드에서는 설치되는 모든 앱이 앱 개발자가 갖고 있는 개인 키를 적용한 인증서를 통해 디

지털 서명돼야 한다. 안드로이드는 앱의 저작자를 확인하고 앱 사이의 신뢰 관계를 구축하기 위해

이런 인증서를 사용한다. 이 인증서는 인증 기관의 서명을 받지 않아도 된다. 안드로이드에서는 자

기 서명 인증서가 완전히 허용될 뿐 아니라 대부분의 인증서가 자기 서명 인증서에 해당한다.

Page 102: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

78 l 안드로이드 레시피

알아두기

안드로이드는 서명인의 인증서 만기일을 설치 시점에만 확인한다. 앱이 설치된 후 앱을 서명

한 인증서가 유효기간이 만료되더라도 앱은 계속해서 정상 실행된다.

UC-unsigned.apk를 서명하기 전에 먼저 적당한 개인 키를 얻어야 한다. 개인 키는 다음 조건

에 부합해야 한다.

■ 키가 앱을 통해 식별할 수 있는 개인, 기업, 또는 조직을 나타낸다.

■ 키가 앱의 예상 수명 기간을 초과하는 유효 기간을 갖고 있다. 구글에서는 유효 기간을

최소 25년 이상 지정할 것을 권장한다. 안드로이드 마켓에 앱을 배포할 생각이라면 2033

년 10월 22일보다 늦게 만료되는 유효 기간을 지정하도록 주의하자. 유효 기간이 이 날짜

보다 빨리 만료되는 키를 사용해 서명한 앱은 마켓에 업로드할 수 없다.

■ 키가 안드로이드 SDK 툴에서 생성한 디버그 키가 아니다.

이런 개인 키를 생성할 때는 JDK의 keytool을 사용한다. 다음 명령(읽기 쉽게 두 줄로 나눴다)

은 keytool을 사용해 키를 생성하는 명령이다.

keytool -genkey -v -keystore uc-release-key.keystore -alias uc_key -keyalg RSA

-keysize 2048 -validity 10000

이때 다음 명령행 인자를 지정할 수 있다.

■ -genkey은 keytool에서 공개 키와 개인 키를 생성하게 해준다(키 쌍).

■ -v는 출력 결과를 많이 보여주는 옵션을 활성화한다.

■ -keystore는 개인 키를 갖고 있는 키 저장소(파일)를 지정한다. 이 명령에서 사용한 키 저

장소는 uc-release-key.keystore이다.

■ -alias는 키에 대한 별칭(별칭을 지정할 경우 실제 서명 과정에서 처음 8글자만 사용된다)

을 지정한다. 이 명령에서 사용한 별칭은 uc_key이다.

■ -keyalg는 키를 생성할 때 사용할 암호화 알고리즘을 지정한다. DSA와 RAS를 모두 지원

하지만 이 명령에서는 RSA를 지정했다.

Page 103: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 79

■ -keysize는 생성된 각 키의 크기(비트 단위)를 지정한다. 구글에서는 2048비트 이상의 키

크기(기본값은 1024비트)를 권장하므로 이 명령에서는 2048을 지정했다.

■ -validity는 키가 유효한 기간(일 단위)를 지정한다(구글에서는 10000 이상의 값을 권장

한다). 이 명령에서는 10000을 지정했다.

keytool에서는 (키 저장소에 대한 접근 보안을 위해) 비밀번호를 입력하게 하고, 같은 비밀번호

를 한 번 더 입력하게 한다. 그런 다음 이름과 성, 기관명, 도시명과 지역명, 주의 이름, 기관의 두

글자 국가 코드를 입력하게 한다.

이후 keytool에서는 여러분이 지정한 정보가 올바른지 (예와 엔터 입력 또는 아니오와 엔터 입

력을 통해) 확인할 수 있게 해준다. 예를 입력할 경우 keytool은 키에 또 다른 비밀번호를 선택하

거나 키 저장소에 사용한 비밀번호와 같은 비밀번호를 사용할 수 있게 해준다.

주의

개인 키는 안전하게 보호해야 한다. 개인 키를 제대로 보호하지 않으면 앱 저작 신원 증명을

도난당하거나 사용자의 신뢰를 잃을 수 있다. 개인 키를 안전하게 보호하는 팁을 몇 가지 정리

해 봤다.

■ 키 저장소와 키에 어려운 비밀번호를 사용한다.

■ keytool을 사용해 키를 생성할 때 명령행에서 -storepass와 -keypass 옵션을 제공하

지 않는다. 이렇게 하면 셸 히스토리에 비밀번호가 남게 돼 여러분의 컴퓨터를 사용

하는 사람이 이에 접근할 수 있다.

■ jarsigner를 사용해 앱을 서명할 때 명령행에서 -storepass와 -keypass를 제공하지

않는다(앞의 팁과 같은 이유에서다).

■ 개인 키를 다른 사람에게 주거나 빌려주지 않는다. 또 인증되지 않은 사람은 여러분

의 키 저장소와 키 비밀번호를 알 수 없게 한다.

keytool은 현재 디렉터리에 uc-release-key.keystore를 생성한다. 이 키 저장소의 정보를 보려

면 다음 명령을 실행하면 된다.

keytool -list -v -keystore uc-release-key.keystore

Page 104: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

80 l 안드로이드 레시피

키 저장소 비밀번호를 요청한 후 keytool은 키 저장소에 들어 있는 다양한 항목(현재는 항목

이 하나뿐일 것이다)과 인증서 비밀번호를 출력해준다.

JDK의 jarsigner 툴은 UC-unsigned.apk를 서명하는 데 사용한다. 현재 디렉터리가 C:\prj\

dev\UC라고 가정하고 이 디렉터리에 keytool에서 생성한 uc-release-key.keystore 파일이 들어

있으며, UC-unsigned.apk가 들어 있는 bin 하위 디렉터리도 이 디렉터리에 들어 있는 경우 다음

명령을 사용하면 이 파일을 서명할 수 있다.

jarsigner -verbose -keystore uc-release-key.keystore bin/UC-unsigned.apk uc_key

이 명령에서는 다음 명령행 인자를 지정했다.

■ -verbose는 상세한 출력 결과 옵션을 활성화한다.

■ -keystore는 개인 키를 보관하는 키 저장소를 지정한다. 이 명령에서는 개인 키로 uc-

release-key.keystore를 지정했다.

■ bin/UC-unsigned.apk는 서명되는 APK의 위치와 이름을 지정한다.

■ uc-key는 개인 키에 대해 앞에서 생성한 별칭을 지정한다.

jarsigner는 앞서 keytool을 통해 지정한 키 저장소 비밀번호를 입력하게 한다. 이어서 이 툴은

다음과 같은 메시지를 출력한다.

adding: META-INF/MANIFEST.MF

adding: META-INF/UC_KEY.SF

adding: META-INF/UC_KEY.RSA

signing: res/layout/main.xml

signing: AndroidManifest.xml

signing: resources.arsc

signing: res/drawable-hdpi/icon.png

signing: res/drawable-ldpi/icon.png

signing: res/drawable-mdpi/gradientbg.xml

signing: res/drawable-mdpi/icon.png

signing: classes.dex

jarsigner -verify bin/UC-unsigned.apk를 실행하면 UC-unsigned.apk가 서명된 것을 확인

할 수 있다.

Page 105: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 81

서명이 성공적으로 이뤄지면 “jar veri�ed.” 메시지를 볼 수 있다. 서명에 실패하면 다음과 같은

메시지가 나타난다.

no manifest.

jar is unsigned. (signatures missing or not parsable)

UC 앱 패키지의 정렬

성능 최적화를 위해 안드로이드는 서명된 APK의 압축을 해제한 내용이 파일 시작 위치를 기준

으로 정렬하도록 규정하며 이 작업에 사용할 수 있게 zipalign SDK 툴을 제공한다. 구글의 문서

에 따르면 APK 내에 있는 압축 해제된 모든 데이터(예를 들어 이미지, 로 파일 등)는 4바이트 경

계로 정렬된다.

zipalign을 사용해 지정한 APK를 정렬해 새로운 APK를 생성하려면 다음 구문이 필요하다.

zipalign [-f] [-v] <alignment> infile.apk outfile.apk

이때 다음과 같은 명령행 인자를 지정한다.

■ -f는 결과물 파일이 존재할 경우 out�le.apk 파일을 덮어쓰게 한다.

■ -v는 상세 결과 출력 옵션을 활성화한다.

■ alignment는 APK의 내용을 바이트 수 경계를 따라 정렬하게 한다. zipalign에서는 4 이

외의 다른 값은 모두 무시하는 것으로 보인다.

■ in�le.apk는 정렬을 적용할 서명된 APK 파일을 지정한다.

■ out�le.apk는 서명되고 정렬된 APK 결과를 지정한다.

C:\prj\dev\UC\bin이 현재 디렉터리일 경우 명령행에서 UC-unsigned.apk to UC.apk를 정렬

하려면 다음 명령을 실행하면 된다.

zipalign –f –v 4 UC-unsigned.apk UC.apk

zipalign에서는 다음 구문을 사용해 기존 APK가 정렬됐는지 확인할 수 있다.

zipalign -c -v <alignment> existing.apk

이 명령에서는 다음 인자를 지정한다.

Page 106: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

82 l 안드로이드 레시피

■ -c는 existing.apk의 정렬을 확인한다.

■ -v는 상세 결과 출력 옵션을 활성화한다.

■ alignment는 APK의 내용이 이 인자에서 지정한 바이트 숫자 경계로 정렬되게 한다.

alignment 인자는 4 이외의 다른 값은 무시하는 것처럼 보인다.

■ in�le.apk는 정렬할 서명된 APK 파일을 지정한다.

UC.apk가 정렬됐는지 확인하려면 다음 명령을 실행하면 된다.

zipalign –c –v 4 UC.apk

zipalign은 어떤 APK가 압축돼 있고 어떤 APK가 압축돼 있지 않은지 알 수 있게 APK 목록

을 보여주고, 이어서 확인 결과 성공했는지 또는 실패했는지 여부를 메시지로 보여준다.

1–9. 이클립스에서 작업하기

◈ 문제

명령행보다는 이클립스 IDE를 사용해 앱을 개발하고 싶다.

◈ 해결책

이클립스를 사용해 앱을 개발하려면 이클립스 클래식 3.6.1 같은 IDE를 설치해야 한다. 또 ADT

플러그인도 설치해야 한다.

◈ 문제 풀이

이클립스를 사용해 안드로이드 앱을 개발하려면 먼저 다음 세 작업 중 최소 앞의 두 가지 작업

은 완료해야 한다.

Page 107: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 83

1. 안드로이드 SDK와 최소한 한 개 이상의 안드로이드 플랫폼(레시피 1-1과 1-2 참고)을 설

치한다. JDK 5 또는 JDK 6도 설치돼 있어야 한다.

2. 안드로이드 SDK와 호환되는 이클립스 버전을 설치하고 이클립스 IDE용 안드로이드 개

발 툴(ADT) 플러그인을 설치한다.

3. ADT 플러그인을 설치한다.

이 작업은 순서대로 해야 한다. 예를 들어 ADT 플러그인을 이클립스보다 먼저 설치할 수 없으

며, 안드로이드 SDK와 하나 이상의 안드로이드 플랫폼을 설치하기 전에 ADT 플러그인을 설정

하거나 사용할 수는 없다.

편리한 ADT 플러그인

물론 ADT 플러그인을 사용하지 않고 이클립스에서 안드로이드 앱을 개발할 수도 있지만

ADT 플러그인을 사용하면 더 빠르고 편리하게 앱을 생성하고, 디버그하고, 개발할 수 있다.

ADT 플러그인은 다음 기능을 제공한다.

■ 이클립스 IDE 내에서 다른 안드로이드 개발 툴에 접근할 수 있다. 예를 들어 ADT를 사

용하면 DDMS(달빅 디버그 모니터 서버) 툴의 많은 기능을 활용할 수 있다. DDMS는

화면을 캡처하고 포트 포워딩을 관리하며, 브레이크포인트를 설정하고 스레드와 프로

세스 정보를 이클립스에서 직접 볼 수 있게 해준다.

■ ADT 플러그인은 새 프로젝트 마법사를 제공한다. 이 마법사를 활용하면 새 안드로이드

앱에 필요한 기반 파일들을 손쉽게 만들고 설정할 수 있다.

■ 안드로이드 앱을 개발하는 과정을 자동화해 주고 단순화해 준다.

■ 안드로이드 매니페스트와 리소스 파일에 사용할 XML에서 유효한 XML을 입력하도록

안드로이드 코드 편집기를 제공한다.

■ 사용자들에게 배포할 수 있게끔 프로젝트를 서명된 APK로 내보내게 해준다.

ADT 플러그인을 설치하는 법은 이클립스를 설치하는 법을 배운 다음에 살펴본다.

Page 108: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

84 l 안드로이드 레시피

Eclipse.org 웹사이트에서는 각기 다른 조건에 맞는 다양한 IDE 패키지를 내려받을 수 있게

해준다. 구글에서는 안드로이드 개발자가 내려받아 설치하기 적합한 IDE 패키지에 대해 몇 가지

권장사항을 정했다.

■ 이클립스 3.4(가니메데) 이상의 IDE 패키지를 설치한다.

■ 내려받은 이클립스 패키지에 이클립스 JDT(자바 개발 툴) 플러그인이 포함돼 있어야 한

다. 대부분의 패키지에는 이 플러그인이 포함돼 있다.

■ 이클립스 클래식(3.5.1 버전 이상), 자바 개발자용 이클립스 IDE, 자바 EE 개발자용 이클

립스 IDE 패키지 중 하나를 설치해야 한다.

이클립스 클래식 3.6.1을 설치하려면 다음 과정을 따라 하면 된다.

1. 브라우저에서 이클립스 3.6.1 페이지(www.eclipse.org/downloads/packages/eclipse-

classic-361/heliossr1)로 이동한다.

2. 이 페이지의 오른쪽에 있는 다운로드 링크 중 하나를 클릭해 적절한 배포 파일을 선택한

다. 예를 들어 Windows 32-bit 플랫폼을 선택할 수 있다.

3. 다운로드 링크를 클릭하고 배포 파일을 하드 드라이브에 저장한다. 예를 들어 eclipse-

SDK-3.6.1–win32.zip 파일을 하드 드라이브에 저장한다.

4. 배포 파일의 압축을 풀고 eclipse 홈 디렉터리를 작업하기 편한 위치로 옮긴다. 예를 들어

eclipse를 C:\Program Files 디렉터리로 옮긴다.

5. eclipse 홈 디렉터리에 들어 있는 eclipse 애플리케이션에 대한 바탕화면 바로가기를 만들

어 두면 더 편리할 것이다.

가장 최신 버전의 ADT 플러그인을 설치하려면 다음 절차를 따라 한다.

1. 이클립스를 시작한다.

2. 이클립스를 처음 시작하면 환영 애니메이션 다음에 워크스페이스 론처 대화상자가 나타

난다. 이 대화상자를 사용하면 프로젝트를 저장할 워크스페이스 폴더를 선택할 수 있다.

Page 109: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 85

또 다음번에 이클립스를 시작할 때 이 대화상자가 보이지 않게 이클립스를 설정할 수도

있다. 기본 폴더를 변경하거나 그대로 둔 체 OK를 클릭한다.

3. 이클립스에서 메인 창을 보여주면 Help 메뉴에서 Install New So�ware를 선택한다.

4. 이어지는 Install 대화상자의 Available So�ware 영역에서 Add 버튼을 클릭한다.

5. 그럼 Add Repository 대화상자가 나타나는데, 이 대화상자의 Name 필드에 원격 사이

트의 이름(예를 들어 안드로이드 플러그인)을 입력하고 Location 필드에 https://dl-ssl.

google.com/android/eclipse/를 입력한다. OK를 클릭한다.

6. 그럼 Install 대화상자 가운데에 있는 목록에서 Developer Tools를 볼 수 있을 것이다.

7. Developer Tools 옆에 있는 체크박스를 체크한다. 그럼 Android DDMS, Android

Development Tools, Android Hierarchy Viewer처럼 안에 들어 있는 플러그인들도 모

두 자동으로 체크된다. Next를 클릭한다.

8. 이어서 나오는 Install Details 창에는 Android DDMS, Android Development Tools,

Android Hierarchy Viewer 목록이 보일 것이다. 라이선스 동의서를 읽고 동의한 후

Next를 클릭하고 모든 의존성을 설치한 후 Finish를 클릭한다.

9. 그럼 Installing Software 대화상자가 나타나고 설치 과정을 처리해줄 것이다. 이때

Security Warning 대화상자가 나타나면 그냥 OK를 클릭하면 된다.

10. 끝으로 이클립스에서 IDE를 재시작해야 한다는 So�ware Updates 대화상자를 보여줄 것

이다. Restart Now 버튼을 클릭해 바로 재시작한다.

5단계에서 플러그인을 가져오는 게 어렵다면 Location 필드에서 https 대신 http를 지정해

보자(https는 보안상의 이유로 권장한다).

ADT 플러그인의 설치를 마무리하려면 이클립스에서 ADT 환경설정을 수정해 안드로이드

SDK 홈 디렉터리를 지정해줘야 한다. 다음과 같이 하면 ADT 플러그인에 안드로이드 SDK 홈 디

렉터리를 지정할 수 있다.

Page 110: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

86 l 안드로이드 레시피

1. Window 메뉴에서 Preferences를 선택해 환경설정 패널을 연다. 맥 OS X의 경우 Eclipse

메뉴에서 Preferences를 선택한다.

2. 왼쪽 패널에서 Android를 선택한다.

3. SDK Location 텍스트 필드 옆에 있는 찾기 버튼을 클릭해 내려받은 안드로이드 SDK의

홈 디렉터리(C:\android-sdk-windows 등)를 지정한다.

4. Apply를 누르고 OK를 클릭한다.

알아두기

설치 과정에서 생긴 문제에 대한 추가 정보나 ADT 플러그인 설치와 관련한 상세 정보는 구

글의 온라인 안드로이드 개발자 가이드에서 이클립스용 ADT 플러그인 페이지(http://

developer.android.com/sdk/eclipse-adt.html)를 참고하자.

1–10. 이클립스를 활용한 UC 앱 개발

◈ 문제

이제 이클립스 클래식 3.6.1과 ADT 플러그인을 설치했는데 이클립스와 플러그인을 사용해 UC

앱을 개발하는 방법이 궁금하다.

◈ 해결책

먼저 UC라는 이름의 안드로이드 이클립스 프로젝트를 생성해야 한다. 그런 다음 다양한 소스

파일과 리소스를 여러 디렉터리에 집어넣어야 한다. 끝으로 메뉴바의 Run을 선택해 UC를 실행

하면 된다.

Page 111: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 87

◈ 문제 풀이

이클립스를 활용해 UC 앱을 개발할 때 첫 번째로 할 일은 새 안드로이드 프로젝트를 생성하는

것이다. 이 프로젝트를 생성하려면 다음 절차를 따라 하면 된다.

1. 실행 중이 아니라면 이클립스를 실행한다.

2. File 메뉴에서 New를 선택하고 이어서 나오는 팝업 메뉴에서 Project를 선택한다.

3. New Project 대화상자에서 마법사 트리의 Android 노드를 펼치고 이 노드 아래 있는

Android Project 노드를 선택한 후 Next 버튼을 클릭한다.

4. 이어서 나오는 New Android Project 대화상자에서 Project name 필드에 UC를 입력한

다. 여기서 입력한 이름은 UC 프로젝트가 저장되는 폴더를 지정하는 데 사용된다.

5. 선택돼 있지 않다면 워크스페이스 라디오 버튼에서 Create new project를 선택한다.

6. Build Target 아래에서 UC 앱이 빌드 대상으로 사용할 안드로이드 대상 버전을 선택한

다. 이 버전은 애플리케이션이 빌드 대상으로 삼는 안드로이드 플랫폼을 지정한다. 안드

로이드 2.3 플랫폼만 설치된 경우 이 빌드 대상만 나타날 것이므로 자동으로 이 플랫폼이

빌드 대상으로 선택돼 나타난다.

7. Properties 아래에서 Application name 텍스트 필드에 Units Converter를 입력한다. 이

이름은 안드로이드 기기에 표시할 제목으로 사용자들에게 보여주기 위한 용도다. 계속해

서 Package name 필드에 com.apress.uc를 입력한다. 이 값은 패키지 네임스페이스(자바

프로그래밍 언어의 패키지 명명 규칙을 그대로 따른다)이며 모든 소스 코드를 보관할 장

소다. 체크돼 있지 않다면 Create Activity를 선택하고 이 체크박스 아래 나오는 앱의 시

작 액티비티 이름에 UC를 입력한다. 이 텍스트 필드는 앞의 체크박스가 선택돼 있지 않

은 경우에는 비활성화된다. 끝으로 Min SDK Version 텍스트 필드에 정수 9를 입력해

UC를 실행할 수 있는 최소 API 레벨을 안드로이드 2.3 플랫폼으로 지정한다.

8. Finish를 클릭한다.

이렇게 하면 이클립스는 이클립스의 워크스페이스 디렉터리 내에 다음 하위 디렉터리와 파일

들이 들어 있는 UC 디렉터리를 생성해준다.

Page 112: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

88 l 안드로이드 레시피

■ .settings: 이 디렉터리는 프로젝트 관련 설정을 기록하는 org.eclipse.jdt.core.prefs 파일

을 저장한다.

■ assets: 이 디렉터리는 구조화되지 않은 계층구조의 파일들을 저장하는 데 사용된다. 이

디렉터리에 저장된 내용은 앱에서 나중에 로 바이트 스트림으로 조회할 수 있다.

■ bin: APK 파일은 이곳에 저장된다.

■ gen: 자동 생성된 R.java 파일은 패키지 계층구조를 반영하는 하위 디렉터리 구조로 이

디렉터리 내에 저장된다(예를 들어 com\apress\uc).

■ res: 앱 리소스는 이 디렉터리의 여러 하위 디렉터리에 저장된다.

■ src: 앱 소스 코드는 패키지 계층구조를 따라 이 디렉터리 내에 저장된다.

■ .classpath: 이 파일은 이 프로젝트에서 의존하는 외부 라이브러리를 찾을 수 있게끔 프

로젝트의 클래스패스 정보를 저장한다.

■ .project: 이 파일은 프로젝트 종류, 프로젝트에서 포함하는 빌더, 프로젝트에 첨부된 링

크 리소스 같은 중요한 프로젝트 정보를 포함한다.

■ AndroidManifest.xml: 이 파일은 UC의 매니페스트 정보를 보관한다.

■ default.properties: 이 파일은 프로젝트 설정을 보관한다.

■ Proguard.cfg: 이 파일은 프로가드 설정 데이터를 보관한다.

Welcome 탭을 닫는다. 그럼 이클립스에서 그림 1-17과 같은 UI를 보여줄 것이다.

Page 113: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 89

그림 1-17. 메뉴바, 툴바, 몇 개의 창(패키지 익스플로러와 아웃라인 등), 상태바, 편집창으로 사용할 빈 영

역으로 구성된 이클립스의 UI

이 UI는 워크벤치라고 부른다. 패키지 익스플로러 창은 왼쪽에 나타나며 현재 워크스페이스에

들어 있는 다양한 프로젝트와 이들 프로젝트의 컴포넌트를 노드 목록으로 펼쳐 볼 수 있게 해준

다. 그림 1-17은 워크스페이스에 UC 프로젝트 하나만 들어 있는 모습을 보여준다.

이클립스에서 UC 프로젝트를 어떻게 조직화하는지 살펴보려면 이 노트의 왼쪽에 있는 + 아

이콘을 클릭하면 된다. 그림 1-18은 프로젝트 계층구조를 펼친 모습을 보여준다.

Page 114: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

90 l 안드로이드 레시피

그림 1-18. 추가로 + 아이콘을 더 클릭하면 UC의 파일 구조를 더 자세히 볼 수 있다.

UC.java 노드를 더블클릭해 보자. 그럼 이클립스는 그림 1-19에 보이는 것처럼 UC.java 창을

보여준다.

그림 1-19. UC.java의 기본 예제

UC.java의 기본 예제를 예제 1-9로 바꾸고 이클립스에서 보고하는 에러는 일단 무시한다. 이

들 에러는 이어서 바로 수정할 것이다.

다음 절차에 따라 프로젝트에 필요한 리소스를 지정한다.

Page 115: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 91

1. main.xml 노드를 더블클릭한다. 그럼 이클립스는 그래픽 레이아웃 모드로 main.xml 편

집기를 보여준다.

2. 창 아래 있는 main.xml 탭을 클릭해 텍스트 모드로 전환한다. 창의 내용을 예제 1-10의

코드로 바꾼다.

3. strings.xml 노드를 더블클릭한다. 그럼 이클립스는 리소스 모드로 strings.xml 편집창

을 보여줄 것이다.

4. 창 아래 있는 strings.xml을 클릭해 텍스트 모드로 전환한다. 창의 내용을 예제 1-12로

바꾼다.

5. values 노드를 마우스 오른쪽 클릭하고 New를 선택한 후 이어지는 팝업 메뉴에서 Other

를 선택한다. 그럼 새 대화상자가 나타날 것이다.

6. 마법사 목록에서 XML 노드를 펼친 후 XML 파일을 선택하고 Next를 클릭한다. 다음 화

면에서 File Name 필드의 NewFile.xml을 arrays.xml로 바꾼다. Finish를 클릭한다.

7. 그럼 이클립스는 디자인 모드에서 arrays.xml 편집창을 보여줄 것이다. 창 아래 있는 소

스 탭을 클릭해 텍스트 모드로 전환한다. 창의 내용을 예제 1-13으로 바꾼다.

8. drawable-mdpi 노드를 마우스 오른쪽 클릭하고 New를 선택한 후, 이어서 나오는 팝업

메뉴에서 Other를 클릭한다. 그럼 새 대화상자가 나타난다.

9. 마법사 목록에서 XML 노드를 펼쳐 XML File을 선택한 후 Next를 클릭한다. 다음 화면

에서 File Name 필드의 NewFile.xml을 gradientbg.xml로 바꾼다. Finish를 클릭한다.

10. 그럼 이클립스에서 gradientbg.xml 편집창을 디자인 모드로 보여줄 것이다. 창 아래 있

는 소스 탭을 클릭해 텍스트 모드로 전환한다. 창의 내용을 예제 1-11로 바꾼다.

11. drawable-mdpi 아래 있는 icon.png 노드를 마우스 오른쪽 클릭한다. 팝업 메뉴에서

Delete를 선택해 이 노드를 삭제한다.

12. 이 책의 압축 파일 내에 들어 있는 이 장의 폴더에서 icon.png 파일을 클립보드로 복사한

다. drawable-mdpi를 마우스 오른쪽 클릭하고 팝업 메뉴에서 Paste를 선택한다.

메뉴바에서 Run을 선택하고 이어서 나오는 드롭다운 메뉴에서 Run을 선택한다. 그럼 Run As

대화상자가 나타나는데, 이 대화상자에서 Android Application을 선택하고 OK를 클릭한다.

Page 116: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

92 l 안드로이드 레시피

모든 작업을 제대로 했다면 이클립스는 test_AVD 기기가 지정된 emulator 툴을 실행하고

UC 앱을 설치한 후 앱이 실행을 시작되게 해줄 것이다(그림 1-13 참고).

알아두기

이클립스는 이 레시피에서 다루는 내용보다 안드로이드 앱 개발과 관련해 훨씬 더 많은 기능

을 지원한다. 예를 들어 실행이 자주 종료되는 안드로이드 앱을 디버그하려는 경우 Window

메뉴에서 Open Perspective를 선택하고 이어서 나오는 팝업 메뉴에서 Other를 선택한 후,

Open Perspective 대화상자에서 DDMS를 선택해 Dalvik Debug Monitor Service를 시

작할 수 있다. DDMS에 대해서 더 알고 싶다면 J Beer가 작성한 “달빅 디버그 모니터 서비

스 툴을 구글 안드로이드에서 사용하는 법(How-to use Dalvik Debug Monitor Service

(DDMS) Tool With Google Android)” 튜토리얼 (www.brighthub.com/mobile/

google-android/articles/25023.aspx)과 James Sugrue가 작성한 “안드로이드 디버깅

- DDMS를 활용해 내부 들여다 보기(Debugging Android: Using DDMS To Look Under

The Hood)” 튜토리얼 (http://java.dzone.com/articles/debugging-android-using-

ddms)을 참고하자.

이클립스와 ADT 플러그인을 활용해 안드로이드 앱을 개발하는 법을 자세히 알고 싶다

면 Lars Vogel의 “안드로이드 개발 튜토리얼 - 진저브레드(Android Development Tutorial –

Gingerbread”, www.vogella.de/articles/Android/article.html)을 참고하자.

정리

안드로이드는 안드로이드 플랫폼용 앱을 개발하고 판매하는 많은 사람을 열광케 했다. 이런 재

미있는 개발 대열에 합류하기에 아직 늦은 때는 아니다. 이 장에서는 안드로이드의 핵심 개념과

개발 툴을 빠르게 살펴보고 어떻게 안드로이드 개발을 시작할 수 있을지 배웠다.

먼저 안드로이드가 모바일 기기용 소프트웨어 스택이란 사실과 이 스택이 앱, 미들웨어, 리눅

스 운영체제로 이뤄진다는 사실을 배웠다. 그런 다음 지금까지 나온 다양한 SDK 업데이트를 비

롯해 안드로이드의 역사를 살펴봤다.

Page 117: 안드로이드 레시피 : 빠르게 활용하는 안드로이드 문제 해법서

01 안드로이드 시작하기 l 93

이어서 안드로이드의 계층화된 아키텍처를 접했다. 안드로이드 아키텍처에서는 최상단에 앱

이 있고 이어서 애플리케이션 프레임워크, C/C++ 라이브러리, 미들웨어인 달빌 가상 머신, 그리

고 수정된 리눅스 커널 버전이 제일 아래 있었다.

계속해서 앱의 아키텍처도 살펴봤다. 앱의 아키텍처는 매니페스트에서 선언한 인텐트를 사용

해 서로 통신할 수 있는 컴포넌트(액티비티, 서비스, 브로드캐스트 리시버, 콘텐츠 프로바이더)

를 기반으로 하며, 이들 컴포넌트는 모두 앱 패키지 내에 포함된다.

다음으로 android.app.Activity 하위 클래스를 생성해 액티비티를 구현하는 법과 android.

app.Service 추상 클래스의 하위 클래스를 통해 서비스를 구현하는 법, android.content.

BroadcastReceiver 추상 클래스의 하위 클래스를 통해 브로드캐스트 리시버를 구현하는 법, 또

android.content.ContentProvider 추상 클래스의 하위 클래스를 통해 콘텐츠 프로바이더를 구

현하는 법을 배웠다.

이후 1장에서는 기본 이론을 벗어나 여러 레시피를 통해 좀 더 실질적인 문제들을 다뤄봤다.

첫 번째 레시피에서는 안드로이드 SDK와 안드로이드 플랫폼을 설치하고 AVD를 생성하는 법,

이 AVD를 사용해 에뮬레이터를 시작하는 법에 초점을 맞췄다.

이어서 여러 레시피를 통해 Units Converter 앱 예제를 소개했다. 이들 레시피에서는 이 앱을

생성하는 법, 에뮬레이터에 앱을 설치하는 법, 에뮬레이터에서 앱을 실행하는 법, 구글의 안드로

이드 마켓에 배포할 배포 버전을 준비하는 법을 모두 살펴봤다.

명령행 환경에서 명령행 툴을 사용하면 작업이 번거로워진다. 이로 인해 이 장의 마지막 두 레

시피에서는 이클립스 IDE로 작업 환경을 옮기는 법에 초점을 맞추고 그래픽 환경에서 Units

Converter 앱을 개발하는 법을 살펴봤다.

Units Converter 앱을 개발하는 법을 다루는 도중 우리는 일부 UI 개념도 살펴봤다. 2장에서

는 다양한 안드로이드 UI 기술에 초점을 맞춘 레시피를 통해 이들 개념을 좀 더 다져볼 것이다.