관리 메뉴

드럼치는 프로그래머

[안드로이드] [번역] 안드로이드 2.0 Service API 의 변화 -1- 본문

★─Programing/☆─Android

[안드로이드] [번역] 안드로이드 2.0 Service API 의 변화 -1-

드럼치는한동이 2013. 6. 24. 18:19
 

Service API changes starting with Android 2.0

원문 : http://android-developers.blogspot.com/2010/02/service-api-changes-starting-with.htm

 구글 Android Developer 블로그에 Android 2.0 에서사 변경된 Service API 에 관한 글이 있어서 간단하게 정리해 봅니다. 안드로이드에서 Service 에대해 잠시 이야기 해보자면, Service는 GUI 를 구성하는 Activity 와는 다르게, 화면상에 직접적으로 표시 되지는 않지만, 데몬 형태로 작동하며 유용한 작업(음악 재생이라던가 메일 폴링이라던가...)을 수행합니다. Activity 의 생명주기가 굉장히 짧기 때문에, 어플리케이션을 구성하는데 매우 중요 하지만,  개인적으로 Service 는 사용하는 일은  관리가 어렵고 생각 못한 문제가 많이 발생해서 서 늘 고생하고 있는 부분입니다.

Service API changes starting with Android 2.0
  • Service.setForeground() 가 더 이상 작동하지 않음. (Deprecated)
  • 상황에 따라 더 이상 필요하지 않은 Service 가 죽지 않는 경우가 많았는데, 이런한 에외사항을 보다 잘 처리할 수 있도록 새로운 API 를 제공함.
  • 사용자들이 현재 시스템에서 작동 중인 Service 를 확인하고 관리할 수 있는 UI 를 제공.
Background on services

  서비스 API 는 어플리케이션이 백그라운드에서 특정 작업을 수행할 수 있게 해주는.안드로이드의 핵심 매커니즘의 하나이다. 안드로이드 플랫폼 특성상, 화면상에 표시되지 않은 개별 어플리케이션은 시스템이 종료시키고, 필요한 경우 다시 시작할 수 있다고 여겨진다. 따라서 특정 어플리이션이 백그라운드에서도 특정 일을 수행할 필요가 있을 경우, Service 를 시작해야 하며, 시스템은 Service 를 구동중인 어플리케이션은 강제로 종료시키지 않는다. (최대한...)
   Service 는 매우 유용한 수단이지만, 언제나 권한에는 책임이 뒤 따른다. (power comes some responsibility). Service 를 가동하고 있는 어플리케이션은 시스템 자원을 소모기 때문에 전반적인 시스템 성능에 문제를 일으킬 수 있다. 따라서 개발자들은 정말로 필요한 경우에만 Service 를 사용해야 하며, 필요없는 Service 가 백그라운드에 남아있지 않도록 주의를 기울여야 한다. (서비스 릭 버그를 내지 말아야 한다...)

Redesigning Service.setForeground()

 Androi 1.6 안정화 기간동에, 너문 많은 어플리케이션이 Service.setForeground() API 를 사용하기 때문에 발생하는 문제들이 발견되었다.  이 API가 호출 되면,  시스템은 특정 Service가 구동중인 프로세스가 포그라운드 프로세스로 간주한다. (우선 순위 부여)  따라서, 시스템이 메모리 부족등의 예외 상황을 처리하게 매우 어렵게 만든다. 
  이러한 문제점이 발견된 시점이 너무 늦었기에, 1.6 버전 SDK는 이 API에 대하여 큰 변화 없이 공개되었지만, 2.0 에서는 중요한 변경사항이 이루어졌다. Service.setForeground() 는 이제 작동하지 않는다. 따라서 기존의 API 를 대체해서 2.0 버전에서는 두가지  새로운 API 가 추가되었다. 이제 Service 가 포그라운드로 진입하고자 할  경우, 사용자가 해당 사실을 확인하고 필요한 경우, 포그라운드에서 작동중인 서비스를 정지 시킬 수 있도록, Notification 을 보내야 한다. (주> 음악 재생 서비스 와 같이, 상태바에 특정 메세지를 표시하도록...)
 
