drake пре 4 година
родитељ
комит
3a95d1c7fb
85 измењених фајлова са 2290 додато и 195 уклоњено
  1. 2 2
      build.gradle
  2. 2 2
      gradle/wrapper/gradle-wrapper.properties
  3. 4 0
      kalle/src/main/java/com/yanzhenjie/kalle/CancelerManager.java
  4. 3 0
      kalle/src/main/java/com/yanzhenjie/kalle/simple/Work.java
  5. 9 1
      net/src/main/AndroidManifest.xml
  6. 181 61
      net/src/main/java/com/drake/net/Net.kt
  7. 3 3
      net/src/main/java/com/drake/net/convert/DefaultConvert.kt
  8. 10 0
      net/src/main/res/xml/network_security_config.xml
  9. 15 12
      sample/build.gradle
  10. 3 3
      sample/src/main/AndroidManifest.xml
  11. BIN
      sample/src/main/assets/upload_file.jpg
  12. 0 36
      sample/src/main/java/com/drake/net/sample/App.kt
  13. 0 12
      sample/src/main/java/com/drake/net/sample/Model.kt
  14. 47 0
      sample/src/main/java/com/drake/net/sample/base/App.kt
  15. 19 0
      sample/src/main/java/com/drake/net/sample/callback/GsonConvert.kt
  16. 2 2
      sample/src/main/java/com/drake/net/sample/callback/JsonConvert.kt
  17. 5 0
      sample/src/main/java/com/drake/net/sample/mod/Model.kt
  18. 0 20
      sample/src/main/java/com/drake/net/sample/ui/activity/Main2Activity.kt
  19. 20 12
      sample/src/main/java/com/drake/net/sample/ui/activity/MainActivity.kt
  20. 60 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/AsyncTaskFragment.kt
  21. 40 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/ConfigDialogFragment.kt
  22. 64 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/CoroutineScopeFragment.kt
  23. 44 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/CustomConvertFragment.kt
  24. 14 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/Demo.java
  25. 76 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/DownloadFileFragment.kt
  26. 37 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/DownloadImageFragment.kt
  27. 33 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/ErrorHandlerFragment.kt
  28. 41 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/ExceptionTraceFragment.kt
  29. 34 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/InterceptorFragment.kt
  30. 37 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/ParallelNetworkFragment.kt
  31. 34 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/PullRefreshFragment.kt
  32. 33 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/PushRefreshFragment.kt
  33. 37 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/ReadCacheFragment.kt
  34. 103 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/RequestMethodFragment.kt
  35. 46 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/StateLayoutFragment.kt
  36. 66 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/SuperIntervalFragment.kt
  37. 50 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/SwitchDispatcherFragment.kt
  38. 52 0
      sample/src/main/java/com/drake/net/sample/ui/fragment/UploadFileFragment.kt
  39. BIN
      sample/src/main/res/drawable/header.png
  40. 16 0
      sample/src/main/res/drawable/ic_async_task.xml
  41. 16 0
      sample/src/main/res/drawable/ic_config_dialog.xml
  42. 16 0
      sample/src/main/res/drawable/ic_convert.xml
  43. 16 0
      sample/src/main/res/drawable/ic_download_file.xml
  44. 16 0
      sample/src/main/res/drawable/ic_download_img.xml
  45. 16 0
      sample/src/main/res/drawable/ic_error_handler.xml
  46. 16 0
      sample/src/main/res/drawable/ic_exception_trace.xml
  47. 16 0
      sample/src/main/res/drawable/ic_interceptor.xml
  48. 16 0
      sample/src/main/res/drawable/ic_interval.xml
  49. 16 0
      sample/src/main/res/drawable/ic_menu.xml
  50. 16 0
      sample/src/main/res/drawable/ic_parallel_network.xml
  51. 16 0
      sample/src/main/res/drawable/ic_pull_refresh.xml
  52. 16 0
      sample/src/main/res/drawable/ic_push_refresh.xml
  53. 16 0
      sample/src/main/res/drawable/ic_read_cache.xml
  54. 16 0
      sample/src/main/res/drawable/ic_request_method.xml
  55. 16 0
      sample/src/main/res/drawable/ic_scope.xml
  56. 16 0
      sample/src/main/res/drawable/ic_state_layout.xml
  57. 16 0
      sample/src/main/res/drawable/ic_switch_dispatcher.xml
  58. 16 0
      sample/src/main/res/drawable/ic_upload_file.xml
  59. 27 11
      sample/src/main/res/layout/activity_main.xml
  60. 0 14
      sample/src/main/res/layout/activity_main2.xml
  61. 21 0
      sample/src/main/res/layout/fragment_async_task.xml
  62. 23 0
      sample/src/main/res/layout/fragment_config_dialog.xml
  63. 21 0
      sample/src/main/res/layout/fragment_coroutine_scope.xml
  64. 21 0
      sample/src/main/res/layout/fragment_custom_convert.xml
  65. 40 0
      sample/src/main/res/layout/fragment_download_file.xml
  66. 21 0
      sample/src/main/res/layout/fragment_download_image.xml
  67. 21 0
      sample/src/main/res/layout/fragment_error_handler.xml
  68. 21 0
      sample/src/main/res/layout/fragment_exception_trace.xml
  69. 21 0
      sample/src/main/res/layout/fragment_interceptor.xml
  70. 21 0
      sample/src/main/res/layout/fragment_parallel_network.xml
  71. 21 0
      sample/src/main/res/layout/fragment_pull_refresh.xml
  72. 21 0
      sample/src/main/res/layout/fragment_push_refresh.xml
  73. 21 0
      sample/src/main/res/layout/fragment_read_cache.xml
  74. 15 0
      sample/src/main/res/layout/fragment_request_method.xml
  75. 22 0
      sample/src/main/res/layout/fragment_state_layout.xml
  76. 23 0
      sample/src/main/res/layout/fragment_super_interval.xml
  77. 21 0
      sample/src/main/res/layout/fragment_switch_dispatcher.xml
  78. 40 0
      sample/src/main/res/layout/fragment_upload_file.xml
  79. 49 0
      sample/src/main/res/layout/layout_drawer_nav_header.xml
  80. 28 0
      sample/src/main/res/menu/menu_interval.xml
  81. 84 0
      sample/src/main/res/menu/menu_main.xml
  82. 33 0
      sample/src/main/res/menu/menu_request_method.xml
  83. 106 0
      sample/src/main/res/navigation/nav_main.xml
  84. 17 3
      sample/src/main/res/values/colors.xml
  85. 1 1
      sample/src/main/res/values/styles.xml

+ 2 - 2
build.gradle

