2017년 7월 4일 화요일

Service AIDL


다른 앱들간 통신하기 위한 여러 방법이 있지만 method 를 직접적으로 호출하고 바로 리턴 받을 수 있는 AIDL 구현 예제 설명

용어


Bind

서비스와 통신하기 위해 연결과정을 binding 이라고 함
bindService() 로 실행된 서비스는 unbindService() 될 때 종료됨

AIDL (Android Interface Definition Language)

Service 와 client 간 통신 규약 파일이며 service 측과 client 측이 통신하기 위해 동일한 aidl 파일을 공유해야 함
예를 들어 service 와 client 가 다른 프로젝트라면 aidl 을 같은 package 위치에 복사해야 하며 변경 사항 발생시 공유 해야 함

AIDL 예제

1. Service (com.sample.myservice)


com.sample.myservice.IMyService.aidl 작성


// IMyServiceInterface.aidl
package com.sample.myservice;

// Declare any non-default types here with import statements

interface IMyServiceInterface {

    String greet();
}


  • greet() 함수 제공


com.sample.myservice.MyService.java 작성



package com.sample.myservice;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

public class MyService extends Service {
    public MyService() {
    }

    private IMyServiceInterface.Stub binder = new IMyServiceInterface.Stub() {
        @Override
        public String greet() throws RemoteException {
            return "Hello";
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
}


  • aidl 파일 작성 후 Android Studio 의 build -> make project 를 해야 aidl 을 자동 인식
  • IMyServiceInterface.Stub 을 구현
  • onBind() 시 구현한 IMyServiceInterface.Stub 리턴

2. Client 예제 (com.sample.myapp 다른 프로젝트)


IMyService.aidl 복사



  • 위에 생성한 aidl 을 복사


com.sample.myapp.MainActivity.java 생성


package com.sample.myapp;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import com.sample.myservice.IMyServiceInterface;

public class MainActivity extends AppCompatActivity {

    private final static String TAG = "MainActivity";

    private IMyServiceInterface myservice;

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            myservice = IMyServiceInterface.Stub.asInterface(iBinder);
            try {
                Log.d(TAG, myservice.greet());
            } catch (RemoteException e) {
                Log.e(TAG, "exception", e);
            }
        }
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            myservice = null;
        }
    };

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

        Intent intent = new Intent();
        intent.setClassName("com.sample.myservice", "com.sample.myservice.MyService");
        bindService(intent, serviceConnection, BIND_AUTO_CREATE);
    }

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


  • 마찬가지로 make project 를 해야 Anroid Studio 가 aidl 파일을 자동 인식
  • 외부 service 이기 때문에 intent 에 setClassName() 으로 서비스 지정
    • Service 의 package 명과 Service 의 full package 명이 파라메터
  • ServiceConnection 의 onServiceConnected() 와 onSerivceDisconnected() 처리
    • IMyServiceInterface.Stub.asInterface() 로 따로 casting 필요없음
  • aidl 구현 함수들은 RemoteException 을 throw 할 수 있음
    • 연결 해제된 경우 등
  • onCreate() 에서 bindService() 했으니 onDestroy() 시 unbindService()

Callback 예제


Service -> Client 방향으로 통신할 수 있도록 callback 을 등록

1. Service 수정


com.sample.myservice.IMyCallbackInterface.aidl 생성


// IMyCallbackInterface.aidl
package com.sample.myservice;

// Declare any non-default types here with import statements

interface IMyCallbackInterface {
    void onUpdate(int value);
}

  • Service 에서 호출할 onUpdate() 함수 제공

com.sample.myservice.IMyServiceInterface 수정


// IMyServiceInterface.aidl
package com.sample.myservice;

// Declare any non-default types here with import statements
import com.sample.myservice.IMyCallbackInterface;

interface IMyServiceInterface {

    String greet();
    boolean registerCallback(IMyCallbackInterface callback);
    boolean unregisterCallback(IMyCallbackInterface callback);
}


  • registerCallback(), unregisterCallback() 추가

com.sample.service.MyService.java 수정


package com.sample.myservice;

import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;

public class MyService extends Service {

    private final static String TAG = "MyService";
    private RemoteCallbackList<IMyCallbackInterface> callbackList = new RemoteCallbackList<>();
    private int value;

    public MyService() {
    }

    private IMyServiceInterface.Stub binder = new IMyServiceInterface.Stub() {

        @Override
        public String greet() throws RemoteException {
            return "Hello";
        }

        @Override
        public boolean registerCallback(IMyCallbackInterface callback) throws RemoteException {
            return callbackList.register(callback);
        }

        @Override
        public boolean unregisterCallback(IMyCallbackInterface callback) throws RemoteException {
            return callbackList.unregister(callback);
        }

    };

    private final static int MSG_BROADCAST = 0;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case MSG_BROADCAST:
                {
                    value++;
                    int size = callbackList.beginBroadcast();
                    for (int i = 0; i < size; i++) {
                        try {
                            callbackList.getBroadcastItem(i).onUpdate(value);
                        } catch (RemoteException e) {
                            Log.e(TAG, "exception", e);
                        }
                    }
                    callbackList.finishBroadcast();
                    sendEmptyMessageDelayed(MSG_BROADCAST, 1000);
                }
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        handler.sendEmptyMessageDelayed(MSG_BROADCAST, 1000);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        handler.removeCallbacksAndMessages(null);
    }
}


  • android.os.RemoteCallbackList 사용
    • broadcast
      • beginBroadcast() 로 등록된 callback 통신 시작
        • 등록된 callback 객수 리턴
      • getBroadcastItem() 로 등록된 callback 접근
      • finishBroadcast() 로 callback 통신 작업 종료
    • register/unregister


2. Client 수정


com.sample.myapp.MainActivity.java 수정



package com.sample.myapp;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import com.sample.myservice.IMyCallbackInterface;
import com.sample.myservice.IMyServiceInterface;

public class MainActivity extends AppCompatActivity {

    private final static String TAG = "MainActivity";

    private IMyCallbackInterface.Stub callback = new IMyCallbackInterface.Stub() {
        @Override
        public void onUpdate(int value) throws RemoteException {
            Log.d(TAG, "value: " + value);
        }
    };

    private IMyServiceInterface myservice;

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            myservice = IMyServiceInterface.Stub.asInterface(iBinder);
            try {
                Log.d(TAG, myservice.greet());
                myservice.registerCallback(callback);
            } catch (RemoteException e) {
                Log.e(TAG, "exception", e);
            }
        }
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            myservice = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate()");
        setContentView(R.layout.activity_main);

        Intent intent = new Intent();
        intent.setClassName("com.sample.myservice", "com.sample.myservice.MyService");
        bindService(intent, serviceConnection, BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy()");
        try {
            myservice.unregisterCallback(callback);
        } catch (RemoteException e) {
            Log.e(TAG, "exception", e);
        }
        unbindService(serviceConnection);
    }
}


  • IMyCallbackInterface.Stub 구현
    • onUpdate() 구현
  • onServiceConnected() 에서 callback 등록
  • unbindService() 하기 전에 callback 등록 해제

댓글 없음:

댓글 쓰기