구글안드로이드프로그래밍액티비티, 인텐트수신자, 그리고서비스 안드로이드애플리케이션의구성요소에는액티비티, 인텐트수신자, 서비스, 컨텐트제공자가있다. 이번호에서는사용자인터페이스를위한액티비티와백그라운드서비스를위한인텐트수신자, 그리고서비스의라이프사이클과활용법에대해살펴보도록하자. 6 연재순서 1 회 2008. 1 애플리케이션구조분석 2 회 2008. 2 GUI 설계, 위젯과레이아웃 3 회 2008. 3 액티비티와인텐트, 그리고서비스 4 회 2008. 4 리소스와컨텐트프로바이더 5 회 2008. 5 웹인터페이스와구글맵연동 6 회 2008. 6 XMPP 기반 P2P 서비스 배준현 joonion@gmail.com 언젠가개방형휴대폰플랫폼에서자유롭게모든모바일서비스를개발할수있는날이올것이라고믿는낙관론자. 개방형모바일소프트웨어컨설팅전문기업인 ( 주 ) 케이마루를창업했다. 휴대폰소프트웨어의구조가복잡해지면서자바, 브루, 위피등의미들웨어가등장하게되었지만, 지금까지의미들웨어는주로다운로드기반의독립적인응용애플리케이션의실행환경을제공하는것이주요목표였다. 최근에는이런미들웨어들이휴대폰소프트웨어전체를운영하도록하는것이주요과제로떠올랐다. 자바에서는 MVM(Multi-tasking VM) 을통해이런목표를향해나아가고있으며, 브루는설계당시부터휴대폰의모든응용을브루기반으로개발할수있도록정적인애플리케이션개발구조를가지고있었다. 위피역시 T-PAK에서다운로드영역뿐만이아니라, MMI 영역의애플리케이션까지포함할수있는구조를갖추게되었다. 이렇게휴대폰소프트웨어전체를미들웨어상의응용애플리케이션들로구성하기위해가장중요한기술은 IPC, 즉애플리케이션들간의통신방법에관한것이다. 게임을하는도중에전화가오거나, MP3를듣고있는중에전화가올때, 또는 DMB 를시청하는중에웹브라우징을해야하는등의다양한멀티태스킹기능을제공하기위해서는애플리케이션상호간의통신규약을잘정의해야하는것이다. 인텐트와애플리케이션간통신안드로이드에서는이러한애플리케이션간의통신이인텐트기반으로이뤄진다. 인텐트는어떤동작을수행하기위한내용을담고있는일종의메시지객체라고정의할수있다. 즉, 인텐트는인텐트를수신하는객체가수행해야하는작업인액션과그액션을수행하는데필요한데이터를포함하고있는메시지이며, 특정애플리케이션이자신, 혹은다른애플리케이션에해당작업을수행할것을인텐트를통해발송할수있다. 예를들어보자. 내가작성하는애플리케이션에서특정웹사이트로이동해야할경우에는어떻게해야할까? 물론직접 WebView를사용해서웹브라우징기능을구현해도되겠지만, 대부분의경우에는내장된웹브라우저에게 URL을던져주는것으로충분할것이다. 이때다음과같이웹사이트를보는액션 (VIEW_ACTION) 을 URL과함께인텐트로생성해 startacti vity() 메소드를통해웹브라우저를호출할수있다. Intent intent = new Intent(Intent.VIEW_ACTION, Uri.parse( http://www.google.com )); startactivity(intent); 234 m a s o
마찬가지로, 특정위치의지도화면을보기위해서는 1월호에서구현했듯이 MapView를사용해서직접액티비티를구현하는방법대신에다음과같이안드로이드폰에내장된구글맵에인텐트를발생시킬수도있다. Intent myintent = new Intent(Intent.VIEW_ACTION, Uri.create( geo:35.892577,128.61382 )); startactivity(myintent); 애플리케이션라이프사이클이렇게거의완전한멀티태스킹환경을갖춘임베디드시스템에서는메모리관리가매우어려운이슈로다가온다. 예외는있지만, 안드로이드에서는대부분하나의애플리케이션이하나의운영체제프로세스에할당되기때문에, 애플리케이션이실행되면서리눅스의프로세스하나가생성 (fork) 된다. 일반적으로프로세스의생성과종료는시스템에많은부하를주므로, 안드로이드에서는한번생성된프로세스는메모리에상주한채로남아있게된다. 이것은자주실행되는애플리케이션의구동속도를향상시키는좋은방법이다. < 화면 1> 은에뮬레이터를처음구동했을때 DDMS의 Devices 탭에서확인한초기프로세스들이다. system_process는안드로이드시스템을관리하는기본프로세스이고 home, phone, content 애플리케이션은휴대폰의동작을위해기본으로생성되어야하므로부팅시에메모리에적재되는애플리케이션들임을알수있다. 안드로이드 SDK 의버전업그레이드 2008년 2월 13 일자로안드로이드 SDK의 m5-rc14 버전이릴리즈되었다. 이전에도 m3 버전에서두번의릴리즈가있긴했지만주로버그수정의수준이었다. 반면에이번 m5 버전의릴리즈에서는 API를포함한대량의수정사항이발생해서필자역시기존에만들어두었던예제들을다시수정해야하는상황이발생했다 ( 이번호의예제들은 m5 이전버전에서는실행되지않음에주의 ). 이러한 SDK의변경으로인해구글은자사의개발자공모전데드라인을 4월 14일로약한달가량연기했다. m5 버전의 SDK에서변경된주요사항은다음과같다. - 사용자인터페이스 : < 화면 3> 에서보는바와같이화면이좀더예뻐졌다. - android.locaiton.geocoder : 위도, 경도좌표와주소를변환할수있는 API가추가되었다. 대구야식집 이라고입력하면대구지역야식집좌표정보를리턴하도록할수있다. - 미디어코덱 : MIDI, XMF, imelody, RTTL/RTX, OTA 등의코덱이추가되었다. 윈도우환경에서의오디오문제는그이전버전에서이미수정된덕분에사운드플레이가안정화되었다. - 플러그인수정 : AndroidManifest.xml 파일의편집기가제공된다. - API 변경 : m5 버전에서는안드로이드 API가대폭변경되었다. 자세한내용은다음주소를참고하길바란다. http://code.google.com/android/migrating/m3-to-m5/m5-apichanges.html < 화면 1> 초기프로세스화면 여기서 Maso03이라는애플리케이션을실행해앞에서작성한것처럼브라우저와구글맵을실행하면 < 화면 2> 와같이각각의 < 화면 3> m5-rc15 버전 SDK 화면 프로세스가생성됨을확인할수있다. 하지만 Maso03 애플리케이션과브라우저및구글맵을모두종료해도프로세스는종료되지않을것이다. 그렇다면언제이프로세스들은종료하게되는것일까? < 화면 2> 신규프로세스의생성 프로세스의우선순위프로세스를종료시키는것은시스템프로세스가담당한다. 시 m a s o 235
6 _ 액티비티, 인텐트수신자, 그리고서비스 스템프로세스는메모리가부족할때현재메모리에적재된프로세스중에서가장우선순위가낮은프로세스를종료시키고메모리를수거한다. 프로세스의우선순위는다음과같다. - 포어그라운드프로세스 : 현재액티비티스택의최상위에있는액티비티를포함한애플리케이션프로세스. 즉지금사용자가사용하고있는프로세스에해당하므로가장우선순위가높다. - 가시적프로세스 : 현재사용자가사용하고있지는않지만, 사용자의눈에보이는액티비티를포함한프로세스. 즉액티비티스택의최상위에다이얼로그가있거나, 투명한화면이있을경우에는액티비티스택의아래에있는화면이사용자에게보이므로우선순위가높은것이다. - 서비스프로세스 : 현재실행중인서비스나인텐트수신자를포함하고있는애플리케이션의프로세스. 서비스나인텐트수신자는백그라운드에서동작하지만 MP3 사운드를플레이하는등의동작을수행중이므로우선순위가높다. - 백그라운드프로세스 : 실행이중단된상태에있는액티비티를포함하고있는애플리케이션프로세스. 액티비티들이중단된상태에있어서프로세스를종료해도문제가없어야한다. - 빈프로세스 : 모든애플리케이션컴포넌트들 ( 액티비티, 인텐트수신자, 서비스, 컨텐트제공자 ) 이액티브상태에있지않은애플리케이션프로세스. 우선순위가가장낮기때문에프로세스를종료해도무방하지만, 나중에다시실행될때의속도향상을위해메모리가부족한경우가아니면종료하지않는다. 액티비티상태이처럼하나의프로세스에하나의애플리케이션이할당되지만하나의애플리케이션은여러개의액티비티를포함할수있다. 각각의액티비티들은다음과같은상태를가진다. - 실행 (running) : 액티비티스택의최상위에존재하며현재사용자인터페이스를담당하고있는상태. - 정지 (paused) : 다이얼로그와같이전체화면이아니거나투명한 UI를가진액티비티가실행상태에있을때, 포커스는없지만화면에나타나고있는상태. - 중단 (stopped) : 액티비티스택의하위에있어서화면에보이지않는상태. - 종료 (killed) : 애플리케이션프로세스가종료되어메모리에서삭제된상태. 의해당하는콜백메소드를호출해준다. < 그림 1> 은각액티비티의상태변경과호출되는액티비티의콜백메소드를보여준다. start oncreate() onstart() onrestart() onresume() killed running onfreeze() onpause() onstop() stopped ondestroy() < 그림 1> 액티비티의상태다이어그램 < 리스트 1> 은 < 그림 1> 의각각콜백메소드에 android.util.log 로로그메시지를남기도록구현한예제이다. 이예제를실행하기전에 DDMS의 LogCat 탭에서 [E] 버튼을선택해놓고, 메뉴에서예제를실행해보자. 최초에애플리케이션을실행하면 oncreate(), onstart(), onresume() 메소드가차례로호출되고 Maso03 액티비티의상태는 running 상태가된다. E/ActivityTest: ( 602): oncreate E/ActivityTest: ( 602): onstart E/ActivityTest: ( 602): onresume 실행상태에서 Go 버튼을클릭하면 startactivity() 메소드에의해웹브라우저에인텐트가전달되고, 웹브라우저는맵액티비티를실행한다. 다른액티비티가실행되었으므로, 각각 onfreeze(), onpause() 가호출된다. 맵액티비티는화면전체를차지하므로맵액티비티가생성되고난후에는 onstop() 이호출되어 Maso03 액티비티는 paused 상태가된다. 이렇게각각의액티비티상태가변경될때시스템은액티비티 E/ActivityTest: ( 602): onfreeze 236 m a s o
E/ActivityTest: ( 602): onpause E/ActivityTest: ( 602): onstop Log.e(TAG, "oncreate"); 브라우저에서 BACK 버튼으로다시 Maso03 액티비티로되돌아오면각각 onrestart() 와 onstart(), onresume() 이호출된다. E/ActivityTest: ( 602): onrestart E/ActivityTest: ( 602): onstart E/ActivityTest: ( 602): onresume 모든안드로이드애플리케이션실행중에는전화또는 SMS를수신하거나, 알람이나통지가발생할수있기때문에동적으로생성되는객체의인스턴스를시스템에서호출해주는각각의액티비티상태콜백메소드에서적절하게처리해줘야애플리케이션이정상적으로동작할수있다. < 리스트 1> Maso03.java package com.kmaru.maso03; import android.app.activity; import android.content.contenturis; import android.content.intent; import android.net.uri; import android.os.bundle; import android.util.log; import android.view.view; import android.widget.button; public class Maso03 extends Activity { public static final String TAG = "ActivityTest: "; protected void ondestroy() { super.ondestroy(); Log.e(TAG, "ondestroy"); protected void onfreeze(bundle outstate) { super.onfreeze(outstate); Log.e(TAG, "onfreeze"); protected void onpause() { super.onpause(); Log.e(TAG, "onpause"); protected void onrestart() { super.onrestart(); Log.e(TAG, "onrestart"); protected void onresume() { super.onresume(); Log.e(TAG, "onresume"); protected void onstart() { super.onstart(); Log.e(TAG, "onstart"); protected void onstop() { super.onstop(); Log.e(TAG, "onstop"); public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); Button btn = (Button)findViewById(R.id.button); btn.setonclicklistener(new Button.OnClickListener() { public void onclick(view v) { Intent intent = new Intent(Intent.VIEW_ACTION, Uri.parse("http://www.google.com")); startactivity(intent); ); 인텐트수신자인텐트수신자는다른애플리케이션이발송하는특정한인텐트를수신할수있는애플리케이션컴포넌트이다. 예를들어설명하는것이가장이해가빠르므로 SMS를수신하는인텐트수신자를만들어보자. 우선 < 화면 4> 와같이 DDMS에서 Emulator Control을열면에뮬레이터에음성호와 SMS를발송할수있는도구가있다. 여기에서음성호를발신하면에뮬레이터에서전화를수신할것이다. 그런데 SMS를발송하면에뮬레이터에아무런반응이없을것이다. 이것은현재에뮬레이터내에 SMS를처리하는애플리케이션이탑재되지않았기때문이다. m a s o 237
6 _ 액티비티, 인텐트수신자, 그리고서비스 < 리스트 3> SmsReceiver.java package com.kmaru.sms; import android.content.context; import android.content.intent; import android.content.intentreceiver; import android.os.bundle; import android.provider.telephony; import android.telephony.gsm.smsmessage; import android.widget.toast; < 화면 4> 에뮬레이터제어도구 SMS 수신자구현 단말기가 SMS를수신했을때, 이 SMS를수신하기위해서는 SMS_RECEIVED 액션에대한인텐트수신자가필요하다. 이러한인텐트를수신하는 SmsReceiver라는인텐트수신자를 < 리스트 2> 와같이 AndroidManifest.xml 파일에정의하자. 주의할점은 SMS를수신하기위해애플리케이션은 RECEIVE_SMS라는권한을정의해줘야한다는점이다. <uses-permission android:name= android.permission.receive_sms /> IntentReceiver 클래스는 onreceiveintent() 라는추상메소드를갖고있으므로이메소드를오버라이딩해 < 리스트 3> 과같이수신한 SMS를화면에뿌리도록하자. Toast 클래스는 m5 버전에서기존의투명한통지 (Notification) 를대체하기위해등장한새로운위젯이다. < 리스트 2> AndroidManifest.xml <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/ res/android" package="com.kmaru.sms"> <uses-permission android:name="android.permission.receive_sms"/> <application android:icon="@drawable/icon"> <!-- activity 태그는지면관계상생략 --> <receiver android:name=".smsreceiver"> <intent-filter> <action android:name= "android.provider.telephony.sms_received" /> </intent-filter> </receiver> </application> </manifest> public class SmsReceiver extends IntentReceiver { public void onreceiveintent(context context, Intent intent) { StringBuilder sb = new StringBuilder(); Bundle bundle = intent.getextras(); if (bundle!= null) { SmsMessage[] messages = Telephony.Sms.Intents.getMessagesFromIntent(intent); "\n"); for (int i=0; i<messages.length; i++) { sb.append("sms 수신 : \n" ); sb.append(" 발신번호 : " + messages[i].getoriginatingaddress() + sb.append(" 내용 : " + messages[i].getdisplaymessagebody() ); Toast.makeText(context, sb.tostring(), Toast.LENGTH_SHORT).show(); 서비스와 MP3 플레이어인텐트수신자는특정액션을백그라운드에서실행하기위한훌륭한메커니즘을제공하지만완전한백그라운드서비스를제공하는데에는제약이따른다. 예를들면 MP3 사운드를백그라운드에서플레이한후나중에음악을중단하기위해서는백그라운드서비스가좀더오래살아있을필요가있다. 이런경우에는 Service 애플리케이션컴포넌트를활용할수있다. < 리스트 4> 는 MP3 플레이어를구현하기위한백그라운드서비스를구현한것이다. 서비스가실행되면 /sdcard/music.mp3 파일을플레이하고서비스가종료될때음악을종료하도록구현했다. 이서비스는 AndroidManifest.xml 파일에다음과같이정의해야한다. <service android:name=.mp3service /> 238 m a s o
< 리스트 4> Mp3Service.java package com.kmaru.mp3; import java.io.ioexception; import android.app.service; import android.content.intent; import android.media.mediaplayer; import android.os.binder; import android.os.bundle; import android.os.ibinder; public class Mp3Service extends Service { { MediaPlayer mp = new MediaPlayer(); Binder binder = new Binder(); public IBinder onbind(intent intent) { return binder; protected void onstart(int startid, Bundle arguments) super.onstart(startid, arguments); try { mp.reset(); mp.setdatasource("/sdcard/music.mp3"); mp.prepare(); mp.start(); catch (IOException e) { e.printstacktrace(); protected void ondestroy() { super.ondestroy(); Intent intent = new Intent(Mp3Player.this, Mp3Service.class); startservice(intent, null); ); Button stop = (Button)findViewById(R.id.stop); stop.setonclicklistener(new Button.OnClickListener() { public void onclick(view v) { Intent intent = new Intent(Mp3Player.this, Mp3Service.class); stopservice(intent); ); MP3 플레이어의실행앞에서작성한 MP3 플레이어를실행하기위해서는 /sdcard 디렉토리에 music.mp3 파일을복사해줘야한다. 먼저다음과같이 mksdcard 도구를이용해서 /sdcard 디렉토리에마운트할이미지를생성하도록한다. tools> mksdcard 16M lib/images/sdcard.img 생성된이미지를마운트하기위해서는에뮬레이터를실행할때 -sdcard 옵션을주고, 위에서생성한파일을지정해야한다. 이클립스에서는파일의경로를절대경로로설정해야실행될것이다. tools> emulator -sdcard lib/images/sdcard.img mp.stop(); 에뮬레이터가실행된후에 adb를이용해서다음과같이 /sdcard 디렉토리로 mp3 파일을복사해준다. < 리스트 4> 에정의된 Mp3Service를실행하거나종료하는것은다음과같이버튼의리스너에서 startservice(), stopser vice() 메소드를각각호출하는방식으로구현된다. Button play = (Button)findViewById(R.id.play); play.setonclicklistener(new Button.OnClickListener() { public void onclick(view v) { tools> adb push music.mp3 /sdcard/music.mp3 지금까지안드로이드애플리케이션의컴포넌트들가운데액티비티와인텐트수신자, 서비스에대해살펴봤다. 다음달에는마지막남은컴포넌트인컨텐트제공자에대해살펴보도록하자. m a s o 239