From 54d7cd5651803463c8c45bf7bf298a254ceaabe7 Mon Sep 17 00:00:00 2001 From: Kimchohee Date: Mon, 4 Apr 2022 23:10:33 +0900 Subject: [PATCH] =?UTF-8?q?[#17]=20=ED=86=B5=EC=8B=A0=EC=9D=84=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=EB=84=A4=ED=8A=B8=EC=9B=8C=ED=81=AC=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EB=B2=A0=EC=9D=B4=EC=8A=A4=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20-=20=EB=A0=88=ED=8A=B8=EB=A1=9C=ED=95=8F?= =?UTF-8?q?=20CallAdapter=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 2 + .../moyerun/moyeorun_android/MainActivity.kt | 30 +----- .../moyeorun_android/network/ApiErrorCode.kt | 5 - .../moyeorun_android/network/Response.kt | 5 - .../network/api/ApiService.kt | 8 +- .../NetworkResponseAdapterFactory.kt | 47 ---------- .../callAdapter/NetworkResponseCall.kt | 94 ------------------- .../callAdapter/NetworkResponseCallAdapter.kt | 16 ---- .../network/callAdapter/NetworkResult.kt | 10 -- .../network/calladapter/ApiResponse.kt | 10 ++ .../network/calladapter/ApiResponseCall.kt | 77 +++++++++++++++ .../calladapter/ApiResponseCallAdapter.kt | 14 +++ .../ApiResponseCallAdapterFactory.kt | 44 +++++++++ .../network/client/Retrofit.kt | 4 +- 14 files changed, 154 insertions(+), 212 deletions(-) delete mode 100644 app/src/main/java/com/moyerun/moyeorun_android/network/ApiErrorCode.kt delete mode 100644 app/src/main/java/com/moyerun/moyeorun_android/network/callAdapter/NetworkResponseAdapterFactory.kt delete mode 100644 app/src/main/java/com/moyerun/moyeorun_android/network/callAdapter/NetworkResponseCall.kt delete mode 100644 app/src/main/java/com/moyerun/moyeorun_android/network/callAdapter/NetworkResponseCallAdapter.kt delete mode 100644 app/src/main/java/com/moyerun/moyeorun_android/network/callAdapter/NetworkResult.kt create mode 100644 app/src/main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResponse.kt create mode 100644 app/src/main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResponseCall.kt create mode 100644 app/src/main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResponseCallAdapter.kt create mode 100644 app/src/main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResponseCallAdapterFactory.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5a00dcc..19ebc43 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + { + is ApiResponse.Success -> { Lg.d("getUserRepoList Success") } - is NetworkResult.Failure -> { + is ApiResponse.Failure -> { // 비즈니스 로직 상의 에러 - // 이넘 클래스로 별도 관리하는 에러 코드와 비교하여 알맞은 로직 작성 - val errorCode = response1.code // 이건 상태코드이긴 한데, error 안에 코드 넣어서 보내주는 것 사용. - if (errorCode == ApiErrorCode.NOT_EXIST_USER.code) { - // 에러 대응 - } Lg.d("getUserRepoList ApiError") } - is NetworkResult.NetworkError -> { - // 네트워크 에러와 언논 에러는 전처리되면 좋을 듯 - // Success랑 API Error만 뷰 모델에서 관리하도록. - Lg.d("getUserRepoList NetworkError") - } - is NetworkResult.UnknownError -> { Lg.d("getUserRepoList UnknownError") } - } - - val response2 = apiService.getAppName() - when (response2) { - is NetworkResult.Success -> { Lg.d("getAppName Success") } - is NetworkResult.Failure -> { - // 비즈니스 로직 상의 에러 - // 이넘 클래스로 별도 관리하는 에러 코드와 비교하여 알맞은 로직 작성 - Lg.d("getAppName ApiError") - } - is NetworkResult.NetworkError -> { Lg.d("getAppName NetworkError") } - is NetworkResult.UnknownError -> { Lg.d("getAppName UnknownError") } } } } diff --git a/app/src/main/java/com/moyerun/moyeorun_android/network/ApiErrorCode.kt b/app/src/main/java/com/moyerun/moyeorun_android/network/ApiErrorCode.kt deleted file mode 100644 index c9382bb..0000000 --- a/app/src/main/java/com/moyerun/moyeorun_android/network/ApiErrorCode.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.moyerun.moyeorun_android.network - -enum class ApiErrorCode(val code: Int) { - NOT_EXIST_USER(900) -} \ No newline at end of file diff --git a/app/src/main/java/com/moyerun/moyeorun_android/network/Response.kt b/app/src/main/java/com/moyerun/moyeorun_android/network/Response.kt index 1f0633d..8de8657 100644 --- a/app/src/main/java/com/moyerun/moyeorun_android/network/Response.kt +++ b/app/src/main/java/com/moyerun/moyeorun_android/network/Response.kt @@ -6,9 +6,4 @@ package com.moyerun.moyeorun_android.network data class Success( val message: String, val data: T -) - -data class Failure( - val message: String, - val error: T ) \ No newline at end of file diff --git a/app/src/main/java/com/moyerun/moyeorun_android/network/api/ApiService.kt b/app/src/main/java/com/moyerun/moyeorun_android/network/api/ApiService.kt index 2a88d3e..ba0b361 100644 --- a/app/src/main/java/com/moyerun/moyeorun_android/network/api/ApiService.kt +++ b/app/src/main/java/com/moyerun/moyeorun_android/network/api/ApiService.kt @@ -1,15 +1,11 @@ package com.moyerun.moyeorun_android.network.api -import com.moyerun.moyeorun_android.network.Failure import com.moyerun.moyeorun_android.network.Success -import com.moyerun.moyeorun_android.network.callAdapter.NetworkResult +import com.moyerun.moyeorun_android.network.calladapter.ApiResponse import retrofit2.http.GET import retrofit2.http.Path interface ApiService { @GET("users/{user}/repos") - suspend fun getUserRepoList(@Path("user") user: String): NetworkResult, Failure> - - @GET("appName") - suspend fun getAppName(): NetworkResult, Failure> + suspend fun getUserRepoList(@Path("user") user: String): ApiResponse> } \ No newline at end of file diff --git a/app/src/main/java/com/moyerun/moyeorun_android/network/callAdapter/NetworkResponseAdapterFactory.kt b/app/src/main/java/com/moyerun/moyeorun_android/network/callAdapter/NetworkResponseAdapterFactory.kt deleted file mode 100644 index 6317229..0000000 --- a/app/src/main/java/com/moyerun/moyeorun_android/network/callAdapter/NetworkResponseAdapterFactory.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.moyerun.moyeorun_android.network.callAdapter - -import retrofit2.Call -import retrofit2.CallAdapter -import retrofit2.Retrofit -import java.lang.reflect.ParameterizedType -import java.lang.reflect.Type - -class NetworkResponseAdapterFactory : CallAdapter.Factory() { - - override fun get( - returnType: Type, - annotations: Array, - retrofit: Retrofit - ): CallAdapter<*, *>? { - - // suspend functions wrap the response type in `Call` - if (Call::class.java != getRawType(returnType)) { - return null - } - - // check first that the return type is `ParameterizedType` - check(returnType is ParameterizedType) { - "return type must be parameterized as Call> or Call>" - } - - // get the response type inside the `Call` type - val responseType = getParameterUpperBound(0, returnType) - - // if the response type is not ApiResponse then we can't handle this type, so we return null - if (getRawType(responseType) != NetworkResult::class.java) { - return null - } - - // the response type is ApiResponse and should be parameterized - check(responseType is ParameterizedType) { - "Response must be parameterized as Result or Result" - } - - val successBodyType = getParameterUpperBound(0, responseType) - val errorBodyType = getParameterUpperBound(1, responseType) - - val errorBodyConverter = retrofit.nextResponseBodyConverter(null, errorBodyType, annotations) - - return NetworkResponseCallAdapter(successBodyType, errorBodyConverter) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/moyerun/moyeorun_android/network/callAdapter/NetworkResponseCall.kt b/app/src/main/java/com/moyerun/moyeorun_android/network/callAdapter/NetworkResponseCall.kt deleted file mode 100644 index 905831b..0000000 --- a/app/src/main/java/com/moyerun/moyeorun_android/network/callAdapter/NetworkResponseCall.kt +++ /dev/null @@ -1,94 +0,0 @@ -package com.moyerun.moyeorun_android.network.callAdapter - -import okhttp3.Request -import okhttp3.ResponseBody -import okio.Timeout -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Converter -import retrofit2.Response -import java.io.IOException -import java.lang.Exception -import java.lang.UnsupportedOperationException - -internal class NetworkResponseCall( - private val delegate: Call, - private val errorConverter: Converter -) : Call> { - - override fun enqueue(callback: Callback>) { - return delegate.enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - val body = response.body() - val code = response.code() - val error = response.errorBody() - - if (response.isSuccessful) { - // status code가 200번대 일 때 - if (body != null) { - callback.onResponse( - this@NetworkResponseCall, - Response.success(NetworkResult.Success(body)) - ) - } else { - // body 없을 때 별도 에러 만들어도 될 듯. - callback.onResponse( - this@NetworkResponseCall, - Response.success(NetworkResult.UnknownError(null)) - ) - } - } else { - val errorBody = when { - error == null -> null - error.contentLength() == 0L -> null - else -> try { - errorConverter.convert(error) - } catch (e: Exception) { - null - } - } - - if (errorBody != null) { - callback.onResponse( - this@NetworkResponseCall, - Response.success(NetworkResult.Failure(errorBody, code)) - ) - } else { - callback.onResponse( - this@NetworkResponseCall, - Response.success(NetworkResult.UnknownError(null)) - ) - } - } - } - - override fun onFailure(call: Call, t: Throwable) { - val networkResponse = when (t) { - is IOException -> NetworkResult.NetworkError(t) - else -> NetworkResult.UnknownError(t) - } - // 여기서 전처리 하면 됨. - callback.onResponse( - this@NetworkResponseCall, - Response.success(networkResponse) - ) - } - }) - } - - override fun clone(): Call> = NetworkResponseCall(delegate.clone(), errorConverter) - - override fun execute(): Response> { - throw UnsupportedOperationException("NetworkResponseCall doesn't support execute") - } - - override fun isExecuted(): Boolean = delegate.isExecuted - - override fun cancel() = delegate.cancel() - - override fun isCanceled(): Boolean = delegate.isCanceled - - override fun request(): Request = delegate.request() - - override fun timeout(): Timeout = delegate.timeout() -} \ No newline at end of file diff --git a/app/src/main/java/com/moyerun/moyeorun_android/network/callAdapter/NetworkResponseCallAdapter.kt b/app/src/main/java/com/moyerun/moyeorun_android/network/callAdapter/NetworkResponseCallAdapter.kt deleted file mode 100644 index 47721e8..0000000 --- a/app/src/main/java/com/moyerun/moyeorun_android/network/callAdapter/NetworkResponseCallAdapter.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.moyerun.moyeorun_android.network.callAdapter - -import okhttp3.ResponseBody -import retrofit2.Call -import retrofit2.CallAdapter -import retrofit2.Converter -import java.lang.reflect.Type - -class NetworkResponseCallAdapter( - private val successType: Type, - private val errorBodyConverter: Converter -) : CallAdapter>> { - override fun responseType(): Type = successType - - override fun adapt(call: Call): Call> = NetworkResponseCall(call, errorBodyConverter) -} \ No newline at end of file diff --git a/app/src/main/java/com/moyerun/moyeorun_android/network/callAdapter/NetworkResult.kt b/app/src/main/java/com/moyerun/moyeorun_android/network/callAdapter/NetworkResult.kt deleted file mode 100644 index 1545e3c..0000000 --- a/app/src/main/java/com/moyerun/moyeorun_android/network/callAdapter/NetworkResult.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.moyerun.moyeorun_android.network.callAdapter - -import java.io.IOException - -sealed class NetworkResult { - data class Success(val body: T) : NetworkResult() - data class Failure(val body: U, val code: Int) : NetworkResult() - data class NetworkError(val error: IOException) : NetworkResult() - data class UnknownError(val error: Throwable?) : NetworkResult() -} \ No newline at end of file diff --git a/app/src/main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResponse.kt b/app/src/main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResponse.kt new file mode 100644 index 0000000..acbbcc3 --- /dev/null +++ b/app/src/main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResponse.kt @@ -0,0 +1,10 @@ +package com.moyerun.moyeorun_android.network.calladapter + +/** + * Success : API 호출 성공 시, 데이터를 Wrapping 합니다. + * Failure : API 호출 실패 시, 에러를 Wrapping 합니다. + */ +sealed class ApiResponse { + data class Success(val data: T) : ApiResponse() + data class Failure(val error: Throwable) : ApiResponse() +} \ No newline at end of file diff --git a/app/src/main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResponseCall.kt b/app/src/main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResponseCall.kt new file mode 100644 index 0000000..bc496b6 --- /dev/null +++ b/app/src/main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResponseCall.kt @@ -0,0 +1,77 @@ +package com.moyerun.moyeorun_android.network.calladapter + +import okhttp3.Request +import okio.Timeout +import retrofit2.* +import java.lang.UnsupportedOperationException + +internal class ApiResponseCall( + private val delegate: Call +) : Call> { + + override fun enqueue(callback: Callback>) { + delegate.enqueue(object : Callback { + + override fun onResponse(call: Call, response: Response) { + if (response.isSuccessful) { + val body = response.body() + + if (body != null) { + callback.onResponse( + this@ApiResponseCall, + Response.success(ApiResponse.Success(body)) + ) + } else { + val invocation = call.request().tag(Invocation::class.java) + val method = invocation?.method() + val message = if (method != null) { + "Response from " + + method.declaringClass.name + + '.' + + method.name + + " was null but response body type was declared as non-null" + } else { + "No tag is attached with Invocation::class.java. So, invocation can't find." + } + val e = KotlinNullPointerException(message) + + callback.onResponse( + this@ApiResponseCall, + Response.success(ApiResponse.Failure(e)) + ) + } + } else { + callback.onResponse( + this@ApiResponseCall, + Response.success(ApiResponse.Failure(HttpException(response))) + ) + } + } + + override fun onFailure(call: Call, t: Throwable) { + callback.onResponse( + this@ApiResponseCall, + Response.success(ApiResponse.Failure(t)) + ) + } + }) + } + + override fun clone(): Call> = ApiResponseCall(delegate.clone()) + + override fun execute(): Response> { + throw UnsupportedOperationException("ApiResponseCall doesn't support execute") + } + + override fun isExecuted(): Boolean = delegate.isExecuted + + override fun cancel() { + delegate.cancel() + } + + override fun isCanceled(): Boolean = delegate.isCanceled + + override fun request(): Request = delegate.request() + + override fun timeout(): Timeout = delegate.timeout() +} \ No newline at end of file diff --git a/app/src/main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResponseCallAdapter.kt b/app/src/main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResponseCallAdapter.kt new file mode 100644 index 0000000..348e4d6 --- /dev/null +++ b/app/src/main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResponseCallAdapter.kt @@ -0,0 +1,14 @@ +package com.moyerun.moyeorun_android.network.calladapter + +import retrofit2.Call +import retrofit2.CallAdapter +import java.lang.reflect.Type + +class ApiResponseCallAdapter( + private val returnType: Type +) : CallAdapter>> { + + override fun responseType(): Type = returnType + + override fun adapt(call: Call): Call> = ApiResponseCall(call) +} \ No newline at end of file diff --git a/app/src/main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResponseCallAdapterFactory.kt b/app/src/main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResponseCallAdapterFactory.kt new file mode 100644 index 0000000..63a9622 --- /dev/null +++ b/app/src/main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResponseCallAdapterFactory.kt @@ -0,0 +1,44 @@ +package com.moyerun.moyeorun_android.network.calladapter + +import retrofit2.Call +import retrofit2.CallAdapter +import retrofit2.Retrofit +import java.lang.reflect.ParameterizedType +import java.lang.reflect.Type + +class ApiResponseCallAdapterFactory : CallAdapter.Factory() { + + override fun get( + returnType: Type, + annotations: Array, + retrofit: Retrofit + ): CallAdapter<*, *>? { + + // suspend functions wrap the response type in `Call` + if (getRawType(returnType) != Call::class.java) { + return null + } + + // check first that the return type is `ParameterizedType` + if (returnType !is ParameterizedType) { + return null + } + + // get the response type inside the `Call` type + val responseType = getParameterUpperBound(0, returnType) + + // if the response type is not ApiResponse then we can't handle this type, so we return null + if (getRawType(responseType) != ApiResponse::class.java) { + return null + } + + // the response type is ApiResponse and should be parameterized + if (responseType !is ParameterizedType) { + return null + } + + val actualResponseType = getParameterUpperBound(0, responseType) + + return ApiResponseCallAdapter(actualResponseType) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/moyerun/moyeorun_android/network/client/Retrofit.kt b/app/src/main/java/com/moyerun/moyeorun_android/network/client/Retrofit.kt index 624beb2..57c6dd8 100644 --- a/app/src/main/java/com/moyerun/moyeorun_android/network/client/Retrofit.kt +++ b/app/src/main/java/com/moyerun/moyeorun_android/network/client/Retrofit.kt @@ -1,6 +1,6 @@ package com.moyerun.moyeorun_android.network.client -import com.moyerun.moyeorun_android.network.callAdapter.NetworkResponseAdapterFactory +import com.moyerun.moyeorun_android.network.calladapter.ApiResponseCallAdapterFactory import com.moyerun.moyeorun_android.network.api.ApiService import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory @@ -9,7 +9,7 @@ private const val BASE_URL = "https://api.github.com/" private val retrofit: Retrofit = Retrofit.Builder() .baseUrl(BASE_URL) - .addCallAdapterFactory(NetworkResponseAdapterFactory()) + .addCallAdapterFactory(ApiResponseCallAdapterFactory()) .addConverterFactory(GsonConverterFactory.create()) .build()