Share
Embed
Retrofit2の仕組み 〜CallAdapterを理解する〜 / How Retrofit2 works ~ Understand CallAdapter ~
Android研究&発表会#3で発表した資料です!
https://arap.connpass.com/event/153097/
サンプル
https://github.com/kwmt/RetrofitSample
Yasutaka Kawamoto
November 30, 2019
Transcript
77 slides
-
• Տຊ ହ(͔Θͱ ͔ͨ͢) • ॴଐɿגࣜձࣾ tech vein (େࡕࢢதԝ۠ຊொ)
• ϞόΠϧΞϓϦΤϯδχΞ (AndroidଟΊɺiOSŧŔŕŪũƄŝſ) • ͬͨ͜ͱ͋Δ HTTPΫϥΠΞϯτϥΠϒϥϦ - AndroidQuery,Volley,OkHttp,Retrofit 2 ࣗݾհ -
• Squareࣾ • HTTPΫϥΠΞϯτ • 2016ʹv2ϦϦʔε • 2019/11/30 ݱࡏ2.6.1 •
JavaϥΠϒϥϦ • Android or JavaͰॻ͔ΕͨαʔόʔͰಈ͘ • GitHubελʔ34.3k Retrofitͱ -
class GitHubServiceImpl : GitHubServiceWithoutRetrofit { override suspend fun listRepos(user: String):
List<Repo> { return withContext(Dispatchers.IO) { val url = URL("https://api.github.com/users/$user/repos") val urlConnection = url.openConnection() as HttpURLConnection val result: List<Repo> try { val inputStream = BufferedInputStream(urlConnection.inputStream) result = readStream(inputStream) } finally { urlConnection.disconnect() } return@withContext result } } private fun readStream(inputStream: BufferedInputStream) = inputStream.bufferedReader().use { convert(it) } private fun convert(reader: BufferedReader): List<Repo> { val content = StringBuilder() reader.use { var line = it.readLine() while (line != null) { content.append(line) line = it.readLine() } } return Json { strictMode = false } .parse(Repo.serializer().list, content.toString()) } } -
class GitHubServiceImpl : GitHubServiceWithoutRetrofit { override suspend fun listRepos(user: String):
List<Repo> { return withContext(Dispatchers.IO) { val url = URL("https://api.github.com/users/$user/repos") val urlConnection = url.openConnection() as HttpURLConnection val result: List<Repo> try { val inputStream = BufferedInputStream(urlConnection.inputStream) result = readStream(inputStream) } finally { urlConnection.disconnect() } return@withContext result } } private fun readStream(inputStream: BufferedInputStream) = inputStream.bufferedReader().use { convert(it) } private fun convert(reader: BufferedReader): List<Repo> { val content = StringBuilder() reader.use { var line = it.readLine() while (line != null) { content.append(line) line = it.readLine() } } return Json { strictMode = false } .parse(Repo.serializer().list, content.toString()) } } URL Λࢦఆ -
class GitHubServiceImpl : GitHubServiceWithoutRetrofit { override suspend fun listRepos(user: String):
List<Repo> { return withContext(Dispatchers.IO) { val url = URL("https://api.github.com/users/$user/repos") val urlConnection = url.openConnection() as HttpURLConnection val result: List<Repo> try { val inputStream = BufferedInputStream(urlConnection.inputStream) result = readStream(inputStream) } finally { urlConnection.disconnect() } return@withContext result } } private fun readStream(inputStream: BufferedInputStream) = inputStream.bufferedReader().use { convert(it) } private fun convert(reader: BufferedReader): List<Repo> { val content = StringBuilder() reader.use { var line = it.readLine() while (line != null) { content.append(line) line = it.readLine() } } return Json { strictMode = false } .parse(Repo.serializer().list, content.toString()) } } URL Λࢦఆ openConnneciton ͨ͠Γ -
class GitHubServiceImpl : GitHubServiceWithoutRetrofit { override suspend fun listRepos(user: String):
List<Repo> { return withContext(Dispatchers.IO) { val url = URL("https://api.github.com/users/$user/repos") val urlConnection = url.openConnection() as HttpURLConnection val result: List<Repo> try { val inputStream = BufferedInputStream(urlConnection.inputStream) result = readStream(inputStream) } finally { urlConnection.disconnect() } return@withContext result } } private fun readStream(inputStream: BufferedInputStream) = inputStream.bufferedReader().use { convert(it) } private fun convert(reader: BufferedReader): List<Repo> { val content = StringBuilder() reader.use { var line = it.readLine() while (line != null) { content.append(line) line = it.readLine() } } return Json { strictMode = false } .parse(Repo.serializer().list, content.toString()) } } URL Λࢦఆ openConnneciton ͨ͠Γ InputStream ΛѻͬͨΓ -
class GitHubServiceImpl : GitHubServiceWithoutRetrofit { override suspend fun listRepos(user: String):
List<Repo> { return withContext(Dispatchers.IO) { val url = URL("https://api.github.com/users/$user/repos") val urlConnection = url.openConnection() as HttpURLConnection val result: List<Repo> try { val inputStream = BufferedInputStream(urlConnection.inputStream) result = readStream(inputStream) } finally { urlConnection.disconnect() } return@withContext result } } private fun readStream(inputStream: BufferedInputStream) = inputStream.bufferedReader().use { convert(it) } private fun convert(reader: BufferedReader): List<Repo> { val content = StringBuilder() reader.use { var line = it.readLine() while (line != null) { content.append(line) line = it.readLine() } } return Json { strictMode = false } .parse(Repo.serializer().list, content.toString()) } } URL Λࢦఆ openConnneciton ͨ͠Γ Ϩεϙϯε݁ՌΛࢦ ఆͨ͠ܕʹม͢ΔͨΊ ͷ࣮ͨ͠Γ InputStream ΛѻͬͨΓ -
class GitHubServiceImpl : GitHubServiceWithoutRetrofit { override suspend fun listRepos(user: String):
List<Repo> { return withContext(Dispatchers.IO) { val url = URL("https://api.github.com/users/$user/repos") val urlConnection = url.openConnection() as HttpURLConnection val result: List<Repo> try { val inputStream = BufferedInputStream(urlConnection.inputStream) result = readStream(inputStream) } finally { urlConnection.disconnect() } return@withContext result } } private fun readStream(inputStream: BufferedInputStream) = inputStream.bufferedReader().use { convert(it) } private fun convert(reader: BufferedReader): List<Repo> { val content = StringBuilder() reader.use { var line = it.readLine() while (line != null) { content.append(line) line = it.readLine() } } return Json { strictMode = false } .parse(Repo.serializer().list, content.toString()) } } URL Λࢦఆ openConnneciton ͨ͠Γ Ϩεϙϯε݁ՌΛࢦ ఆͨ͠ܕʹม͢ΔͨΊ ͷ࣮ͨ͠Γ connectionΛ அͨ͠Γ InputStream ΛѻͬͨΓ -
গ͠ͷઃఆͱ͍͏ͷ interface GitHubServiceWithoutRetrofit { suspend fun listRepos(user: String): List<Repo> }
interface GitHubServiceWithRetrofit { @GET("users/{user}/repos") fun listRepos(@Path("user") user: String): Call<List<Repo>> } -
গ͠ͷઃఆͱ͍͏ͷ interface GitHubServiceWithoutRetrofit { suspend fun listRepos(user: String): List<Repo> }
interface GitHubServiceWithRetrofit { @GET("users/{user}/repos") fun listRepos(@Path("user") user: String): Call<List<Repo>> } -
গ͠ͷઃఆͱ͍͏ͷ interface GitHubServiceWithoutRetrofit { suspend fun listRepos(user: String): List<Repo> }
interface GitHubServiceWithRetrofit { @GET("users/{user}/repos") fun listRepos(@Path("user") user: String): Call<List<Repo>> } -
গ͠ͷઃఆͱ͍͏ͷ class GitHubServiceImpl : GitHubServiceWithoutRetrofit { override suspend fun listRepos(user:
String): List<Repo> { … } } -
গ͠ͷઃఆͱ͍͏ͷ class GitHubServiceImpl : GitHubServiceWithoutRetrofit { override suspend fun listRepos(user:
String): List<Repo> { … } } val gitHubServiceWithRetrofit: GitHubServiceWithRetrofit = Retrofit.Builder() .baseUrl("https://api.github.com/") .addConverterFactory(MyConverterFactory()) .build() .create(GitHubServiceWithRetrofit::class.java) -
ඞཁͳͷ͜Ε͚ͩ interface GitHubServiceWithRetrofit { @GET("users/{user}/repos") fun listRepos(@Path("user") user: String): Call<List<Repo>>
} val gitHubServiceWithRetrofit: GitHubServiceWithRetrofit = Retrofit.Builder() .baseUrl("https://api.github.com/") .addConverterFactory(MyConverterFactory()) .addCallAdapterFactory(MyCallAdapterFactory()) .build() .create(GitHubServiceWithRetrofit::class.java) -
ඞཁͳͷ͜Ε͚ͩ interface GitHubServiceWithRetrofit { @GET("users/{user}/repos") fun listRepos(@Path("user") user: String): Call<List<Repo>>
} val gitHubServiceWithRetrofit: GitHubServiceWithRetrofit = Retrofit.Builder() .baseUrl("https://api.github.com/") .addConverterFactory(MyConverterFactory()) .addCallAdapterFactory(MyCallAdapterFactory()) .build() .create(GitHubServiceWithRetrofit::class.java) -
ඞཁͳͷ͜Ε͚ͩ interface GitHubServiceWithRetrofit { @GET("users/{user}/repos") fun listRepos(@Path("user") user: String): Call<List<Repo>>
} val gitHubServiceWithRetrofit: GitHubServiceWithRetrofit = Retrofit.Builder() .baseUrl("https://api.github.com/") .addConverterFactory(MyConverterFactory()) .addCallAdapterFactory(MyCallAdapterFactory()) .build() .create(GitHubServiceWithRetrofit::class.java) -
interface GitHubService { @GET("users/{user}/repos") fun listRepos(@Path("user") user: String): Call<List<Repo>> }
val gitHubService: GitHubService = Retrofit.Builder() .baseUrl("https://api.github.com/") .addConverterFactory(MyConverterFactory()) .addCallAdapterFactory(MyCallAdapterFactory()) .build() .create(GitHubService::class.java) -
interface GitHubService { @GET("users/{user}/repos") fun listRepos(@Path("user") user: String): Call<List<Repo>> }
val gitHubService: GitHubService = Retrofit.Builder() .baseUrl("https://api.github.com/") .addConverterFactory(MyConverterFactory()) .addCallAdapterFactory(MyCallAdapterFactory()) .build() .create(GitHubService::class.java) -
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); } }); } -
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); } }); } -
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); } }); } -
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); } }); } -
Proxy.newProxyInstanceͷྫ interface Service { fun doSomething(value: Int) } fun <T>
create(service: Class<T>): T { return Proxy.newProxyInstance( service.classLoader, Array(1) { service }, InvocationHandler { proxy, method, args -> Log.d(TAG, "start") Log.d(TAG, method.name + if (args.isNotEmpty()) args[0] else "") Log.d(TAG, "end") } ) as T } -
Proxy.newProxyInstanceͷྫ interface Service { fun doSomething(value: Int) } fun <T>
create(service: Class<T>): T { return Proxy.newProxyInstance( service.classLoader, Array(1) { service }, InvocationHandler { proxy, method, args -> Log.d(TAG, "start") Log.d(TAG, method.name + if (args.isNotEmpty()) args[0] else "") Log.d(TAG, "end") } ) as T } val service = create(Service::class.java) service.doSomething(1000) -
Proxy.newProxyInstanceͷྫ interface Service { fun doSomething(value: Int) } fun <T>
create(service: Class<T>): T { return Proxy.newProxyInstance( service.classLoader, Array(1) { service }, InvocationHandler { proxy, method, args -> Log.d(TAG, "start") Log.d(TAG, method.name + if (args.isNotEmpty()) args[0] else "") Log.d(TAG, "end") } ) as T } val service = create(Service::class.java) service.doSomething(1000) ϩάग़ྗ start doSomething1000 end -
Proxy.newProxyInstanceͷྫ interface Service { fun doSomething(value: Int) } fun <T>
create(service: Class<T>): T { return Proxy.newProxyInstance( service.classLoader, Array(1) { service }, InvocationHandler { proxy, method, args -> Log.d(TAG, "start") Log.d(TAG, method.name + if (args.isNotEmpty()) args[0] else "") Log.d(TAG, "end") } ) as T } val service = create(Service::class.java) service.doSomething(1000) ϩάग़ྗ start doSomething1000 end -
gitHubService.listRepos("kwmt") val gitHubService: GitHubService = Retrofit.Builder() ɹɹɹɹɹ// লུ .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); } }); } -
gitHubService.listRepos("kwmt") val gitHubService: GitHubService = Retrofit.Builder() ɹɹɹɹɹ// লུ .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); } }); } -
Retrofit#loadServiceMethod ServiceMethod<?> loadServiceMethod(Method method) { ServiceMethod<?> result = serviceMethodCache.get(method); if
(result != null) return result; synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); if (result == null) { result = ServiceMethod.parseAnnotations(this, method); serviceMethodCache.put(method, result); } } return result; } -
Factory#getɺCallAdapter#adapt ͕ͲͷΑ͏ʹݺΕ͍ͯΔ͔ʁ ServiceMethod<?> loadServiceMethod(Method method) { ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result; synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); if (result == null) { result = ServiceMethod.parseAnnotations(this, method); serviceMethodCache.put(method, result); } } return result; } -
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); } }); } -
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); } }); } -
ServiceMethod#invoke @Override invoke() { // লུ return adapt(call, args); }
@Override adapt(): T { return callAdapter.adapt(call); } -
ServiceMethod#invoke @Override invoke() { // লུ return adapt(call, args); }
@Override adapt(): T { return callAdapter.adapt(call); } -
ServiceMethod#invoke @Override invoke() { // লུ return adapt(call, args); }
@Override adapt(): T { return callAdapter.adapt(call); } -
CallAdapter.Factory class MyCallAdapterFactory : CallAdapter.Factory() { override fun get( returnType:
Type, annotations: Array<Annotation>, retrofit: Retrofit ): CallAdapter<*, *>? { val responseType = getParameterUpperBound(0, returnType as ParameterizedType) return MyCallAdapter<Any>(responseType) } } -
CallAdapter.Factory class MyCallAdapterFactory : CallAdapter.Factory() { override fun get( returnType:
Type, annotations: Array<Annotation>, retrofit: Retrofit ): CallAdapter<*, *>? { val responseType = getParameterUpperBound(0, returnType as ParameterizedType) return MyCallAdapter<Any>(responseType) } } -
CallAdapter.Factory class MyCallAdapterFactory : CallAdapter.Factory() { override fun get( returnType:
Type, annotations: Array<Annotation>, retrofit: Retrofit ): CallAdapter<*, *>? { val responseType = getParameterUpperBound(0, returnType as ParameterizedType) return MyCallAdapter<Any>(responseType) } } -
CallAdapter Call<R> → T = interface MyCall<R> { fun execute():
Response<R> } class MyCallAdapter<R>( private val responseType: Type ) : CallAdapter<R, MyCall<R>> { override fun responseType(): Type = responseType override fun adapt(call: Call<R>): MyCall<R> { return object : MyCall<R> { override fun execute(): Response<R> { return call.execute() } } } } -
CallAdapter Call<R> → T = interface MyCall<R> { fun execute():
Response<R> } class MyCallAdapter<R>( private val responseType: Type ) : CallAdapter<R, MyCall<R>> { override fun responseType(): Type = responseType override fun adapt(call: Call<R>): MyCall<R> { return object : MyCall<R> { override fun execute(): Response<R> { return call.execute() } } } } -
CallAdapter Call<R> → T = interface MyCall<R> { fun execute():
Response<R> } class MyCallAdapter<R>( private val responseType: Type ) : CallAdapter<R, MyCall<R>> { override fun responseType(): Type = responseType override fun adapt(call: Call<R>): MyCall<R> { return object : MyCall<R> { override fun execute(): Response<R> { return call.execute() } } } } -
CallAdapter Call<R> → T = interface MyCall<R> { fun execute():
Response<R> } class MyCallAdapter<R>( private val responseType: Type ) : CallAdapter<R, MyCall<R>> { override fun responseType(): Type = responseType override fun adapt(call: Call<R>): MyCall<R> { return object : MyCall<R> { override fun execute(): Response<R> { return call.execute() } } } } -
CallAdapterͰΤϥʔϋϯυϦϯά class MyCallAdapter<R>( private val responseType: Type ) : CallAdapter<R,
MyCall<R>> { override fun responseType(): Type = responseType override fun adapt(call: Call<R>): MyCall<R> { return object : MyCall<R> { override fun execute(): Response<R> { val response = call.execute() val code = response.code() // 401ͳΒϩάΠϯࣦഊ if(code == 401) { throw AuthException() } return response } } } }