@@ -8,7 +8,7 @@ buildscript {
         brv_version = '1.2.12'
         coroutine_version = '1.3.0'
         glide_version = '4.9.0'
-        room_version = "2.2.3"
+        room_version = "2.2.5"
     }
 
     repositories {
@@ -17,7 +17,7 @@ buildscript {
 
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.5.3'
+        classpath 'com.android.tools.build:gradle:3.6.3'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
         classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
     }

+ 2 - 2
gradle/wrapper/gradle-wrapper.properties

@@ -1,6 +1,6 @@
-#Sun Sep 15 22:43:32 PHT 2019
+#Thu Apr 16 16:41:43 CST 2020
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip

+ 4 - 0
kalle/src/main/java/com/yanzhenjie/kalle/CancelerManager.java

@@ -18,6 +18,8 @@ package com.yanzhenjie.kalle;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
+import static android.util.Log.d;
+
 /**
  * Created by Zhenjie Yan on 2018/2/27.
  */
@@ -57,7 +59,9 @@ public class CancelerManager {
         for (Map.Entry<Request, Canceller> entry : mCancelMap.entrySet()) {
             Request request = entry.getKey();
             Object oldTag = request.tag();
+            d("日志", "(CancelerManager.java:62)    ");
             if ((tag == oldTag) || (tag != null && tag.equals(oldTag))) {
+                d("日志", "(CancelerManager.java:61)    ");
                 entry.getValue().cancel();
             }
         }

+ 3 - 0
kalle/src/main/java/com/yanzhenjie/kalle/simple/Work.java

@@ -21,6 +21,8 @@ import java.util.concurrent.CancellationException;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.FutureTask;
 
+import static android.util.Log.d;
+
 /**
  * Created by Zhenjie Yan on 2018/2/13.
  */
@@ -69,6 +71,7 @@ final class Work<T extends SimpleRequest, S, F> extends FutureTask<Result<S, F>>
     @Override
     public void cancel() {
         cancel(true);
+        d("日志", "(Work.java:72)    取消");
         mWorker.cancel();
     }
 }

+ 9 - 1
net/src/main/AndroidManifest.xml

@@ -1 +1,9 @@
-<manifest package="com.drake.net" />
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.drake.net">
+
+    <application
+        android:networkSecurityConfig="@xml/network_security_config"
+        tools:targetApi="n" />
+
+</manifest>

+ 181 - 61
net/src/main/java/com/drake/net/Net.kt

@@ -5,7 +5,7 @@
  * Date:9/16/19 12:54 AM
  */
 
-@file:Suppress("unused")
+@file:Suppress("unused", "FunctionName")
 
 package com.drake.net
 
@@ -13,6 +13,8 @@ import android.content.Context
 import com.bumptech.glide.Glide
 import com.drake.net.error.ResponseException
 import com.yanzhenjie.kalle.Kalle
+import com.yanzhenjie.kalle.RequestMethod
+import com.yanzhenjie.kalle.Url
 import com.yanzhenjie.kalle.download.UrlDownload
 import com.yanzhenjie.kalle.simple.SimpleBodyRequest
 import com.yanzhenjie.kalle.simple.SimpleUrlRequest
@@ -32,81 +34,112 @@ import java.io.File
  * @return Observable<M> 结果会在主线程
  */
 
-inline fun <reified M> CoroutineScope.get(
+inline fun <reified M> CoroutineScope.Get(
     path: String,
     tag: Any? = null,
     cache: CacheMode = CacheMode.HTTP,
     absolutePath: Boolean = false,
     noinline block: (SimpleUrlRequest.Api.() -> Unit)? = null
 ): Deferred<M> = async(Dispatchers.IO) {
+    _submitUrl<M>(RequestMethod.GET, path, tag, cache, absolutePath, block)
+}
 
-    val request = Kalle.get(if (absolutePath) path else (NetConfig.host + path))
-        .tag(tag)
-        .cacheKey(path)
-        .cacheMode(cache)
 
-    val response = if (block == null) {
-        request.perform(M::class.java, ResponseException::class.java)
-    } else {
-        request.apply(block).perform<M, String>(M::class.java, ResponseException::class.java)
-    }
+inline fun <reified M> CoroutineScope.Post(
+    path: String,
+    tag: Any? = null,
+    cache: CacheMode = CacheMode.HTTP,
+    absolutePath: Boolean = false,
+    noinline block: (SimpleBodyRequest.Api.() -> Unit)? = null
+): Deferred<M> = async(Dispatchers.IO) {
+    _submitBody<M>(RequestMethod.POST, path, tag, cache, absolutePath, block)
+}
 
-    if (response.isSucceed) {
-        response.success!!
-    } else {
-        throw response.failure as ResponseException
-    }
+inline fun <reified M> CoroutineScope.Head(
+    path: String,
+    tag: Any? = null,
+    cache: CacheMode = CacheMode.HTTP,
+    absolutePath: Boolean = false,
+    noinline block: (SimpleUrlRequest.Api.() -> Unit)? = null
+): Deferred<M> = async(Dispatchers.IO) {
+    _submitUrl<M>(RequestMethod.HEAD, path, tag, cache, absolutePath, block)
+}
+
+inline fun <reified M> CoroutineScope.Options(
+    path: String,
+    tag: Any? = null,
+    cache: CacheMode = CacheMode.HTTP,
+    absolutePath: Boolean = false,
+    noinline block: (SimpleUrlRequest.Api.() -> Unit)? = null
+): Deferred<M> = async(Dispatchers.IO) {
+    _submitUrl<M>(RequestMethod.OPTIONS, path, tag, cache, absolutePath, block)
 }
 
 
-inline fun <reified M> CoroutineScope.post(
+inline fun <reified M> CoroutineScope.Trace(
     path: String,
     tag: Any? = null,
     cache: CacheMode = CacheMode.HTTP,
     absolutePath: Boolean = false,
-    noinline block: (SimpleBodyRequest.Api.() -> Unit)? = null
+    noinline block: (SimpleUrlRequest.Api.() -> Unit)? = null
 ): Deferred<M> = async(Dispatchers.IO) {
+    _submitUrl<M>(RequestMethod.TRACE, path, tag, cache, absolutePath, block)
+}
 
-    val request =
-        Kalle.post(if (absolutePath) path else (NetConfig.host + path))
-            .tag(tag)
-            .cacheKey(path)
-            .cacheMode(cache)
+inline fun <reified M> CoroutineScope.Delete(
+    path: String,
+    tag: Any? = null,
+    cache: CacheMode = CacheMode.HTTP,
+    absolutePath: Boolean = false,
+    noinline block: (SimpleBodyRequest.Api.() -> Unit)? = null
+): Deferred<M> = async(Dispatchers.IO) {
+    _submitBody<M>(RequestMethod.DELETE, path, tag, cache, absolutePath, block)
+}
 
-    val response = if (block == null) {
-        request.perform<M, String>(M::class.java, ResponseException::class.java)
-    } else {
-        request.apply(block).perform<M, String>(M::class.java, ResponseException::class.java)
-    }
+inline fun <reified M> CoroutineScope.Put(
+    path: String,
+    tag: Any? = null,
+    cache: CacheMode = CacheMode.HTTP,
+    absolutePath: Boolean = false,
+    noinline block: (SimpleBodyRequest.Api.() -> Unit)? = null
+): Deferred<M> = async(Dispatchers.IO) {
+    _submitBody<M>(RequestMethod.PUT, path, tag, cache, absolutePath, block)
+}
 
-    if (response.isSucceed) {
-        response.success!!
-    } else {
-        throw response.failure as ResponseException
-    }
+inline fun <reified M> CoroutineScope.Patch(
+    path: String,
+    tag: Any? = null,
+    cache: CacheMode = CacheMode.HTTP,
+    absolutePath: Boolean = false,
+    noinline block: (SimpleBodyRequest.Api.() -> Unit)? = null
+): Deferred<M> = async(Dispatchers.IO) {
+    _submitBody<M>(RequestMethod.PATCH, path, tag, cache, absolutePath, block)
 }
 
 
 /**
  * 下载文件
  *
- * @param path String 网络路径, 非绝对路径会加上HOST[NetConfig.host]为前缀
- * @param directory String 下载文件存放目录 {默认存在android/data/packageName/cache目录}
+ * @param path  网络路径, 非绝对路径会加上HOST[NetConfig.host]为前缀
+ * @param method 请求方式, 默认GET
+ * @param dir  下载文件存放目录 {默认存在android/data/packageName/cache目录}
  * @param tag 可以传递对象给Request请求
- * @param absolutePath Boolean 下载链接是否是绝对路径
+ * @param absolutePath  下载链接是否是绝对路径
  * @param block 请求参数
- * @return Observable<String> 结果会在主线程
  */
-fun CoroutineScope.download(
+fun CoroutineScope.Download(
     path: String,
-    directory: String = NetConfig.app.externalCacheDir!!.absolutePath,
+    method: RequestMethod = RequestMethod.GET,
+    dir: String = NetConfig.app.externalCacheDir!!.absolutePath,
     tag: Any? = null,
     absolutePath: Boolean = false,
     block: (UrlDownload.Api.() -> Unit)? = null
 ): Deferred<String> = async(Dispatchers.IO) {
+
     val realPath = if (absolutePath) path else (NetConfig.host + path)
 
-    val download = Kalle.Download.get(realPath).directory(directory).tag(tag)
+    val download =
+        UrlDownload.newApi(Url.newBuilder(realPath).build(), method).directory(dir).tag(tag)
 
     if (isActive) {
         if (block == null) {
@@ -119,7 +152,6 @@ fun CoroutineScope.download(
     }
 }
 
-
 /**
  * 下载图片, 图片宽高要求要么同时指定要么同时不指定
  *
@@ -129,16 +161,13 @@ fun CoroutineScope.download(
  * @param height Int 图片高度
  * @return Observable<File>
  */
-fun CoroutineScope.downImage(
-    context: Context,
+fun CoroutineScope.DownloadImg(
     url: String,
     with: Int = -1,
     height: Int = -1
 ): Deferred<File> = async(Dispatchers.IO) {
 
-    Glide.with(context).downloadOnly()
-
-    val download = Glide.with(context).download(url)
+    val download = Glide.with(NetConfig.app).download(url)
 
     val futureTarget = if (with == -1 && height == -1) {
         download.submit()
@@ -149,21 +178,23 @@ fun CoroutineScope.downImage(
     futureTarget.get()
 }
 
-// </editor-fold>
-
-// <editor-fold desc="同步请求">
 
-inline fun <reified M> syncGet(
+inline fun <reified M> _submitBody(
+    method: RequestMethod,
     path: String,
     tag: Any? = null,
     cache: CacheMode = CacheMode.HTTP,
     absolutePath: Boolean = false,
-    noinline block: (SimpleUrlRequest.Api.() -> Unit)? = null
+    noinline block: (SimpleBodyRequest.Api.() -> Unit)? = null
 ): M {
 
-    val request =
-        Kalle.get(if (absolutePath) path else (NetConfig.host + path)).tag(tag).cacheKey(path)
-            .cacheMode(cache)
+    val realPath = if (absolutePath) path else (NetConfig.host + path)
+
+    val request = SimpleBodyRequest.newApi(Url.newBuilder(realPath).build(), method)
+        .tag(tag)
+        .cacheKey(path)
+        .cacheMode(cache)
+
     val response = if (block == null) {
         request.perform(M::class.java, ResponseException::class.java)
     } else {
@@ -177,19 +208,24 @@ inline fun <reified M> syncGet(
     }
 }
 
-inline fun <reified M> syncPost(
+inline fun <reified M> _submitUrl(
+    method: RequestMethod,
     path: String,
     tag: Any? = null,
     cache: CacheMode = CacheMode.HTTP,
     absolutePath: Boolean = false,
-    noinline block: (SimpleBodyRequest.Api.() -> Unit)? = null
+    noinline block: (SimpleUrlRequest.Api.() -> Unit)? = null
 ): M {
 
-    val request =
-        Kalle.post(if (absolutePath) path else (NetConfig.host + path)).tag(tag).cacheKey(path)
-            .cacheMode(cache)
+    val realPath = if (absolutePath) path else (NetConfig.host + path)
+
+    val request = SimpleUrlRequest.newApi(Url.newBuilder(realPath).build(), method)
+        .tag(tag)
+        .cacheKey(path)
+        .cacheMode(cache)
+
     val response = if (block == null) {
-        request.perform<M, String>(M::class.java, ResponseException::class.java)
+        request.perform(M::class.java, ResponseException::class.java)
     } else {
         request.apply(block).perform<M, String>(M::class.java, ResponseException::class.java)
     }
@@ -201,6 +237,90 @@ inline fun <reified M> syncPost(
     }
 }
 
+// </editor-fold>
+
+// <editor-fold desc="同步请求">
+
+inline fun <reified M> syncGet(
+    path: String,
+    tag: Any? = null,
+    cache: CacheMode = CacheMode.HTTP,
+    absolutePath: Boolean = false,
+    noinline block: (SimpleUrlRequest.Api.() -> Unit)? = null
+): M {
+    return _submitUrl<M>(RequestMethod.GET, path, tag, cache, absolutePath, block)
+}
+
+inline fun <reified M> syncPost(
+    path: String,
+    tag: Any? = null,
+    cache: CacheMode = CacheMode.HTTP,
+    absolutePath: Boolean = false,
+    noinline block: (SimpleBodyRequest.Api.() -> Unit)? = null
+): M {
+    return _submitBody<M>(RequestMethod.POST, path, tag, cache, absolutePath, block)
+}
+
+inline fun <reified M> syncHead(
+    path: String,
+    tag: Any? = null,
+    cache: CacheMode = CacheMode.HTTP,
+    absolutePath: Boolean = false,
+    noinline block: (SimpleUrlRequest.Api.() -> Unit)? = null
+): M {
+    return _submitUrl<M>(RequestMethod.HEAD, path, tag, cache, absolutePath, block)
+}
+
+inline fun <reified M> syncOptions(
+    path: String,
+    tag: Any? = null,
+    cache: CacheMode = CacheMode.HTTP,
+    absolutePath: Boolean = false,
+    noinline block: (SimpleUrlRequest.Api.() -> Unit)? = null
+): M {
+    return _submitUrl<M>(RequestMethod.OPTIONS, path, tag, cache, absolutePath, block)
+}
+
+inline fun <reified M> syncTrace(
+    path: String,
+    tag: Any? = null,
+    cache: CacheMode = CacheMode.HTTP,
+    absolutePath: Boolean = false,
+    noinline block: (SimpleUrlRequest.Api.() -> Unit)? = null
+): M {
+    return _submitUrl<M>(RequestMethod.TRACE, path, tag, cache, absolutePath, block)
+}
+
+inline fun <reified M> syncDelete(
+    path: String,
+    tag: Any? = null,
+    cache: CacheMode = CacheMode.HTTP,
+    absolutePath: Boolean = false,
+    noinline block: (SimpleBodyRequest.Api.() -> Unit)? = null
+): M {
+    return _submitBody<M>(RequestMethod.DELETE, path, tag, cache, absolutePath, block)
+}
+
+inline fun <reified M> syncPut(
+    path: String,
+    tag: Any? = null,
+    cache: CacheMode = CacheMode.HTTP,
+    absolutePath: Boolean = false,
+    noinline block: (SimpleBodyRequest.Api.() -> Unit)? = null
+): M {
+    return _submitBody<M>(RequestMethod.PUT, path, tag, cache, absolutePath, block)
+}
+
+inline fun <reified M> syncPatch(
+    path: String,
+    tag: Any? = null,
+    cache: CacheMode = CacheMode.HTTP,
+    absolutePath: Boolean = false,
+    noinline block: (SimpleBodyRequest.Api.() -> Unit)? = null
+): M {
+    return _submitBody<M>(RequestMethod.PATCH, path, tag, cache, absolutePath, block)
+}
+
 fun syncDownload(
     path: String,
     directory: String = NetConfig.app.externalCacheDir!!.absolutePath,
@@ -220,7 +340,7 @@ fun syncDownload(
     }
 }
 
-fun Context.syncDownImage(url: String, with: Int = 0, height: Int = 0): File {
+fun Context.syncDownloadImg(url: String, with: Int = 0, height: Int = 0): File {
 
     Glide.with(this).downloadOnly()
 

+ 3 - 3
net/src/main/java/com/drake/net/convert/DefaultConvert.kt

@@ -29,9 +29,9 @@ import java.lang.reflect.Type
  */
 @Suppress("UNCHECKED_CAST")
 abstract class DefaultConvert(
-        val success: String = "0",
-        val code: String = "code",
-        val message: String = "message"
+    val success: String = "0",
+    val code: String = "code",
+    val message: String = "msg"
 ) : Converter {
 
     override fun <S, F> convert(

+ 10 - 0
net/src/main/res/xml/network_security_config.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 7:13 AM
+  -->
+
+<network-security-config>
+    <base-config cleartextTrafficPermitted="true" />
+</network-security-config>

+ 15 - 12
sample/build.gradle

@@ -14,6 +14,7 @@ android {
         minSdkVersion 19
         targetSdkVersion 29
         versionCode 1
+        multiDexEnabled true
         versionName "1.0"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
     }
@@ -32,31 +33,33 @@ dependencies {
     implementation "androidx.appcompat:appcompat:1.1.0"
     implementation "androidx.core:core-ktx:1.2.0"
     implementation "androidx.constraintlayout:constraintlayout:1.1.3"
+    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
     testImplementation "junit:junit:4.13"
     androidTestImplementation "androidx.test:runner:1.2.0"
     androidTestImplementation "androidx.test.espresso:espresso-core:3.2.0"
-
-    implementation project(path: ":net")
-
     implementation "androidx.recyclerview:recyclerview:1.1.0"
     implementation "com.google.android.material:material:1.1.0"
+    implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2'
+    implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'
 
+    implementation project(path: ":net")
 
-    implementation "com.github.liangjingkanji:BRV:$brv_version"
-    implementation "com.github.bumptech.glide:glide:$glide_version"
-    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
+    // ------------------------------配合Net使用的库-------------------------------------
+    implementation "com.github.liangjingkanji:BRV:$brv_version" // 提供自动分页/缺省页/自动下拉刷新功能
+    implementation "com.github.bumptech.glide:glide:$glide_version" // 提供下载图片功能
+    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version" // 协程基础库
     implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
 
-
+    // ------------------------------JSON解析-------------------------------------
     implementation "com.squareup.moshi:moshi-kotlin:1.8.0"
     kapt "com.squareup.moshi:moshi-kotlin-codegen:1.8.0"
+    implementation 'com.google.code.gson:gson:2.8.6'
 
-    implementation "com.scwang.smart:refresh-header-classics:2.0.0-alpha-1"
-    implementation "com.scwang.smart:refresh-footer-classics:2.0.0-alpha-1"
-
-    implementation "com.github.liangjingkanji:debugkit:1.2.9"
-    implementation "com.github.liangjingkanji:LogCat:1.0"
+    // ------------------------------我的其他库-------------------------------------
+    implementation 'com.github.liangjingkanji:StatusBar:1.0.3' // 透明状态栏
+    implementation "com.github.liangjingkanji:LogCat:1.0" // 日志输出工具
 
+    // ------------------------------Google数据库-------------------------------------
     implementation "androidx.room:room-runtime:$room_version"
     kapt "androidx.room:room-compiler:$room_version"
     implementation "androidx.room:room-ktx:$room_version"

+ 3 - 3
sample/src/main/AndroidManifest.xml

@@ -4,7 +4,7 @@
     package="com.drake.net.sample">
 
     <application
-        android:name=".App"
+        android:name=".base.App"
         android:allowBackup="true"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"
@@ -12,14 +12,14 @@
         android:supportsRtl="true"
         android:theme="@style/AppTheme"
         tools:ignore="GoogleAppIndexingWarning">
-        <activity android:name=".ui.activity.Main2Activity"></activity>
+
         <activity android:name=".ui.activity.MainActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
-
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+
     </application>
 
 </manifest>

BIN
sample/src/main/assets/upload_file.jpg


+ 0 - 36
sample/src/main/java/com/drake/net/sample/App.kt

@@ -1,36 +0,0 @@
-package com.drake.net.sample
-
-import android.app.Application
-import com.drake.net.cacheEnabled
-import com.drake.net.initNet
-import com.drake.statelayout.StateConfig
-import com.scwang.smart.refresh.footer.ClassicsFooter
-import com.scwang.smart.refresh.header.ClassicsHeader
-import com.scwang.smart.refresh.layout.SmartRefreshLayout
-
-class App : Application() {
-
-    override fun onCreate() {
-        super.onCreate()
-
-        // 缺省页初始化
-        StateConfig.apply {
-            emptyLayout = R.layout.layout_empty
-            loadingLayout = R.layout.layout_loading
-            errorLayout = R.layout.layout_error
-        }
-
-        initNet("") {
-            //            converter(JsonConvert())
-            cacheEnabled()
-        }
-
-        SmartRefreshLayout.setDefaultRefreshHeaderCreator { context, layout ->
-            ClassicsHeader(context)
-        }
-        SmartRefreshLayout.setDefaultRefreshFooterCreator { context, layout ->
-            ClassicsFooter(context)
-        }
-
-    }
-}

+ 0 - 12
sample/src/main/java/com/drake/net/sample/Model.kt

@@ -1,12 +0,0 @@
-package com.drake.net.sample
-
-import androidx.lifecycle.SavedStateHandle
-import com.drake.net.utils.SavedViewModel
-
-class Model(saved: SavedStateHandle) : SavedViewModel(saved) {
-    var name: String?
-        get() = saved.get("name")
-        set(value) {
-            saved["name"] = value
-        }
-}

+ 47 - 0
sample/src/main/java/com/drake/net/sample/base/App.kt

@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/16/20 3:42 PM
+ */
+
+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.R
+import com.drake.net.sample.callback.JsonConvert
+import com.drake.statelayout.StateConfig
+import com.scwang.smart.refresh.footer.ClassicsFooter
+import com.scwang.smart.refresh.header.MaterialHeader
+import com.scwang.smart.refresh.layout.SmartRefreshLayout
+
+class App : Application() {
+
+    override fun onCreate() {
+        super.onCreate()
+
+        // 全局缺省页配置 [https://github.com/liangjingkanji/StateLayout]
+        StateConfig.apply {
+            emptyLayout = R.layout.layout_empty
+            loadingLayout = R.layout.layout_loading
+            errorLayout = R.layout.layout_error
+        }
+
+        // Net 网络初始化信息
+        initNet("http://192.168.0.107:80/") {
+            converter(JsonConvert()) // 自动解析JSON映射到实体类中, 转换器分为全局和单例, 覆盖生效(拦截器允许多个)
+            cacheEnabled()
+        }
+
+        // 初始化SmartRefreshLayout, 这是自动下拉刷新和上拉加载采用的第三方库  [https://github.com/scwang90/SmartRefreshLayout/tree/master] V2版本
+        SmartRefreshLayout.setDefaultRefreshHeaderCreator { context, _ ->
+            MaterialHeader(context)
+        }
+        SmartRefreshLayout.setDefaultRefreshFooterCreator { context, _ ->
+            ClassicsFooter(context)
+        }
+
+    }
+}

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

@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/21/20 12:24 PM
+ */
+
+package com.drake.net.sample.callback
+
+import com.drake.net.convert.DefaultConvert
+import com.google.gson.GsonBuilder
+import java.lang.reflect.Type
+
+class GsonConvert : DefaultConvert(code = "code", message = "msg") {
+
+    override fun <S> String.parseBody(succeed: Type): S? {
+        return GsonBuilder().serializeNulls().create().fromJson(this, succeed)
+    }
+}

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

@@ -5,13 +5,13 @@
  * Date:1/15/20 4:22 PM
  */
 
-package com.drake.net.sample
+package com.drake.net.sample.callback
 
 import com.drake.net.convert.DefaultConvert
 import com.squareup.moshi.Moshi
 import java.lang.reflect.Type
 
-class JsonConvert : DefaultConvert(code = "errno", message = "errstr") {
+class JsonConvert : DefaultConvert(code = "code", message = "msg") {
 
     override fun <S> String.parseBody(succeed: Type): S? {
         return Moshi.Builder().build().adapter<S>(succeed).fromJson(this)

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

@@ -0,0 +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)
+}

+ 0 - 20
sample/src/main/java/com/drake/net/sample/ui/activity/Main2Activity.kt

@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
- * Project:Net
- * Author:Drake
- * Date:2/15/20 5:36 PM
- */
-
-package com.drake.net.sample.ui.activity
-
-import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
-import com.drake.net.sample.R
-
-class Main2Activity : AppCompatActivity() {
-
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        setContentView(R.layout.activity_main2)
-    }
-}

+ 20 - 12
sample/src/main/java/com/drake/net/sample/ui/activity/MainActivity.kt

@@ -2,35 +2,44 @@
  * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
  * Project:Net
  * Author:Drake
- * Date:1/18/20 4:41 PM
+ * Date:4/16/20 3:42 PM
  */
 
 package com.drake.net.sample.ui.activity
 
 import android.os.Bundle
 import androidx.appcompat.app.AppCompatActivity
-import com.drake.net.get
+import androidx.core.view.GravityCompat
+import androidx.navigation.fragment.findNavController
+import androidx.navigation.ui.setupWithNavController
 import com.drake.net.sample.R
-import com.drake.net.utils.scope
+import com.drake.statusbar.immersiveDark
 import kotlinx.android.synthetic.main.activity_main.*
 
 class MainActivity : AppCompatActivity() {
 
-
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-
         setContentView(R.layout.activity_main)
+        immersiveDark(toolbar)
 
-        content.onRefresh {
+        fragment_nav.findNavController().addOnDestinationChangedListener { _, destination, _ ->
+            toolbar.title = destination.label // 更新标题
+            toolbar.menu.clear() // 清除菜单
+        }
 
-            scope {
-                val data = get<String>("https://github.com/liangjingkanji")
-                textView.text = data.await()
-            }
+        toolbar.setNavigationOnClickListener { drawer.openDrawer(GravityCompat.START) }
+        nav.setupWithNavController(fragment_nav.findNavController())
+    }
 
-        }.showLoading()
+    override fun onSupportNavigateUp(): Boolean {
+        return false
+    }
 
+    override fun onBackPressed() {
+        if (drawer.isDrawerOpen(GravityCompat.START)) {
+            drawer.closeDrawers()
+        } else super.onBackPressed()
     }
 }
 
@@ -42,4 +51,3 @@ class MainActivity : AppCompatActivity() {
 
 
 
-

+ 60 - 0
sample/src/main/java/com/drake/net/sample/ui/fragment/AsyncTaskFragment.kt

@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/16/20 3:24 PM
+ */
+
+package com.drake.net.sample.ui.fragment
+
+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.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
+
+class AsyncTaskFragment : Fragment() {
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+
+        return inflater.inflate(R.layout.fragment_async_task, container, false)
+    }
+
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+
+        scope {
+            tv_fragment.text = withContext(Dispatchers.IO) {
+                "结果"
+            }
+//            withIO() // 抽出异步任务函数
+
+//            tv_fragment.text = async {
+//                "结果"
+//            }.await()
+        }
+    }
+
+
+    /**
+     * 抽出异步任务为一个函数
+     */
+    private suspend fun withDownloadFile() = withContext(Dispatchers.IO) {
+        "结果"
+    }
+
+    private fun CoroutineScope.asyncDownloadFile() = async {
+        "结果"
+    }
+}

+ 40 - 0
sample/src/main/java/com/drake/net/sample/ui/fragment/ConfigDialogFragment.kt

@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/16/20 3:34 PM
+ */
+
+package com.drake.net.sample.ui.fragment
+
+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.Get
+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() {
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+
+        return inflater.inflate(R.layout.fragment_config_dialog, container, false)
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+
+        scopeDialog {
+            delay(1000)
+            tv_fragment.text = Get<String>("dialog").await()
+        }
+    }
+}

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

@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/16/20 3:41 PM
+ */
+
+package com.drake.net.sample.ui.fragment
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Lifecycle
+import com.drake.net.sample.R
+import com.drake.net.utils.scope
+import com.drake.net.utils.scopeLife
+import com.drake.net.utils.scopeNet
+import com.drake.net.utils.scopeNetLife
+import kotlinx.android.synthetic.main.fragment_coroutine_scope.*
+import kotlinx.coroutines.delay
+
+
+class CoroutineScopeFragment : Fragment() {
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        return inflater.inflate(R.layout.fragment_coroutine_scope, container, false)
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+
+        // 其作用域在应用进程销毁时才会被动取消
+        scope {
+
+        }
+
+        // 其作用域在Activity或者Fragment销毁(onDestroy)时被动取消 [scopeNetLife]
+        scopeLife {
+            delay(2000)
+            tv_fragment.text = "任务结束"
+        }
+
+        // 自定义取消跟随的生命周期, 失去焦点时立即取消作用域
+        scopeLife(Lifecycle.Event.ON_PAUSE) {
+
+        }
+
+        // 此作用域会捕捉发生的异常, 如果是网络异常会进入网络异常的全局处理函数, 例如自动弹出吐司 [NetConfig.onError]
+        scopeNet {
+
+        }
+
+        // 自动网络处理 + 生命周期管理
+        scopeNetLife {
+
+        }
+    }
+
+}

+ 44 - 0
sample/src/main/java/com/drake/net/sample/ui/fragment/CustomConvertFragment.kt

@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/16/20 3:38 PM
+ */
+
+package com.drake.net.sample.ui.fragment
+
+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.Get
+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
+
+
+class CustomConvertFragment : Fragment() {
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+
+        return inflater.inflate(R.layout.fragment_custom_convert, container, false)
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+
+
+        scopeNetLife {
+            val model = Get<Model>("") {
+                converter(GsonConvert()) // 单例转换器, 此时会忽略全局转换器
+            }.await()
+        }
+
+    }
+
+}

+ 14 - 0
sample/src/main/java/com/drake/net/sample/ui/fragment/Demo.java

@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/24/20 12:07 AM
+ */
+
+package com.drake.net.sample.ui.fragment;
+
+public class Demo {
+    public static void main(String[] args) {
+
+    }
+}

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

@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/17/20 7:38 AM
+ */
+
+package com.drake.net.sample.ui.fragment
+
+import android.os.Bundle
+import android.text.format.Formatter
+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.Download
+import com.drake.net.sample.R
+import com.drake.net.utils.scopeNetLife
+import com.yanzhenjie.kalle.Kalle
+import kotlinx.android.synthetic.main.fragment_download_file.*
+
+
+class DownloadFileFragment : Fragment() {
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+
+        return inflater.inflate(R.layout.fragment_download_file, container, false)
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+
+        scopeNetLife {
+            val fileDir = requireContext().cacheDir.path
+
+            Download("download/img", dir = fileDir, tag = "drake") {
+
+                // 下载进度回调 (普通接口或者上传进度也可以监听)
+                onProgress { progress, byteCount, speed ->
+
+                    Log.d(
+                        "日志",
+                        "(DownloadFileFragment.kt:52)    progress = $progress"
+                    )
+
+                    seek ?: return@onProgress
+
+                    seek.progress = progress // 进度条
+                    // 格式化显示单位
+                    val downloadSize = Formatter.formatFileSize(requireContext(), byteCount)
+                    val downloadSpeed = Formatter.formatFileSize(requireContext(), speed)
+
+                    // 显示下载信息
+                    tv_progress.text = "下载进度: $progress% 已下载: $downloadSize 下载速度: $downloadSpeed"
+                }
+            }.await()
+        }
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        Kalle.Download.cancel("drake")
+        Log.d("日志", "(DownloadFileFragment.kt:68) -> onDestroyView    ")
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        Log.d("日志", "(DownloadFileFragment.kt:67) -> onDestroy    ")
+
+    }
+
+}

+ 37 - 0
sample/src/main/java/com/drake/net/sample/ui/fragment/DownloadImageFragment.kt

@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/17/20 7:38 AM
+ */
+
+package com.drake.net.sample.ui.fragment
+
+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.sample.R
+import com.drake.net.utils.scopeDialog
+
+
+class DownloadImageFragment : Fragment() {
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+
+        return inflater.inflate(R.layout.fragment_download_image, container, false)
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+
+        scopeDialog {
+            val file = DownloadImg("download/img", 200, 200).await()
+        }
+    }
+}

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

@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/16/20 3:35 PM
+ */
+
+package com.drake.net.sample.ui.fragment
+
+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.sample.R
+
+
+class ErrorHandlerFragment : Fragment() {
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+
+        return inflater.inflate(R.layout.fragment_error_handler, container, false)
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+
+    }
+
+}

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

@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/16/20 3:40 PM
+ */
+
+package com.drake.net.sample.ui.fragment
+
+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.Get
+import com.drake.net.sample.R
+import com.drake.net.utils.scopeNetLife
+import kotlinx.android.synthetic.main.fragment_exception_trace.*
+
+
+class ExceptionTraceFragment : Fragment() {
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+
+        return inflater.inflate(R.layout.fragment_exception_trace, container, false)
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+
+        scopeNetLife {
+            // 这是一个错误的地址, 请查看LogCat的错误信息, 在[Convert]中你也可以进行自定义错误信息打印
+            tv_fragment.text = Get<String>("").await()
+
+        }
+    }
+
+}

+ 34 - 0
sample/src/main/java/com/drake/net/sample/ui/fragment/InterceptorFragment.kt

@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/16/20 9:07 PM
+ */
+
+package com.drake.net.sample.ui.fragment
+
+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.sample.R
+
+
+class InterceptorFragment : Fragment() {
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+
+        return inflater.inflate(R.layout.fragment_interceptor, container, false)
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+
+
+    }
+
+}

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

@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/16/20 3:31 PM
+ */
+
+package com.drake.net.sample.ui.fragment
+
+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.sample.R
+import com.drake.net.utils.scopeNetLife
+
+
+class ParallelNetworkFragment : Fragment() {
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+
+        return inflater.inflate(R.layout.fragment_parallel_network, container, false)
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+
+        scopeNetLife {
+
+        }
+    }
+
+}

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

@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/16/20 4:58 PM
+ */
+
+package com.drake.net.sample.ui.fragment
+
+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.sample.R
+
+
+class PullRefreshFragment : Fragment() {
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+
+        return inflater.inflate(R.layout.fragment_pull_refresh, container, false)
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+
+
+    }
+
+}

+ 33 - 0
sample/src/main/java/com/drake/net/sample/ui/fragment/PushRefreshFragment.kt

@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/16/20 3:24 PM
+ */
+
+package com.drake.net.sample.ui.fragment
+
+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.sample.R
+
+
+class PushRefreshFragment : Fragment() {
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+
+        return inflater.inflate(R.layout.fragment_push_refresh, container, false)
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+
+    }
+
+}

+ 37 - 0
sample/src/main/java/com/drake/net/sample/ui/fragment/ReadCacheFragment.kt

@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/16/20 3:27 PM
+ */
+
+package com.drake.net.sample.ui.fragment
+
+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.sample.R
+import com.drake.net.utils.scopeNetLife
+
+
+class ReadCacheFragment : Fragment() {
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+
+        return inflater.inflate(R.layout.fragment_read_cache, container, false)
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+
+        scopeNetLife {
+
+        }
+    }
+
+}

+ 103 - 0
sample/src/main/java/com/drake/net/sample/ui/fragment/RequestMethodFragment.kt

@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/16/20 3:24 PM
+ */
+
+@file:Suppress("FunctionName")
+
+package com.drake.net.sample.ui.fragment
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.widget.Toolbar
+import androidx.fragment.app.Fragment
+import com.drake.net.*
+import com.drake.net.sample.R
+import com.drake.net.utils.scopeNetLife
+import kotlinx.android.synthetic.main.fragment_async_task.*
+
+
+class RequestMethodFragment : Fragment() {
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        initToolbar()
+        return inflater.inflate(R.layout.fragment_request_method, container, false)
+    }
+
+
+    private fun GET() {
+        scopeNetLife {
+            val data = Get<String>("api").await()
+            tv_fragment.text = data
+        }
+    }
+
+    private fun POST() {
+        scopeNetLife {
+            tv_fragment.text = Post<String>("api").await()
+        }
+    }
+
+    private fun HEAD() {
+        scopeNetLife {
+            tv_fragment.text = Head<String>("api").await()
+        }
+    }
+
+    private fun PUT() {
+        scopeNetLife {
+            tv_fragment.text = Put<String>("api").await()
+        }
+    }
+
+    private fun PATCH() {
+        scopeNetLife {
+            tv_fragment.text = Patch<String>("api").await()
+        }
+    }
+
+    private fun DELETE() {
+        scopeNetLife {
+            tv_fragment.text = Delete<String>("api").await()
+        }
+    }
+
+    private fun TRACE() {
+        scopeNetLife {
+            tv_fragment.text = Trace<String>("api").await()
+        }
+    }
+
+    private fun OPTIONS() {
+        scopeNetLife {
+            tv_fragment.text = Options<String>("api").await()
+        }
+    }
+
+
+    private fun initToolbar() {
+        val toolbar = requireActivity().findViewById<Toolbar>(R.id.toolbar)
+        toolbar.inflateMenu(R.menu.menu_request_method)
+        toolbar.setOnMenuItemClickListener {
+            when (it.itemId) {
+                R.id.get -> GET()
+                R.id.post -> POST()
+                R.id.head -> HEAD()
+                R.id.put -> PUT()
+                R.id.patch -> PATCH()
+                R.id.delete -> DELETE()
+                R.id.trace -> TRACE()
+                R.id.options -> OPTIONS()
+            }
+            true
+        }
+    }
+
+}

+ 46 - 0
sample/src/main/java/com/drake/net/sample/ui/fragment/StateLayoutFragment.kt

@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/16/20 3:35 PM
+ */
+
+package com.drake.net.sample.ui.fragment
+
+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.Get
+import com.drake.net.sample.R
+import com.drake.net.utils.scope
+import kotlinx.android.synthetic.main.fragment_state_layout.*
+import kotlinx.coroutines.delay
+
+
+class StateLayoutFragment : Fragment() {
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+
+        return inflater.inflate(R.layout.fragment_state_layout, container, false)
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+
+        state.onRefresh {
+            scope {
+                delay(1000)
+
+                tv_fragment.text = Get<String>("method") {
+                    param("userId", "drake")
+                }.await()
+            }
+        }.showLoading()
+    }
+
+}

+ 66 - 0
sample/src/main/java/com/drake/net/sample/ui/fragment/SuperIntervalFragment.kt

@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/16/20 3:36 PM
+ */
+
+package com.drake.net.sample.ui.fragment
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.widget.Toolbar
+import androidx.fragment.app.Fragment
+import com.drake.net.sample.R
+import com.drake.net.time.Interval
+import kotlinx.android.synthetic.main.fragment_super_interval.*
+import java.util.concurrent.TimeUnit
+
+
+class SuperIntervalFragment : Fragment() {
+
+    lateinit var interval: Interval
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+
+        return inflater.inflate(R.layout.fragment_super_interval, container, false)
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+
+        initToolbar()
+
+        interval = Interval(1, TimeUnit.SECONDS, 0) // 每秒回调一次, 不会自动结束
+        // interval = Interval(10, 1, TimeUnit.SECONDS, 0, 0) // 自定义计数器个数的轮循器
+
+        interval.subscribe {
+            tv_fragment.text = it.toString()
+        }.finish {
+            tv_fragment.text = "计时完成"
+        }.start()
+    }
+
+
+    private fun initToolbar() {
+        val toolbar = requireActivity().findViewById<Toolbar>(R.id.toolbar)
+        toolbar.inflateMenu(R.menu.menu_interval)
+        toolbar.setOnMenuItemClickListener {
+            when (it.itemId) {
+                R.id.start -> interval.start()
+                R.id.pause -> interval.pause()
+                R.id.resume -> interval.resume()
+                R.id.reset -> interval.reset()
+                R.id.switch_interval -> interval.switch()
+                R.id.stop -> interval.stop()
+            }
+            true
+        }
+    }
+
+}

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

