티스토리 뷰
앞서 설명한 Local Service 를 구현하는 방법중에 하나인 startService를 통해 구현하는 방법에 대해 설명하겠다.
특징
1. 특정 서비스를 백그라운드로 동작시키는 것에 목적을 둔 형태.
2. 발생시킨 곳에서는 중단시키는 것 이외에는 어떤 제어도 불가능하다.
3. 발생시키는 곳과 서비스는 독립적이여서 Activity가 종료되어도 동작한다.
Start Service의 생명주기
Start/Bound 인지에 따라서 생명주기에 차이가 있다.
Service가 실행되면 onCreate - onStartCommand 생명주기 함수가 호출되고, 종료될 땐 onDestroy 생명주기 함수가 호출된다.
결국 서비스가 동작중일땐 onStartCommand의 코드가 계속해서 동작하는 것이다.
구현
1. Service 클래스를 상속받은 Java 클래스를 만든다.
( 당연히 AndroidManifest.xml에 Service 태그 등록해야한다. )
2. onBind, onCreate, onStartCommand, onDestroy Method들을 Overring한다.
3. Service의 동작과정은 onStartCommand에 작성한다. onBind는 bind Service에서 사용한다.
4. intent에 Service의 클래스 정보를 담은뒤에 startService(intent) / stopService(intent)
public class CountService extends Service {
public void onCreate() {
super.onCreate();
}
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
public void onDestroy() {
super.onDestroy();
}
// onBind 함수는 Bound Service에서 사용하는 함수지만 여기서도 Overriding은 해줘야한다.
public IBinder onBind(Intent arg0) {
return null;
}
}
Service는 Component이기 때문에 반드시 AndroidManifest.xml에 Service에 대한 정보를 기록해야 한다.
<service android:name=".CountService">
<intent-filter>
<action android:name="ch4njun_count">
</intent-filter>
</service>
그렇다면 이렇게 생성한 Start Service는 어떻게 실행시키고 종료시키는지 살펴보자.
public void onClick(View v) {
switch(v.getId()) {
case R.id.start_count_btn:
Intent serviceIntent = new Intent("ch4njun_count");
startService(serviceIntent);
break;
case R.id.stop_count_btn:
Intent serviceIntent = new Intent("ch4njun_count");
stopService(serviceIntent);
break;
}
}
이미 Service가 실행중일 때 startService가 다시한번 호출될 경우 onCreate는 생략되고 onStartCommnad 생명주기 함수가 한번 더 호출되게 된다.
onStartCommand의 반환값
Service가 실행되는 도중 시스템이 자원이 부족해서 강제로 Service를 종료시킬 수도 있다.
이 때 onStartCommand의 반환값이 START_STICKY 이면 자원이 확보되는대로 다시 Service를 동작시킨다.
그러나 START_NOT_STICKY를 반환한다면 사용자가 다시 Service를 실행시키지 않는한 실행되지 않는다.
여기서 LMK의 우선순위에 대해서 설명하는데 꼭 순서를 기억하자 (시험에 나온대요..ㅠㅠ)
Service의 종료
우리는 Service를 생성한 컴포넌트에서 stopService 함수를 호출해 해당 Service를 강제로 종료시킬 수 있다.
stopService를 사용하지 않고 Service를 종료시키는 방법도 당연히 존재한다. Service가 자기할일을 마치고 스스로 종료하게 하는 방법은 다음 함수를 사용하면 된다. ( stopSelf, stopSelfResult )
이렇게 스스로 종료될 수 있는 여지가 있기 때문에 Service의 생명을 영원히 보장할 수 없다.
메모리 부족시 시스템은 강제로 Service를 종료시킬 수 있다.
이러한 작업을 LMK(Low Memory Killer)가 담당한다.
두 가지 방법의 동작방식은 동일하지만 왠만하면 stopSelf Method를 사용하는 것을 권장한다.
참고로 둘다 적용되어있지 않으면, Service는 절대 onDestroy를 호출하지 않는다.
이미 해당 Service가 동작하면 추가 동작 막기
( 굉장히 유용하게 사용할 수 있을 것 같다. )
→ 동기화 문제의 역이용..? 같은 느낌이다.
private ExecutorService exec = Executors.newSingleThreadExecutor();
private boolean isRunning = false;
public int onStartCommand(Intent intent, int flags, int startId) {
if(isRunning) {
return START_NOT_STICKY;
}
isRunning = true;
exec.submit(new Runnable() {
// ....
stopSelf();
});
return START_STICKY;
}
public void onDestroy() {
isRunning = false;
}
간단하게 설명을 덧붙이자면 isRunning 변수가 true라면 해당 Service가 동작하는 것이고,
false라면 Service가 동작중이지 않은 것이다. ( 코드는 굉장히 단순한듯 ! )
(매우중요★★★)
다시한번 주의해야할 점은 Service도 Main Thread에 속해서 동작한다는 점이다.
그래서 반드시 ANR에 걸릴만한 작업은 Sub Thread를 통해서 동작시켜야 한다.
( Thread로 종료시킬 때, Service가 종료된다면 반드시 Thread도 interrupt() 로 종료해주고
null로 초기화 해줘야한다. 당연히 이러한 작업을 하는 위치는 onDestroy method이다. )
ANR에 걸리는 시간은 20초이다. (액티비티는 5초, 리시버는 10/60초)
위에서 말했던 방법으로 onDestory를 구성하는 코드를 살펴보자.
private Thread mCountThread = null;
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if(mCountThread == null) {
mCountThread = new Thread("Count ThreaD") {
public void run() {
while(true) { ... }
}
}.start();
}
return START_STICKY;
}
public void onDestroy() {
if(mCountThread != null) {
mCountThread.interrupt();
mCountThread = null;
mCurNum = 0;
}
super.onDestroy();
}
그리고 startService에는 같은 Service에대해
여러번 startService가 실행되면 동기화문제가 발생한다.
동기화문제에 대한 자세한 설명은 생략하겠다. 너가 생각하는 그거 맞음ㅇㅇ;
동기화 문제를 해결할 수 있는 방법에 대한 정보는 못찾았고...
대체할 만한 놈으로 다음 포스팅에서 등장할 IntentService가 있다!!
'Android > Concept' 카테고리의 다른 글
[Android] Local Bound Service (Local Service) (0) | 2019.08.21 |
---|---|
[Android] IntentService (Local Service) (0) | 2019.08.20 |
[Android] Service (0) | 2019.08.19 |
[Android] Thread Helper Class → AsyncTask ★★★ (0) | 2019.08.19 |
[Android] Thread와 Handler ( Feat. Sub Thread에서의 UI 설정 ) (0) | 2019.08.19 |