drake пре 4 година
родитељ
комит
6cb5cf0de2
41 измењених фајлова са 270 додато и 116 уклоњено
  1. 1 1
      build.gradle
  2. 63 16
      net/src/main/java/com/drake/net/Net.kt
  3. 8 10
      net/src/main/java/com/drake/net/scope/NetCoroutineScope.kt
  4. 6 0
      sample/build.gradle
  5. 5 1
      sample/src/main/java/com/drake/net/sample/base/App.kt
  6. 1 1
      sample/src/main/java/com/drake/net/sample/callback/GsonConvert.kt
  7. 1 1
      sample/src/main/java/com/drake/net/sample/callback/JsonConvert.kt
  8. 2 9
      sample/src/main/java/com/drake/net/sample/mod/ListModel.kt
  9. 1 1
      sample/src/main/java/com/drake/net/sample/mod/Model.kt
  10. 2 9
      sample/src/main/java/com/drake/net/sample/ui/fragment/AsyncTaskFragment.kt
  11. 5 4
      sample/src/main/java/com/drake/net/sample/ui/fragment/ConfigDialogFragment.kt
  12. 3 3
      sample/src/main/java/com/drake/net/sample/ui/fragment/CoroutineScopeFragment.kt
  13. 6 5
      sample/src/main/java/com/drake/net/sample/ui/fragment/CustomConvertFragment.kt
  14. 1 1
      sample/src/main/java/com/drake/net/sample/ui/fragment/DownloadFileFragment.kt
  15. 10 5
      sample/src/main/java/com/drake/net/sample/ui/fragment/DownloadImageFragment.kt
  16. 10 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/ErrorHandlerFragment.kt
  17. 1 1
      sample/src/main/java/com/drake/net/sample/ui/fragment/ExceptionTraceFragment.kt
  18. 8 1
      sample/src/main/java/com/drake/net/sample/ui/fragment/InterceptorFragment.kt
  19. 12 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/ParallelNetworkFragment.kt
  20. 2 2
      sample/src/main/java/com/drake/net/sample/ui/fragment/PullRefreshFragment.kt
  21. 4 6
      sample/src/main/java/com/drake/net/sample/ui/fragment/PushRefreshFragment.kt
  22. 15 5
      sample/src/main/java/com/drake/net/sample/ui/fragment/ReadCacheFragment.kt
  23. 3 4
      sample/src/main/java/com/drake/net/sample/ui/fragment/StateLayoutFragment.kt
  24. 1 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/SwitchDispatcherFragment.kt
  25. 29 10
      sample/src/main/java/com/drake/net/sample/ui/fragment/UploadFileFragment.kt
  26. 8 0
      sample/src/main/res/drawable/bg_card.xml
  27. 2 1
      sample/src/main/res/layout/fragment_async_task.xml
  28. 1 0
      sample/src/main/res/layout/fragment_coroutine_scope.xml
  29. 2 1
      sample/src/main/res/layout/fragment_custom_convert.xml
  30. 6 6
      sample/src/main/res/layout/fragment_download_image.xml
  31. 2 1
      sample/src/main/res/layout/fragment_error_handler.xml
  32. 2 1
      sample/src/main/res/layout/fragment_exception_trace.xml
  33. 2 1
      sample/src/main/res/layout/fragment_interceptor.xml
  34. 2 1
      sample/src/main/res/layout/fragment_parallel_network.xml
  35. 3 1
      sample/src/main/res/layout/fragment_read_cache.xml
  36. 1 0
      sample/src/main/res/layout/fragment_request_method.xml
  37. 2 1
      sample/src/main/res/layout/fragment_state_layout.xml
  38. 2 1
      sample/src/main/res/layout/fragment_switch_dispatcher.xml
  39. 33 4
      sample/src/main/res/layout/item_list.xml
  40. 1 1
      sample/src/main/res/navigation/nav_main.xml
  41. 1 0
      sample/src/main/res/values/colors.xml

+ 1 - 1
build.gradle

