Intent로도 data를 넘기기 어려울 땐? ResultReceiver
728x90
반응형

곤란한 상황을 마주하였다.

상황은 다음과 같다


  1. TestService.java에서 TestWebviewActivity로 data를 전달한다.
  2. 전달 받은 값을 parameter 삼아서 webview를 연다.
  3. Web에서 JavascriptInterface를 호출, webview에서 web의 javascript를 호출한다.
  4. javascript에서 필요한 값을 다시 TestService.java에 전달한다.

여기서 startActivityForResult()를 쓸 수 없다.
intent를 사용하는 곳이 Activity가 아닌 service이기 때문이다.
이 때 사용한 것이 다름아닌 ResultReceiver이다.

객체를 전달하면서 callback을 같이 받고 싶을 때를 위해 android에서는 ResultReceiver를 제공한다. (출저)
나 역시 필요하니깐 아래와 같이 구현했다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Defines a generic receiver used to pass data to Activity from a Service
public class WebviewReceiver extends ResultReceiver {
    private Receiver receiver;
    // Constructor takes a handler
    public WebviewReceiver(Handler handler) {
        super(handler);
    }
    // Setter for assigning the receiver
    public void setReceiver(Receiver receiver) {
        this.receiver = receiver;
    }
    // Defines our event interface for communication
    public interface Receiver {
        void onReceiveResult(int resultCode, Bundle resultData);
    }
    // Delegate method which passes the result to the receiver if the receiver has been assigned
    @Override
    protected void onReceiveResult(int resultCode, Bundle resultData) {
        if (receiver != null) {
            receiver.onReceiveResult(resultCode, resultData);
        }
    }
}
cs

위 코드는 Starting Background Services라는 글에서 가지고 왔다.
위 인터페이스에 주목해야 한다. 결국 저기에 callback을 받아 내기 때문이다.
이해가 가지 않는다면 아래 코드에서 이용한 사례를 보면 이해가 갈 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public WebviewReceiver receiverForTest;
 
setupServiceReceiver();
intent.putExtra("receiver", receiverForTest);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
 
/**
 * Setup the callback for when data is received from the service
 */
public void setupServiceReceiver() {
    receiverForTest = new WebviewReceiver(new Handler());
    // This is where we specify what happens when data is received from the service
    receiverForTest.setReceiver(new WebviewReceiver.Receiver() {
        @Override
        public void onReceiveResult(int resultCode, Bundle resultData) {
            if (resultCode == RESULT_OK) {
                String resultValue = resultData.getString("resultValue");
                Log.e(TAG, "onReceiveResult ::: "+response.toString());
            }
        }
    });
}
 
cs

setupServiceReceiver method를 살펴보자
Receiver()가 callback 자리를 잡고 있다.
그렇다면 intent를 받은 자리는 어떻게 하고 있을까?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Extract the receiver passed into the service
final ResultReceiver rec = mainIntent.getParcelableExtra("receiver");
 
 
class MyJavaScriptInterface {
    @JavascriptInterface
    public void processWebData(String html)
    {
        // Extract additional values from the bundle
        // To send a message to the Activity, create a pass a Bundle
        Bundle bundle = new Bundle();
        bundle.putString("resultValue", html);
        // Here we call send passing a resultCode and the bundle of extras
        rec.send(Activity.RESULT_OK, bundle);
 
        finish();
    }
}
cs

getParcelableExtra를 ResultReceiver에 담는다.
선언된 ResultReceiver에서 보내야 될 값을 send에 담아서 수행하면 
receiverForTest.setReceiver에서 override된 onReceiveResult가 동작하게 된다.
이렇게 무사히 service에 원하는 값을 전달했다.


참고로 Lifecycle에서 자유롭게 data를 쓰고 싶다면 ViewModel을 사용할 수 있다.


728x90
반응형