@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/16/20 3:42 PM
+ */
+
+package com.drake.net.sample.ui.fragment
+
+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.sample.R
+import com.drake.net.utils.scopeLife
+import com.drake.net.utils.withIO
+import com.drake.net.utils.withMain
+import kotlinx.coroutines.launch
+
+
+class SwitchDispatcherFragment : Fragment() {
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+
+        return inflater.inflate(R.layout.fragment_switch_dispatcher, container, false)
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+
+
+        scopeLife {
+
+            // 点击函数名查看更多相关函数
+            launch {
+                val data = withMain {
+                    "异步调度器切换到主线程"
+                }
+            }
+            val data = withIO {
+                "主线程切换到IO调度器"
+            }
+        }
+    }
+
+}

+ 52 - 0
sample/src/main/java/com/drake/net/sample/ui/fragment/UploadFileFragment.kt

@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+ * Project:Net
+ * Author:Drake
+ * Date:4/17/20 7:38 AM
+ */
+
+package com.drake.net.sample.ui.fragment
+
+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.sample.R
+
+
+class UploadFileFragment : Fragment() {
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+
+        return inflater.inflate(R.layout.fragment_upload_file, container, false)
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+
+        /*   scopeNetLife {
+               val fileDir = requireContext().cacheDir.path
+
+               Post<String>("download/img", fileDir) {
+
+                   // 下载进度回调 (普通接口或者上传进度也可以监听)
+                   onProgress { progress, byteCount, speed ->
+
+                       seek.progress = progress // 进度条
+                       // 格式化显示单位
+                       val downloadSize = Formatter.formatFileSize(requireContext(), byteCount)
+                       val downloadSpeed = Formatter.formatFileSize(requireContext(), speed)
+
+                       // 显示下载信息
+                       tv_progress.text = "上传进度: $progress% 已下载: $downloadSize 下载速度: $downloadSpeed"
+                   }
+               }.await()
+           }*/
+
+    }
+
+}