@@ -3,7 +3,7 @@
 buildscript {
     ext {
         kotlin_version = '1.3.72'
-        brv_version = '1.3.3'
+        brv_version = '1.3.4'
         coroutine_version = '1.3.0'
         glide_version = '4.9.0'
         room_version = "2.2.5"

+ 63 - 16
net/src/main/java/com/drake/net/Net.kt

@@ -13,9 +13,9 @@ import android.content.Context
 import com.bumptech.glide.Glide
 import com.drake.net.error.ResponseException
 import com.yanzhenjie.kalle.Canceler
-import com.yanzhenjie.kalle.Kalle
 import com.yanzhenjie.kalle.RequestMethod
 import com.yanzhenjie.kalle.Url
+import com.yanzhenjie.kalle.download.BodyDownload
 import com.yanzhenjie.kalle.download.UrlDownload
 import com.yanzhenjie.kalle.simple.SimpleBodyRequest
 import com.yanzhenjie.kalle.simple.SimpleUrlRequest
@@ -58,8 +58,7 @@ inline fun <reified M> CoroutineScope.Get(
             .cacheMode(cache)
 
     val response = try {
-        request.apply(block)
-                .perform<M, ResponseException>(M::class.java, ResponseException::class.java)
+        request.apply(block).perform<M, ResponseException>(M::class.java, ResponseException::class.java)
     } catch (e: SocketException) {
         throw CancellationException()
     }
@@ -292,9 +291,8 @@ inline fun <reified M> CoroutineScope.Patch(
             if (response.isSucceed) response.success!! else throw response.failure!!
         }
 
-
 /**
- * 下载文件
+ * 用于提交URL体下载文件(默认GET请求)
  *
  * @param path  网络路径, 非绝对路径会加上HOST[NetConfig.host]为前缀
  * @param method 请求方式, 默认GET
@@ -305,14 +303,18 @@ inline fun <reified M> CoroutineScope.Patch(
  */
 fun CoroutineScope.Download(
         path: String,
-        method: RequestMethod = RequestMethod.GET,
         dir: String = NetConfig.app.externalCacheDir!!.absolutePath,
         tag: Any? = null,
         absolutePath: Boolean = false,
+        method: RequestMethod = RequestMethod.GET,
         block: UrlDownload.Api.() -> Unit = {}): Deferred<String> = async(Dispatchers.IO) {
 
     if (!isActive) throw CancellationException()
 
+    if (method == RequestMethod.POST || method == RequestMethod.DELETE || method == RequestMethod.PUT || method == RequestMethod.PATCH) {
+        throw UnsupportedOperationException("You should use [DownloadBody] function")
+    }
+
     val uid = coroutineContext[CoroutineExceptionHandler]
     coroutineContext[Job]?.invokeOnCompletion {
         if (it != null && it !is CancellationException) Canceler.cancel(uid) else Canceler.removeCancel(uid)
@@ -329,6 +331,39 @@ fun CoroutineScope.Download(
     }
 }
 
+/**
+ * 用于提交请求体下载文件(默认POST请求)
+ */
+fun CoroutineScope.DownloadBody(
+        path: String,
+        dir: String = NetConfig.app.externalCacheDir!!.absolutePath,
+        tag: Any? = null,
+        absolutePath: Boolean = false,
+        method: RequestMethod = RequestMethod.POST,
+        block: BodyDownload.Api.() -> Unit = {}): Deferred<String> = async(Dispatchers.IO) {
+
+    if (!isActive) throw CancellationException()
+
+    if (method == RequestMethod.GET || method == RequestMethod.HEAD || method == RequestMethod.OPTIONS || method == RequestMethod.TRACE) {
+        throw UnsupportedOperationException("You should use [Download] function")
+    }
+
+    val uid = coroutineContext[CoroutineExceptionHandler]
+    coroutineContext[Job]?.invokeOnCompletion {
+        if (it != null && it !is CancellationException) Canceler.cancel(uid) else Canceler.removeCancel(uid)
+    }
+
+    val realPath = if (absolutePath) path else (NetConfig.host + path)
+
+    val download = BodyDownload.newApi(Url.newBuilder(realPath).build(), method).directory(dir).tag(tag).uid(uid)
+
+    try {
+        download.apply(block).perform()
+    } catch (e: SocketException) {
+        throw CancellationException()
+    }
+}
+
 /**
  * 下载图片, 图片宽高要求要么同时指定要么同时不指定
  * 要求依赖 Glide
@@ -339,7 +374,7 @@ fun CoroutineScope.Download(
  * @param height Int 图片高度
  * @return Observable<File>
  */
-fun CoroutineScope.DownloadImg(url: String, with: Int = -1, height: Int = -1): Deferred<File> =
+fun CoroutineScope.DownloadImage(url: String, with: Int = -1, height: Int = -1): Deferred<File> =
         async(Dispatchers.IO) {
 
             val download = Glide.with(NetConfig.app).download(url)
@@ -551,33 +586,45 @@ inline fun <reified M> syncPatch(
 
 fun syncDownload(
         path: String,
-        directory: String = NetConfig.app.externalCacheDir!!.absolutePath,
+        dir: String = NetConfig.app.externalCacheDir!!.absolutePath,
         tag: Any? = null,
         absolutePath: Boolean = false,
+        method: RequestMethod = RequestMethod.GET,
         block: UrlDownload.Api.() -> Unit = {}): String {
-
+    if (method == RequestMethod.POST || method == RequestMethod.DELETE || method == RequestMethod.PUT || method == RequestMethod.PATCH) {
+        throw UnsupportedOperationException("You should use [syncDownloadBody] function")
+    }
     val realPath = if (absolutePath) path else (NetConfig.host + path)
+    val download = UrlDownload.newApi(Url.newBuilder(realPath).build(), method).directory(dir).tag(tag)
+    return download.apply(block).perform()
+}
 
-    val download = Kalle.Download.get(realPath).directory(directory).tag(tag)
-
+fun syncDownloadBody(
+        path: String,
+        dir: String = NetConfig.app.externalCacheDir!!.absolutePath,
+        tag: Any? = null,
+        absolutePath: Boolean = false,
+        method: RequestMethod = RequestMethod.GET,
+        block: BodyDownload.Api.() -> Unit = {}): String {
+    if (method == RequestMethod.GET || method == RequestMethod.HEAD || method == RequestMethod.OPTIONS || method == RequestMethod.TRACE) {
+        throw UnsupportedOperationException("You should use [syncDownload] function")
+    }
+    val realPath = if (absolutePath) path else (NetConfig.host + path)
+    val download = BodyDownload.newApi(Url.newBuilder(realPath).build(), method).directory(dir).tag(tag)
     return download.apply(block).perform()
 }
 
-fun Context.syncDownloadImg(url: String, with: Int = 0, height: Int = 0): File {
+fun Context.syncDownloadImage(url: String, with: Int = 0, height: Int = 0): File {
 
     Glide.with(this).downloadOnly()
-
     val download = Glide.with(this).download(url)
-
     val futureTarget = if (with == 0 && height == 0) {
         download.submit()
     } else {
         download.submit(with, height)
     }
-
     return futureTarget.get()
 }
-
 // </editor-fold>
 
 

+ 8 - 10
net/src/main/java/com/drake/net/scope/NetCoroutineScope.kt

@@ -37,9 +37,9 @@ open class NetCoroutineScope() : AndroidScope() {
     var animate: Boolean = false
 
     constructor(
-        lifecycleOwner: LifecycleOwner,
-        lifeEvent: Lifecycle.Event = Lifecycle.Event.ON_DESTROY
-    ) : this() {
+            lifecycleOwner: LifecycleOwner,
+            lifeEvent: Lifecycle.Event = Lifecycle.Event.ON_DESTROY
+               ) : this() {
         lifecycleOwner.lifecycle.addObserver(object : LifecycleEventObserver {
             override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
                 if (lifeEvent == event) cancel()
@@ -48,8 +48,8 @@ open class NetCoroutineScope() : AndroidScope() {
     }
 
     override fun launch(
-        block: suspend CoroutineScope.() -> Unit
-    ): NetCoroutineScope {
+            block: suspend CoroutineScope.() -> Unit
+                       ): NetCoroutineScope {
         launch(EmptyCoroutineContext) {
             start()
             if (onCache != null && isReadCache) {
@@ -94,11 +94,9 @@ open class NetCoroutineScope() : AndroidScope() {
      * @param animate 是否在缓存成功后依然显示加载动画
      * @param onCache 该作用域内的所有异常都算缓存读取失败, 不会吐司和打印任何错误
      */
-    fun cache(
-            error: Boolean = false,
-            animate: Boolean = false,
-            onCache: suspend CoroutineScope.() -> Unit
-             ): AndroidScope {
+    fun cache(error: Boolean = false,
+              animate: Boolean = false,
+              onCache: suspend CoroutineScope.() -> Unit): AndroidScope {
         this.animate = animate
         this.error = error
         this.onCache = onCache

+ 6 - 0
sample/build.gradle

@@ -25,6 +25,12 @@ android {
         }
     }
 
+    buildFeatures.dataBinding = true
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
 }
 
 dependencies {

+ 5 - 1
sample/src/main/java/com/drake/net/sample/base/App.kt

@@ -10,6 +10,7 @@ package com.drake.net.sample.base
 import android.app.Application
 import com.drake.net.cacheEnabled
 import com.drake.net.initNet
+import com.drake.net.sample.BR
 import com.drake.net.sample.R
 import com.drake.net.sample.callback.JsonConvert
 import com.drake.statelayout.StateConfig
@@ -30,7 +31,8 @@ class App : Application() {
         }
 
         // Net 网络初始化信息
-        initNet("http://192.168.1.222:80/") {
+        // initNet("http://192.168.1.222:80/") {
+        initNet("http://182.92.97.186/") {
             converter(JsonConvert()) // 自动解析JSON映射到实体类中, 转换器分为全局和单例, 覆盖生效(拦截器允许多个)
             cacheEnabled()
         }
@@ -43,5 +45,7 @@ class App : Application() {
             ClassicsFooter(context)
         }
 
+        com.drake.brv.BindingAdapter.modelId = BR.m
+
     }
 }

+ 1 - 1
sample/src/main/java/com/drake/net/sample/callback/GsonConvert.kt

@@ -11,7 +11,7 @@ import com.drake.net.convert.DefaultConvert
 import com.google.gson.GsonBuilder
 import java.lang.reflect.Type
 
-class GsonConvert : DefaultConvert(code = "code", message = "msg") {
+class GsonConvert : DefaultConvert(code = "code", message = "msg", success = "200") {
 
     override fun <S> String.parseBody(succeed: Type): S? {
         return GsonBuilder().serializeNulls().create().fromJson(this, succeed)

+ 1 - 1
sample/src/main/java/com/drake/net/sample/callback/JsonConvert.kt

@@ -11,7 +11,7 @@ import com.drake.net.convert.DefaultConvert
 import com.squareup.moshi.Moshi
 import java.lang.reflect.Type
 
-class JsonConvert : DefaultConvert(code = "code", message = "msg") {
+class JsonConvert : DefaultConvert(code = "code", message = "msg", success = "200") {
 
     override fun <S> String.parseBody(succeed: Type): S? {
         return Moshi.Builder().build().adapter<S>(succeed).fromJson(this)

+ 2 - 9
sample/src/main/java/com/drake/net/sample/mod/ListModel.kt

@@ -7,13 +7,6 @@
 
 package com.drake.net.sample.mod
 
-data class ListModel(
-        var code: Int,
-        var msg: String,
-        var data: Data
-                    ) {
-    data class Data(
-            var totalPage: Int,
-            var list: List<String>
-                   )
+data class ListModel(var code: Int, var msg: String, var data: Data) {
+    data class Data(var total: Int, var list: List<String>)
 }

+ 1 - 1
sample/src/main/java/com/drake/net/sample/mod/Model.kt

@@ -1,5 +1,5 @@
 package com.drake.net.sample.mod
 
 data class Model(var code: Int, var msg: String, var data: Data) {
-    data class Data(var info: String)
+    data class Data(var request_method: String)
 }

+ 2 - 9
sample/src/main/java/com/drake/net/sample/ui/fragment/AsyncTaskFragment.kt

@@ -15,10 +15,7 @@ import androidx.fragment.app.Fragment
 import com.drake.net.sample.R
 import com.drake.net.utils.scope
 import kotlinx.android.synthetic.main.fragment_async_task.*
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.async
-import kotlinx.coroutines.withContext
+import kotlinx.coroutines.*
 
 class AsyncTaskFragment : Fragment() {
 
@@ -36,13 +33,9 @@ class AsyncTaskFragment : Fragment() {
 
         scope {
             tv_fragment.text = withContext(Dispatchers.IO) {
+                delay(2000)
                 "结果"
             }
-//            withIO() // 抽出异步任务函数
-
-//            tv_fragment.text = async {
-//                "结果"
-//            }.await()
         }
     }
 

+ 5 - 4
sample/src/main/java/com/drake/net/sample/ui/fragment/ConfigDialogFragment.kt

@@ -12,11 +12,10 @@ import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import androidx.fragment.app.Fragment
-import com.drake.net.Get
+import com.drake.net.Post
 import com.drake.net.sample.R
 import com.drake.net.utils.scopeDialog
 import kotlinx.android.synthetic.main.fragment_config_dialog.*
-import kotlinx.coroutines.delay
 
 
 class ConfigDialogFragment : Fragment() {
@@ -33,8 +32,10 @@ class ConfigDialogFragment : Fragment() {
         super.onActivityCreated(savedInstanceState)
 
         scopeDialog {
-            delay(1000)
-            tv_fragment.text = Get<String>("dialog").await()
+            tv_fragment.text = Post<String>("dialog") {
+                param("u_name", "drake")
+                param("pwd", "123456")
+            }.await()
         }
     }
 }

+ 3 - 3
sample/src/main/java/com/drake/net/sample/ui/fragment/CoroutineScopeFragment.kt

@@ -25,9 +25,9 @@ import kotlinx.coroutines.delay
 class CoroutineScopeFragment : Fragment() {
 
     override fun onCreateView(
-        inflater: LayoutInflater, container: ViewGroup?,
-        savedInstanceState: Bundle?
-    ): View? {
+            inflater: LayoutInflater, container: ViewGroup?,
+            savedInstanceState: Bundle?
+                             ): View? {
         return inflater.inflate(R.layout.fragment_coroutine_scope, container, false)
     }
 

+ 6 - 5
sample/src/main/java/com/drake/net/sample/ui/fragment/CustomConvertFragment.kt

@@ -17,14 +17,15 @@ import com.drake.net.sample.R
 import com.drake.net.sample.callback.GsonConvert
 import com.drake.net.sample.mod.Model
 import com.drake.net.utils.scopeNetLife
+import kotlinx.android.synthetic.main.fragment_custom_convert.*
 
 
 class CustomConvertFragment : Fragment() {
 
     override fun onCreateView(
-        inflater: LayoutInflater, container: ViewGroup?,
-        savedInstanceState: Bundle?
-    ): View? {
+            inflater: LayoutInflater, container: ViewGroup?,
+            savedInstanceState: Bundle?
+                             ): View? {
 
         return inflater.inflate(R.layout.fragment_custom_convert, container, false)
     }
@@ -34,9 +35,9 @@ class CustomConvertFragment : Fragment() {
 
 
         scopeNetLife {
-            val model = Get<Model>("") {
+            tv_fragment.text = Get<Model>("api") {
                 converter(GsonConvert()) // 单例转换器, 此时会忽略全局转换器
-            }.await()
+            }.await().data.request_method
         }
 
     }

+ 1 - 1
sample/src/main/java/com/drake/net/sample/ui/fragment/DownloadFileFragment.kt

@@ -32,7 +32,7 @@ class DownloadFileFragment : Fragment() {
         super.onActivityCreated(savedInstanceState)
 
         scopeNetLife {
-            Download("download", dir = requireContext().filesDir.path) {
+            Download("download", requireContext().filesDir.path) {
                 // 下载进度回调
                 onProgress { progress, byteCount, speed ->
                     // 进度条

+ 10 - 5
sample/src/main/java/com/drake/net/sample/ui/fragment/DownloadImageFragment.kt

@@ -7,22 +7,25 @@
 
 package com.drake.net.sample.ui.fragment
 
+import android.net.Uri
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import androidx.fragment.app.Fragment
-import com.drake.net.DownloadImg
+import com.drake.net.DownloadImage
+import com.drake.net.NetConfig
 import com.drake.net.sample.R
 import com.drake.net.utils.scopeDialog
+import kotlinx.android.synthetic.main.fragment_download_image.*
 
 
 class DownloadImageFragment : Fragment() {
 
     override fun onCreateView(
-        inflater: LayoutInflater, container: ViewGroup?,
-        savedInstanceState: Bundle?
-    ): View? {
+            inflater: LayoutInflater, container: ViewGroup?,
+            savedInstanceState: Bundle?
+                             ): View? {
 
         return inflater.inflate(R.layout.fragment_download_image, container, false)
     }
@@ -31,7 +34,9 @@ class DownloadImageFragment : Fragment() {
         super.onActivityCreated(savedInstanceState)
 
         scopeDialog {
-            val file = DownloadImg("download/img", 200, 200).await()
+            val file = DownloadImage(NetConfig.host + "download/img", 100, 100).await()
+            val uri = Uri.fromFile(file)
+            iv_img.setImageURI(uri)
         }
     }
 }

+ 10 - 0
sample/src/main/java/com/drake/net/sample/ui/fragment/ErrorHandlerFragment.kt

@@ -12,7 +12,10 @@ import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import androidx.fragment.app.Fragment
+import com.drake.net.Get
 import com.drake.net.sample.R
+import com.drake.net.utils.scopeNetLife
+import kotlinx.android.synthetic.main.fragment_error_handler.*
 
 
 class ErrorHandlerFragment : Fragment() {
@@ -28,6 +31,13 @@ class ErrorHandlerFragment : Fragment() {
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
 
+        scopeNetLife {
+            // 该请求是错误的路径会在控制台打印出错误信息
+            Get<String>("error").await()
+        }.catch {
+            // 重写该函数后, 错误不会流到[NetConfig.onError]中的全局错误处理, 在App.kt中可以自定义该全局处理, 同时包含onStateError
+            tv_fragment.text = it.message
+        }
     }
 
 }

+ 1 - 1
sample/src/main/java/com/drake/net/sample/ui/fragment/ExceptionTraceFragment.kt

@@ -33,7 +33,7 @@ class ExceptionTraceFragment : Fragment() {
 
         scopeNetLife {
             // 这是一个错误的地址, 请查看LogCat的错误信息, 在[Convert]中你也可以进行自定义错误信息打印
-            tv_fragment.text = Get<String>("").await()
+            tv_fragment.text = Get<String>("error").await()
         }
     }
 

+ 8 - 1
sample/src/main/java/com/drake/net/sample/ui/fragment/InterceptorFragment.kt

@@ -12,7 +12,10 @@ import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import androidx.fragment.app.Fragment
+import com.drake.net.Get
 import com.drake.net.sample.R
+import com.drake.net.utils.scopeNetLife
+import kotlinx.android.synthetic.main.fragment_exception_trace.*
 
 
 class InterceptorFragment : Fragment() {
@@ -28,7 +31,11 @@ class InterceptorFragment : Fragment() {
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
 
-
+        scopeNetLife {
+            tv_fragment.text = Get<String>("api") {
+                // 拦截器只支持全局, 无法单例
+            }.await()
+        }
     }
 
 }

+ 12 - 0
sample/src/main/java/com/drake/net/sample/ui/fragment/ParallelNetworkFragment.kt

@@ -12,6 +12,9 @@ import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import androidx.fragment.app.Fragment
+import com.drake.net.Get
+import com.drake.net.Post
+import com.drake.net.Trace
 import com.drake.net.sample.R
 import com.drake.net.utils.scopeNetLife
 
@@ -31,6 +34,15 @@ class ParallelNetworkFragment : Fragment() {
 
         scopeNetLife {
 
+            // 同时发起三个请求
+            val deferred = Get<String>("api")
+            val deferred1 = Post<String>("api")
+            val deferred2 = Trace<String>("api")
+
+            // 同时接收三个请求数据
+            deferred.await()
+            deferred1.await()
+            deferred2.await()
         }
     }
 

+ 2 - 2
sample/src/main/java/com/drake/net/sample/ui/fragment/PullRefreshFragment.kt

@@ -35,7 +35,7 @@ class PullRefreshFragment : Fragment() {
         super.onActivityCreated(savedInstanceState)
 
         rv_pull.linear().setup {
-            addType<ListModel.Data>(R.layout.item_list)
+            addType<String>(R.layout.item_list)
         }
 
         page.onRefresh {
@@ -44,7 +44,7 @@ class PullRefreshFragment : Fragment() {
                     param("page", index)
                 }.await().data
                 addData(data.list) {
-                    index < data.totalPage
+                    index < data.total
                 }
             }
         }.autoRefresh()

+ 4 - 6
sample/src/main/java/com/drake/net/sample/ui/fragment/PushRefreshFragment.kt

@@ -24,11 +24,9 @@ import kotlinx.android.synthetic.main.fragment_push_refresh.*
 
 class PushRefreshFragment : Fragment() {
 
-    override fun onCreateView(
-            inflater: LayoutInflater, container: ViewGroup?,
-            savedInstanceState: Bundle?
-                             ): View? {
-
+    override fun onCreateView(inflater: LayoutInflater,
+                              container: ViewGroup?,
+                              savedInstanceState: Bundle?): View? {
         return inflater.inflate(R.layout.fragment_push_refresh, container, false)
     }
 
@@ -36,7 +34,7 @@ class PushRefreshFragment : Fragment() {
         super.onActivityCreated(savedInstanceState)
 
         rv_push.linear().setup {
-            addType<ListModel.Data>(R.layout.item_list)
+            addType<String>(R.layout.item_list)
         }
 
         page.onRefresh {

+ 15 - 5
sample/src/main/java/com/drake/net/sample/ui/fragment/ReadCacheFragment.kt

@@ -8,20 +8,24 @@
 package com.drake.net.sample.ui.fragment
 
 import android.os.Bundle
+import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import androidx.fragment.app.Fragment
+import com.drake.net.Get
+import com.drake.net.Post
 import com.drake.net.sample.R
 import com.drake.net.utils.scopeNetLife
+import com.yanzhenjie.kalle.simple.cache.CacheMode
+import kotlinx.android.synthetic.main.fragment_read_cache.*
 
 
 class ReadCacheFragment : Fragment() {
 
-    override fun onCreateView(
-        inflater: LayoutInflater, container: ViewGroup?,
-        savedInstanceState: Bundle?
-    ): View? {
+    override fun onCreateView(inflater: LayoutInflater,
+                              container: ViewGroup?,
+                              savedInstanceState: Bundle?): View? {
 
         return inflater.inflate(R.layout.fragment_read_cache, container, false)
     }
@@ -30,7 +34,13 @@ class ReadCacheFragment : Fragment() {
         super.onActivityCreated(savedInstanceState)
 
         scopeNetLife {
-
+            // 然后执行这里(网络请求)
+            tv_fragment.text = Post<String>("api", cache = CacheMode.NETWORK_YES_THEN_WRITE_CACHE).await()
+            Log.d("日志", "网络请求")
+        }.cache {
+            // 先执行这里(仅读缓存), 任何异常都视为读取缓存失败
+            tv_fragment.text = Get<String>("api", cache = CacheMode.READ_CACHE).await()
+            Log.d("日志", "读取缓存")
         }
     }
 

+ 3 - 4
sample/src/main/java/com/drake/net/sample/ui/fragment/StateLayoutFragment.kt

@@ -20,10 +20,9 @@ import kotlinx.android.synthetic.main.fragment_state_layout.*
 
 class StateLayoutFragment : Fragment() {
 
-    override fun onCreateView(
-        inflater: LayoutInflater, container: ViewGroup?,
-        savedInstanceState: Bundle?
-    ): View? {
+    override fun onCreateView(inflater: LayoutInflater,
+                              container: ViewGroup?,
+                              savedInstanceState: Bundle?): View? {
 
         return inflater.inflate(R.layout.fragment_state_layout, container, false)
     }

+ 1 - 0
sample/src/main/java/com/drake/net/sample/ui/fragment/SwitchDispatcherFragment.kt

@@ -41,6 +41,7 @@ class SwitchDispatcherFragment : Fragment() {
                     "异步调度器切换到主线程"
                 }
             }
+
             val data = withIO {
                 "主线程切换到IO调度器"
             }

+ 29 - 10
sample/src/main/java/com/drake/net/sample/ui/fragment/UploadFileFragment.kt

@@ -17,14 +17,16 @@ import com.drake.net.sample.R
 import com.drake.net.utils.scopeNetLife
 import com.yanzhenjie.kalle.FormBody
 import kotlinx.android.synthetic.main.fragment_upload_file.*
+import java.io.File
+import java.io.FileOutputStream
 
 
 class UploadFileFragment : Fragment() {
 
     override fun onCreateView(
-        inflater: LayoutInflater, container: ViewGroup?,
-        savedInstanceState: Bundle?
-    ): View? {
+            inflater: LayoutInflater, container: ViewGroup?,
+            savedInstanceState: Bundle?
+                             ): View? {
 
         return inflater.inflate(R.layout.fragment_upload_file, container, false)
     }
@@ -33,26 +35,43 @@ class UploadFileFragment : Fragment() {
         super.onActivityCreated(savedInstanceState)
 
         scopeNetLife {
-            val fileDir = requireContext().cacheDir.path
 
+            Post<String>("upload", requireContext().cacheDir.path) {
 
-            Post<String>("download/img", fileDir) {
+                val saveFile = getFile()
+                val form = FormBody.newBuilder().file("file", saveFile).build()
+
+                form.onProgress { origin, progress ->
 
-                val form = FormBody.newBuilder().build().onProgress { origin, progress ->
                     seek.progress = progress // 进度条
                     // 格式化显示单位
-                    val downloadSize =
-                        android.text.format.Formatter.formatFileSize(requireContext(), 23)
-                    val downloadSpeed =
-                        android.text.format.Formatter.formatFileSize(requireContext(), 23)
+                    val downloadSize = android.text.format.Formatter.formatFileSize(requireContext(), 23)
+                    val downloadSpeed = android.text.format.Formatter.formatFileSize(requireContext(), 23)
 
                     // 显示下载信息
                     tv_progress.text = "上传进度: $progress% 已下载: $downloadSize 下载速度: $downloadSpeed"
                 }
 
+                body(form)
+
             }.await()
         }
 
     }
 
+    /**
+     * @return 上传的文件
+     */
+    private fun getFile(): File {
+        val open = resources.assets.open("upload_file.jpg")
+        val saveFile = File("${requireContext().filesDir.path}/upload_file.jpg")
+        val buffer = ByteArray(open.available())
+        open.read(buffer)
+        val fileOutputStream = FileOutputStream(saveFile)
+        fileOutputStream.write(buffer)
+        open.close()
+        fileOutputStream.close()
+        return saveFile
+    }
+
 }

+ 8 - 0
sample/src/main/res/drawable/bg_card.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <stroke
+        android:width="2dp"
+        android:color="@color/stroke" />
+    <corners android:radius="4dp" />
+    <solid android:color="@color/white" />
+</shape>

+ 2 - 1
sample/src/main/res/layout/fragment_async_task.xml

@@ -16,6 +16,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:gravity="center"
-        android:text="AsyncTaskFragment" />
+        android:textStyle="bold"
+        android:text="等待2秒" />
 
 </FrameLayout>

+ 1 - 0
sample/src/main/res/layout/fragment_coroutine_scope.xml

@@ -16,6 +16,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:gravity="center"
+        android:textStyle="bold"
         android:text="等待2秒" />
 
 </FrameLayout>

+ 2 - 1
sample/src/main/res/layout/fragment_custom_convert.xml

@@ -16,6 +16,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:gravity="center"
-        android:text="CustomConvertFragment" />
+        android:text="等待请求完成"
+        android:textStyle="bold" />
 
 </FrameLayout>

+ 6 - 6
sample/src/main/res/layout/fragment_download_image.xml

@@ -11,11 +11,11 @@
     android:layout_height="match_parent"
     tools:context=".ui.fragment.DownloadImageFragment">
 
-    <TextView
-        android:id="@+id/tv_fragment"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:gravity="center"
-        android:text="DownloadImageFrament" />
+    <ImageView
+        android:id="@+id/iv_img"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:gravity="center" />
 
 </FrameLayout>

+ 2 - 1
sample/src/main/res/layout/fragment_error_handler.xml

@@ -16,6 +16,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:gravity="center"
-        android:text="ErrorHandlerFragment" />
+        android:textStyle="bold"
+        android:text="LogCat查看请求返回的错误信息" />
 
 </FrameLayout>

+ 2 - 1
sample/src/main/res/layout/fragment_exception_trace.xml

@@ -16,6 +16,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:gravity="center"
-        android:text="ExceptionTraceFragment" />
+        android:textStyle="bold"
+        android:text="查看控制台" />
 
 </FrameLayout>

+ 2 - 1
sample/src/main/res/layout/fragment_interceptor.xml

@@ -16,6 +16,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:gravity="center"
-        android:text="IntercepterFragment" />
+        android:textStyle="bold"
+        android:text="拦截器仅支持全局" />
 
 </FrameLayout>

+ 2 - 1
sample/src/main/res/layout/fragment_parallel_network.xml

@@ -16,6 +16,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:gravity="center"
-        android:text="ParallelNetworkFragment" />
+        android:textStyle="bold"
+        android:text="查看Demo源码" />
 
 </FrameLayout>

+ 3 - 1
sample/src/main/res/layout/fragment_read_cache.xml

@@ -14,8 +14,10 @@
     <TextView
         android:id="@+id/tv_fragment"
         android:layout_width="match_parent"
+        android:padding="16dp"
         android:layout_height="match_parent"
+        android:textStyle="bold"
         android:gravity="center"
-        android:text="PreReadCacheFragment" />
+        android:text="仅请求成功一次后才会存在缓存" />
 
 </FrameLayout>

+ 1 - 0
sample/src/main/res/layout/fragment_request_method.xml

@@ -10,6 +10,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:gravity="center"
+        android:textStyle="bold"
         android:text="点击右上角菜单开始请求" />
 
 </FrameLayout>

+ 2 - 1
sample/src/main/res/layout/fragment_state_layout.xml

@@ -14,9 +14,10 @@
 
     <TextView
         android:id="@+id/tv_fragment"
+        android:padding="32dp"
+        android:textStyle="bold"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:gravity="center"
         android:text="内容" />
 
 </com.drake.statelayout.StateLayout>

+ 2 - 1
sample/src/main/res/layout/fragment_switch_dispatcher.xml

@@ -16,6 +16,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:gravity="center"
-        android:text="SwitchDispatcherFragment" />
+        android:textStyle="bold"
+        android:text="查看Demo源码" />
 
 </FrameLayout>

+ 33 - 4
sample/src/main/res/layout/item_list.xml

@@ -5,8 +5,37 @@
   ~ Date:6/23/20 4:40 PM
   -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
 
-</LinearLayout>
+    <data>
+
+        <variable
+            name="m"
+            type="String" />
+    </data>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <FrameLayout
+            android:id="@+id/item"
+            android:layout_width="match_parent"
+            android:layout_height="100dp"
+            android:layout_margin="16dp"
+            android:background="@drawable/bg_card"
+            android:foreground="?selectableItemBackgroundBorderless">
+
+            <TextView
+                android:text="@{m}"
+                tools:text="1"
+                android:textStyle="bold"
+                android:gravity="center"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent" />
+
+        </FrameLayout>
+
+    </LinearLayout>
+</layout>

+ 1 - 1
sample/src/main/res/navigation/nav_main.xml

@@ -9,7 +9,7 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/main"
-    app:startDestination="@id/download_file">
+    app:startDestination="@id/download_img">
 
     <fragment
         android:id="@+id/async_task"

+ 1 - 0
sample/src/main/res/values/colors.xml

@@ -7,6 +7,7 @@
     <!--辅助颜色-->
     <color name="divider">#339e9e9e</color>
     <color name="windowBackground">#f5f5f5</color>
+    <color name="stroke">#737373</color>
 
     <!--字体颜色-->
     <color name="textColor">#333333</color>