Google Play Service의 PendingResult 흉내내기

Google Play에서 API style이 변경되어서 대부분의 경우 PendingResult가 반환됩니다. 이러한 style이 완전히 새로 나온 것은 아니고 기존의 Future를 Google의 Guava Library에서 확장한 ListenableFuture에서 이러한 pattern을 사용하고 있습니다. 보통 Future 는 multi-threading 에서 concurrency를 위해 사용하지만 여기서는 Runnable이 아닌 경우에도 의미가 있네요. ListenableFuture에 대한 내용은 http://www.nurkiewicz.com/2013/02/listenablefuture-in-guava.html을 참고하세요.

아래와 같이 google의 API style을 흉내내어 보았습니다.
public class RawPendingResult implements PendingResult {

private final CountDownLatch mLatch = new CountDownLatch(1);
private final ResultHandler mHandler;
private ResultCallback mCallbackFn;
private volatile T mResult;

public RawPendingResult(Looper looper) {
mHandler = new ResultHandler(looper);
}

public RawPendingResult(ResultHandler handler) {
mHandler = handler;
}

@Override
public T await() {
try {
mLatch.await();
} catch (InterruptedException e) {

}
return mResult;
}

@Override
public void cancel() {
// TODO Auto-generated method stub

}

@Override
public boolean isCanceled() {
// TODO Auto-generated method stub
return false;
}

public final boolean isReady() {
return mLatch.getCount() == 0L;
}

public void setResult(T result) {
mResult = result;
mLatch.countDown();
}

@Override
public void setResultCallback(ResultCallback callback) {
if (isCanceled()) {
return;
}
if (isReady()) {
mHandler.sendResult(callback, mResult);
} else {
mCallbackFn = callback;
}
}

private static class ResultHandler extends Handler {
public ResultHandler() {
this(Looper.getMainLooper());
}

public ResultHandler(Looper looper) {
super();
}

public void sendResult(ResultCallback callback, T param) {
sendMessage(obtainMessage(1, new Pair(callback, param)));
}

public void handleMEssage(Message msg) {
switch (msg.what) {
case 1:
Pair pair = (Pair) msg.obj;
involeResult((ResultCallback) pair.first, (T) pair.second);
}
}

private void involeResult(ResultCallback callback, T param) {
try {
callback.onResult(param);
} catch (RuntimeException e) {
}
}
}
}

사용은 다음과 같이 하면 됩니다.

public void testFunc() {
PendingResult result = testResult(0, 1);
int i = result.await();
Log.e("TEST", "RESULT is " + i);
}

PendingResult testResult(int a, int b) {
RawPendingResult result = new RawPendingResult(Looper.getMainLooper());

result.setResult(a + b);
return result;
}

위의 경우는 간단하게 결과를 반환하기 전에 값이 설정되지만 필요에 따라 async로 처리 할 수 있습니다. 다만 이 경우 서로 다른 process인 경우 parcelable 객체 처리가 고민입니다.

P.S. source code 때문에 javascript code를 넣었더니 글이 날아가네요 --;

덧글

  • shin\'s 2014/12/03 14:26 # 삭제

    안녕하세요 구글 리더보드 연동해서, 리더보드 뷰는 띄우지 않고 값들만 받아오는 것을 하고 있는데... 위에서 말씀하신것 처럼 PendingResult을 결과 값으로 반환하는데.. 아직 초보이다 보니까 이 값을 어떻게 핸들링해서 써야 하는지 잘 모르겠습니다.. ㅜㅜ
    실례가 안된다면... 위에 예제 처럼 PendingResult도 비슷하게 사용 할 수 있는 건가요...?
  • uriel 2014/12/07 20:10 #

    제가 쓴 부분은 PendingResult 자체를 구현하는 쪽이라 크게 도움은 안될 듯 합니다. 대신에 http://developer.android.com/google/auth/api-client.html 에 예제들이 있는데 참고하시면 될 듯 합니다.

    PendingResult는 java의 Future (android는 http://developer.android.com/reference/java/util/concurrent/Future.html 에 link가 있습니다) 처럼 생각하시면 됩니다. 즉, PendingResult 자체는 결과가 오기로 약속된 자리인데 PendingResult.await()나 Future.get()을 하면 결과가 나옵니다.
※ 이 포스트는 더 이상 덧글을 남길 수 없습니다.