-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #33 from MoyeoRun/feature/network-base
[#17] 통신을 위한 네트워크 관련 베이스 코드 작성
- Loading branch information
Showing
12 changed files
with
262 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
app/src/main/java/com/moyerun/moyeorun_android/common/exceptions/ApiException.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.moyerun.moyeorun_android.common.exceptions | ||
|
||
import com.moyerun.moyeorun_android.network.api.Error | ||
|
||
class ApiException(val url: String, val error: Error) : RuntimeException() { | ||
val case: String | ||
get() = error.case | ||
|
||
override val message: String? | ||
get() = error.message | ||
} |
5 changes: 5 additions & 0 deletions
5
app/src/main/java/com/moyerun/moyeorun_android/common/exceptions/NetworkException.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.moyerun.moyeorun_android.common.exceptions | ||
|
||
import java.lang.RuntimeException | ||
|
||
class NetworkException(message: String, cause: Throwable) : RuntimeException(message, cause) |
22 changes: 22 additions & 0 deletions
22
app/src/main/java/com/moyerun/moyeorun_android/network/api/ApiErrorCase.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.moyerun.moyeorun_android.network.api | ||
|
||
import com.moyerun.moyeorun_android.common.exceptions.ApiException | ||
|
||
enum class ApiErrorCase(val case: String) { | ||
// 서버와 합의된 에러 케이스들을 정의합니다. | ||
NOT_LOGIN("100"), // TODO : 예시입니다. 실제 로그인 작업 시 수정해주세요. | ||
UNKNOWN("999"); | ||
|
||
companion object { | ||
private fun findError(case: String): ApiErrorCase { | ||
return values().find { it.case == case } ?: UNKNOWN | ||
} | ||
|
||
fun getException(url: String, error: Error, cause: Throwable? = null): Throwable { | ||
return when(findError(error.case)) { | ||
NOT_LOGIN -> { TODO() } | ||
else -> ApiException(url, error) | ||
} | ||
} | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
app/src/main/java/com/moyerun/moyeorun_android/network/api/ApiResponse.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.moyerun.moyeorun_android.network.api | ||
|
||
/** | ||
* 서버 팀과 합의한 성공/실패 응답 값에 대한 모델입니다. | ||
*/ | ||
data class Success<T>( | ||
val data: T | ||
) | ||
|
||
data class Error( | ||
val message: String?, | ||
val case: String | ||
) |
5 changes: 5 additions & 0 deletions
5
app/src/main/java/com/moyerun/moyeorun_android/network/api/ApiService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.moyerun.moyeorun_android.network.api | ||
|
||
interface ApiService { | ||
|
||
} |
10 changes: 10 additions & 0 deletions
10
app/src/main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResult.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.moyerun.moyeorun_android.network.calladapter | ||
|
||
/** | ||
* Success : API 호출 성공 시, body를 Wrapping 합니다. | ||
* Failure : API 호출 실패 시, throwable을 Wrpping 합니다. | ||
*/ | ||
sealed class ApiResult<out T> { | ||
data class Success<T>(val body: T) : ApiResult<T>() | ||
data class Failure(val throwable: Throwable) : ApiResult<Nothing>() | ||
} |
107 changes: 107 additions & 0 deletions
107
app/src/main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResultCall.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package com.moyerun.moyeorun_android.network.calladapter | ||
|
||
import com.google.gson.Gson | ||
import com.moyerun.moyeorun_android.common.exceptions.NetworkException | ||
import com.moyerun.moyeorun_android.network.api.ApiErrorCase | ||
import com.moyerun.moyeorun_android.network.api.Error | ||
import okhttp3.Request | ||
import okio.Timeout | ||
import retrofit2.* | ||
|
||
class ApiResultCall<S>( | ||
private val delegate: Call<S> | ||
) : Call<ApiResult<S>> { | ||
override fun enqueue(callback: Callback<ApiResult<S>>) { | ||
delegate.enqueue(object : Callback<S> { | ||
override fun onResponse(call: Call<S>, response: Response<S>) { | ||
val requestUrl = delegate.request().url().toString() | ||
|
||
// status code 200번대. | ||
if (response.isSuccessful) { | ||
val body = response.body() | ||
if (body != null) { | ||
callback.onResponse( | ||
this@ApiResultCall, | ||
Response.success(ApiResult.Success(body)) | ||
) | ||
} else { | ||
callback.onResponse( | ||
this@ApiResultCall, | ||
Response.success( | ||
ApiResult.Failure( | ||
NetworkException( | ||
"Response from $requestUrl was null " + | ||
"but response body type was decleared as non-null", | ||
HttpException(response) | ||
) | ||
) | ||
) | ||
) | ||
} | ||
} else { // status code 200번대 아님. | ||
val errorBody = response.errorBody() | ||
if (errorBody != null) { | ||
val errorResponse = Gson().fromJson(errorBody.string(), Error::class.java) | ||
callback.onResponse( | ||
this@ApiResultCall, | ||
Response.success( | ||
ApiResult.Failure( | ||
ApiErrorCase.getException( | ||
requestUrl, | ||
errorResponse, | ||
HttpException(response) | ||
) | ||
) | ||
) | ||
) | ||
} else { | ||
callback.onResponse( | ||
this@ApiResultCall, | ||
Response.success( | ||
ApiResult.Failure( | ||
NetworkException( | ||
requestUrl, | ||
HttpException(response) | ||
) | ||
) | ||
) | ||
) | ||
} | ||
} | ||
} | ||
|
||
override fun onFailure(call: Call<S>, throwable: Throwable) { | ||
callback.onResponse( | ||
this@ApiResultCall, | ||
Response.success( | ||
ApiResult.Failure( | ||
NetworkException( | ||
call.request().url().toString(), | ||
throwable | ||
) | ||
) | ||
) | ||
) | ||
} | ||
}) | ||
} | ||
|
||
override fun clone(): Call<ApiResult<S>> = ApiResultCall(delegate.clone()) | ||
|
||
override fun execute(): Response<ApiResult<S>> { | ||
throw UnsupportedOperationException("ApiResultCall 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() | ||
|
||
} |
13 changes: 13 additions & 0 deletions
13
app/src/main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResultCallAdapter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.moyerun.moyeorun_android.network.calladapter | ||
|
||
import retrofit2.Call | ||
import retrofit2.CallAdapter | ||
import java.lang.reflect.Type | ||
|
||
class ApiResultCallAdapter<T>( | ||
private val returnType: Type | ||
) : CallAdapter<T, Call<ApiResult<T>>> { | ||
override fun responseType(): Type = returnType | ||
|
||
override fun adapt(call: Call<T>): Call<ApiResult<T>> = ApiResultCall(call) | ||
} |
44 changes: 44 additions & 0 deletions
44
...main/java/com/moyerun/moyeorun_android/network/calladapter/ApiResultCallAdapterFactory.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ApiResultCallAdapterFactory : CallAdapter.Factory() { | ||
|
||
override fun get( | ||
returnType: Type, | ||
annotations: Array<out Annotation>, | ||
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) != ApiResult::class.java) { | ||
return null | ||
} | ||
|
||
// the response type is ApiResponse and should be parameterized | ||
if (responseType !is ParameterizedType) { | ||
return null | ||
} | ||
|
||
val successResponseType = getParameterUpperBound(0, responseType) | ||
|
||
return ApiResultCallAdapter<Any>(successResponseType) | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
app/src/main/java/com/moyerun/moyeorun_android/network/client/Retrofit.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.moyerun.moyeorun_android.network.client | ||
|
||
import com.moyerun.moyeorun_android.BuildConfig | ||
import com.moyerun.moyeorun_android.network.calladapter.ApiResultCallAdapterFactory | ||
import com.moyerun.moyeorun_android.network.api.ApiService | ||
import retrofit2.Retrofit | ||
import retrofit2.converter.gson.GsonConverterFactory | ||
|
||
private const val BASE_URL = BuildConfig.BASE_URL | ||
|
||
val retrofit: Retrofit = Retrofit.Builder() | ||
.baseUrl(BASE_URL) | ||
.addCallAdapterFactory(ApiResultCallAdapterFactory()) | ||
.addConverterFactory(GsonConverterFactory.create()) | ||
.build() | ||
|
||
val apiService: ApiService = retrofit.create(ApiService::class.java) |