PAAD 8장 백그라운드에서 작업하기 - 발표: 2010년 03월 3일(수) - 오후 07시 30분 ~
8장 목차
|
주요 용어: Service, Bind, Thread, Handler, Toast, Notification, 소리, 진동, 불빛, Alarm
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. 서비스 소개
- 서비스는 백그라운드에서 실행되어지며, 비활성 액티비티보다 더 높은 우선순위를 갖는다.
- 사용자 입력에 직접 의존하지 않는 동작을 규칙적이며 연속적으로 수행할 때 사용하면 좋다.
- 서비스의 시작과 종료는 다른서비스, 엑티비티, 브로드캐스트 수신자를 포함한 다른 애플리케이션으로부터 가능하다.
- 서비스 활용 사례 (별도의 쓰레드를 통해서 사용 가능하다)
. 파일 다운로드
. Media Player
- 안드로이드 소프트웨어 스택내의 서비스 사용 사례
. Location Manger
. Media Controller
. Alarm Manager 등
- 서비스와 바인딩 서비스의 생명주기
> entire lifetime : onCreate() - onDestroy()
> active lifetime : onStart()
* onStop() 메쏘드는 없다.
* 인텐트오브젝트를 받을 수 있는 시점: onStart(), onBind(), onUnbind()
* 상기 두가지 생명주기는 서로 분리된 것은 아니다. 서비스로 시작되었다 하더라도 새로이 클라이언트에서 bind를 호출할
서비스 만들고 제어하기
1) 서비스를 상속 받은 MyService class를 만든다.
2) MyService에서 onCreate(), onStart() 메소드를 재정의해서 자신의 기능을 구현한다.
3) 새로 만든 Service는 Manifest에 등록한다.
4) MyController 클래스에서 startService()를 통해서 MyService를 시작한다.
* 한번 생성된 서비스를 다시 startService()로 실행할 때는 onCreate()는 타지 않고 onStart() 실행됨(상기그림의 회색선)
또한 여러번 시작한 서비스라 할지라도 종료는 stopService()로 단 한번만으로 종료 된다.
<service android:enabled="true" android:name=".MyServic"></service>
암시적 서비스 실행 | 명시적 서비스 실행 |
startService(new Intent(MyService.MY_ACTION); | startService(new Intent(this, MyService.class); |
* MyService 클래스에 MY_ACTION을 포함시키고, 인텐트 필더에 MY_ACTION을 공급자로 등록해야 한다.
서비스명을 통한 서비스 종료 | 명시적 서비스 종료 |
ComponentName service = startService(new Intent(this, MyService.class); stopService(new Intent(this, service.getClass()); | try{ Class serviceClass = Class.forName(service.getClassName()); stopService(new Intent(this, serviceClass); }catch(ClassNotFoundException e){} |
* stopSelf(): 서비스 스스로 종료하기 (Activity의 finish()와 동급)
* stopSelfResult(): 서비스 스스로 종료하기 (정확한 의미는 미파악상태임)
바인더에 대해서
Binder는 리눅서 커널에 있는 Binder를 통해서 관리된다.
1) ServiceManager는 BinderDriver 에 대한 special Node 0 로 동작
2) ServiceProvider (예 : AudioFlinger) 는 자신의 RPC에 대한 Interface를 ServiceManager proxy object에 대한 Interface를 통해 등록한다.
- 이 과정을 통해 BinderDriver에서는 ServiceProvider로의 path를 생성
3) Service User는 사용하려는 Service에 대한 Interface를 생성하는데, 이를 위해 ServiceManager에게 RPC call을 통해 요청한다.
4) ServiceManager는 Bind되어있는 Object들의 목록에서 해당하는 ServiceProvider에 대한 Node를 요청한 ServiceUser에게 return
5) ServiceUser는 return된 Node정보를 통해 해당 Service Provider로의 RPC를 수행함
* 인용) http://andstudy.springnote.com/pages/4314107 / 수원안드로이드플랫폼스터디
서비스에서 바인더를 사용하는 이유:
* 액티비티가 서비스에 바인딩되면, 그 액티비티는 해당 서비스 인스턴스 자체에 대한 레퍼런스를 유지하고 실행중에 서비스에 대해 메서드를 호출할 수 있도록 해 준다.
* 서비스가 바인드되고 나면, 서비스가 가지고 있는 모든 public 메서드와 프로퍼티는 onServiceConnected핸들러를 통해서 얻어진 serviceBinder객체를 통해서 이용 가능하다.
* 브로드캐스트 인텐트 또는 서비스 시작시 사용되는 인텐스 익스트라 번들을 이용해서 간단히 다른 프로세스로 실행 중인 서비스와 간단히 통신할 수 있다.
* 좀더 단단히 결합된 연결을 원할 경우에는 AIDL을 이용하면 된다.
서비스에 액티비티 바인딩하기
서비스 작성 순서
서비스 동작
예제) Toast를 사용한 Service Bind 예제:PAAD08_service.zip
일반 서비스와 바인드 서비스의 사용예
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2.백그라운 작업자 Thread 이용하기
2-1.백그라운드의 thread의 필요성
- 모든 콤포넌트는 하나의 Main thread위에서 동작한다.
- 따라서 시간이 많이 걸리는 작업을 하면 화면에 보이는 Activity뿐만아니라 다른 모든 콤포넌트까지 불록시킨다.
- 이때 "Application Unresponsive" 메시지가 출력될 것이다.
2-2 백그라운드 thread 활용 추천
- 입력 이벤트(키 눌림)에 대해서 5초 이내에 반응하지 않을 때
- onReceive 핸들러를 10초 이내에 완료하지 않는 Broadcast Receiver
- 이런 현상이 발생되는 주요 작업 사항으로는
. 파일작업
. 네트워크 조회
. 데이터베이스 트랜잭션
. 복잡한 계산
2-3 백그라운드 스레드 사용의 결론
- 느리고 시간이 많이 드는 모든 작업을 Main thread에서 Child thread로 옮기는 것이 좋은 습관이다.
2-4 새로운 thread와 GUI 작업을 위한 thread 동기화
- 골격 코드
// GUI Handler 처리.. private Handler handler = new Handler(); // 이 메서드는 메인 GUI 스레드에서 호출된다. private void mainProcessing(){ // 이는 시간이 많이 드는 작업을 자식 스레드로 옮긴다. Thread thread = new Thread(null, doBackgroundThreadProcessing, "Background"); thread.start(); } // 백그라운드 처리 메서드를 실행하는 Runnable private Runnable doBackgroundThreadProcessing = new Runnable(){ @Override public void run() { backgroundThreadProcessing(); } }; // 백그라운드에서 몇 가지 처리를 수행하는 메서드 private void backgroundThreadProcessing(){ //[필요한 코드] // 처리가 끝나고 결과를 UI로 출력을 해야 할 때 아래 핸들러를 추가해서 사용한다. handler.post(doUpdateGUI); } // GUI 업데이트 메서드를 실행하는 Runnable. private Runnable doUpdateGUI = new Runnable(){ @Override public void run() { updateGUI(); } }; private void updateGUI() { // [[ 필요한 UI 코드 ]] }
Thread를 만드는 핵심 코드
private void mainProcessing(){
Thread thread = new Thread(null, doBackgroundThreadProcessing, "Background");
thread.start();
}
=>> Thread(ThreadGroup group, Runnable runnable, String threadName)
Thread와 GUI연결을 위한 핵심 코드
handler.post(doUpdateGUI);
- Handler를 사용하는 이유
Child Thread는 GUI를 갖고 있지 않다. 모든 GUI는 Main thread가 갖고 있어서 뷰, Toast등은 모두 Main thread에서 처리해야됨
따라서 handler를 통해서 child thread의 처리 결과를 Main thread에서 처리할 수 있게 해준다.
- Handler 클래스의 추가 메소드
. pastDelayed: 포스팅을 늦추어 처리한다.
. pastAtTime : 포스팅을 특정 시간에 실행하도록 한다.
- UIThreadUtilities 클래스
이 클래스는 runOnUIThread를 갖고 있어서 View, Activity, Dialog와 같은 thread에서 메소드를 강제적으로 실행해 줌
- Thread에서 꼭 기억해야 할 사항
다른 모든 경우에 있어, GUI스레드(뷰 같은)에서 생성된 객체와 명시적으로 상호작용하거나 메시지를 표시하는 작업은
메인 스레드에서 띄워져야만 한다."
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3.Toast 만들기
==========>
토스트란?
- 형태: 몇 초간만 화면에 보였다가 페이드 아웃되는 일시적 다이얼로그.
- 특징: focus를 훔치지 않으며 비 모달로 활성화 애플리케이션에 방해를 주지 않는다.
- 용도: 앱에 방해주지 않고 정보를 사용자에게 쉽게 제공한다.
원형
Toast.makeText(this, "10분 남았습니다",1000).show(); public static Toast makeText (Context context, CharSequence text, int duration)
- makeText: 표준 토스트 표시 윈도우를 생성하는 static 메서드
> context: 애플리케이션 컨텍스트
> text : 표시할 텍스트 메시지
> duration : 다이얼로그가 표시될 시간
- show() : 이것을 통해서 토스트를 화면에 진정으로 표시하게 된다.
팁
- 위치변경: mToast.setGravity(Gravity.CENTER, offesetX, offsetY); mtoast.show();
- 커스텀View를 통해 모양변경: mToast.setView(customLayout);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4.Notification 소개
용도
- 액티비티를 사용하지 않고 사용자에게 알리는 기능
- notification manager를 사용함
- 화면이 보이지 않는 앱 콤포넌트에서 주의 이벤트를 발생하는데 선호하는 방식이다.
기능
> 새로운 상태바 아이콘 만들기
> 확장 상태바 윈도우에서 추가 정보표시 (인테트 사용)
> 불빛/LED 동작
> 진동 알림
> 가청 경보 울림
Notification Manager / 시스템서비스
- 레퍼런스는 콘텍스트의 서비스네임을 인자로 사용하는 getSystemService()메소드를 통해서 얻는다.
- 용도: 새로운 알림을 만듬, 기존 알림을 수정할 수 있음, 기존 알림을 제거할 수 있음
사용방법
* 상기 내용중 noti.FLAG_SHOW_LIGHTS => Notification.FLAG_SHOW_LIGHTS로 정정함
String svcName = Context.NOTIFICATION_SERVICE; NotificationManager notificationManager; notificationManager = (NotificationManager)getSystemService(svcName); // 상태바 구성 int icon = R.drawable.icon; String tickerText = "알림" long when = System.currentTimeMillis(); Notification noti = new Notification(icon, tickerText, when); // 확장 상태바 윈도우 구성 Context context = getApplicationContext(); String expandedText = "확장 상태 텍스트"; String expandedTitle = "알림 제목"; Intent intent = new Intent(this, MyActivity.class); PendingIntent launchIntent = PendingIntent.getActivity(context, 0, intent, 0); noti.setLatestEventInfo(context, expandedTitle, expandedText, lauchIntent); // 상태바 반복 숫자 표시 구성 noti.number++; // 사운드 효과 구성 noti.sound = ringURI; // 진동 효과 구성 long[] vibrate = new long[] = {1000, 1000, 1000, 1000, 1000}; noti.vibrate = vibrate; // LED 효과 구성 noti.ledARGB = color.RED; noti.ledOffMS = 0; noti.ledOnMS = 1; noti.flags = noti.flags | Notification.FLAG_SHOW_LIGHTS; // 확장 상태바 윈도우에 진행중 또는 강조 구성 noti.flags = noti.flas | Notification.FLAG_ONGOING_EVENT; //noti.flags = noti.flas | Notification.FLAG_INSISTENT; // 상기 구성 내용 알림 발생 레퍼런스ID관리 필요 int notificationRef = 1; noticationManager.notify(notificationRef, noti); // 해당 레퍼런스 ID의 알림 취소, 상태바, 확장 상태바 윈도우 내용 삭제됨 단, 효과는 진행됨 noticationManager.cancel(notificationRef);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5. Alarm 소개
- 알람은 미리 예정된 시간에 인텐트를 발생키는 애플리케이션 독립적인 방법
- 애플리케이션 범위 밖에서 설정 가능
- BroadCast Receiver와 함께 Alarm이 조합 될 때 더욱 강력한 위력을 발휘한다.
- 활용: 애플리케이션을 실행시킴, 액션을 수행하는 알람을 설정할 수 있음
> 알람 시계
> 규칙적으로 네트워크 조회
> 시간, 비용이 많이 드는 작업을 유후때 실행할 수 있도록 예약 할 수 있다.
- 설정의 유지 및 해제
> 절전모드 일때도 알람은 활성화 되어 있다. (RTC, 백업배터리)
> 절전모드에서 알람이 실행되어야 할때 WakeUp 여부를 선택할 수 있도록 설정할 수 있다.
> 모든 알람은 전원 재부팅시 모두 소멸된다.
* 애플리케이션이 실행되어 있을 때는 알람을 사용하는 것보다는 Timer와 Thread를 사용하는 것을 권장한다.
이것은 시스템 리소스를 더욱 잘 제어할 수 있기 때문이다.
- 핵심코드
alarms.set(alamType, timeOrLenghtOfWait, pendingIntent):
> alarmType: 알람타입
> timeOrLengthOfWait: 트리거시간
> pendingIntent: 트기거될 때 발생시킬 대기 인텐트
* 동일한 pendingInetent로 알람을 설정하면 최종것으로만 설정되게 된다.
- AlarmType: 트리거 시간의 종류가 wakeup여부에 따라서 4종류가 있다.
> RTC
> RTC_WAKEUP
> ELAPSED_REALTIME
> ELAPSED_REALTIME_WAKEUP

