티스토리 뷰

반응형

AIDL을 사용해 두 개의 서로다른 Application에서 Remote Bound Service를 구현하는 코드를 보자.

 

우선 잘못 구현된 케이스의 코드이다.Started Service를 사용한 Service를 구현해본다.

public class CountService extends Service {
	public void onCreate() {
    	// ...
    }
    
    public int onStartCommand(Intent intent, int flags, int startId) {
    	// ...
    }
    
    public void onDestroy() {
    	// ...
    }
    
    // 이 밑이 꼭 필요한 함수들이다.
    public IBinder onBind(Intent intent) {
    	// ...
        return null;
    }
    
    public boolean onUnbind(Intent intent) {
    	// ...
    	return super.onUnbind(intent);
    }
}

 

 

이렇게 생성한 Local BindService를 사용할 Application을 하나 만들고 버튼을 통해 조작해보자.

private ServiceConnection mConnection = new ServiceConnection() {
	// bind시 해당 함수가 호출된다.
	public void onServiceConnected(ComponentName name, IBinder service) {
    	// ....
    }
    // unbind시 해당 함수가 호출된다.
    public void onServiceDisconnected(ComponentName name) {
    	// ....
    }
};

protected void onCreate(Bundle savedInstanceState) {
	....
    Intent serviceIntent = new Intent("ch4njun_count");
    bindService(serviceIntent, mConnection, BIND_AUTO_CREATE);
}

protected void onDestroy() {
	unbindService(mConnection);
    super.onDestroy();
}

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;
        case R.id.get_cur_count_number_btn:
        	break;
    }
}

ServiceConnection 클래스에 대한 객체를 마찬가지로 생성하고, onCreate에서 bindService 함수를 사용해 연결한다. 그리고 onDestroy 생명주기 함수에서 unbindService 함수를 사용해 연결을 해제한다.

 

앱과 서비스간의 매개체, 즉 인터페이스가 없으면 클라이언트(Application)는 Service에게 아무런 요청도 보낼 수 없다. 안드로이드에서는 통신을 가능케하는 매개체를 Binder라고 부른다.

 

 

 

Binder

Android Service에 존재하는 Interface를 포함한다. (Interface에는 메소드들이 들어가겠다)

클라이언트 Application은 Binder를 사용해 Service의 Interface를 호출할 수 있다.

 

Binder를 만드는 일이 복잡하기 때문에 AIDL(Android Interface Definition Language)이라는 언어를 사용한다. AIDL 언어로 인터페이스를 작성하면 자동으로 Binder를 생성해준다.

 

 

 

위에 보이는 것처럼 AIDL 파일을 생성한다. Service가 존재하는 Application(즉, Server역할)과 Service에서 제공하는 Interface를 사용한 Application(즉, Client역할)에 동일하게 추가해줘야 한다.

(번거로운데.....?)

 

 

ICountService.aidl

interface ICountService {
	oneway void getCurCountNumber(ICountServiceCallback callback);
}

 

 

ICountServiceCallback.aidl

interface ICountServiceCallback {
	oneway void getCurCountNumberCallback(int curNum);
}

 

이와 같이 작성한 AIDL들에 저장된 Interface를 Binder Interface라고 한다.

 

 

