Program/Android Java

AIDL을 이용한 Service <-> Activity간 Callback통신.

너구리V 2013. 1. 24. 17:53

출처 : http://darphin.tistory.com/29


 Service를 이용하여 백그라운드에서 작업을 하다보면, Activity로 데이터를 전달 해야 한다던가, 혹은 어떠한 순간에 데이터를 전달해 주어야 할 때가 있습니다. 처음에 이 작업을 위하여 삽질을 하다보니 좋은 방법이 있다는걸 뒤늣게 알아 버렸습니다.


 Android에서는 Service에 Bind된 클라이언트들은 Service로부터 특정 시점에 특정 데이터를 Callback 받기 위하여 RemoteCallbackList 라는 방법을 제공합니다. 해당 클래스는 템플릿 형태로 되어 있으며 원하는 타입을 지정하여 사용할 수 있습니다.


 사용법은 간단합니다. Bind를 위한 ADIL과, Callback 시점에서 수행할 interface class를 정의한 ADIL이 준비물의 전부 입니다. Bind를 위한 ADIL에서는 Callback를 등록/제거 할 수 있는 정도가 있으면 되겠네요. 예제 코드가 길지 않으니 바로 소스를 보시면 바로 이해하실 수 있을것 같습니다.



IRemoteService (ADIL)

1.package com.example.servicetest.service;
2. 
3.import com.example.servicetest.service.IRemoteServiceCallback;
4. 
5.interface IRemoteService {
6.boolean registerCallback(IRemoteServiceCallback callback);
7.boolean unregisterCallback(IRemoteServiceCallback callback);
8.}


IRemoteServiceCallback (ADIL)

1.package com.example.servicetest.service;
2. 
3.oneway interface IRemoteServiceCallback {
4.void valueChanged(long value);
5.}


TestService (Class - Service)

