# HTTP4K on Android
## 測試的Android版本
```groovy
compileSdkVersion 29
buildToolsVersion "29.0.2"
minSdkVersion 21
targetSdkVersion 29
```
## 測試的[Code](https://gist.github.com/tonynowater87/99fe401e4199f3e42efc079184afa109), [Android Project](https://github.com/tonynowater87/Android-Library-Test/tree/http4k)
## 測試項目
* 當進入 HttpHandler 時是在 那一支 thread?
> 是在DefaultDispatcher-worker-[1]。
> Http4K貌似是用Coroutine的Dispatchers.Default,是一種Thead Pool,
Thread的數量會依照CPU的核心數決定。
* 是不是每一個 request 都是在新的 thread?
> 不一定,因為是用Thread Pool。
* 有沒有方法把 response 用 RxJava 的方法返回?
> Library貌似沒有直接支援,[需要自己引入RX搭配使用](#建立Client,送出Request,處理Response)。
* 是不是在 HttpHandler 裡面都要用同步的方法做 IO 的事情?
> 是的,如果用非同步的話,Response會直接返回,不會等待。
## Library的使用方式
### 設定Manifest
```xml
<uses-permission android:name="android.permission.INTERNET" />
```
### 設定Dependencies
```groovy
compile (group: "org.http4k", name: "http4k-core", version: "3.205.0")
compile (group: "org.http4k", name: "http4k-server-ktorcio", version: "3.205.0")
compile (group: "org.http4k", name: "http4k-client-okhttp", version: "3.205.0")
// RX Libraries
implementation "io.reactivex.rxjava2:rxkotlin:2.4.0"
implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
```
> 官方範例是使用http4k-server-jetty,但是Run起來會Runtime Crash
> 所以這裡改用另一個http4k-server-ktorcio
#### 其它的server和client的module連結
[servers](https://www.http4k.org/guide/modules/servers/)
[clients](https://www.http4k.org/guide/modules/clients/)
### 建立API Handler並啟動Server
```kotlin
val apiHandler: HttpHandler = routes(
"/hello" bind GET to { Response(OK).body("hello world!") }
)
apiHandler
.asServer(KtorCIO(port = 5566))
.start()
```
### 建立Client,送出Request,處理Response
```kotlin
private val client: HttpHandler by lazy {
ClientFilters
.BasicAuth(user = "userName", password = "userPwd")
.then(DebuggingFilters.PrintRequestAndResponse())
.then(OkHttp())
}
Single
.fromCallable {
val requestGet = Request(GET, "http://localhost:5566/hello/")
client(requestGet) // 送出Request
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ response ->
// 處理 Response
tvMain.text = response.bodyString()
},
{
tvMain.text = it.message
}
)
```
### 使用訂閱PublishSubject的方式接收Response
```kotlin
private var comingResponse = PublishSubject.create<Response>()
val apiFilter = Filter { next: HttpHandler ->
{ request: Request ->
val response = next(request)
response.also { this.comingResponse.onNext(it) }
}
}
val server = CachingFilters.Response.NoCache()
.then(apiFilter)
.then(apiHandler)
.asServer(KtorCIO(port = 5566))
.start()
```
```kotlin
comingResponse
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ response ->
// 處理 Response
tvMain.text = response.toString()
},
{
tvMain.text = it.toString()
}
)
```
### 遇到的錯誤
* Execution failed for task ':app:mergeDebugJavaResource'.
A failure occurred while executing
More than one file was found with OS independent path 'META-INF/kotlinx-io.kotlin_module', 'META-INF/atomicfu.kotlin_module'
, 'META-INF/kotlinx-coroutines-io.kotlin_module'
> 解法:
```groovy
//app裡的build.gradle
android {
packagingOptions {
pickFirst 'META-INF/kotlinx-io.kotlin_module'
pickFirst 'META-INF/atomicfu.kotlin_module'
pickFirst 'META-INF/kotlinx-coroutines-io.kotlin_module'
}
}
```
* Rejecting re-init on previously-failed class java.lang.Class<okhttp3.internal.platform.ConscryptPlatform$configureTrustManager$1>: java.lang.NoClassDefFoundError: Failed resolution of: Lorg/conscrypt/ConscryptHostnameVerifier;
> 解法
```groovy
implementation 'org.conscrypt:conscrypt-android:2.2.1'
```
* Transform artifact javax.servlet-api.jar (javax.servlet:javax.servlet-api:4.0.1) with DexingNoClasspathTransform
AGPBI: {"kind":"error","text":"Default interface methods are only supported starting with Android N (--min-api 24): void javax.servlet.Filter.destroy()","sources":[{}],"tool":"D8"}
> 解法
```groovy
//app裡的build.gradle
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
```