BIN
sample/src/main/res/drawable/header.png


+ 16 - 0
sample/src/main/res/drawable/ic_async_task.xml

@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 6:12 AM
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14,5h8v2h-8zM14,10.5h8v2h-8zM14,16h8v2h-8zM2,11.5C2,15.08 4.92,18 8.5,18L9,18v2l3,-3 -3,-3v2h-0.5C6.02,16 4,13.98 4,11.5S6.02,7 8.5,7L12,7L12,5L8.5,5C4.92,5 2,7.92 2,11.5z" />
+</vector>

+ 16 - 0
sample/src/main/res/drawable/ic_config_dialog.xml

@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 6:13 AM
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18.6,6.62c-1.44,0 -2.8,0.56 -3.77,1.53L12,10.66 10.48,12h0.01L7.8,14.39c-0.64,0.64 -1.49,0.99 -2.4,0.99 -1.87,0 -3.39,-1.51 -3.39,-3.38S3.53,8.62 5.4,8.62c0.91,0 1.76,0.35 2.44,1.03l1.13,1 1.51,-1.34L9.22,8.2C8.2,7.18 6.84,6.62 5.4,6.62 2.42,6.62 0,9.04 0,12s2.42,5.38 5.4,5.38c1.44,0 2.8,-0.56 3.77,-1.53l2.83,-2.5 0.01,0.01L13.52,12h-0.01l2.69,-2.39c0.64,-0.64 1.49,-0.99 2.4,-0.99 1.87,0 3.39,1.51 3.39,3.38s-1.52,3.38 -3.39,3.38c-0.9,0 -1.76,-0.35 -2.44,-1.03l-1.14,-1.01 -1.51,1.34 1.27,1.12c1.02,1.01 2.37,1.57 3.82,1.57 2.98,0 5.4,-2.41 5.4,-5.38s-2.42,-5.37 -5.4,-5.37z" />
+</vector>

