출처 : 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 =
new
RemoteCallbackList<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 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 |