public class CountService extends Service {
	ICountService.Stub mBinder = new ICountService.Stub() {
    	public int getCurCountNumber(ICountServiceCallback callback) throws RemoteException {
        	try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            callback.getCurCountNumberCallback(mCurNum);
        }
    }
    public IBinder onBind(Intent intent) {
        Log.d("superdroid","onBind()");
        return mBinder;
    }
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        new Thread(){
            public void run(){
                while(true){
                    mCurNum++;
                    Log.d("superdroid","number:"+mCurNum);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        return START_STICKY;
    }
    public boolean onUnbind(Intent intent) {
        Log.d("superdroid","onUnbind()");
        return super.onUnbind(intent);
    }
    public void onCreate() {
        super.onCreate();
        Log.d("superdroid","onCreate()");
    }
    public void onDestroy() {
        super.onDestroy();
        Log.d("superdroid","onDestory()");
    }
}

 

AIDL로 Interface를 생성하게되면 자동적으로 Stub함수를 지원한다. 이 함수를 통해 Binder를 생성할 수 있게 된다. 그리고 이렇게 생성된 Binder를 onBind 생명주기 함수에서 반환해줘야 한다.

 

이제 이렇게 생성된 Service를 사용하고싶은 Client Application의 코드를 살펴보자.

public class MainActivity extends AppCompatActivity {
    private ICountService mBinder=null;

    ICountServiceCallback mCurCountCallback = new ICountServiceCallback.Stub() {
        @Override
        public void getCurCountNumberCallback(final int curNum) throws RemoteException {
            Log.d("superdroid","Cur Count Number : "+curNum);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(getApplicationContext(),"Cur Count:"+curNum,Toast.LENGTH_SHORT).show();
                }
            });
        }
    };

    private ServiceConnection mConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d("superdroid","onServiceConnected()");
            // Service가 전달한 Binder 객체를 저장하는 과정이다.
            mBinder = ICountService.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d("superdroid","onServiceDisconnected()");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent serviceIntent=new Intent("CountService");
        serviceIntent.setPackage("com.example.a1127");
        bindService(serviceIntent,mConnection,BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        super.onDestroy();
    }

    public void onClick(View view) {
        switch (view.getId()){
            case R.id.start_count_btn:
            {
                Intent serviceIntent=new Intent("CountService");
                serviceIntent.setPackage("com.example.a1127");
                startService(serviceIntent);
                break;
            }
            case R.id.stop_count_btn:
            {
            	// Stop이 안된다.. 왜그러지?...
                Intent serviceIntent=new Intent("CountService");
                serviceIntent.setPackage("com.example.a1127");
                stopService(serviceIntent);
                break;
            }
            case R.id.get_cur_count_number_btn:
            {
                int curCountNumber;
                try {
                	// 이게 Client에서 Server로 Request를 보내는 함수이다.
                    mBinder.getCurCountNumber(mCurCountCallback);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            }
        }
    }
}

 

 

bindService를 호출할 때 사용되는 BIND_AUTO_CREATE에 대해서 설명해보겠다.

 

BIND_AUTO_CREATE 플래그를 주게되면, 앱이 종료되더라도 Service가 Destroy하지 않는다. 따라서 앱이 다시 실행되고 동일한 Service에 Bind될 경우 기존 정보를 그대로 사용할 수 있고,

 

이러한 부분에서 안정성을 시스템이 보장해주는 것이된다.

 

또한, 기본적으로는 startService가 되지 않은 상태에서 bindService를 수행할 수 없다. 하지만 BIND_AUTO_CREATE 플래그를 통해 bindService를 호출할 경우 startService를 자동적으로 호출할 수 있도록 도와준다. 이미 onStartCommand 생명주기 함수가 동작중인 Service라면 그대로 bind만 수행하는듯... 하다...? (아마도....)

 

 

 


Bound Service의 Callback Interface

이미 위 코드에는 이 내용이 추가되어 있기 때문에, 위 코드를 그대로 참조하면 될 것 같다.

 

Service의 공개된 Interface가 오래 걸리는 작업을 수행한다고 가정해보자. 이 때, 그 만큼 해당 Interface를 사용한 클라이언트는 대기해야하고, ANR이 발생할 수 있다.

 

 

 

1. getCurCountNumber 함수의 결과를 받을 Callback 함수를 구현한다.

2. Service의 getCurCountNumber 함수를 호출할 때 Callback 함수를 인자로 전달한다.

3. Service는 getCurCountNumber 함수를 처리한다.

4. Service의 처리가 끝나면, 클라이언트에서 전달받은 Callback 함수를 호출하여 결과를 전달한다.

   > 클라이언트는 Callback 함수의 결과를 받는다.

 

 

Interface에 공유된 함수와, callback 함수는 각각 단 방향 호출이기 때문에 oneway 키워드를 붙여 단 방향 함수임을 지정한다. 이렇게 지정하면 해당 함수를 호출한 클라이언트는 반환을 기다리지 않는다.

반응형

'Android > Concept' 카테고리의 다른 글

[Android] Shared Preference  (0) 2020.12.04
[Android] 파일과 데이터베이스  (0) 2020.12.01
[Android] Local Broadcast  (0) 2020.11.29
[Android] Broadcast Receiver 의 ANR  (0) 2020.11.25
[Android] Activity간 데이터 주고 받기  (0) 2020.10.12
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함