---
# System prepended metadata

title: HTTP4K on Android

---

# 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
    }
}
```