+ 16 - 0
sample/src/main/res/drawable/ic_convert.xml

@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 6:21 AM
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,12l-4,-4v3H3v2h15v3z" />
+</vector>

+ 16 - 0
sample/src/main/res/drawable/ic_download_file.xml

@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 7:44 AM
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z" />
+</vector>

+ 16 - 0
sample/src/main/res/drawable/ic_download_img.xml

@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 7:45 AM
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,16L22,4c0,-1.1 -0.9,-2 -2,-2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2zM11,12l2.03,2.71L16,11l4,5L8,16l3,-4zM2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6L2,6z" />
+</vector>

+ 16 - 0
sample/src/main/res/drawable/ic_error_handler.xml

@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 6:16 AM
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11.99,2C6.47,2 2,6.47 2,12s4.47,10 9.99,10S22,17.53 22,12 17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM16.18,7.76l-1.06,1.06 -1.06,-1.06L13,8.82l1.06,1.06L13,10.94 14.06,12l1.06,-1.06L16.18,12l1.06,-1.06 -1.06,-1.06 1.06,-1.06zM7.82,12l1.06,-1.06L9.94,12 11,10.94 9.94,9.88 11,8.82 9.94,7.76 8.88,8.82 7.82,7.76 6.76,8.82l1.06,1.06 -1.06,1.06zM12,14c-2.33,0 -4.31,1.46 -5.11,3.5h10.22c-0.8,-2.04 -2.78,-3.5 -5.11,-3.5z" />
+</vector>

