[android] Service 앱과 async로 통신하는 RxMessengerService

참고 url

라이브러리: https://github.com/Aevi-UK/android-rxmessenger

예제: https://github.com/susemi99/RxMessenger-sample

 

클라이언트

  • 사용할 명령어를 선언한다.
    서버에서도 이걸 사용해야 한다.

    private static final String IMMEDIATE = "IMMEDIATE";
    private static final String DELAY = "DELAY";
    private static final String CONTINUOUS = "CONTINUOUS";
    private static final String FORCE_INTERRUPT = "FORCE_INTERRUPT";
  • 전송에 사용할 클라이언트 클래스를 만든다.
    clients 배열에 넣는 건, onDestroy 될 때 연결이 끊기지 않은 것들의 연결을 끊기 위함이다.

    private ArrayList<ObservableMessengerClient> clients = new ArrayList<>();
    
    private ObservableMessengerClient client() {
      ObservableMessengerClient client = new ObservableMessengerClient(getApplicationContext(), SERVICE);
      clients.add(client);
      return client;
    }
  •  즉시 응답이 오는 요청 보내기
    응답을 받으면 연결을 끊어준다.
    요청을 받기 전에 dispose() 를 먼저 시켜주는 건 아직 오지 않은 응답을 취소시키고 새로운 요청을 보내기 위함이다. 안하면 먼저간 요청이 오지 않은 상태에서 새로운 요청이 또 가기 때문에, 2개의 요청이 오게된다.

    private void requestImmediate() {
      immediateResponse.setText(null);
    
      try {
        immediateDisposable.dispose();
      } catch (Exception ignore) {}
    
      ObservableMessengerClient client = client();
    
      immediateDisposable = client.sendMessage(IMMEDIATE)
        .subscribe(response -> {
          Log.i("APP# MainActivity | requestImmediate", "response: " + response);
          immediateResponse.setText(response);
        }, throwable -> {
          throwable.printStackTrace();
          immediateResponse.setText(throwable.getMessage());
        }, () -> {
          client.closeConnection();
          clients.remove(client);
        });
    }
  • 30초 후에 응답이 오는 요청 보내기
    기본적으로 즉시 응답 받는 것과 큰 차이 없다.

    private void requestDelay() {
      delayResponse.setText("request: " + DateFormatter.format(System.currentTimeMillis()));
    
      try {
        delayDisposable.dispose();
      } catch (Exception ignore) {}
    
      ObservableMessengerClient client = client();
    
      delayDisposable = client.sendMessage(DELAY)
        .subscribe(response -> {
          Log.i("APP# MainActivity | requestDelay", "response: " + response);
          delayResponse.append("\n" + response);
        }, throwable -> {
          throwable.printStackTrace();
          delayResponse.setText(throwable.getMessage());
        }, () -> {
          client.closeConnection();
          clients.remove(client);
        });
    }
  • 3초 마다 계속 응답이 오는 요청 보내기
    연결을 끊으면 안된다.

    private void requestContinuous() {
      continuousResponse.setText(null);
    
      try {
        continuousDisposable.dispose();
      } catch (Exception ignore) {}
    
      continuousDisposable = client().sendMessage(CONTINUOUS)
        .subscribe(
          response -> continuousResponse.setText(response),
          throwable -> {
            throwable.printStackTrace();
            continuousResponse.setText(throwable.getMessage());
          });
    }
  • 테스트를 위해 강제로 오류를 내는 신호도 보내본다.
    private void requestForceInterrupt() {
      client().sendMessage(FORCE_INTERRUPT).subscribe();
    }

     

서버

onCreate(), onDestroy() 에 로그를 찍어보니, 모든 요청에 응답을 하면 서비스가 종료된다. 즉시응답을 보내고 나면 종료되고, 3초 마다 응답을 보내는 중이라면 죽지 않았다. 강제로 오류를 발생시켜도, 그 이후의 요청에도 응답을 잘 해줬다. 혹시나 싶어서 Singleton 클래스도 넣어봤는데, 강제로 종료 시켰어도 값이 유지됐다.

  • `AbstractMessengerService`를 상속받은 서비스 클래스를 생성한다.
    <service
      android:name=".MyService"
      android:enabled="true"
      android:exported="true"/>
    public class MyService extends AbstractMessengerService {
    .
    .
    .
    }
  • 입력받을 명령어를 설정한다.
    private static final String IMMEDIATE = "IMMEDIATE";
    private static final String DELAY = "DELAY";
    private static final String CONTINUOUS = "CONTINUOUS";
    private static final String FORCE_INTERRUPT = "FORCE_INTERRUPT";
  • 요청을 받는다.
    @Override
    protected void handleRequest(String clientId, String requestData, String packageName) {
      Log.i("APP# MyService | handleRequest", "clientId: " + clientId + ", request data: " + requestData + ", packageName: " + packageName);
    
      if (IMMEDIATE.equals(requestData)) {
        handleImmediate(clientId);
      }
      else if (DELAY.equals(requestData)) {
        handleDelay(clientId);
      }
      else if (CONTINUOUS.equals(requestData)) {
        handleContinuous(clientId);
      }
      else if (FORCE_INTERRUPT.equals(requestData)) {
        handleForceInterrupt();
      }
      else {
        sendErrorMessageToClient(clientId, "404", "not found");
        sendEndStreamMessageToClient(clientId);
      }
    }
  • 기본적인 사용법은 아래와 같다.
    sendMessageToClient(clientId, "response"); // 응답을 보낸다.
    sendEndStreamMessageToClient(clientId); // 연결을 닫는다.
  • 즉각적인 응답을 보내준다.
    private void handleImmediate(String clientId) {
      try {
        sendMessageToClient(clientId, DateFormatter.format(System.currentTimeMillis()) + "(" + MySingleton.getInstance().getCount() + ")");
      } catch (Exception e) {
        sendErrorMessageToClient(clientId, "001", e.getMessage());
        sendEndStreamMessageToClient(clientId);
      }
    
      sendEndStreamMessageToClient(clientId);
    }
  • 30초 뒤에 응답을 보낸다.
    테스트로 30분 뒤에 응답 보내게 해봤는데, 잘 왔다.

    private void handleDelay(String clientId) {
      ArrayList<String> response = new ArrayList<>();
      response.add("received: " + DateFormatter.format(System.currentTimeMillis()));
    
      delayDisposable = Completable.timer(30, TimeUnit.SECONDS)
        .subscribeOn(Schedulers.computation())
        .subscribe(() -> {
          response.add("response: " + DateFormatter.format(System.currentTimeMillis()));
          sendMessageToClient(clientId, TextUtils.join("\n", response));
          sendEndStreamMessageToClient(clientId);
        }, throwable -> {
          throwable.printStackTrace();
          sendErrorMessageToClient(clientId, "002", throwable.getMessage());
          sendEndStreamMessageToClient(clientId);
        });
    }
  • 3초 마다 응답 보내기
    private void handleContinuous(String clientId) {
      continuousDisposable = Observable.interval(3, TimeUnit.SECONDS)
        .subscribeOn(Schedulers.computation())
        .subscribe(__ -> sendMessageToClient(clientId, "time tick: " + DateFormatter.format(System.currentTimeMillis())),
          throwable -> {
            sendErrorMessageToClient(clientId, "003", throwable.getMessage());
            sendEndStreamMessageToClient(clientId);
          });
    }