public final void startForeground(int id, Notification notification);

public final void stopForeground(boolean removeNotification);
 
 시스템은 Service 가 포그라운드에서 작동 중이라면, 어떠한 형태든 Notification 이 남아 있음을 확신 할 수 있고 따라서, Service 와 연관된 Notification 상태를 쉽게 관리 할 수 있다.  당연히, 많은 개발자들이 2.0 뿐만 아니라 이전 플랫폼에서도 작동하는 Service 코드를 작성하고 싶어한다. 아래와 같이 가능한 경우에 한해서 새롭게 추가된 API 를 호출하는 방법으로 예전 버전과 2.0 이후 버전에서 모두 작동하는 코드를 작성 할 수 있다.

private static final Class[] mStartForegroundSignature = new Class[] {
int.class, Notification.class};
private static final Class[] mStopForegroundSignature = new Class[] {
boolean.class};

private NotificationManager mNM;
private Method mStartForeground;
private Method mStopForeground;
private Object[] mStartForegroundArgs = new Object[2];
private Object[] mStopForegroundArgs = new Object[1];

@Override
public void onCreate() {
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
try {
mStartForeground = getClass().getMethod("startForeground",
mStartForegroundSignature);
mStopForeground = getClass().getMethod("stopForeground",
mStopForegroundSignature);
} catch (NoSuchMethodException e) {
// Running on an older platform.
mStartForeground = mStopForeground = null;
}
}

/**
* This is a wrapper around the new startForeground method, using the older
* APIs if it is not available.
*/
void startForegroundCompat(int id, Notification notification) {
// If we have the new startForeground API, then use it.
if (mStartForeground != null) {
mStartForegroundArgs[0] = Integer.valueOf(id);
mStartForegroundArgs[1] = notification;
try {
mStartForeground.invoke(this, mStartForegroundArgs);
} catch (InvocationTargetException e) {
// Should not happen.
Log.w("MyApp", "Unable to invoke startForeground", e);
} catch (IllegalAccessException e) {
// Should not happen.
Log.w("MyApp", "Unable to invoke startForeground", e);
}
return;
}

// Fall back on the old API.
setForeground(true);
mNM.notify(id, notification);
}

/**
* This is a wrapper around the new stopForeground method, using the older
* APIs if it is not available.
*/
void stopForegroundCompat(int id) {
// If we have the new stopForeground API, then use it.
if (mStopForeground != null) {
mStopForegroundArgs[0] = Boolean.TRUE;
try {
mStopForeground.invoke(this, mStopForegroundArgs);
} catch (InvocationTargetException e) {
// Should not happen.
Log.w("MyApp", "Unable to invoke stopForeground", e);
} catch (IllegalAccessException e) {
// Should not happen.
Log.w("MyApp", "Unable to invoke stopForeground", e);
}
return;
}

// Fall back on the old API. Note to cancel BEFORE changing the
// foreground state, since we could be killed at that point.
mNM.cancel(id);
setForeground(false);
}
* 정렬 문제로 코드 보기가 수월하지 않습니다. 원본 사이트를 참조하시는게 좋을 듯.

 코드가 길긴 하지만, 핵심적인 내용은 Reflect 패키지를 사용해서 현재 Class 에 새롭게 추가된 startForeground Method 를 제공할 경우, 해당 Method 를 Invoke 하고 그렇지 않으면 이전 Method 를 Invoke 하는 거 같습니다. 으음... 조금 복잡하네요. 개인 개발자가 신경써야하는 호환성 이슈가 하나 더 추가되었습니다. 이런 변경사항이 앞으로 최소화 되어야 할텐데 말입니다...

 Service Lifecycle 의 변화에 관한 내용은 다음 포스트에서 이어가도록 하겠습니다.
Comments