001.package com.example.servicetest.service;
002. 
003.import android.app.Service;
004.import android.content.Intent;
005.import android.os.Handler;
006.import android.os.IBinder;
007.import android.os.Message;
008.import android.os.RemoteCallbackList;
009.import android.os.RemoteException;
010.import android.util.Log;
011. 
012.public class TestService extends Service{
013. 
014.public static final String INTENT_ACTION = "intent.action.bhc.test.service";
015.private static final int MSG_WORK = 1;
016. 
017.final RemoteCallbackList<iremoteservicecallback> callbacks = newRemoteCallbackList<iremoteservicecallback>();
018. 
019.private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
020. 
021.@Override
022.public boolean unregisterCallback(IRemoteServiceCallback callback)
023.throws RemoteException {
024.boolean flag = false;
025. 
026.if(callback != null){
027.flag = callbacks.register(callback);
028.}
029. 
030.return flag;
031.}
032. 
033.@Override
034.public boolean registerCallback(IRemoteServiceCallback callback)
035.throws RemoteException {
036.boolean flag = false;
037. 
038.if(callback != null){
039.flag = unregisterCallback(callback);
040.}
041. 
042.return flag;
043.}
044.};
045. 
046.@Override
047.public IBinder onBind(Intent intent) {
048.Log.d("BHC_TEST""onBind..");
049. 
050.if(intent.getAction().equals(INTENT_ACTION)){
051.Log.d("BHC_TEST""action is equals.. :: " + intent.getAction());
052.return mBinder;
053.}
054.Log.d("BHC_TEST""action is not equals..");
055.return null;
056.}
057. 
058. 
059.@Override
060.public void onCreate() {
061.Log.d("BHC_TEST""Service is onCrreate..");
062. 
063.handler.sendEmptyMessage(MSG_WORK);
064. 
065.super.onCreate();
066.}
067. 
068.@Override
069.public void onDestroy() {
070.Log.d("BHC_TEST""Service is onDestory..");
071. 
072.handler.removeMessages(MSG_WORK);
073. 
074.super.onDestroy();
075.}
076. 
077.private Handler handler = new Handler(new Handler.Callback() {
078. 
079.@Override
080.public boolean handleMessage(Message msg) {
081.// TODO Auto-generated method stub
082. 
083.switch (msg.what) {
084.case MSG_WORK:
085. 
086.int N = callbacks.beginBroadcast();
087. 
088.for(int i = 0; i < N; i++){
089.try {
090.callbacks.getBroadcastItem(i).valueChanged(System.currentTimeMillis());
091.catch (RemoteException e) {
092.// TODO Auto-generated catch block
093.e.printStackTrace();
094.}
095.}
096. 
097.Log.d("BHC_TEST""Handler work.. :: callbacks clients count is " + N);
098.callbacks.finishBroadcast();
099. 
100.try {
101.Thread.sleep(2000);
102.catch (InterruptedException e) {
103.// TODO Auto-generated catch block
104.e.printStackTrace();
105.}
106.handler.sendEmptyMessage(MSG_WORK);
107. 
108.break;
109. 
110.default:
111.break;
112.}
113.return false;
114.}
115.});
116.}
117.</iremoteservicecallback></iremoteservicecallback>


MainActivity (Class - Activity)

001.package com.example.servicetest;
002. 
003.import android.app.Activity;
004.import android.content.ComponentName;
005.import android.content.Context;
006.import android.content.Intent;
007.import android.content.ServiceConnection;
008.import android.os.Bundle;
009.import android.os.IBinder;
010.import android.os.RemoteException;
011.import android.util.Log;
012.import android.view.Menu;
013. 
014.import com.example.servicetest.service.IRemoteService;
015.import com.example.servicetest.service.IRemoteServiceCallback;
016.import com.example.servicetest.service.TestService;
017. 
018.public class MainActivity extends Activity {
019. 
020. 
021. 
022.IRemoteServiceCallback mCallbcak = new IRemoteServiceCallback.Stub() {
023. 
024.@Override
025.public void valueChanged(long value) throws RemoteException {
026.Log.i("BHC_TEST""Activity Callback value : " + value);
027.}
028.};
029. 
030.IRemoteService mService;
031.ServiceConnection mConntection = new ServiceConnection() {
032. 
033.@Override
034.public void onServiceDisconnected(ComponentName name) {
035.// TODO Auto-generated method stub
036. 
037.if(mService != null){
038.try {
039.mService.unregisterCallback(mCallbcak);
040.catch (RemoteException e) {
041.// TODO Auto-generated catch block
042.e.printStackTrace();
043.}
044.}
045.}
046. 
047.@Override
048.public void onServiceConnected(ComponentName name, IBinder service) {
049.// TODO Auto-generated method stub
050. 
051.if(service != null){
052.mService = IRemoteService.Stub.asInterface(service);
053.try {
054.mService.registerCallback(mCallbcak);
055.catch (RemoteException e) {
056.// TODO Auto-generated catch block
057.e.printStackTrace();
058.}
059.}
060.}
061.};
062. 
063.private void startServiceBind(){
064.startService(new Intent(this, TestService.class));
065.bindService(new Intent(TestService.INTENT_ACTION), mConntection, Context.BIND_AUTO_CREATE);
066.}
067. 
068.private void stopServiceBind(){
069.unbindService(mConntection);
070.stopService(new Intent(this, TestService.class));
071.}  
072. 
073.@Override
074.public void onCreate(Bundle savedInstanceState) {
075.super.onCreate(savedInstanceState);
076.setContentView(R.layout.activity_main);
077. 
078.}
079. 
080. 
081.@Override
082.protected void onResume() {
083.// TODO Auto-generated method stub
084.startServiceBind();
085.super.onResume();
086.}
087. 
088. 
089.@Override
090.protected void onStop() {
091.// TODO Auto-generated method stub
092.stopServiceBind();
093.super.onStop();
094.}
095. 
096.@Override
097.public boolean onCreateOptionsMenu(Menu menu) {
098.getMenuInflater().inflate(R.menu.activity_main, menu);
099.return true;
100.}
101. 
102.}




위의 코드를 실행하면 결과를 Logcat를 통해서 확인할 수 있습니다.

서비스에서 생성한 숫자를 Activity에서 Service에 Bind한 뒤, 등록된 Callback를 통하여 원하는 데이터를 전달 받습니다. 마치 Observer패턴의 구조와 비슷하다고 느껴지기도 하네요..





테스트코드의 첨부파일 입니다.

 AIDL_Service_Example.zip




 아직 궁금한점은... AIDL Interface 작성시.. oneway라고 명시해 주어야 하는데, 저도 지식이 얕은지라..;;; oneway 가 무슨 이유로 붙는지는 잘 모르겠습니다. 대충 알아본 바로는 Service와 통신을 하기 위한 과정에서 데이터의 동기화를 보장하기 위한 용도로 사용된다는 것 같은데.. 정확히 이해를 못했네요.. (영어가 짧아서.. -_-;;) 혹시 자세히 아시는 분은 댓글 부탁 드립니다.


좋은 자료가 되어서 블로그에 방문하신 분들께 유익한 자료가 되었으면 좋겠습니다.

반응형

'Program > Android Java' 카테고리의 다른 글

안드로이드 APP 공개소스  (0) 2013.02.08
안드로이드 Handler 이야기 remove send  (0) 2013.02.06
Android AsyncTaskLoader  (0) 2013.01.10
삼성 멀티윈도우 어플 지원 방법  (6) 2013.01.08
app 기획 자료  (0) 2013.01.08