57
Android + JSON-RPC shibuya.apk #10

Android + JSON-RPC

Embed Size (px)

Citation preview

Page 1: Android + JSON-RPC

Android + JSON-RPC

shibuya.apk #10

Page 2: Android + JSON-RPC

About Me

Shinobu Okano

@operandoOS

Mercari, Inc.

Souzoh, Inc.

Page 3: Android + JSON-RPC
Page 4: Android + JSON-RPC

shinobu.apk #3 やります!!

Page 5: Android + JSON-RPC

Androidについて語る!

shinobu.apk #1 を開催しました!

http://tech.mercari.com/entry/2016/02/12/122918

Page 6: Android + JSON-RPC

shinobu.apk #2 のパネルディスカッション

録音データとShow Notesを公開しました!

http://hack-it-iron.hatenablog.com/entry/2016/04/20/124823

Page 7: Android + JSON-RPC

http://shinobu-apk.connpass.com/event/40202/

Join!!shinobu.apk #3

Page 8: Android + JSON-RPC

JSON-RPC??

Page 9: Android + JSON-RPC

JSON-RPC

“JSON-RPC is a stateless, light-weight remote procedure call (RPC) protocol.”

Page 10: Android + JSON-RPC

JSON-RPC spec

http://www.jsonrpc.org/specification

Page 11: Android + JSON-RPC

RPC

• gRPC

• http://www.grpc.io/

• Go Conference 2016 SpringでgRPCの現状について発表してきました

• http://tech.mercari.com/entry/2016/04/27/145227

Page 12: Android + JSON-RPC

JSON-RPC 2.0

Page 13: Android + JSON-RPC

JSON-RPC

HTTP bodyのJSONでリクエストを記述する

Page 14: Android + JSON-RPC

JSON-RPC Request

{ "jsonrpc": "2.0", "method": "subtract", "params": [ 42, 23 ], "id": 1}

Page 15: Android + JSON-RPC

JSON-RPC Response

{ "jsonrpc": "2.0", "result": 19, "id": 1}

Page 16: Android + JSON-RPC

JSON-RPC Batch

1回の通信で複数のリクエストを送れる

Page 17: Android + JSON-RPC

JSON-RPC Request

[ { "id": 1, "jsonrpc": "2.0", "method": "shibuya.apk", "params": { ... } }, { "id": 2, "jsonrpc": "2.0", "method": "shinobu.apk", "params": { ... } }]

Page 18: Android + JSON-RPC

JSON-RPC Response

[ { "id": 1, "jsonrpc": "2.0", "result": { ... } }, { "id": 2, "jsonrpc": "2.0", "result": { ... } }]

Page 19: Android + JSON-RPC

何に使うの??

Page 20: Android + JSON-RPC

何に使うの??

Page 21: Android + JSON-RPC

何に使うの??

• タイムラインの投稿を取得

• 現在地の座標を住所に変換する

• バナーを取得する

以下のリクエストが1回のリクエストでできる

Page 22: Android + JSON-RPC

GET /home??

Page 23: Android + JSON-RPC

複数の処理をまとめたAPIは

仕様が壊れやすい

Page 24: Android + JSON-RPC

JSON-RPC Batch

• APIは個々の処理をメソッドとして定義

• クライアント側で個々の処理を1つのリクエストにまとめて呼び出す

Page 25: Android + JSON-RPC

JSON-RPC Request

[ { "id": 1, "jsonrpc": "2.0", "method": "GetTimeline", "params": { ... } }, { "id": 2, "jsonrpc": "2.0", "method": "GetBanner", "params": { ... } }]

Page 26: Android + JSON-RPC

JSON-RPC Response

[ { "id": 1, "jsonrpc": "2.0", "result": { ... } }, { "id": 2, "jsonrpc": "2.0", "result": { ... } }]

Page 27: Android + JSON-RPC

よさそう!!

Page 28: Android + JSON-RPC

じゃAndroidで実装するか!!

Page 29: Android + JSON-RPC

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();

Page 30: Android + JSON-RPC

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

Page 31: Android + JSON-RPC

これイケてますかね??

Page 32: Android + JSON-RPC

イケてないですよね...

Page 33: Android + JSON-RPC

