-
서비스 ServiceAndroid 2019. 7. 3. 23:28
서비스
https://developer.android.com/guide/components/services?hl=ko
서비스 개념
- 백그라운드에서 실행되는 컴포넌트
- java파일로만 구성. (화면X 레이아웃파일X)
- Service 클래스 or IntentService 클래스 를 상속하여 구현
- 서비스는 자신의 생명주기를 스스로 관리해야함
- 시스템에서 서비스를 중단하거나 소멸시키지 않음 (메모리가 부족한경우 잠시 중단하고 재시작시킴)
- 현재 화면상에 통지(notification)나 토스트(toast)같은 메시지를 띄울 순 있음
- 액티비티나 브로드캐스트 수신자나 다른 서비스가 관리함
- 인텐트 요청도 가능
Service 클래스
- 모든 서비스의 기본클래스
- 동시시작 요청을 처리 해야할때 사용 (멀티 스레딩)
- 새 스레드를 생성하여 여기서 모든 작업을 수행
※새 스레드를 생성하는 이유
서비스는 기본적으로 선언된 앱과 같은 프로세스를 실행하거나
앱의 기본스레드에서 실행
서비스가 너무 집약적으로 수행된다면 액티비티 성능이 느려질수있음
=> 그래서 보통 서비스내에서 새로 스레드를 시작
- 기본 원형
public class MyService extends Service { public void onCreate() { Thread t = new Thread() { void run() { } }; t.start(); } public void onDestroy() { } }※주의※
호출될때마다 스레드를 생성하여 다중스레딩이 되는데
다중스레드는 다루기가 어려움
대신 스레드를 사용하지 않거나
최선의 선택은 IntentService 클래스를 사용하는것
IntentService 클래스
- Service의 서브클래스
- 기본 작업자 스레드가 있음
- 작업자 스레드에서 모든 시작 요청을 한번에 하나씩 처리
- 서비스가 여러 개의 요청을 동시에 처리할필요가 없을때 사용
- onHandleIntent() 를 구현하여 시작요청에 대한 Intent 수신
- 기본 원형
public class HelloIntentService extends IntentService { public HelloIntentService() { super("HelloIntentService"); } @Override protected void onHandleIntent(Intent intent) { try { Thread.sleep(5000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }- 동작방식
1. 앱의 기본스레드와는 별도로, onStartCommand()에 전달된
모든 인텐트를 처리하는 기본 작업자 스레드 생성
2. 작업큐를 생성하여 한번에 인텐트 하나씩 onHandleIntent() 에 전달
3. 시작요청이 모두 처리되면 스스로 서비스 중단 (개발자가 따로 호출할 필요 X)
onStartCommand() 에서 기본구현을 하고 인텐트를 작업큐로 보낸뒤 하나씩
onHandleIntent() 에서 처리
개발자는 작업수행을 위한 onHandleIntent() 만 구현
※ 매니페스트 AndroidManifest.xml 에 서비스 선언
기본형식
<manifest ... > ... <application ... > <service android:name=".ExampleService" /> ... </application> </manifest>다른 컴포넌트와 마찬가지로
※ 서비스 정의시 AndroidManifest.xml 에 선언해야함 ※
<application> 하위에 <service> 추가
- 필수속성
android:name="서비스클래스 이름"
*서비스를 본인앱에서만 사용가능하도록 보장하려면
서비스속성에 android:exported="false" 설정
*보안적으로 서비스 사용시하려면 명시적 인텐트를 사용
서비스 하위에 인텐트 필터 선언X
(호출방식에따른) 서비스 형식 2가지
1. startService() 로 시작된 서비스
2. bindService() 로 바인드된 서비스
각자 사용법과 생명주기가 다름
-
startService() 로 시작된 서비스
- 한번 시작되면 종료하지 않는한 계속 실행됨 (서비스를 시작한 컴포넌트가 소멸되어도)
- 수행 후 결과반환 X
- 인텐트가 유일한 통신수단
서비스 예시
public class ExampleService extends Service { int mStartMode; // indicates how to behave if the service is killed boolean mAllowRebind; // indicates whether onRebind should be used @Override public void onCreate() { } @Override public int onStartCommand(Intent intent, int flags, int startId) { return mStartMode; } @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public boolean onUnbind(Intent intent) { return mAllowRebind; } @Override public void onRebind(Intent intent) { } @Override public void onDestroy() { } }*각 메소드는 서비스 생명주기에 따라 자동으로 호출됨
(설명은 뒤에 서비스 생명주기에서)
서비스 시작
Intent intent = new Intent(this, 서비스클래스이름.class); //intent에는 서비스가 사용할 모든 데이터를 포함 startService(intent);기존에 해당 서비스가 실행중이 아니었다면, onCreate() 를 먼저 호출
그다음 서비스를 시작하여, onStartCommand()를 호출하고 이 메서드에 Intent 전달
이때 전달한 Intent가 유일한 통신수단임
*결과를 반환하고 싶은경우
브로드캐스트를 위한 PendingIntent를 만들고 Intent에 추가하면
서비스는 브로드캐스트를 통해 결과를 전달할수있음
서비스 종료
- 스스로 stopSelf() 호출
- 다른컴포넌트가 stopService() 호출
(스레드 종료랑 착각하지 말것)
stopSelf(int) 에
onStartCommand(int startId) 의
startId를 전달
id가 일치하지 않다=새 시작 요청이 있다=>서비스는 중단되지 않음
-
bindService()로 바인딩한 서비스
https://developer.android.com/guide/components/bound-services
- 서비스와 다른 컴포넌트가 상호작용을 원할때 사용
- 앱 기능 몇가지를 IPC(프로세스간 통신)를 통해 다른앱에 노출할때 사용
- 일반적으로 startService() 호출 허용 X
- 스스로 중단할 필요없음. 클라이언트가 없으면 시스템이 소멸시킴
서버-클라이언트 개념
- 호출한 컴포넌트(바인딩함) : 클라이언트
- 바인드된 서비스 : 서버
- IBinder 객체 를 통해 클라이언트와 서버간 통신 가능 (결과 가져오기 가능)
- 서비스는 IBinder를 구현해야함
- 클라이언트는 ServiceConnection를 구현해야함
- 여러 클라이언트에서 동일한 서비스를 호출가능
IBinder 구현방식에 따른 바인드 서비스 구현
- Binder 클래스 상속
로컬앱만 서비스를 사용하고, 여러 프로세스에 걸쳐 작동할필요가 없는경우
public class LocalService extends Service { private final IBinder mBinder = new LocalBinder(); private final Random mGenerator = new Random(); /* 상속하여 구현 */ public class LocalBinder extends Binder { LocalService getService() { //클라이언트가 서비스의 메소드를 호출할 수 있도록 자기자신(객체)을 전달 return LocalService.this; } } @Override public IBinder onBind(Intent intent) { //클라이언트는 ServiceConnection을 통해 이 값을 전달받음 return mBinder; } public int getRandomNumber() { return mGenerator.nextInt(100); } }기존에 해당 서비스가 실행중이 아니었다면, onCreate() 를 먼저 호출
※ 클라이언트에게 binder를 전달했으므로
클라이언트는 바인더를 통해 서비스 객체를 얻어
서비스의 메소드(예시에서는 getRandomNumber() 등)를 호출할 수 있다
- Messenger 사용
서비스가 원격 프로세스와 통신할때 서비스 인터페이스를 제공하고싶을때
이 기법을 사용하면 AIDL을 쓰지 않고도 IPC 가능
서비스 시작
*ServiceConnection 을 구현해야함
ServiceConnection 가 onServiceConnected()를 호출하여 클라이언트에게 IBinder를 전달
public class BindingActivity extends Activity { LocalService mService; //서비스를 저장할 변수 boolean mBound = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override protected void onStart() { super.onStart(); Intent intent = new Intent(this, LocalService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); if (mBound) { unbindService(mConnection); mBound = false; } } public void onButtonClick(View v) { if (mBound) { int num = mService.getRandomNumber(); Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show(); } } /* ServiceConnection 구현 */ private ServiceConnection mConnection = new ServiceConnection() { //서비스의 onBind 메소드에서 반환한 IBinder 객체를 여기서 받음 @Override public void onServiceConnected(ComponentName className, IBinder service) { LocalBinder binder = (LocalBinder) service; mService = binder.getService(); mBound = true; } @Override public void onServiceDisconnected(ComponentName arg0) { mBound = false; } }; }연결 종료
- 클라이언트가 unbindService() 호출 : 해당 클라이언트 연결 종료됨
서비스 종료
- 모든 클라이언트가 연결종료시
*startService() 로 시작된 서비스일지라도
다른데서 해당 서비스를 bindService()로 호출하면
그 서비스는 바인드된 서비스의 생명주기를 갖는다
*서비스는 두가지 방식 모두 작동가능
한가지 서비스가 시작 될수도 바인드 될수도 있음
*생명주기에 따른 몇가지 콜백메서드 구현 차이만 존재
*메모리가 부족하면 시스템이 서비스를 강제중단 시킬수있음
- 활성화된 액티비티에 바인딩된 서비스 : 거의 중단될 일 없음
- 포그라운드에서 실행되는 서비스 : 거의 절대 중단될일 없음
=> 중단될때 서비스가 정상적으로 재시작될 수 있도록 설계
포그라운드 서비스?
사용자가 인식하고 있어서
메모리 부족시 중단 후보에 고려되지 않는 서비스
상태 표시줄에 "진행중" 임을 알려야함
ex) 음악 재생
포그라운드 서비스 호출
startForeground(int 알림식별정수, Notification)식별정수는 0이면 안됨
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text), System.currentTimeMillis()); Intent notificationIntent = new Intent(this, ExampleActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); notification.setLatestEventInfo(this, getText(R.string.notification_title), getText(R.string.notification_message), pendingIntent); startForeground(ONGOING_NOTIFICATION_ID, notification);서비스를 포그라운드에서 제거하려면
stopForeground(boolean 상태표시제거여부) 호출
이 메서드는 서비스를 중단시키지 않음
서비스와 스레드 차이?
- 서비스를 사용하는 경우
액티비티 등 다른 컴포넌트와 상관없이 외부에서 작업을 계속 수행하고 싶은 경우 사용
ex) 앱이 닫혀도 음악 재생
- 스레드를 사용하는 경우
앱과 상호작용할때만 수행할거라면 액티비티에서 스레드 사용
ex) 앱이 실행되고있을때만 음악 재생
액티비티 onCreate에서 스레드생성 -> onStart에서 스레드실행 -> onStop에서 스레드중단
서비스 생명주기 life cycle

https://developer.android.com/guide/components/services?hl=ko -
startService() 로 시작
- onCreate : 서비스 생성
- onStartCommand : startService() 를 통해 서비스가 시잘될때 호출됨
- onResume :
- onDestroy : 서비스 종료
순서
create > start > resume > 서비스실행 > 서비스중단 > destroy > 서비스 종료
-
bindService() 로 바인드
- onCreate : 서비스 생성
- onBind : bindService() 를 통해 호출될때 호출됨. IBinder onBind(Intent intent)
- onRebind : 다시 bind될때 . void onRebind(Intent intent)
- onUnbind : 서비스와 연결이 끊어질때 호출. void onUnbind(Intent intent)
- onDestroy : 서비스 종료
순서
create > bind > 서비스와 상호작용 > unbind > destroy > 서비스종료
unbind > rebind > 서비스와 상호작용
재정의 해야할 주요 콜백 메서드
- onCreate()
서비스가 처음 생성될때
여기서 최초 초기화 작업을 수행
- onStartCommand()
다른 컴포넌트가 startService() 호출시 호출됨
서비스가 시작됨
int onStartCommand(Intent intent, int flags, int startId) { ... }서비스가 소멸될때 어떻게 행동할것인지 int 값 반환 필요
보통은 슈퍼클래스 메소드 호출로 대체
return super.onStartCommand(intent, flags, startId);- onBind()
다른 컴포넌트가 bindService() 호출시 호출됨
클라이언트와 통신할 인터페이스를 제공해야함
->클라이언트에게 IBinder 객체 반환
IBinder onBind(Intent intent) { ... }*이 메서드는 항상 구현해야함.
대신 바인딩을 허용하지 않으려면 null을 반환
- onDestroy()
서비스가 소멸될때
여기서 스레드,리스너, 수신기 등의 모든 리소스를 정리
※ Service 상속시 슈퍼클래스 호출 필요 없음
※ IntentService 상속시 메서드 재정의시 반드시 슈퍼 메소드 호출 ※
그래야 IntentService가 작업자 스레드 수명을 적절히 처리가능
onStartCommand()시 반드시 기본 구현 반환 필요
@Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); return super.onStartCommand(intent,flags,startId); }*슈퍼 메소드 호출하지 않아도 되는 것
onHandleIntent()
onBind
'Android' 카테고리의 다른 글
콘텐츠 제공자 Content providers (0) 2019.07.09 브로드캐스트 수신자 Broadcast Receiver (0) 2019.07.09 안드로이드 매니페스트 (AndroidManifest.xml) (0) 2019.07.03 Intent 인텐트, 인텐트 필터 정리 (0) 2019.07.03 액티비티 Activity (0) 2019.07.01 -