AlarmManager alarms = (AlarmManager)getSystemService(Context.ALARM_SERVICE); String MY_RTC_ALARM = “MY_RTC_ALARM”; String ALARM_ACTION = “MY_ELAPSED_ALARM”; PendingIntent rtcIntent = PendingIntent.getBroadcast(this, 0,new Intent(MY_RTC_ALARM),1); PendingIntent elapsedIntent = PendingIntent.getBroadcast(this, 0,new Intent(ALARM_ACTION),1); // Wakeup and fire intent in 5 hours. Date t = new Date();t.setTime(java.lang.System.currentTimeMillis() + 60*1000*5); alarms.set(AlarmManager.RTC_WAKEUP, t.getTime(), rtcIntent); // Fire intent in 30 mins if already awake. alarms.set(AlarmManager.ELAPSED_REALTIME, 30*60*1000, elapsedIntent); // Cancel the first alarm. alarms.cancel(rtcIntent);
알람을 반복해서 발생시키기
pi //PendingIntent
);
상기의 것을 사용할 경우 너무 자주 깨어나면 아래 것을 사용한다.
alarms.setInexactRepeating(type, triggerAtTime, interval, operation)
- 정해진 시간에 실행되고 작업 수행시 종료된다.
- 정해진 시간은 AlarmManager.INTERVAL_FIFTEEN_MINUTES을 거의 사용한다.
- 1.backgroud.png (87.7KB)(49)
- 2-1_service_lifecycle.png (57.9KB)(35)
- 2_service02.png (20.6KB)(12)
- 3_1_Binder.PNG (79.0KB)(13)
- 3_binder2.png (60.7KB)(11)
- 4_binder4.png (30.8KB)(11)
- 5_binder5.png (42.0KB)(11)
- 6_Service_MP3P.PNG (23.1KB)(8)
- 7_Bind_MP3P.PNG (24.7KB)(10)
- 8_thread.png (72.5KB)(12)
- 9.Thread_Handler.png (19.3KB)(16)
- 10_1_toast.PNG (3.1KB)(14)
- 10_toast.png (28.3KB)(13)
- 11_toast_center.PNG (14.8KB)(13)
- 12_notification.png (158.5KB)(24)
- 13_alarm.png (46.8KB)(45)

http://developerlife.com/tutorials/?p=356 를 참조해 보면 service 객체에 update listener를 구현해서 UIThreadUtilities.runOnUIThread() 를 구현하는 것 같음. MyService.setUpdateListener(new ServiceUpdateUIListener() { public void updateUI(final Hashtable<String, Hashtable<String, String>> newData) { // make sure this runs in the UI thread... since it's messing with views... UIThreadUtilities.runOnUIThread( context, new Runnable() { public void run() { ...UI 처리 작업 } } ); } }
[Quez] 서비스 중에 bind가 있는 서비스의 장점이 뭘까요?
정답 : 빙고