いい感じのLibraryあるんじゃね… 検索検索

Page 34: Android + JSON-RPC

あまりない??

• jsonrpc4j

• https://github.com/briandilley/jsonrpc4j

• Batchに対応してない??

• retrofit-jsonrpc

• https://github.com/segmentio/retrofit-jsonrpc

• Batchに対応してない??

Page 35: Android + JSON-RPC

途方に暮れる日々...

Page 36: Android + JSON-RPC

あーなんでこんなAPIに なってるんだ...

Page 37: Android + JSON-RPC

うぅ…僕はわるくない... (。-ω-)

Page 38: Android + JSON-RPC

そうも言ってられない...

Page 39: Android + JSON-RPC

そうだ! 自分で作ればいいんだ!

Page 40: Android + JSON-RPC

作ってみた

※当日までにコードが公開できませんでしたすいません🙇

Page 41: Android + JSON-RPC

作る前に考えたこと

• 毎回同じ処理は書きたくない

• リクエストの型が決まればレスポンスの型が決まる

• リクエストとレスポンスを型で定義

• Batch リクエストをいい感じに受け取りたい

• Rxとか使っちゃう??

• 特定の通信ライブラリに依存しない

Page 42: Android + JSON-RPC

JSON-RPCのクライアント実装で大変だったこと

• Batch リクエストの受け取り方

• エラーハンドリング

• Retrofitの恩恵が受けられない…

Page 43: Android + JSON-RPC

レスポンスのパターン

• OK 1つのレスポンス

• OK 複数のレスポンス(2,3つ)

• NG 全レスポンスエラー

• NG 一部のレスポンスエラー

Page 44: Android + JSON-RPC

Batch リクエストの受け取り方

• 複数のリクエストを作る

• 作ったリクエストをまとめてAPIに投げる

• 複数のレスポンスが返ってくる

• ハンドリングしてレスポンスを通知する

Page 45: Android + JSON-RPC

複数のレスポンス...??

Page 46: Android + JSON-RPC

複数のレスポンス...??

• List<Object> ??

• List<?> ??

• List<JSONObject> ??

Page 47: Android + JSON-RPC

どれもイケてない

Page 48: Android + JSON-RPC

そうだ...Tuple💪だ!!

Page 49: Android + JSON-RPC

💪

Page 50: Android + JSON-RPC

筋肉 2つ 💪💪

@Getterpublic 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); }}

Page 51: Android + JSON-RPC

筋肉 3つ 💪💪💪

@Getterpublic 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); }}

Page 52: Android + JSON-RPC

複数のリクエストを作る

// リクエストとレスポンスをクラスとして定義// リクエスト : GetBanner レスポンス : GetBannerclass GetBannerResponse { String url;}

// リクエストの型が決まればレスポンスの型が決まる = RequestType<レスポンスの型>class GetBanner extends RequestType<GetBannerResponse> { @Override public String getMethod() { return "GetBanner"; }

@Override protected Class<GetBannerResponse> getResponseType() { return GetBannerResponse.class; }}

Page 53: Android + JSON-RPC

複数のリクエストを作る// 同じように別APIのリクエストとレスポンスをクラスとして定義// リクエスト : GetTimeline レスポンス : GetTimelineResponseclass 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; }}

Page 54: Android + JSON-RPC

作ったリクエストをまとめてAPIに投げる

GetTimeline getTimeline = new GetTimeline(1);GetBanner getBanner = new GetBanner();

RxApiClient rxApiClient = new RxApiClient();

// Observable<Pair<GetBannerResponse, GetTimelineResponse>>が返ってくるrxApiClient.responseFrom(getTimeline,getBanner)....

Page 55: Android + JSON-RPC

リクエストのクラスはJSONになってHTTP bodyに設定される

[ { "params": { "timelineId": 1 }, "method": "GetTimeline", "jsonrpc": "2.0", "id": "1" }, { "params": {}, "method": "GetBanner", "jsonrpc": "2.0", "id": "2" }]

Page 56: Android + JSON-RPC

複数のレスポンスが返ってくる + ハンドリングしてレスポンスを通知する

// 複数のレスポンス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 } });

Page 57: Android + JSON-RPC

Thanks!