+ 16 - 0
sample/src/main/res/drawable/ic_exception_trace.xml

@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 6:17 AM
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5c-0.49,0 -0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8L4,8v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1L4,12v2h2v1c0,0.34 0.04,0.67 0.09,1L4,16v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3L20,18v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1L20,10L20,8zM14,16h-4v-2h4v2zM14,12h-4v-2h4v2z" />
+</vector>

+ 16 - 0
sample/src/main/res/drawable/ic_interceptor.xml

@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 6:21 AM
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,8.41l9,9 7,-7V15h2V7h-8v2h4.59L12,14.59 4.41,7 3,8.41z" />
+</vector>

+ 16 - 0
sample/src/main/res/drawable/ic_interval.xml

@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 6:25 AM
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM12.5,8L11,8v6l4.75,2.85 0.75,-1.23 -4,-2.37L12.5,8zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,20c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z" />
+</vector>

+ 16 - 0
sample/src/main/res/drawable/ic_menu.xml

@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 4:49 PM
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,18h18v-2L3,16v2zM3,13h18v-2L3,11v2zM3,6v2h18L21,6L3,6z" />
+</vector>

+ 16 - 0
sample/src/main/res/drawable/ic_parallel_network.xml

@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 6:18 AM
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,20.41L18.41,19 15,15.59 13.59,17 17,20.41zM7.5,8H11v5.59L5.59,19 7,20.41l6,-6V8h3.5L12,3.5 7.5,8z" />
+</vector>

+ 16 - 0
sample/src/main/res/drawable/ic_pull_refresh.xml

@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/16/20 4:55 PM
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,7h2.5L5,3.5 1.5,7L4,7v10L1.5,17L5,20.5 8.5,17L6,17L6,7zM10,5v2h12L22,5L10,5zM10,19h12v-2L10,17v2zM10,13h12v-2L10,11v2z" />
+</vector>

+ 16 - 0
sample/src/main/res/drawable/ic_push_refresh.xml

@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 6:27 AM
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.55,5.55L11,1v3.07C7.06,4.56 4,7.92 4,12s3.05,7.44 7,7.93v-2.02c-2.84,-0.48 -5,-2.94 -5,-5.91s2.16,-5.43 5,-5.91L11,10l4.55,-4.45zM19.93,11c-0.17,-1.39 -0.72,-2.73 -1.62,-3.89l-1.42,1.42c0.54,0.75 0.88,1.6 1.02,2.47h2.02zM13,17.9v2.02c1.39,-0.17 2.74,-0.71 3.9,-1.61l-1.44,-1.44c-0.75,0.54 -1.59,0.89 -2.46,1.03zM16.89,15.48l1.42,1.41c0.9,-1.16 1.45,-2.5 1.62,-3.89h-2.02c-0.14,0.87 -0.48,1.72 -1.02,2.48z" />
+</vector>

+ 16 - 0
sample/src/main/res/drawable/ic_read_cache.xml

@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 6:25 AM
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.54,5.23l-1.39,-1.68C18.88,3.21 18.47,3 18,3H6c-0.47,0 -0.88,0.21 -1.16,0.55L3.46,5.23C3.17,5.57 3,6.02 3,6.5V19c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V6.5c0,-0.48 -0.17,-0.93 -0.46,-1.27zM12,17.5L6.5,12H10v-2h4v2h3.5L12,17.5zM5.12,5l0.81,-1h12l0.94,1H5.12z" />
+</vector>

+ 16 - 0
sample/src/main/res/drawable/ic_request_method.xml

@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 7:44 AM
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM19,18H6c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4h0.71C7.37,7.69 9.48,6 12,6c3.04,0 5.5,2.46 5.5,5.5v0.5H19c1.66,0 3,1.34 3,3s-1.34,3 -3,3z" />
+</vector>

+ 16 - 0
sample/src/main/res/drawable/ic_scope.xml

@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 8:09 PM
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,3.01H3c-1.1,0 -2,0.9 -2,2V9h2V4.99h18v14.03H3V15H1v4.01c0,1.1 0.9,1.98 2,1.98h18c1.1,0 2,-0.88 2,-1.98v-14c0,-1.11 -0.9,-2 -2,-2zM11,16l4,-4 -4,-4v3H1v2h10v3z" />
+</vector>

+ 16 - 0
sample/src/main/res/drawable/ic_state_layout.xml

@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 6:27 AM
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14,2L6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6zM12,18c-2.05,0 -3.81,-1.24 -4.58,-3h1.71c0.63,0.9 1.68,1.5 2.87,1.5 1.93,0 3.5,-1.57 3.5,-3.5S13.93,9.5 12,9.5c-1.35,0 -2.52,0.78 -3.1,1.9l1.6,1.6h-4L6.5,9l1.3,1.3C8.69,8.92 10.23,8 12,8c2.76,0 5,2.24 5,5s-2.24,5 -5,5z" />
+</vector>

