JSON-RPC is a stateless, light-weight remote procedure call (RPC) protocol.
HTTP bodyのJSONでリクエストを記述する
{
"jsonrpc" : " 2.0" ,
"method" : " subtract" ,
"params" : [
42 ,
23
],
"id" : 1
}
{
"jsonrpc" : " 2.0" ,
"result" : 19 ,
"id" : 1
}
[
{
"id" : 1 ,
"jsonrpc" : " 2.0" ,
"method" : " shibuya.apk" ,
"params" : { ... }
},
{
"id" : 2 ,
"jsonrpc" : " 2.0" ,
"method" : " shinobu.apk" ,
"params" : { ... }
}
]
[
{
"id" : 1 ,
"jsonrpc" : " 2.0" ,
"result" : { ... }
},
{
"id" : 2 ,
"jsonrpc" : " 2.0" ,
"result" : { ... }
}
]
以下のリクエストが1回のリクエストでできる!!
タイムラインの投稿を取得
現在地の座標を住所に変換する
バナーを取得する
タイムラインの投稿を取得
現在地の座標を住所に変換する
バナーを取得する
APIは個々の処理をメソッドとして定義
クライアント側で個々の処理を1つのリクエストにまとめて呼び出す
[
{
"id" : 1 ,
"jsonrpc" : " 2.0" ,
"method" : " GetTimeline" ,
"params" : { ... }
},
{
"id" : 2 ,
"jsonrpc" : " 2.0" ,
"method" : " GetBanner" ,
"params" : { ... }
}
]
[
{
"id" : 1 ,
"jsonrpc" : " 2.0" ,
"result" : { ... }
},
{
"id" : 2 ,
"jsonrpc" : " 2.0" ,
"result" : { ... }
}
]
OkHttp使って試しに書いてみる Request
OkHttpClient client ;
JSONObject json = new JSONObject ();
JsonArray params = new JsonArray ();
json .put ("method" ,"subtract" );
params .put (42 );
params .put (23 );
json .put ("params" ,params );
RequestBody requestBody = RequestBody .create (MEDIA_TYPE , json .toString ().getBytes ());
Request request = new Request .Builder ()
.url ("https://...." )
.post (requestBody )
.build ();
Response response = client .newCall (request ).execute ();
OkHttp使って試しに書いてみる Response
Response response = client .newCall (request ).execute ();
// ... isSuccessfulのチェックとか...
ResponseBody value = response .body ();
JSONObject j = new JSONObject (value .string ());
int result = j .getInt ("result" );// return 19
いい感じのLibraryあるんじゃね...検索検索
毎回同じ処理は書きたくない
リクエストの型が決まればレスポンスの型が決まる
リクエストとレスポンスを型で定義
Batch リクエストをいい感じに受け取りたい
Rxとか使っちゃう??
特定の通信ライブラリに依存しない
JSON-RPCのクライアント実装で大変だったこと
Batch リクエストの受け取り方
エラーハンドリング
Retrofitの恩恵が受けられない…
OK 1つのレスポンス
OK 複数のレスポンス(2,3つ)
NG 全レスポンスエラー
NG 一部のレスポンスエラー
複数のリクエストを作る
作ったリクエストをまとめてAPIに投げる
複数のレスポンスが返ってくる
ハンドリングしてレスポンスを通知する
List ??
List<?> ??
List ??
@ Getter
public class Pair <F , S > {
private final F first ;
private final S second ;
private Pair (F first , S second ) {
this .first = first ;
this .second = second ;
}
public static <F , S > Pair <F , S > create (F first , S second ) {
return new Pair <>(first , second );
}
}
@ Getter
public class Triplet <F , S , T > {
private final F first ;
private final S second ;
private final T thread ;
private Triplet (F first , S second , T thread ) {
this .first = first ;
this .second = second ;
this .thread = thread ;
}
public static <F , S , T > Triplet <F , S , T > create (F first , S second , T thread ) {
return new Triplet <>(first , second , thread );
}
}
// リクエストとレスポンスをクラスとして定義
// リクエスト : GetBanner レスポンス : GetBanner
class GetBannerResponse {
String url ;
}
// リクエストの型が決まればレスポンスの型が決まる = RequestType<レスポンスの型>
class GetBanner extends RequestType <GetBannerResponse > {
@ Override
public String getMethod () {
return "GetBanner" ;
}
@ Override
protected Class <GetBannerResponse > getResponseType () {
return GetBannerResponse .class ;
}
}
// 同じように別APIのリクエストとレスポンスをクラスとして定義
// リクエスト : GetTimeline レスポンス : GetTimelineResponse
class GetTimelineResponse {
List <Item > items ;
}
class GetTimeline extends RequestType <GetTimelineResponse > {
private final long timelineId ;
GetTimeline (long timelineId ) {
this .timelineId = timelineId ;
}
@ Override
public String getMethod () {
return "GetTimeline" ;
}
@ Override
protected Class <GetTimelineResponse > getResponseType () {
return GetTimelineResponse .class ;
}
}
GetTimeline getTimeline = new GetTimeline (1 );
GetBanner getBanner = new GetBanner ();
RxApiClient rxApiClient = new RxApiClient ();
// Observable<Pair<GetBannerResponse, GetTimelineResponse>>が返ってくうる
rxApiClient .responseFrom (getTimeline ,getBanner )
....
リクエストのクラスはJSONになってHTTP bodyに設定される
[
{
"params" : {
"timelineId" : 1
},
"method" : " GetTimeline" ,
"jsonrpc" : " 2.0" ,
"id" : " 1"
},
{
"params" : {},
"method" : " GetBanner" ,
"jsonrpc" : " 2.0" ,
"id" : " 2"
}
]
複数のレスポンスが返ってくる +
ハンドリングしてレスポンスを通知する
// 複数のレスポンスTupleに包んでRxに流す
// responseFrom内でTupleに包む処理をしてる
rxApiClient .responseFrom (getTimeline , getBanner )
.subscribeOn (Schedulers .io ())
.observeOn (AndroidSchedulers .mainThread ())
.subscribe (new Action1 <Pair <GetTimelineResponse , GetBannerResponse >>() {
@ Override
public void call (Pair <GetTimelineResponse , GetBannerResponse > pair ) {
// Tupleからレスポンスを取り出す
pair .getFirst (); // return GetTimelineResponse
pair .getSecond (); // retrun GetBannerResponse
}
});