Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Retrofit2の仕組み 〜オリジナルConverterを作ってみる〜 / How Retr...

Retrofit2の仕組み 〜オリジナルConverterを作ってみる〜 / How Retrofit2 works ~ Try making your own Converter ~

Android研究&発表会#2で発表した資料です!
https://arap.connpass.com/event/148500/

サンプル
https://github.com/kwmt/RetrofitSample

Kotlin Serialization Converter
https://github.com/JakeWharton/retrofit2-kotlinx-serialization-converter

Yasutaka Kawamoto

October 26, 2019
Tweet

More Decks by Yasutaka Kawamoto

Other Decks in Programming

Transcript

  1. • Տຊ ହ޹(͔Θ΋ͱ ΍͔ͨ͢) • ॴଐɿגࣜձࣾ tech vein 
 (େࡕࢢதԝ۠ຊொ)

    • ϞόΠϧΞϓϦΤϯδχΞ
 (AndroidଟΊɺiOS΋ŧŔŕŪũƄŝſ) • GitHub: kwmt ɺtwitter: kwmt27 • Google I/O2018 ॳࢀՃ • ෱Ԭग़਎ • ΫϥϑτϏʔϧ޷͖ 2 ࣗݾ঺հ
  2. val repos: Call<List<Repo>> = gitHubService.listRepos("kwmt") repos.enqueue(object : retrofit2.Callback<List<Repo>> { override

    fun onFailure(call: Call<List<Repo>>, t: Throwable) { Log.d("GitHubSample", t.message) } override fun onResponse( call: Call<List<Repo>>, response: Response<List<Repo>>) { Log.d("GitHubSample", response.toString()) } })
  3. Ϋϥογϡ͠·͢ɻ Caused by: java.lang.IllegalArgumentException: 
 Could not locate ResponseBody converter

    for
 java.util.List<net.kwmt27.retrofitsample.data.model.Repo>. ͳΜ͔ConverterབྷΈͰౖΒΕͯΔ༧૝͸͖ͭ·͕͢ɺ
  4. Ϋϥογϡ͠·͢ɻ Caused by: java.lang.IllegalArgumentException: 
 Could not locate ResponseBody converter

    for
 java.util.List<net.kwmt27.retrofitsample.data.model.Repo>. val repos: Call<List<Repo>> = gitHubService.listRepos("kwmt") ͳΜ͔ConverterབྷΈͰౖΒΕͯΔ༧૝͸͖ͭ·͕͢ɺ
  5. Ϋϥογϡ͠·͢ɻ Caused by: java.lang.IllegalArgumentException: 
 Could not locate ResponseBody converter

    for
 java.util.List<net.kwmt27.retrofitsample.data.model.Repo>. val repos: Call<List<Repo>> = gitHubService.listRepos("kwmt") ͜͜ͰΫϥογϡ͍ͯ͠ΔΑ͏ͳͷͰɺਂ͘ݟͯΈ·͢ɻ ͳΜ͔ConverterབྷΈͰౖΒΕͯΔ༧૝͸͖ͭ·͕͢ɺ
  6. retrofit.create(GitHubService::class.java) public <T> T create(final Class<T> service) { ɹ// লུ

    return (T) Proxy.newProxyInstance( service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { @Override public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { ɹɹɹɹɹ // লུ return loadServiceMethod(method) .invoke(args != null ? args : emptyArgs); } }); }
  7. retrofit.create(GitHubService::class.java) public <T> T create(final Class<T> service) { ɹ// লུ

    return (T) Proxy.newProxyInstance( service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { @Override public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { ɹɹɹɹɹ // লུ return loadServiceMethod(method) .invoke(args != null ? args : emptyArgs); } }); }
  8. retrofit.create(GitHubService::class.java) public <T> T create(final Class<T> service) { ɹ// লུ

    return (T) Proxy.newProxyInstance( service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { @Override public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { ɹɹɹɹɹ // লུ return loadServiceMethod(method) .invoke(args != null ? args : emptyArgs); } }); }
  9. if (annotation instanceof DELETE) { parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false); }

    else if (annotation instanceof GET) { parseHttpMethodAndPath("GET", ((GET) annotation).value(), false); } else if (annotation instanceof HEAD) { parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false); } else if (annotation instanceof PATCH) { parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true); } else if (annotation instanceof POST) { parseHttpMethodAndPath("POST", ((POST) annotation).value(), true); } else if (annotation instanceof PUT) { parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
  10. if (annotation instanceof DELETE) { parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false); }

    else if (annotation instanceof GET) { parseHttpMethodAndPath("GET", ((GET) annotation).value(), false); } else if (annotation instanceof HEAD) { parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false); } else if (annotation instanceof PATCH) { parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true); } else if (annotation instanceof POST) { parseHttpMethodAndPath("POST", ((POST) annotation).value(), true); } else if (annotation instanceof PUT) { parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
  11. if (annotation instanceof DELETE) { parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false); }

    else if (annotation instanceof GET) { parseHttpMethodAndPath("GET", ((GET) annotation).value(), false); } else if (annotation instanceof HEAD) { parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false); } else if (annotation instanceof PATCH) { parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true); } else if (annotation instanceof POST) { parseHttpMethodAndPath("POST", ((POST) annotation).value(), true); } else if (annotation instanceof PUT) { parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
  12. if (annotation instanceof DELETE) { parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false); }

    else if (annotation instanceof GET) { parseHttpMethodAndPath("GET", ((GET) annotation).value(), false); } else if (annotation instanceof HEAD) { parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false); } else if (annotation instanceof PATCH) { parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true); } else if (annotation instanceof POST) { parseHttpMethodAndPath("POST", ((POST) annotation).value(), true); } else if (annotation instanceof PUT) { parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
  13. HTTPϝιουΛ஍ಓʹύʔε if (annotation instanceof DELETE) { parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);

    } else if (annotation instanceof GET) { parseHttpMethodAndPath("GET", ((GET) annotation).value(), false); } else if (annotation instanceof HEAD) { parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false); } else if (annotation instanceof PATCH) { parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true); } else if (annotation instanceof POST) { parseHttpMethodAndPath("POST", ((POST) annotation).value(), true); } else if (annotation instanceof PUT) { parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
  14. body͕ແͯ͘(PATCH,POST,PUTҎ֎) @Multipart΍@FormUrlEncodedΛࢦఆͨ͠৔߹ɺ
 IllegalArgumentException͕εϩʔ͞Ε·͢ if (!hasBody) { if (isMultipart) { throw

    methodError(method, "Multipart can only be specified on HTTP methods with request body (e.g., @POST)."); } if (isFormEncoded) { throw methodError(method, "FormUrlEncoded can only be specified on HTTP methods with " + "request body (e.g., @POST)."); } }
  15. int start = converterFactories.indexOf(skipPast) + 1; for (int i =

    start, count = converterFactories.size(); i < count; i++) { Converter<ResponseBody, ?> converter = converterFactories.get(i) .responseBodyConverter(type, annotations, this); if (converter != null) { return (Converter<ResponseBody, T>) converter; } } StringBuilder builder = new StringBuilder("Could not locate ResponseBody converter for ") .append(type) .append(“.\n"); // লུ throw new IllegalArgumentException(builder.toString());
  16. int start = converterFactories.indexOf(skipPast) + 1; for (int i =

    start, count = converterFactories.size(); i < count; i++) { Converter<ResponseBody, ?> converter = converterFactories.get(i) .responseBodyConverter(type, annotations, this); if (converter != null) { return (Converter<ResponseBody, T>) converter; } } StringBuilder builder = new StringBuilder("Could not locate ResponseBody converter for ") .append(type) .append(“.\n"); // লུ throw new IllegalArgumentException(builder.toString());
  17. Converter<ResponseBody, ?> converter = converterFactories.get(i) .responseBodyConverter(type, annotations, this); if (converter

    != null) { return (Converter<ResponseBody, T>) converter; } responseBodyConverterϝιου͕ nullΛฦ͞ͳ͍Α͏ͳɺConverterFactory͕ converterFactoriesʹηοτͰ͖Ε͹ྑ͍ ͜ͱ͕Θ͔ͬͨɻ
  18. class MyConverterFactory : Converter.Factory() { override fun responseBodyConverter( type: Type,

    annotations: Array<Annotation>, retrofit: Retrofit ): Converter<ResponseBody, *>? { return MyResponseBodyConverter() } class MyResponseBodyConverter : Converter<ResponseBody, String> { override fun convert(value: ResponseBody): String? { return "test" } } }
  19. class MyConverterFactory : Converter.Factory() { override fun responseBodyConverter( type: Type,

    annotations: Array<Annotation>, retrofit: Retrofit ): Converter<ResponseBody, *>? { return MyResponseBodyConverter() } class MyResponseBodyConverter : Converter<ResponseBody, String> { override fun convert(value: ResponseBody): String? { return "test" } } }
  20. class MyConverterFactory : Converter.Factory() { override fun responseBodyConverter( type: Type,

    annotations: Array<Annotation>, retrofit: Retrofit ): Converter<ResponseBody, *>? { return MyResponseBodyConverter() } class MyResponseBodyConverter : Converter<ResponseBody, String> { override fun convert(value: ResponseBody): String? { return "test" } } }
  21. class MyConverterFactory : Converter.Factory() { override fun responseBodyConverter( type: Type,

    annotations: Array<Annotation>, retrofit: Retrofit ): Converter<ResponseBody, *>? { return MyResponseBodyConverter() } class MyResponseBodyConverter : Converter<ResponseBody, String> { override fun convert(value: ResponseBody): String? { return "test" } } }
  22. class MyConverterFactory : Converter.Factory() { override fun responseBodyConverter( type: Type,

    annotations: Array<Annotation>, retrofit: Retrofit ): Converter<ResponseBody, *>? { return MyResponseBodyConverter() } class MyResponseBodyConverter : Converter<ResponseBody, String> { override fun convert(value: ResponseBody): String? { return "test" } } }
  23. class MyConverterFactory : Converter.Factory() { override fun responseBodyConverter( type: Type,

    annotations: Array<Annotation>, retrofit: Retrofit ): Converter<ResponseBody, *>? { return MyResponseBodyConverter() } class MyResponseBodyConverter : Converter<ResponseBody, String> { override fun convert(value: ResponseBody): String? { return "test" } } }
  24. class MyConverterFactory : Converter.Factory() { override fun responseBodyConverter( type: Type,

    annotations: Array<Annotation>, retrofit: Retrofit ): Converter<ResponseBody, *>? { return MyResponseBodyConverter() } class MyResponseBodyConverter : Converter<ResponseBody, String> { override fun convert(value: ResponseBody): String? { return "test" } } }
  25. repos.enqueue(object : retrofit2.Callback<List<Repo>> { override fun onFailure(call: Call<List<Repo>>, t: Throwable)

    { Log.d("GitHubSample", t.message) } override fun onResponse( call: Call<List<Repo>>, response: Response<List<Repo>>) { Log.d("GitHubSample", response.toString()) } })
  26. repos.enqueue(object : retrofit2.Callback<List<Repo>> { override fun onFailure(call: Call<List<Repo>>, t: Throwable)

    { Log.d("GitHubSample", t.message) } override fun onResponse( call: Call<List<Repo>>, response: Response<List<Repo>>) { Log.d("GitHubSample", response.toString()) } }) Ϋϥογϡ͸͠ͳ͘ͳ͕ͬͨɺ ΄͍͠ͷ͸List<Repo>Ͱ StringͰ͸ͳ͍
  27. class MyResponseBodyConverter : Converter<ResponseBody, String> { override fun convert(value: ResponseBody):

    String? { return "test" } } ConverterͰ ResponseBody ͔Β List<Repo>ʹม׵
  28. class MyResponseBodyConverter : Converter<ResponseBody, List<Repo>> { override fun convert(value: ResponseBody):

    List<Repo>? { // ResponseBody͔ΒString΁ͷม׵෦෼͸লུ val json = Json { strictMode = false } return json.parse(Repo.serializer().list, content)) } } class MyResponseBodyConverter : Converter<ResponseBody, String> { override fun convert(value: ResponseBody): String? { return "test" } } ConverterͰ ResponseBody ͔Β List<Repo>ʹม׵
  29. class MyConverterFactory : Converter.Factory() { override fun responseBodyConverter( type: Type,

    annotations: Array<Annotation>, retrofit: Retrofit ): Converter<ResponseBody, *>? { return MyResponseBodyConverter() } }
  30. class MyConverterFactory : Converter.Factory() { override fun responseBodyConverter( type: Type,

    annotations: Array<Annotation>, retrofit: Retrofit ): Converter<ResponseBody, *>? { return MyResponseBodyConverter() } override fun requestBodyConverter( type: Type, parameterAnnotations: Array<Annotation>, methodAnnotations: Array<Annotation>, retrofit: Retrofit ): Converter<*, RequestBody>? { return MyRequestBodyConverter() } }