+ 16 - 0
sample/src/main/res/drawable/ic_switch_dispatcher.xml

@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 6:23 AM
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10.59,9.17L5.41,4 4,5.41l5.17,5.17 1.42,-1.41zM14.5,4l2.04,2.04L4,18.59 5.41,20 17.96,7.46 20,9.5L20,4h-5.5zM14.83,13.41l-1.41,1.41 3.13,3.13L14.5,20L20,20v-5.5l-2.04,2.04 -3.13,-3.13z" />
+</vector>

+ 16 - 0
sample/src/main/res/drawable/ic_upload_file.xml

@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 7:44 AM
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM14,13v4h-4v-4H7l5,-5 5,5h-3z" />
+</vector>

+ 27 - 11
sample/src/main/res/layout/activity_main.xml

@@ -1,24 +1,40 @@
 <?xml version="1.0" encoding="utf-8"?>
 
-<com.drake.brv.PageRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/content"
+<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/drawer"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <ScrollView
+
+    <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:fillViewport="true">
+        android:orientation="vertical">
+
+        <androidx.appcompat.widget.Toolbar
+            android:id="@+id/toolbar"
+            app:navigationIcon="@drawable/ic_menu"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
 
-        <TextView
-            android:id="@+id/textView"
+        <fragment
+            android:id="@+id/fragment_nav"
+            android:name="androidx.navigation.fragment.NavHostFragment"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:gravity="center"
-            android:text="首页" />
+            android:layout_height="match_parent"
+            app:defaultNavHost="false"
+            app:navGraph="@navigation/nav_main" />
 
+    </LinearLayout>
 
-    </ScrollView>
+    <com.google.android.material.navigation.NavigationView
+        android:id="@+id/nav"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_gravity="start"
+        app:headerLayout="@layout/layout_drawer_nav_header"
+        app:menu="@menu/menu_main" />
 
-</com.drake.brv.PageRefreshLayout>
+</androidx.drawerlayout.widget.DrawerLayout>
 

+ 0 - 14
sample/src/main/res/layout/activity_main2.xml

@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
-  ~ Project:Net
-  ~ Author:Drake
-  ~ Date:2/15/20 5:36 PM
-  -->
-
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    tools:context=".ui.activity.Main2Activity">
-
-</androidx.constraintlayout.widget.ConstraintLayout>

+ 21 - 0
sample/src/main/res/layout/fragment_async_task.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/16/20 3:24 PM
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.fragment.AsyncTaskFragment">
+
+    <TextView
+        android:id="@+id/tv_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:text="AsyncTaskFragment" />
+
+</FrameLayout>

+ 23 - 0
sample/src/main/res/layout/fragment_config_dialog.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/16/20 3:34 PM
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.fragment.ConfigDialogFragment">
+
+    <TextView
+        android:id="@+id/tv_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_margin="16dp"
+        android:gravity="center"
+        android:text="等待数据中"
+        android:textStyle="bold" />
+
+</FrameLayout>

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

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/16/20 3:41 PM
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.fragment.CoroutineScopeFragment">
+
+    <TextView
+        android:id="@+id/tv_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:text="等待2秒" />
+
+</FrameLayout>

+ 21 - 0
sample/src/main/res/layout/fragment_custom_convert.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/16/20 3:38 PM
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.fragment.CustomConvertFragment">
+
+    <TextView
+        android:id="@+id/tv_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:text="CustomConvertFragment" />
+
+</FrameLayout>

+ 40 - 0
sample/src/main/res/layout/fragment_download_file.xml

@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 7:38 AM
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.fragment.DownloadFileFragment">
+
+    <SeekBar
+        android:id="@+id/seek"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="16dp"
+        android:gravity="center"
+        android:text="等待下载完成"
+        android:textSize="18dp"
+        android:textStyle="bold"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="0.5" />
+
+    <TextView
+        android:id="@+id/tv_progress"
+        android:layout_width="wrap_content"
+        android:layout_marginTop="16dp"
+        android:layout_height="wrap_content"
+        app:layout_constraintLeft_toLeftOf="@id/seek"
+        app:layout_constraintRight_toRightOf="@id/seek"
+        app:layout_constraintTop_toBottomOf="@id/seek"
+        tools:text="下载进度: 90%" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 21 - 0
sample/src/main/res/layout/fragment_download_image.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 7:38 AM
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    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" />
+
+</FrameLayout>

+ 21 - 0
sample/src/main/res/layout/fragment_error_handler.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/16/20 3:35 PM
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.fragment.ErrorHandlerFragment">
+
+    <TextView
+        android:id="@+id/tv_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:text="ErrorHandlerFragment" />
+
+</FrameLayout>

+ 21 - 0
sample/src/main/res/layout/fragment_exception_trace.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/16/20 3:40 PM
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.fragment.ExceptionTraceFragment">
+
+    <TextView
+        android:id="@+id/tv_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:text="ExceptionTraceFragment" />
+
+</FrameLayout>

+ 21 - 0
sample/src/main/res/layout/fragment_interceptor.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/16/20 9:07 PM
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.fragment.InterceptorFragment">
+
+    <TextView
+        android:id="@+id/tv_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:text="IntercepterFragment" />
+
+</FrameLayout>

+ 21 - 0
sample/src/main/res/layout/fragment_parallel_network.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/16/20 3:31 PM
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.fragment.ParallelNetworkFragment">
+
+    <TextView
+        android:id="@+id/tv_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:text="ParallelNetworkFragment" />
+
+</FrameLayout>

+ 21 - 0
sample/src/main/res/layout/fragment_pull_refresh.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/16/20 4:58 PM
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.fragment.PullRefreshFragment">
+
+    <TextView
+        android:id="@+id/tv_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:text="AutoPagingFragment" />
+
+</FrameLayout>

+ 21 - 0
sample/src/main/res/layout/fragment_push_refresh.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/16/20 3:24 PM
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.fragment.PushRefreshFragment">
+
+    <TextView
+        android:id="@+id/tv_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:text="PushSwipeFrgment" />
+
+</FrameLayout>

+ 21 - 0
sample/src/main/res/layout/fragment_read_cache.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/16/20 3:27 PM
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.fragment.ReadCacheFragment">
+
+    <TextView
+        android:id="@+id/tv_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:text="PreReadCacheFragment" />
+
+</FrameLayout>

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

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.fragment.RequestMethodFragment">
+
+    <TextView
+        android:id="@+id/tv_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:text="点击右上角菜单开始请求" />
+
+</FrameLayout>

+ 22 - 0
sample/src/main/res/layout/fragment_state_layout.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/16/20 3:35 PM
+  -->
+
+<com.drake.statelayout.StateLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:id="@+id/state"
+    android:layout_height="match_parent"
+    tools:context=".ui.fragment.StateLayoutFragment">
+
+    <TextView
+        android:id="@+id/tv_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:text="内容" />
+
+</com.drake.statelayout.StateLayout>

+ 23 - 0
sample/src/main/res/layout/fragment_super_interval.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/16/20 3:36 PM
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.fragment.SuperIntervalFragment">
+
+    <TextView
+        android:id="@+id/tv_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:textSize="32dp"
+        tools:text="0"
+        android:textStyle="bold" />
+
+</FrameLayout>

+ 21 - 0
sample/src/main/res/layout/fragment_switch_dispatcher.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/16/20 3:42 PM
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.fragment.SwitchDispatcherFragment">
+
+    <TextView
+        android:id="@+id/tv_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:text="SwitchDispatcherFragment" />
+
+</FrameLayout>

+ 40 - 0
sample/src/main/res/layout/fragment_upload_file.xml

@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 7:38 AM
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    tools:context=".ui.fragment.UploadFileFragment">
+
+    <SeekBar
+        android:id="@+id/seek"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="16dp"
+        android:gravity="center"
+        android:text="等待下载完成"
+        android:textSize="18dp"
+        android:textStyle="bold"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="0.5" />
+
+    <TextView
+        android:id="@+id/tv_progress"
+        android:layout_width="wrap_content"
+        android:layout_marginTop="16dp"
+        android:layout_height="wrap_content"
+        app:layout_constraintLeft_toLeftOf="@id/seek"
+        app:layout_constraintRight_toRightOf="@id/seek"
+        app:layout_constraintTop_toBottomOf="@id/seek"
+        tools:text="上传进度: 90%" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 49 - 0
sample/src/main/res/layout/layout_drawer_nav_header.xml

@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/16/20 9:41 PM
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_height="240dp">
+
+    <LinearLayout
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        android:layout_marginTop="24dp"
+        android:gravity="center"
+        app:layout_constraintBottom_toBottomOf="parent"
+        android:layout_width="wrap_content"
+        android:orientation="vertical"
+        android:layout_height="wrap_content">
+
+        <ImageView
+            android:layout_width="100dp"
+            android:layout_height="100dp"
+            android:id="@+id/iv"
+            android:src="@drawable/header" />
+
+        <TextView
+            android:id="@+id/tv_name"
+            android:layout_width="wrap_content"
+            android:text="刘强东"
+            android:textSize="16dp"
+            android:textStyle="bold"
+            android:layout_marginTop="16dp"
+            android:layout_height="wrap_content" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_marginTop="8dp"
+            android:textSize="12dp"
+            android:textStyle="italic"
+            android:text="github.com/liangjingkanji/Net"
+            android:layout_height="wrap_content" />
+
+    </LinearLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 28 - 0
sample/src/main/res/menu/menu_interval.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/17/20 7:22 AM
+  -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/start"
+        android:title="开始" />
+    <item
+        android:id="@+id/pause"
+        android:title="暂停" />
+    <item
+        android:id="@+id/resume"
+        android:title="继续" />
+    <item
+        android:id="@+id/reset"
+        android:title="重置" />
+
+    <item
+        android:id="@+id/switch_interval"
+        android:title="结束" />
+    <item
+        android:id="@+id/stop"
+        android:title="结束" />
+</menu>

+ 84 - 0
sample/src/main/res/menu/menu_main.xml

@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/16/20 4:53 PM
+  -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/request_method"
+        android:icon="@drawable/ic_request_method"
+        android:title="请求方式" />
+    <item
+        android:id="@+id/async_task"
+        android:icon="@drawable/ic_async_task"
+        android:title="异步任务" />
+    <item
+        android:id="@+id/config_dialog"
+        android:icon="@drawable/ic_config_dialog"
+        android:title="自动加载框" />
+    <item
+        android:id="@+id/coroutine_scope"
+        android:icon="@drawable/ic_scope"
+        android:title="协程作用域" />
+    <item
+        android:id="@+id/custom_convert"
+        android:icon="@drawable/ic_convert"
+        android:title="自定义转换器" />
+    <item
+        android:id="@+id/error_handler"
+        android:icon="@drawable/ic_error_handler"
+        android:title="错误处理" />
+    <item
+        android:id="@+id/exception_trace"
+        android:icon="@drawable/ic_exception_trace"
+        android:title="异常追踪" />
+    <item
+        android:id="@+id/interceptor"
+        android:icon="@drawable/ic_interceptor"
+        android:title="拦截器" />
+    <item
+        android:id="@+id/parallel_network"
+        android:icon="@drawable/ic_parallel_network"
+        android:title="并发网络" />
+    <item
+        android:id="@+id/push_refresh"
+        android:icon="@drawable/ic_push_refresh"
+        android:title="自动下拉刷新" />
+    <item
+        android:id="@+id/pull_refresh"
+        android:icon="@drawable/ic_pull_refresh"
+        android:title="自动分页加载" />
+
+    <item
+        android:id="@+id/read_cache"
+        android:icon="@drawable/ic_read_cache"
+        android:title="预读缓存" />
+    <item
+        android:id="@+id/state_layout"
+        android:icon="@drawable/ic_state_layout"
+        android:title="自动缺省页" />
+    <item
+        android:id="@+id/switch_dispatcher"
+        android:icon="@drawable/ic_switch_dispatcher"
+        android:title="切换调度器" />
+    <item
+        android:id="@+id/download_file"
+        android:icon="@drawable/ic_download_file"
+        android:title="下载文件" />
+    <item
+        android:id="@+id/upload_file"
+        android:icon="@drawable/ic_upload_file"
+        android:title="上传文件" />
+
+    <item
+        android:id="@+id/download_img"
+        android:icon="@drawable/ic_download_img"
+        android:title="下载图片" />
+
+    <item
+        android:id="@+id/super_interval"
+        android:icon="@drawable/ic_interval"
+        android:title="超级轮循器" />
+</menu>

+ 33 - 0
sample/src/main/res/menu/menu_request_method.xml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/16/20 9:35 PM
+  -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/get"
+        android:title="GET" />
+    <item
+        android:id="@+id/post"
+        android:title="POST" />
+    <item
+        android:id="@+id/head"
+        android:title="HEAD" />
+    <item
+        android:id="@+id/put"
+        android:title="PUT" />
+    <item
+        android:id="@+id/patch"
+        android:title="PATCH" />
+    <item
+        android:id="@+id/delete"
+        android:title="DELETE" />
+    <item
+        android:id="@+id/trace"
+        android:title="TRACE" />
+    <item
+        android:id="@+id/options"
+        android:title="OPTIONS" />
+</menu>

+ 106 - 0
sample/src/main/res/navigation/nav_main.xml

@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018, Umbrella CompanyLimited All rights reserved.
+  ~ Project:Net
+  ~ Author:Drake
+  ~ Date:4/16/20 9:13 PM
+  -->
+
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/main"
+    app:startDestination="@id/request_method">
+
+    <fragment
+        android:id="@+id/async_task"
+        android:name="com.drake.net.sample.ui.fragment.AsyncTaskFragment"
+        android:label="异步任务"
+        tools:layout="@layout/fragment_async_task" />
+
+    <fragment
+        android:id="@+id/config_dialog"
+        android:name="com.drake.net.sample.ui.fragment.ConfigDialogFragment"
+        android:label="自动加载框"
+        tools:layout="@layout/fragment_config_dialog" />
+    <fragment
+        android:id="@+id/coroutine_scope"
+        android:name="com.drake.net.sample.ui.fragment.CoroutineScopeFragment"
+        android:label="协程作用域"
+        tools:layout="@layout/fragment_coroutine_scope" />
+    <fragment
+        android:id="@+id/custom_convert"
+        android:name="com.drake.net.sample.ui.fragment.CustomConvertFragment"
+        android:label="自定义转换器"
+        tools:layout="@layout/fragment_custom_convert" />
+    <fragment
+        android:id="@+id/error_handler"
+        android:name="com.drake.net.sample.ui.fragment.ErrorHandlerFragment"
+        android:label="错误处理"
+        tools:layout="@layout/fragment_error_handler" />
+    <fragment
+        android:id="@+id/exception_trace"
+        android:name="com.drake.net.sample.ui.fragment.ExceptionTraceFragment"
+        android:label="异常追踪"
+        tools:layout="@layout/fragment_exception_trace" />
+    <fragment
+        android:id="@+id/interceptor"
+        android:name="com.drake.net.sample.ui.fragment.InterceptorFragment"
+        android:label="拦截器"
+        tools:layout="@layout/fragment_interceptor" />
+    <fragment
+        android:id="@+id/pull_refresh"
+        android:name="com.drake.net.sample.ui.fragment.PullRefreshFragment"
+        android:label="自动分页加载"
+        tools:layout="@layout/fragment_pull_refresh" />
+    <fragment
+        android:id="@+id/parallel_network"
+        android:name="com.drake.net.sample.ui.fragment.ParallelNetworkFragment"
+        android:label="并发网络任务"
+        tools:layout="@layout/fragment_parallel_network" />
+    <fragment
+        android:id="@+id/push_refresh"
+        android:name="com.drake.net.sample.ui.fragment.PushRefreshFragment"
+        android:label="自动下拉刷新"
+        tools:layout="@layout/fragment_push_refresh" />
+    <fragment
+        android:id="@+id/read_cache"
+        android:name="com.drake.net.sample.ui.fragment.ReadCacheFragment"
+        android:label="预读缓存"
+        tools:layout="@layout/fragment_read_cache" />
+    <fragment
+        android:id="@+id/request_method"
+        android:name="com.drake.net.sample.ui.fragment.RequestMethodFragment"
+        android:label="请求方式"
+        tools:layout="@layout/fragment_request_method" />
+    <fragment
+        android:id="@+id/state_layout"
+        android:name="com.drake.net.sample.ui.fragment.StateLayoutFragment"
+        android:label="自动缺省页"
+        tools:layout="@layout/fragment_state_layout" />
+    <fragment
+        android:id="@+id/super_interval"
+        android:name="com.drake.net.sample.ui.fragment.SuperIntervalFragment"
+        android:label="超级轮循器"
+        tools:layout="@layout/fragment_super_interval" />
+    <fragment
+        android:id="@+id/switch_dispatcher"
+        android:name="com.drake.net.sample.ui.fragment.SwitchDispatcherFragment"
+        android:label="切换调度器"
+        tools:layout="@layout/fragment_switch_dispatcher" />
+    <fragment
+        android:id="@+id/download_file"
+        android:name="com.drake.net.sample.ui.fragment.DownloadFileFragment"
+        android:label="下载文件"
+        tools:layout="@layout/fragment_download_file" />
+    <fragment
+        android:id="@+id/download_img"
+        android:name="com.drake.net.sample.ui.fragment.DownloadImageFragment"
+        android:label="下载图片"
+        tools:layout="@layout/fragment_download_image" />
+    <fragment
+        android:id="@+id/upload_file"
+        android:name="com.drake.net.sample.ui.fragment.UploadFileFragment"
+        android:label="上传文件"
+        tools:layout="@layout/fragment_upload_file" />
+
+</navigation>

+ 17 - 3
sample/src/main/res/values/colors.xml

@@ -1,6 +1,20 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <color name="colorPrimary">#008577</color>
-    <color name="colorPrimaryDark">#00574B</color>
-    <color name="colorAccent">#D81B60</color>
+    <color name="colorPrimary">#2FA8E6</color>
+    <color name="colorPrimaryDark">#2FA8E6</color>
+    <color name="colorAccent">#F44336</color>
+
+    <!--辅助颜色-->
+    <color name="divider">#339e9e9e</color>
+    <color name="windowBackground">#f5f5f5</color>
+
+    <!--字体颜色-->
+    <color name="textColor">#333333</color>
+    <color name="textDescription">#999999</color>
+
+    <!--基本颜色-->
+    <color name="white">#FFFFFF</color>
+    <color name="orange">#F44336</color>
+    <color name="black">#000000</color>
+
 </resources>

+ 1 - 1
sample/src/main/res/values/styles.xml

@@ -1,7 +1,7 @@
 <resources>
 
     <!-- Base application theme. -->
-    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
         <!-- Customize your theme here. -->
         <item name="colorPrimary">@color/colorPrimary</item>
         <item name="colorPrimaryDark">@color/colorPrimaryDark</item>