Browse Source

first commit

liaoshengjian 5 years ago
commit
2d04c9572f
36 changed files with 3209 additions and 0 deletions
  1. 1 0
      README.md
  2. 2 0
      ps_lib_smart/.gitignore
  3. 67 0
      ps_lib_smart/build.gradle
  4. 25 0
      ps_lib_smart/proguard-rules.pro
  5. 26 0
      ps_lib_smart/src/androidTest/java/com/paisheng/lib/network/ExampleInstrumentedTest.java
  6. 9 0
      ps_lib_smart/src/main/AndroidManifest.xml
  7. 14 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/CallAdapter.java
  8. 31 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/GetRequestCall.java
  9. 18 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/IRequestManager.java
  10. 20 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/IRxTransfer.java
  11. 21 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/ISetReloadAction.java
  12. 147 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/NewHttpUtils.java
  13. 204 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/PostRequestCall.java
  14. 72 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/RangeRequestCall.java
  15. 464 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/RequestCall.java
  16. 100 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/Response.java
  17. 80 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/SSLSocketFactoryEx.java
  18. 340 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/Smart.java
  19. 76 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/callback/AbstractCallback.java
  20. 118 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/callback/FileCallback.java
  21. 220 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/callback/RangeRequestCallback.java
  22. 26 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/callback/StringCallback.java
  23. 28 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/converter/IConverter.java
  24. 24 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/converter/SmartConverter.java
  25. 25 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/converter/StringConverter.java
  26. 230 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/exception/ApiException.java
  27. 19 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/extra/DefaultCallAdapter.java
  28. 133 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/extra/ServiceMethod.java
  29. 107 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/extra/_Smart.java
  30. 39 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/http/GET.java
  31. 39 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/http/POST.java
  32. 294 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/interceptor/CustomLoggingInterceptor.java
  33. 123 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/interceptor/DNSTimeoutInterceptor.java
  34. 50 0
      ps_lib_smart/src/main/java/com/paisheng/lib/network/interceptor/NotIOExceptionInterceptor.java
  35. 3 0
      ps_lib_smart/src/main/res/values/strings.xml
  36. 14 0
      ps_lib_smart/src/test/java/com/paisheng/lib/network/ExampleUnitTest.java

+ 1 - 0
README.md

@@ -0,0 +1 @@
+# Smart

+ 2 - 0
ps_lib_smart/.gitignore

@@ -0,0 +1,2 @@
+/build
+*.iml

+ 67 - 0
ps_lib_smart/build.gradle

@@ -0,0 +1,67 @@
+apply plugin: 'com.android.library'
+//kotlin支持
+//apply plugin: 'kotlin-android'
+//apply plugin: 'kotlin-android-extensions'
+//apply plugin: 'kotlin-kapt'
+apply plugin: 'maven'
+
+android {
+    compileSdkVersion 27
+    buildToolsVersion "28.0.1"
+
+    defaultConfig {
+        minSdkVersion 14
+        targetSdkVersion 19
+        versionCode rootProject.ext.versionCode.pslib_smart
+        versionName rootProject.ext.versionName.pslib_smart
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+}
+
+dependencies {
+    api fileTree(dir: 'libs', include: ['*.jar'])
+
+    api 'com.android.support:appcompat-v7:27.1.1'
+
+    //http
+    api 'com.squareup.okhttp3:okhttp:3.9.0'
+}
+
+uploadArchives {
+    configuration = configurations.archives
+    repositories {
+        mavenDeployer {
+            snapshotRepository(url: MAVEN_REPO_SNAPSHOT_URL) {
+                authentication(userName: NEXUS_USERNAME, password: NEXUS_PASSWORD)
+            }
+            repository(url: MAVEN_REPO_RELEASE_URL) {
+                authentication(userName: NEXUS_USERNAME, password: NEXUS_PASSWORD)
+            }
+            pom.project {
+                version rootProject.ext.versionName.pslib_smart
+                artifactId 'pslib-smart'
+                groupId GROUP_ID
+                packaging TYPE
+                description DESCRIPTION_SMART
+            }
+        }
+    }
+}
+
+task androidSourcesJar(type: Jar) {
+    classifier = 'sources'
+    from android.sourceSets.main.java.srcDirs
+}
+
+artifacts {
+    archives file('ps_lib_smart.aar')
+    archives androidSourcesJar
+}

+ 25 - 0
ps_lib_smart/proguard-rules.pro

@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in F:\androidsdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 26 - 0
ps_lib_smart/src/androidTest/java/com/paisheng/lib/network/ExampleInstrumentedTest.java

@@ -0,0 +1,26 @@
+package com.paisheng.lib.network;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static junit.framework.Assert.assertEquals;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() throws Exception {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getTargetContext();
+
+        assertEquals("com.paisheng.lib.network.test", appContext.getPackageName());
+    }
+}

+ 9 - 0
ps_lib_smart/src/main/AndroidManifest.xml

@@ -0,0 +1,9 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.paisheng.lib.network">
+
+    <application android:allowBackup="true"
+                 android:supportsRtl="true">
+
+    </application>
+
+</manifest>

+ 14 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/CallAdapter.java

@@ -0,0 +1,14 @@
+package com.paisheng.lib.network;
+
+/**
+ * @author: liaoshengjian
+ * @Filename:
+ * @Description:
+ * @Copyright: Copyright (c) 2016 Tuandai Inc. All rights reserved.
+ * @date: 2017/7/24 16:31
+ */
+public interface CallAdapter<T> {
+
+    T adapt(RequestCall requestCall);
+
+}

+ 31 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/GetRequestCall.java

@@ -0,0 +1,31 @@
+package com.paisheng.lib.network;
+
+import okhttp3.Request;
+
+/**
+ * @author: liaoshengjian
+ * @Filename:
+ * @Description:
+ * @Copyright: Copyright (c) 2016 Tuandai Inc. All rights reserved.
+ * @date: 2017/9/18 16:39
+ */
+public class GetRequestCall extends RequestCall {
+
+    /**
+     *<br> Description: Get请求
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/9/18 16:41
+     *
+     * @param url String
+     */
+    public GetRequestCall(String url) {
+        super(url);
+    }
+
+    @Override
+    public Request generateRequest() {
+        return new Request.Builder()
+                .url(getUrl())
+                .build();
+    }
+}

+ 18 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/IRequestManager.java

@@ -0,0 +1,18 @@
+package com.paisheng.lib.network;
+
+/**
+ * @author: liaoshengjian
+ * @Filename:
+ * @Description:
+ * @Copyright: Copyright (c) 2016 Tuandai Inc. All rights reserved.
+ * @date: 2017/5/12 11:15
+ */
+public interface IRequestManager<T> {
+
+    void addCalls(T call);
+
+    void removeAllCalls();
+
+    void removeCall(T call);
+
+}

+ 20 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/IRxTransfer.java

@@ -0,0 +1,20 @@
+package com.paisheng.lib.network;
+
+import com.paisheng.lib.network.converter.IConverter;
+
+/**
+ * @author: liaoshengjian
+ * @Filename:
+ * @Description:
+ * @Copyright: Copyright (c) 2016 Tuandai Inc. All rights reserved.
+ * @date: 2017/10/30 14:27
+ */
+public interface IRxTransfer<T> {
+
+    IConverter getIConverter();
+
+    CallAdapter<T> getCallAdapter();
+
+
+
+}

+ 21 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/ISetReloadAction.java

@@ -0,0 +1,21 @@
+package com.paisheng.lib.network;
+
+/**
+ * @author: liaoshengjian
+ * @Filename:
+ * @Description:    设置重新加载接口
+ * @Copyright: Copyright (c) 2016 Tuandai Inc. All rights reserved.
+ * @date: 2017/5/20 09:39
+ */
+public interface ISetReloadAction {
+
+    /**
+     *<br> Description: 设置重新加载
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:40
+     *
+     * @param requestCall RequestCall
+     */
+    void setReloadAction(RequestCall requestCall);
+
+}

+ 147 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/NewHttpUtils.java

@@ -0,0 +1,147 @@
+package com.paisheng.lib.network;
+
+import android.os.Handler;
+
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.X509TrustManager;
+
+import okhttp3.OkHttpClient;
+
+/**
+ * @author: liaoshengjian
+ * @Filename:
+ * @Description:    扩展HttpUtils,统一网络请求
+ * @Copyright: Copyright (c) 2016 Tuandai Inc. All rights reserved.
+ * @date: 2017/3/24 16:04
+ */
+@Deprecated
+public class NewHttpUtils {
+
+    /**
+     *<br> Description: 设置自定义OkHttpClient.Builder
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/8/5 15:19
+     *
+     * @param builder OkHttpClient.Builder
+     */
+    public static void init(OkHttpClient.Builder builder) {
+        Smart.initOkHttp(builder);
+    }
+
+    /**
+     *<br> Description: 返回一个OkHttpClient
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:06
+     *
+     * @return client OkHttpClient
+     */
+    public static OkHttpClient getClient() {
+        return Smart.getClient();
+    }
+
+
+
+    /**
+     *<br> Description: post (Json)请求
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:08
+     *
+     * @param url String
+     * @return PostRequestCall
+     */
+    public static PostRequestCall post(String url) {
+        return new PostRequestCall(url);
+    }
+
+    /**
+     *<br> Description: get 请求
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:08
+     *
+     * @param url String
+     * @return RequestCall
+     */
+    public static GetRequestCall get(String url) {
+        return new GetRequestCall(url);
+    }
+
+
+    public static Handler getDelivery() {
+        return Smart.getDelivery();
+    }
+
+
+    /**
+     *<br> Description: 指定到UI线程执行
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:08
+     *
+     * @param runnable Runnable
+     */
+    public static void runOnUIThread(Runnable runnable) {
+        Smart.runOnUIThread(runnable);
+    }
+
+    /**
+     *<br> Description: 判断是否主线程
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/9/15 17:18
+     */
+    public static boolean isMainThread() {
+        return Smart.isMainThread();
+    }
+
+    public static void setDebugMode(boolean mode) {
+        Smart.setDebugMode(mode);
+    }
+
+    public static boolean getDebugMode() {
+        return Smart.getDebugMode();
+    }
+
+
+
+    /**
+     *<br> Description: createSSLSocketFactory
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:10
+     *
+     * @return SSLSocketFactory
+     */
+    public static SSLSocketFactory createSSLSocketFactory() {
+        return Smart.createSSLSocketFactory();
+    }
+
+    /**
+     *<br> Description: TrustAllManager
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:10
+     */
+    private static class TrustAllManager implements X509TrustManager {
+
+        /**
+         * checkClientTrusted
+         */
+        @Override
+        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType)
+                throws java.security.cert.CertificateException {
+        }
+
+        /**
+         * checkClientTrusted
+         */
+        @Override
+        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType)
+                throws java.security.cert.CertificateException {
+        }
+
+        /**
+         * checkClientTrusted
+         */
+        @Override
+        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+            return new java.security.cert.X509Certificate[0];
+        }
+    }
+
+}

+ 204 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/PostRequestCall.java

@@ -0,0 +1,204 @@
+package com.paisheng.lib.network;
+
+import android.text.TextUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import okhttp3.FormBody;
+import okhttp3.MediaType;
+import okhttp3.MultipartBody;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+
+/**
+ * @author: liaoshengjian
+ * @Filename:
+ * @Description:
+ * @Copyright: Copyright (c) 2016 Tuandai Inc. All rights reserved.
+ * @date: 2017/4/28 10:53
+ */
+public class PostRequestCall extends RequestCall {
+
+
+    /**
+     * Json
+     */
+    private String mContent = "";
+
+    /**
+     * 普通表单
+     */
+    private LinkedHashMap<String, String> mSimpleParams = new LinkedHashMap<>();
+
+    /**
+     * MultiPart
+     */
+    private LinkedHashMap<String, List<MultiPartFile>> mFileParams;
+
+
+    /**
+     *<br> Description: Post请求
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:41
+     *
+     * @param url String
+     */
+    public PostRequestCall(String url) {
+        super(url);
+    }
+
+
+    /**
+     *<br> Description: 增加Json参数
+     *                  *注意:与addParams互斥,addJson优先级最高
+     *
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:41
+     *
+     * @param json String
+     * @return PostRequestCall
+     */
+    public PostRequestCall addJson(String json) {
+        this.mContent = json;
+        return this;
+    }
+
+    /**
+     *<br> Description: 增加表单数据K-V
+     *                  *注意:与addJson互斥,addJson优先级最高
+     *
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/12/21 14:47
+     *
+     * @param key 键
+     * @param value 值
+     * @return PostRequestCall
+     */
+    public PostRequestCall addParams(String key, String value) {
+        mSimpleParams.put(key, value);
+        return this;
+    }
+
+    /**
+     *<br> Description: 批量增加表单数据K-V (Map数据格式)
+     *                  *注意:与addJson互斥,addJson优先级最高
+     *
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/12/21 14:48
+     *
+     * @param paramsMap Map数据格式表单数据
+     * @return PostRequestCall
+     */
+    public PostRequestCall addParams(Map<String, String> paramsMap) {
+        if (paramsMap != null && !paramsMap.isEmpty()) {
+            for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
+                mSimpleParams.put(entry.getKey(), entry.getValue());
+            }
+        }
+        return this;
+    }
+
+    /**
+     *<br> Description: 增加文件(Multipart提交)
+     *                  *注意:与addJson互斥,addJson优先级最高
+     *
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/12/21 14:48
+     *
+     * @param key key
+     * @param file 上传文件
+     * @param fileName 上传文件名
+     * @return PostRequestCall
+     */
+    public PostRequestCall addParams(String key, File file, String fileName) {
+        if (!TextUtils.isEmpty(key) && file != null) {
+            if (mFileParams == null) {
+                mFileParams = new LinkedHashMap<>();
+            }
+
+            List<MultiPartFile> fileList = mFileParams.get(key);
+            if (fileList == null) {
+                fileList = new ArrayList<>();
+                mFileParams.put(key, fileList);
+            }
+            fileList.add(new MultiPartFile(file,
+                    !TextUtils.isEmpty(fileName) ? fileName : file.getName()));
+        }
+        return this;
+    }
+
+    @Override
+    public Request generateRequest() {
+        Request.Builder mBuilder = new Request.Builder().url(getUrl());
+
+        //默认:"application/json; charset=utf-8"
+        if (!TextUtils.isEmpty(mContent) ||
+                (mSimpleParams.isEmpty() && (mFileParams == null || mFileParams.isEmpty()))) {
+            return mBuilder
+                    .post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), mContent))
+                    .build();
+        }
+
+        //普通表单提交
+        if (mFileParams == null || mFileParams.isEmpty()) {
+            FormBody.Builder bodyBuilder = new FormBody.Builder();
+            for (String key : mSimpleParams.keySet()) {
+                bodyBuilder.addEncoded(key, mSimpleParams.get(key));
+            }
+            return mBuilder
+                    .post(bodyBuilder.build())
+                    .build();
+        }
+
+        //multipart
+        MultipartBody.Builder multipartBodybuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
+        if (!mSimpleParams.isEmpty()) {
+            for (String key : mSimpleParams.keySet()) {
+                multipartBodybuilder.addFormDataPart(key, mSimpleParams.get(key));
+            }
+        }
+        //拼接文件
+        for (Map.Entry<String, List<MultiPartFile>> entry : mFileParams.entrySet()) {
+            List<MultiPartFile> fileValues = entry.getValue();
+            for (MultiPartFile multiPartFile : fileValues) {
+                RequestBody fileBody = RequestBody.create(MultipartBody.FORM, multiPartFile.mFile);
+                multipartBodybuilder.addFormDataPart(entry.getKey(), multiPartFile.mFileName, fileBody);
+            }
+        }
+        return mBuilder
+                .post(multipartBodybuilder.build())
+                .build();
+    }
+
+
+    /** 文件类型的包装类 */
+    private static final class MultiPartFile {
+
+        /**
+         * 文件
+         */
+        private File mFile;
+        /**
+         * 文件名
+         */
+        private String mFileName;
+
+        /**
+         *<br> Description: 构造函数
+         *<br> Author:      liaoshengjian
+         *<br> Date:        2017/12/21 14:55
+         *
+         * @param file 文件
+         * @param fileName 文件名
+         */
+        private MultiPartFile(File file, String fileName) {
+            this.mFile = file;
+            this.mFileName = fileName;
+        }
+
+    }
+}

+ 72 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/RangeRequestCall.java

@@ -0,0 +1,72 @@
+package com.paisheng.lib.network;
+
+import android.util.Log;
+
+import com.paisheng.lib.network.callback.AbstractCallback;
+import com.paisheng.lib.network.callback.RangeRequestCallback;
+
+import java.io.File;
+
+import okhttp3.Request;
+
+/**
+ * <br> ClassName:   RangeRequestCall
+ * <br> Description: Range请求
+ * <br>
+ * <br> Author:      yexiaochuan
+ * <br> Date:        2018/3/14 15:18
+ */
+public class RangeRequestCall extends RequestCall {
+    private final String mFileName;
+    private long mRangeSPos;
+    private String mDownLoadPath;
+    private String mFilePath;
+    private boolean mIsRest;
+
+    public RangeRequestCall(String url, String downLoadPath, String fileName) {
+        super(url);
+        mDownLoadPath = downLoadPath;
+        mFileName = fileName;
+        mFilePath = downLoadPath + "/" + mFileName;
+    }
+
+    /**
+     *<br> Description: 重置起始位置为0
+     *<br> Author:      yexiaochuan
+     *<br> Date:        2018/3/20 10:11
+     */
+    public void resetRangeSPos() {
+        mIsRest = true;
+        mRangeSPos = 0;
+    }
+
+    /**
+     *<br> Description: 重置请求成功
+     *<br> Author:      yexiaochuan
+     *<br> Date:        2018/3/20 10:44
+     */
+    public void resetOK() {
+        mIsRest = false;
+    }
+
+    @Override
+    public Request generateRequest() {
+        if (!mIsRest) {
+            mRangeSPos = new File(mFilePath).length();  //向服务器定位下载位置的起始点
+//                    Log.i("update","rangeStart : " + mRangeSPos);
+        }
+        return (new Request.Builder()).url(this.getUrl()).addHeader("RANGE", "bytes=" + mRangeSPos + "-").build();
+    }
+
+    @Override
+    public RequestCall execute(AbstractCallback abstractCallback) {
+        if (abstractCallback == null) {
+            return this;
+        }
+
+        if (abstractCallback instanceof RangeRequestCallback) {
+            ((RangeRequestCallback)abstractCallback).onStart(mDownLoadPath, mFileName);
+        }
+        return super.execute(abstractCallback);
+    }
+}

+ 464 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/RequestCall.java

@@ -0,0 +1,464 @@
+package com.paisheng.lib.network;
+
+import android.text.TextUtils;
+
+import com.paisheng.lib.network.callback.AbstractCallback;
+import com.paisheng.lib.network.converter.IConverter;
+import com.paisheng.lib.network.exception.ApiException;
+
+import java.io.IOException;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLException;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+/**
+ * @author: liaoshengjian
+ * @Filename:
+ * @Description:
+ * @Copyright: Copyright (c) 2016 Tuandai Inc. All rights reserved.
+ * @date: 2017/4/28 10:53
+ */
+public abstract class RequestCall {
+
+    private String mUrl;
+
+    private String mTaskId;
+    private Call mCall;
+
+    private long mReadTimeOut;
+    private long mWriteTimeOut;
+    private long mConnectTimeout;
+
+    private volatile boolean mCanceled;
+
+    /**
+     * 当前请求的AbstractCallback
+     */
+    private AbstractCallback mAbstractCallback;
+
+    /**
+     * 当前请求的IConverter
+     */
+    private IConverter mIConverter;
+
+    /**
+     * 请求管理
+     */
+    private Reference<IRequestManager> mIRequestManager;
+
+    /**
+     *<br> Description: 构造函数
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:44
+     *
+     * @param url String
+     */
+    public RequestCall(String url) {
+        this.mUrl = url;
+        this.mTaskId = url;
+    }
+
+    /**
+     *<br> Description: 设置重新加载请求
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:45
+     *
+     * @param iSetReloadAction ISetReloadAction
+     * @return RequestCall
+     */
+    public RequestCall setReload(ISetReloadAction iSetReloadAction) {
+        if (iSetReloadAction != null) {
+            iSetReloadAction.setReloadAction(this);
+        }
+        return this;
+    }
+
+    /**
+     *<br> Description: 设置请求管理器
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/23 14:11
+     *
+     * @param iRequestManager IRequestManager
+     * @return RequestCall
+     */
+    public RequestCall setRequestManager(IRequestManager iRequestManager) {
+        if (iRequestManager != null) {
+            this.mIRequestManager = new WeakReference<IRequestManager>(iRequestManager);
+        }
+        return this;
+    }
+
+    /**
+     *<br> Description: 设置Url
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/8/14 13:48
+     *
+     * @param url String
+     * @return RequestCall
+     */
+    public RequestCall setUrl(String url) {
+        if (!TextUtils.isEmpty(url)) {
+            this.mUrl = url;
+            this.mTaskId = url;
+        }
+        return this;
+    }
+
+    /**
+     *<br> Description: 设置读取超时
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:45
+     *
+     * @param readTimeOut long
+     * @return RequestCall
+     */
+    public RequestCall setReadTimeout(long readTimeOut) {
+        this.mReadTimeOut = readTimeOut;
+        return this;
+    }
+
+    /**
+     *<br> Description: 设置写入超时
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:45
+     *
+     * @param writeTimeOut long
+     * @return RequestCall
+     */
+    public RequestCall setWriteTimeOut(long writeTimeOut) {
+        this.mWriteTimeOut = writeTimeOut;
+        return this;
+    }
+
+    /**
+     *<br> Description: 设置socket连接超时
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:45
+     *
+     * @param connectTimeout long
+     * @return RequestCall
+     */
+    public RequestCall setConnectTimeout(long connectTimeout) {
+        this.mConnectTimeout = connectTimeout;
+        return this;
+    }
+
+    /**
+     *<br> Description: 包装OkHttpClient
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:45
+     *
+     * @return OkHttpClient
+     */
+    private OkHttpClient generateClient() {
+        if (mReadTimeOut <= 0 && mWriteTimeOut <= 0 && mConnectTimeout <= 0) {
+            return Smart.getClient();
+        } else {
+            OkHttpClient.Builder newClientBuilder = Smart.getClient().newBuilder();
+            if (mReadTimeOut > 0) {
+                newClientBuilder.readTimeout(mReadTimeOut, TimeUnit.MILLISECONDS);
+            }
+            if (mWriteTimeOut > 0) {
+                newClientBuilder.writeTimeout(mWriteTimeOut, TimeUnit.MILLISECONDS);
+            }
+            if (mConnectTimeout > 0) {
+                newClientBuilder.connectTimeout(mConnectTimeout, TimeUnit.MILLISECONDS);
+            }
+            return newClientBuilder.build();
+        }
+    }
+
+    /**
+     *<br> Description: 获取任务Id
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:45
+     *
+     * @return String
+     */
+    public String getTaskId() {
+        return TextUtils.isEmpty(mTaskId) ? "" : mTaskId;
+    }
+
+    /**
+     *<br> Description: 获取Url
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/8/14 13:50
+     *
+     * @return String
+     */
+    public String getUrl() {
+        return TextUtils.isEmpty(mUrl) ? "" : mUrl;
+    }
+
+    /**
+     *<br> Description: 获取OkHttp的Call
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:45
+     *
+     * @return Call
+     */
+    public Call getCall() {
+        return  mCall;
+    }
+
+    /**
+     *<br> Description: 重新请求
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:50
+     *
+     * @return RequestCall
+     */
+    public RequestCall retry() {
+        if (this.mCall != null) {
+            this.mCall.cancel();
+            this.mCall = null;
+        }
+        if (mAbstractCallback != null) {
+            return execute(mAbstractCallback);
+        }
+        return null;
+    }
+
+    /**
+     *<br> Description: 将RequeCall加入请求管理器
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/23 14:17
+     *
+     * @param requestCall RequestCall
+     */
+    private void addToRequestManager(RequestCall requestCall) {
+        if (this.mIRequestManager != null && this.mIRequestManager.get() != null && requestCall != null) {
+            this.mIRequestManager.get().addCalls(requestCall);
+        }
+    }
+
+    /**
+     *<br> Description: 将RequeCall从请求管理器移除
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/23 14:17
+     *
+     * @param requestCall RequestCall
+     */
+    private void removeFromReqManager(RequestCall requestCall) {
+        if (this.mIRequestManager != null && this.mIRequestManager.get() != null && requestCall != null) {
+            this.mIRequestManager.get().removeCall(requestCall);
+        }
+    }
+
+    /**
+     *<br> Description: 兼容RxJava
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/9/14 16:36
+     *
+     * @param adapter CallAdapter
+     */
+    @SuppressWarnings("unchecked")
+    public <R> R adapt(CallAdapter<R> adapter) {
+        return adapter.adapt(this);
+    }
+
+    /**
+     *<br> Description: 兼容RxJava,设置转换器IConverter
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/9/14 16:36
+     *
+     * @param iConverter IConverter
+     * @return RequestCall
+     */
+    @SuppressWarnings("unchecked")
+    public RequestCall converter(IConverter iConverter) {
+        this.mIConverter = iConverter;
+        return this;
+    }
+
+    /**
+     *<br> Description: 获取转换器IConverter
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/9/14 16:42
+     */
+    public IConverter getConverter() {
+        return this.mIConverter;
+    }
+
+    public <R> R toRx(IRxTransfer<R> iRxTransfer) {
+        this.mIConverter = iRxTransfer.getIConverter();
+        return iRxTransfer.getCallAdapter().adapt(this);
+    }
+
+    /**
+     *<br> Description: 取消请求
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/9/19 20:40
+     */
+    public void cancel() {
+        mCanceled = true;
+        if (this.mCall != null) {
+            this.mCall.cancel();
+        }
+    }
+
+    /**
+     *<br> Description: 是否取消了请求
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/9/19 20:40
+     */
+    public boolean isCanceled() {
+        if (mCanceled) {
+            return true;
+        }
+        synchronized (this) {
+            return this.mCall != null && this.mCall.isCanceled();
+        }
+    }
+
+    /**
+     *<br> Description: 包装okhttp3.Request,区分post\get
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/9/19 20:40
+     */
+    public abstract Request generateRequest();
+
+
+    /**
+     *<br> Description: 执行请求
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:50
+     *
+     * @param abstractCallback AbstractCallback
+     * @return RequestCall
+     */
+    public RequestCall execute(AbstractCallback abstractCallback) {
+
+        mAbstractCallback = abstractCallback;
+        mAbstractCallback.onStart();
+
+        if (Smart.isMainThread() && !Smart.isNetWorkAvailable()) {
+            //TODO mTaskId 处理重新刷新
+            sendOnFailure(mAbstractCallback, null, new ApiException(ApiException.CODE_NO_NETWORK, "无网络连接"));
+            addToRequestManager(this);
+            return this;
+        }
+
+        Call mCall = generateClient().newCall(generateRequest());
+        if (mCanceled) {
+            removeFromReqManager(this);
+            return this;
+        }
+        mCall.enqueue(new Callback() {
+            @Override
+            public void onFailure(final Call call, final IOException e) {
+                removeFromReqManager(RequestCall.this);
+
+                Smart.runOnUIThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (!mAbstractCallback.onNeedRetry(RequestCall.this)) {
+                            if (e instanceof SocketTimeoutException) {
+                                sendOnFailure(mAbstractCallback, call,
+                                        new ApiException(ApiException.CODE_OKHTTP_SOCKET_TIMEOUT, e, mUrl));
+                            } else if (e instanceof SocketException) {
+                                sendOnFailure(mAbstractCallback, call,
+                                        new ApiException(ApiException.CODE_OKHTTP_SOCKET_EXP, e, mUrl));
+                            } else if (e instanceof SSLException) {
+                                sendOnFailure(mAbstractCallback, call,
+                                        new ApiException(ApiException.CODE_OKHTTP_SSL_EXP, e, mUrl));
+                            } else {
+                                sendOnFailure(mAbstractCallback, call,
+                                        new ApiException(ApiException.CODE_OKHTTP_FAILURE, e, mUrl));
+                            }
+                        }
+                    }
+                });
+
+            }
+
+            @Override
+            public void onResponse(final Call call, final Response response) throws IOException {
+                try {
+                    removeFromReqManager(RequestCall.this);
+
+                    if (response.isSuccessful()) {
+                        final Object o = mAbstractCallback.parseResponse(call, response);
+                        Smart.runOnUIThread(new Runnable() {
+                            @Override
+                            public void run() {
+                                sendOnSuccess(mAbstractCallback, o);
+                            }
+                        });
+                    } else {
+                        Smart.runOnUIThread(new Runnable() {
+                            @Override
+                            public void run() {
+                                if (!mAbstractCallback.onNeedRetry(RequestCall.this)) {
+                                    ApiException apiException = new ApiException(ApiException.CODE_OKHTTP_NO_SUCCESS, response.message(), response.code());
+                                    apiException.setUrl(mUrl);
+                                    sendOnFailure(mAbstractCallback, call, apiException);
+                                }
+                            }
+                        });
+
+                    }
+
+                } catch (final Exception exp) {
+                    Smart.runOnUIThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (exp instanceof ApiException) {
+                                sendOnFailure(mAbstractCallback, call, (ApiException) exp);
+                            } else {
+                                sendOnFailure(mAbstractCallback, call,
+                                        new ApiException(ApiException.CODE_OTHER_EXCEPTION, exp));
+                            }
+                        }
+                    });
+                } finally {
+                    if (response.body() != null) {
+                        response.body().close();
+                    }
+                }
+            }
+        });
+
+        this.mCall = mCall;
+        addToRequestManager(this);
+        return this;
+    }
+
+    /**
+     *<br> Description: 发送请求成功结果
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:51
+     *
+     * @param abstractCallback AbstractCallback
+     * @param object Object
+     */
+    private void sendOnSuccess(AbstractCallback abstractCallback, Object object) {
+        abstractCallback.onSuccess(object);
+        abstractCallback.onComplete(this, object);
+    }
+
+    /**
+     *<br> Description: 发送请求失败结果
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:51
+     *
+     * @param abstractCallback AbstractCallback
+     * @param call Call
+     * @param apiException ApiException
+     */
+    private void sendOnFailure(AbstractCallback abstractCallback, Call call, ApiException apiException) {
+        abstractCallback.onFailure(this, apiException);
+        abstractCallback.onComplete(this, null);
+    }
+
+}

+ 100 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/Response.java

@@ -0,0 +1,100 @@
+package com.paisheng.lib.network;
+
+import okhttp3.Call;
+import okhttp3.Headers;
+
+/**
+ * @author: liaoshengjian
+ * @Filename:
+ * @Description:
+ * @Copyright: Copyright (c) 2016 Tuandai Inc. All rights reserved.
+ * @date: 2017/7/24 11:57
+ */
+public final class Response<T> {
+
+    private T body;
+    private Throwable throwable;
+    private boolean isFromCache;
+    private okhttp3.Call rawCall;
+    private okhttp3.Response rawResponse;
+
+    public static <T> Response<T> success(boolean isFromCache, T body, Call rawCall, okhttp3.Response rawResponse) {
+        Response<T> response = new Response<>();
+        response.setFromCache(isFromCache);
+        response.setBody(body);
+        response.setRawCall(rawCall);
+        response.setRawResponse(rawResponse);
+        return response;
+    }
+
+    public static <T> Response<T> error(boolean isFromCache, Call rawCall, okhttp3.Response rawResponse, Throwable throwable) {
+        Response<T> response = new Response<>();
+        response.setFromCache(isFromCache);
+        response.setRawCall(rawCall);
+        response.setRawResponse(rawResponse);
+        response.setException(throwable);
+        return response;
+    }
+
+    public Response() {
+    }
+
+    public int code() {
+        if (rawResponse == null) return -1;
+        return rawResponse.code();
+    }
+
+    public String message() {
+        if (rawResponse == null) return null;
+        return rawResponse.message();
+    }
+
+    public Headers headers() {
+        if (rawResponse == null) return null;
+        return rawResponse.headers();
+    }
+
+    public boolean isSuccessful() {
+        return throwable == null;
+    }
+
+    public void setBody(T body) {
+        this.body = body;
+    }
+
+    public T body() {
+        return body;
+    }
+
+    public Throwable getException() {
+        return throwable;
+    }
+
+    public void setException(Throwable exception) {
+        this.throwable = exception;
+    }
+
+    public Call getRawCall() {
+        return rawCall;
+    }
+
+    public void setRawCall(Call rawCall) {
+        this.rawCall = rawCall;
+    }
+
+    public okhttp3.Response getRawResponse() {
+        return rawResponse;
+    }
+
+    public void setRawResponse(okhttp3.Response rawResponse) {
+        this.rawResponse = rawResponse;
+    }
+
+    public boolean isFromCache() {
+        return isFromCache;
+    }
+
+    public void setFromCache(boolean fromCache) {
+        isFromCache = fromCache;
+    }
+}

+ 80 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/SSLSocketFactoryEx.java

@@ -0,0 +1,80 @@
+package com.paisheng.lib.network;
+
+import org.apache.http.conn.ssl.SSLSocketFactory;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * <br> ClassName:   SSLSocketFactoryEx
+ * <br> Description: SSLSocketFactoryEx
+ * <br>
+ * <br> Author:      hehaodong
+ * <br> Date:        2016-09-13 10:43
+ */
+public class SSLSocketFactoryEx extends SSLSocketFactory {
+
+    SSLContext mSSLContext = SSLContext.getInstance("TLS");
+
+    /**
+     *<br> Description: SSLSocketFactoryEx
+     *<br> Author:      hehaodong
+     *<br> Date:        2016-09-13 10:43
+     *
+     * @param truststore KeyStore
+     * @throws NoSuchAlgorithmException ne
+     * @throws KeyManagementException ke
+     * @throws KeyStoreException keye
+     * @throws UnrecoverableKeyException ue
+     */
+    public SSLSocketFactoryEx(KeyStore truststore)
+            throws NoSuchAlgorithmException, KeyManagementException,
+            KeyStoreException, UnrecoverableKeyException {
+        super(truststore);
+
+        TrustManager tm = new X509TrustManager() {
+
+            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+                return null;
+            }
+
+            @Override
+            public void checkClientTrusted(
+                    java.security.cert.X509Certificate[] chain, String authType)
+                    throws java.security.cert.CertificateException {
+
+            }
+
+            @Override
+            public void checkServerTrusted(
+                    java.security.cert.X509Certificate[] chain, String authType)
+                    throws java.security.cert.CertificateException {
+
+            }
+        };
+
+        mSSLContext.init(null, new TrustManager[] { tm }, null);
+    }
+
+    @Override
+    public Socket createSocket(Socket socket, String host, int port,
+                               boolean autoClose) throws IOException, UnknownHostException {
+        return mSSLContext.getSocketFactory().createSocket(socket, host, port,
+                autoClose);
+    }
+
+    @Override
+    public Socket createSocket() throws IOException {
+        return mSSLContext.getSocketFactory().createSocket();
+    }
+}

+ 340 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/Smart.java

@@ -0,0 +1,340 @@
+package com.paisheng.lib.network;
+
+import android.app.Application;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Handler;
+import android.os.Looper;
+
+import com.paisheng.lib.network.converter.SmartConverter;
+import com.paisheng.lib.network.extra.DefaultCallAdapter;
+import com.paisheng.lib.network.extra._Smart;
+import com.paisheng.lib.network.interceptor.NotIOExceptionInterceptor;
+
+import java.security.SecureRandom;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import okhttp3.OkHttpClient;
+
+/**
+ * <br> ClassName:   Smart
+ * <br> Description: Smart 网络请求
+ * <br>
+ * <br> Author:      liaoshengjian
+ * <br> Date:        2017/11/3 14:36
+ */
+public final class Smart {
+
+    //application
+    private static Context mContext;
+
+    private static boolean isDebug = false;
+
+    private static final Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
+
+    private volatile static OkHttpClient client;
+    private static OkHttpClient.Builder customBuilder;
+
+    public static void init(Application application) {
+        mContext = application;
+    }
+
+    /**
+     *<br> Description: 设置自定义OkHttpClient.Builder
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/8/5 15:19
+     *
+     * @param builder OkHttpClient.Builder
+     */
+    public static void initOkHttp(OkHttpClient.Builder builder) {
+        if (builder == null) {
+            throw new IllegalArgumentException("builder must not be null.");
+        }
+        synchronized (Smart.class) {
+            if (customBuilder != null) {
+                throw new IllegalStateException("customBuilder already exists.");
+            }
+            customBuilder = builder;
+        }
+    }
+
+    /**
+     *<br> Description: 返回一个OkHttpClient
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:06
+     *
+     * @return client OkHttpClient
+     */
+    public static OkHttpClient getClient() {
+        if (client == null) {
+            synchronized (Smart.class) {
+                if (client == null) {
+                    if (customBuilder != null) {
+                        customBuilder.addInterceptor(new NotIOExceptionInterceptor());
+                        client = customBuilder.build();
+                    } else {
+                        OkHttpClient.Builder mBuilder = new OkHttpClient.Builder();
+                        mBuilder.addInterceptor(new NotIOExceptionInterceptor());
+                        mBuilder.sslSocketFactory(createSSLSocketFactory());
+                        mBuilder.retryOnConnectionFailure(false);
+                        client = mBuilder.build();
+                    }
+
+                }
+            }
+        }
+        return client;
+    }
+
+    /**
+     *<br> Description: 单独返回一个新的OkHttpClient
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2018/8/10 12:05
+     */
+    public static OkHttpClient getNewClient(OkHttpClient.Builder builder) {
+        if (builder != null) {
+            builder.addInterceptor(new NotIOExceptionInterceptor());
+            return builder.build();
+        }
+
+        OkHttpClient.Builder mBuilder = new OkHttpClient.Builder();
+        mBuilder.addInterceptor(new NotIOExceptionInterceptor());
+        mBuilder.retryOnConnectionFailure(false);
+        return mBuilder.build();
+    }
+
+
+
+    /**
+     *<br> Description: post (Json)请求
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:08
+     *
+     * @param url String
+     * @return PostRequestCall
+     */
+    public static PostRequestCall post(String url) {
+        return new PostRequestCall(url);
+    }
+
+    /**
+     *<br> Description: get 请求
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:08
+     *
+     * @param url String
+     * @return RequestCall
+     */
+    public static GetRequestCall get(String url) {
+        return new GetRequestCall(url);
+    }
+
+    /**
+     *<br> Description: Range请求
+     *<br> Author:      yexiaochuan
+     *<br> Date:        2018/3/14 19:00
+     *
+     * @param url      请求的Get地址
+     * @param downloadPath 下载路径
+     * @param fileName     文件名,必须与Url一一对应,否则会出现多个请求地址下载到同一文件
+     * @return
+     */
+    public static RangeRequestCall range(String url, String downloadPath, String fileName) {
+        return new RangeRequestCall(url, downloadPath, fileName);
+    }
+
+    public static Handler getDelivery() {
+        return MAIN_HANDLER;
+    }
+
+
+    /**
+     *<br> Description: 指定到UI线程执行
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:08
+     *
+     * @param runnable Runnable
+     */
+    public static void runOnUIThread(Runnable runnable) {
+        if (runnable != null) {
+            MAIN_HANDLER.post(runnable);
+        }
+    }
+
+    /**
+     *<br> Description: 判断是否主线程
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/9/15 17:18
+     *
+     * @return boolean 是否主线程
+     */
+    public static boolean isMainThread() {
+        return Looper.getMainLooper().getThread() == Thread.currentThread();
+    }
+
+    /**
+     *<br> Description: 设置为调试模式
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/11/3 14:38
+     *
+     * @param mode boolean 模式
+     */
+    public static void setDebugMode(boolean mode) {
+        isDebug = mode;
+    }
+
+    /**
+     *<br> Description: 获取模式状态
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/11/3 14:38
+     *
+     * @return boolean 是否为调试模式
+     */
+    public static boolean getDebugMode() {
+        return isDebug;
+    }
+
+    /**
+     * <br> Description: 是否有网络
+     * <br> Author:      liaoshengjian
+     * <br> Date:        2017/6/14 16:49
+     *
+     * @return boolean
+     */
+    public static boolean isNetWorkAvailable() {
+        if (mContext == null) {
+            throw new IllegalArgumentException("Method Smart.init() must be called first !!!");
+        }
+        ConnectivityManager mgr = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        if (mgr != null) {
+            NetworkInfo info = mgr.getActiveNetworkInfo();
+            if (info != null && info.getState() == NetworkInfo.State.CONNECTED) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     *<br> Description: createSSLSocketFactory
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:10
+     *
+     * @return SSLSocketFactory
+     */
+    public static SSLSocketFactory createSSLSocketFactory() {
+
+        SSLSocketFactory sSLSocketFactory = null;
+        try {
+            SSLContext sc = SSLContext.getInstance("TLS");
+            sc.init(null, new TrustManager[]{new TrustAllManager()},
+                    new SecureRandom());
+            sSLSocketFactory = sc.getSocketFactory();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return sSLSocketFactory;
+    }
+
+    /**
+     *<br> Description: TrustAllManager
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:10
+     */
+    private static class TrustAllManager implements X509TrustManager {
+
+        /**
+         * checkClientTrusted
+         */
+        @Override
+        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType)
+                throws java.security.cert.CertificateException {
+        }
+
+        /**
+         * checkClientTrusted
+         */
+        @Override
+        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType)
+                throws java.security.cert.CertificateException {
+        }
+
+        /**
+         * checkClientTrusted
+         */
+        @Override
+        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+            return new java.security.cert.X509Certificate[0];
+        }
+    }
+
+
+    private _Smart mSmart;
+    public Smart(Builder builder) {
+        mSmart = new _Smart(builder);
+    }
+
+    public void refreshBaseUrl(String url) {
+        if (mSmart == null || mSmart.getBuilder() == null) {
+            throw new IllegalArgumentException("new Builder().build() has never called");
+        }
+        if (!mSmart.getBaseUrl().equals(url)) {
+            mSmart.refreshBuilder(mSmart.getBuilder().baseUrl(url));
+        }
+    }
+
+
+
+    @SuppressWarnings("unchecked")
+    public <T> T create(final Class<T> service) {
+        return mSmart.create(service);
+    }
+
+    public static final class Builder {
+
+        private SmartConverter mSmartConverter;
+        private CallAdapter mCallAdapter;
+        private String mBaseUrl = "'";
+
+        public SmartConverter getSmartConverter() {
+            return mSmartConverter;
+        }
+
+        public CallAdapter getCallAdapter() {
+            return mCallAdapter;
+        }
+
+        public String getBaseUrl() {
+            return mBaseUrl;
+        }
+
+        public Builder(SmartConverter converter, CallAdapter callAdapter) {
+            this.mSmartConverter = converter;
+            this.mCallAdapter = callAdapter;
+        }
+        public Builder() {
+            this.mCallAdapter = new DefaultCallAdapter();
+        }
+
+        public Builder baseUrl(String baseUrl) {
+            this.mBaseUrl = baseUrl;
+            return this;
+        }
+
+
+        public Smart build() {
+            return new Smart(this);
+        }
+    }
+
+
+
+
+}

+ 76 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/callback/AbstractCallback.java

@@ -0,0 +1,76 @@
+package com.paisheng.lib.network.callback;
+
+import com.paisheng.lib.network.RequestCall;
+import com.paisheng.lib.network.converter.IConverter;
+import com.paisheng.lib.network.exception.ApiException;
+
+/**
+ * @author: liaoshengjian
+ * @Filename:
+ * @Description:    对OkHttp Callback 的扩展
+ * @Copyright: Copyright (c) 2016 Tuandai Inc. All rights reserved.
+ * @date: 2017/3/25 09:52
+ */
+public abstract class AbstractCallback<T> implements IConverter<T>{
+
+    /**
+     *<br> Description: 发起请求前(主线程)
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:17
+     */
+    public void onStart() {
+
+    }
+
+    /**
+     *<br> Description: 请求失败(主线程)
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:18
+     *
+     * @param requestCall RequestCall
+     * @param e ApiException
+     */
+    public abstract void onFailure(RequestCall requestCall, ApiException e);
+
+    /**
+     *<br> Description: 请求成功(主线程)
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:18
+     *
+     * @param response T
+     */
+    public abstract void onSuccess(T response);
+
+    /**
+     *<br> Description: (下载)进度(主线程)
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:18
+     *
+     * @param progress float
+     * @param total long
+     */
+    public void onProgress(float progress, long total) {
+
+    }
+
+    /**
+     *<br> Description: 请求完成(成功、失败都会调用,主线程)
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:19
+     *
+     * @param requestCall RequestCall
+     * @param response T
+     */
+    public void onComplete(RequestCall requestCall, T response) {
+
+    }
+
+    /**
+     *<br> Description: 是否需要重发,默认为false(不需要失败重发)
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2018/3/23 16:18
+     */
+    public boolean onNeedRetry(RequestCall requestCall) {
+        return false;
+    }
+}

+ 118 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/callback/FileCallback.java

@@ -0,0 +1,118 @@
+package com.paisheng.lib.network.callback;
+
+import com.paisheng.lib.network.Smart;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import okhttp3.Call;
+import okhttp3.Response;
+
+/**
+ * @author: liaoshengjian
+ * @Filename:
+ * @Description:    文件下载回调处理
+ * @Copyright: Copyright (c) 2016 Tuandai Inc. All rights reserved.
+ * @date: 2017/3/25 16:11
+ */
+public abstract class FileCallback extends AbstractCallback<File> {
+
+    private String mSaveFileDir;
+
+    private String mSaveFileName;
+
+    /**
+     *<br> Description: 构造函数
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:25
+     *
+     * @param saveFileDir 保存文件目录
+     * @param saveFileName 保存文件名
+     */
+    public FileCallback(String saveFileDir, String saveFileName) {
+        this.mSaveFileDir = saveFileDir;
+        this.mSaveFileName = saveFileName;
+    }
+
+    @Override
+    public File parseResponse(Call call, Response response) throws Exception {
+        return saveFile(response);
+    }
+
+    /**
+     *<br> Description: 保存文件
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:26
+     *
+     * @param response Response
+     * @return File
+     * @throws IOException e
+     */
+    private File saveFile(Response response) throws IOException {
+
+        InputStream is = null;
+        byte[] buf = new byte[2048];
+        int len = 0;
+        FileOutputStream fos = null;
+
+        try {
+            is = response.body().byteStream();
+            final long total = response.body().contentLength();
+
+            long sum = 0;
+            File dir = new File(mSaveFileDir);
+            if (!dir.exists()) {
+                dir.mkdirs();
+            }
+            File file = new File(dir, mSaveFileName);
+            fos = new FileOutputStream(file);
+
+            float lastProgress = 0;
+            while ((len = is.read(buf)) != -1) {
+                sum += len;
+                fos.write(buf, 0, len);
+                final long finalSum = sum;
+
+                //通知刷新进度
+                //过滤频繁刷新抖动,导致的卡顿问题
+                final float newProgress = finalSum * 1.0f / total;
+                if ((int) (100 * newProgress) - (int) (100 * lastProgress) > 1) {
+                    Smart.runOnUIThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            onProgress(newProgress, total);
+                        }
+                    });
+                    lastProgress = newProgress;
+                }
+            }
+            fos.flush();
+
+            return file;
+
+
+        } finally {
+            try {
+                response.body().close();
+                if (is != null) {
+                    is.close();
+                }
+            } catch (IOException exp) {
+                exp.printStackTrace();
+            }
+            try {
+                if (fos != null) {
+                    fos.close();
+                }
+            } catch (IOException exp) {
+                exp.printStackTrace();
+            }
+        }
+    }
+
+
+
+
+}

+ 220 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/callback/RangeRequestCallback.java

@@ -0,0 +1,220 @@
+package com.paisheng.lib.network.callback;
+
+import android.text.TextUtils;
+import android.util.Base64;
+
+import com.paisheng.lib.network.RangeRequestCall;
+import com.paisheng.lib.network.RequestCall;
+import com.paisheng.lib.network.Smart;
+import com.paisheng.lib.network.exception.ApiException;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.security.MessageDigest;
+
+import okhttp3.Call;
+import okhttp3.Headers;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+
+/**
+ * <br> ClassName:   RangeRequestCallback
+ * <br> Description: Range请求的callback
+ * <br>
+ * <br> Author:      yexiaochuan
+ * <br> Date:        2018/3/14 15:29
+ */
+public abstract class RangeRequestCallback extends AbstractCallback<File> {
+    private String mSaveFilePath;
+    private final int MAX_RETRY = 5;
+    private int times = 0;
+
+    public void onStart(String mDownLoadPath, String fileName) {
+        mSaveFilePath = mDownLoadPath + "/" + fileName;
+    }
+
+    /**
+     *<br> Description: 设定416重试机制
+     *<br> Author:      yexiaochuan
+     *<br> Date:        2018/3/14 18:50
+     */
+    @Override
+    public final void onFailure(RequestCall requestCall, ApiException e) {
+        if (!(requestCall instanceof RangeRequestCall)) {
+            onRequestFailure(requestCall,e);
+            return;
+        }
+
+        if (times >= MAX_RETRY) {
+            times = 0;
+            onRequestFailure(requestCall,e);
+            return;
+        }
+
+//        Log.i("update","fail : retry times (" + times + ") " + e.toString() + "code : " + e.getResultObject());
+        times++;
+        //rang起始位置越界 重新设置为0 重试机制重新开始
+        if (e.getResultObject() instanceof Integer && ((Integer)e.getResultObject()) == 416) {
+            times = 0;
+            ((RangeRequestCall) requestCall).resetRangeSPos();
+        }
+        requestCall.retry();
+    }
+
+    public abstract void onRequestFailure(RequestCall requestCall, ApiException e);
+
+    @Override
+    public File parseResponse(Call call, Response response) throws Exception {
+//        Log.i("update","response header : " + response.headers().toString());
+        if (call instanceof RangeRequestCall) {
+            ((RangeRequestCall) call).resetOK();
+        }
+        return readAndSave2File(response.body(),response.headers());
+    }
+
+    private File readAndSave2File(ResponseBody body, Headers headers) throws IOException, ApiException {
+        byte[] buf = new byte[2048];
+        long totalSize = 0;
+        long rangSPos = 0;
+        RandomAccessFile mDownLoadFile = null;
+        try {
+            // 获得下载保存文件
+            mDownLoadFile = new RandomAccessFile(mSaveFilePath, "rwd");
+            // 获得本地下载的文件大小
+            long fileLength = mDownLoadFile.length();
+
+            //获取文件总大小和下载起始位置 content-range:bytes $start-$end/$total
+            if (!TextUtils.isEmpty(headers.get("content-range"))) {
+                String[] value = headers.get("content-range").split("/");
+                if (value.length == 2) {
+                    String[] posValue = value[0].replace("bytes ","").split("-");
+                    if (posValue.length > 0) {
+                        rangSPos =  Long.valueOf(posValue[0]);
+                    }
+                    totalSize = Long.valueOf(value[1]);
+                }
+            } else {
+                //没有content-range 证明不是使用range请求 contentLength就是文件总大小
+                totalSize = body.contentLength();
+                rangSPos = 0;
+            }
+
+            //如果重新获取到的文件总大小等于已下载文件总大小 下载已完成
+            if (totalSize != 0 && totalSize == fileLength) {
+                final long finalTotalSize = totalSize;
+                Smart.runOnUIThread(new Runnable() {
+                    public void run() {
+                        onProgress(1, finalTotalSize);
+                    }
+                });
+                return verifyMD5(mDownLoadFile,mSaveFilePath,headers.get("content-md5"));
+            }
+
+            //文件超过总大小 下载起始位置与文件末尾不一致
+            if (totalSize < fileLength || rangSPos != fileLength) {
+                deleteFileAndThrowError(mDownLoadFile,mSaveFilePath);
+            }
+
+            long sum = rangSPos;
+            float times = 0;
+
+            // 写文件的位置需要与Range Header的起始位置一致
+            mDownLoadFile.seek(sum);
+
+            int length = 0;
+            while ((length = body.byteStream().read(buf)) != -1) {
+                sum += length;
+                mDownLoadFile.write(buf, 0, length);
+                final float progress = sum * 1.0f / totalSize;
+                // 更新进度,回调100次
+                if ((int) (100 * progress) - (int) (100 * times) >= 1) {
+                    times = progress;
+                    // 更新状态
+                    final long finalTotalSize1 = totalSize;
+                    Smart.runOnUIThread(new Runnable() {
+                        public void run() {
+                            onProgress(progress, finalTotalSize1);
+                        }
+                    });
+                }
+            }
+        } finally {
+            // 回收资源
+            close(body.byteStream());
+            close(mDownLoadFile);
+        }
+        return verifyMD5(mDownLoadFile,mSaveFilePath,headers.get("content-md5"));
+    }
+
+    /**
+     *<br> Description: 检验文件MD5
+     *<br> Author:      yexiaochuan
+     *<br> Date:        2018/3/14 18:55
+     */
+    private File verifyMD5(RandomAccessFile file, String path, String md5) throws ApiException {
+        File apk = new File(path);
+        //没有MD5不做检验
+        if (TextUtils.isEmpty(md5)) {
+            return apk;
+        }
+
+        if (getContentMD5(apk).equals(md5)) {
+            return apk;
+        }
+
+        deleteFileAndThrowError(file,path);
+        return null;
+    }
+
+    private String getContentMD5(File file){
+        if (file == null || !file.isFile() || !file.exists()) {
+            return "";
+        }
+        FileInputStream in = null;
+        try {
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            in = new FileInputStream(file);
+            byte[] buffer=new byte[8192];
+            int read=0;
+            while( (read = in.read(buffer)) > 0)
+                md.update(buffer, 0, read);
+            byte[] md5 = md.digest();
+            return Base64.encodeToString(md5,Base64.NO_WRAP);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }finally {
+            if(null!=in){
+                try {
+                    in.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return "";
+    }
+
+    /**
+     *<br> Description: 文件检验失败或者文件大小越界 删除
+     *<br> Author:      yexiaochuan
+     *<br> Date:        2018/3/14 18:48
+     */
+    private void deleteFileAndThrowError(RandomAccessFile file, String path) throws ApiException {
+        close(file);
+        new File(path).delete();
+        throw new ApiException(ApiException.CODE_OTHER_EXCEPTION,"file is wrong",416);
+    }
+
+    private void close(Closeable closeable) {
+        try {
+            if(closeable != null){
+                closeable.close();
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+}

+ 26 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/callback/StringCallback.java

@@ -0,0 +1,26 @@
+package com.paisheng.lib.network.callback;
+
+import android.util.Log;
+
+import okhttp3.Call;
+import okhttp3.Response;
+
+/**
+ * @author: liaoshengjian
+ * @Filename:
+ * @Description:    一般字符串回调处理
+ * @Copyright: Copyright (c) 2016 Tuandai Inc. All rights reserved.
+ * @date: 2017/3/31 17:40
+ */
+public abstract class StringCallback extends AbstractCallback<String> {
+
+    @Override
+    public String parseResponse(Call call, Response response) throws Exception {
+        String result = response.body().string();
+        Log.d("smart", "StringCallback:" + result);
+
+        return result;
+    }
+
+
+}

+ 28 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/converter/IConverter.java

@@ -0,0 +1,28 @@
+package com.paisheng.lib.network.converter;
+
+import okhttp3.Call;
+import okhttp3.Response;
+
+/**
+ * @author: liaoshengjian
+ * @Filename:
+ * @Description:
+ * @Copyright: Copyright (c) 2016 Tuandai Inc. All rights reserved.
+ * @date: 2017/9/14 16:30
+ */
+public interface IConverter<T> {
+
+    /**
+     *<br> Description: 数据解析(子线程)
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:17
+     *
+     * @param call Call
+     * @param response Response
+     * @return T
+     * @throws Exception e
+     */
+    T parseResponse(Call call, Response response) throws Exception;
+
+
+}

+ 24 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/converter/SmartConverter.java

@@ -0,0 +1,24 @@
+package com.paisheng.lib.network.converter;
+
+import java.lang.reflect.Type;
+
+/**
+ * @author: liaoshengjian
+ * @Filename:
+ * @Description:
+ * @Copyright: Copyright (c) 2016 Tuandai Inc. All rights reserved.
+ * @date: 2017/11/4 16:34
+ */
+public abstract class SmartConverter<T> implements IConverter<T> {
+
+    protected Type dataType;
+
+    public SmartConverter(Type type) {
+        this.dataType = type;
+    }
+
+    public void setType(Type type) {
+        this.dataType = type;
+    }
+
+}

+ 25 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/converter/StringConverter.java

@@ -0,0 +1,25 @@
+package com.paisheng.lib.network.converter;
+
+import android.util.Log;
+
+import okhttp3.Call;
+import okhttp3.Response;
+
+/**
+ * @author: liaoshengjian
+ * @Filename:
+ * @Description:
+ * @Copyright: Copyright (c) 2016 Tuandai Inc. All rights reserved.
+ * @date: 2017/9/15 16:18
+ */
+public class StringConverter implements IConverter<String> {
+
+    @Override
+    public String parseResponse(Call call, Response response) throws Exception {
+        String result = response.body().string();
+        Log.d("smart", "StringConverter:" + result);
+
+        return result;
+    }
+
+}

+ 230 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/exception/ApiException.java

@@ -0,0 +1,230 @@
+package com.paisheng.lib.network.exception;
+
+import android.text.TextUtils;
+
+import com.paisheng.lib.network.Response;
+
+/**
+ * @author: liaoshengjian
+ * @Filename:
+ * @Description:    统一异常类
+ * @Copyright: Copyright (c) 2016 Tuandai Inc. All rights reserved.
+ * @date: 2017/4/6 11:27
+ */
+public class ApiException extends Exception {
+
+    /***无网络连接***/
+    public static final int CODE_NO_NETWORK = 4000;
+
+    /***OkHttp 回调 onFailure***/
+    public static final int CODE_OKHTTP_FAILURE = 4001;
+    /***OkHttp response.isSuccessful() == false***/
+    public static final int CODE_OKHTTP_NO_SUCCESS = 4002;
+
+    /***response.code == 299***/
+    public static final int CODE_REQ_TOOFREQUEST = 4003;
+    /***!response.isSuccessful()***/
+    public static final int CODE_REQ_FAILURE = 4004;
+
+    /***OkHttp 回调 onFailure——SocketTimeoutException***/
+    public static final int CODE_OKHTTP_SOCKET_TIMEOUT = 4005;
+    /***OkHttp 回调 onFailure——SocketException***/
+    public static final int CODE_OKHTTP_SOCKET_EXP = 4006;
+    /***OkHttp 回调 onFailure——SSLException***/
+    public static final int CODE_OKHTTP_SSL_EXP = 4007;
+
+
+    //TODO 这个考虑放在哪里比较好
+    /***"数据异常: 500"; 请求JSON数据异常***/
+    public static final int CODE_REQ_JSON_ERROR = 5000;
+    /***"数据解析异常:501"; JSON解析错误***/
+    public static final int CODE_RES_JSON_PARSE_ERROR = 5001;
+    /***"数据解析异常:502"; 解密错误***/
+    public static final int CODE_RES_DECODE_ERROR = 5002;
+    /***"数据解析异常:503"; 加密错误***/
+    public static final int CODE_REQ_ENCRY_ERROR = 5003;
+    /***"数据解析异常:504"; JSON解析错误***/
+    public static final int CODE_RES_JSON_PARSE_ERROR_2 = 5004;
+    /***"数据异常:506"; 其它错误***/
+    public static final int CODE_RES_OTHER_ERROR = 5006;
+    /***"数据异常:507"; 服务器数据错误***/
+    public static final int CODE_RES_DATA_EMPTY = 5007;
+    /***"数据异常:508"; 服务器数据错误***/
+    public static final int CODE_RES_NO_RETURN_CODE = 5008;
+
+    /***系统维护中***/
+    public static final int CODE_REQ_SERVER_DOWN = 5009;
+    /***重新登录***/
+    public static final int CODE_REQ_RELOGIN = 5010;
+    /***学籍信息认证错误***/
+    public static final int CODE_REQ_SCHOOL_AUTH_ERROR = 5011;
+    /***请求时间异常***/
+    public static final int CODE_REQ_TIME_ERROR = 5012;
+    /***其他请求错误***/
+    public static final int CODE_REQ_OTHER_ERROR = 5013;
+
+    /***运营配置数据与本地相同,不做回掉处理***/
+    public static final int CODE_REQ_UNDO_ERROR = 5050;
+
+    /***其余异常***/
+    public static final int CODE_OTHER_EXCEPTION = 7000;
+
+    /**
+     * 错误码
+     */
+    private int mCode;
+
+    /**
+     * 错误信息
+     */
+    private String message;
+
+    /**
+     * 请求URL
+     */
+    private String mUrl;
+
+    /**
+     * 异常处理Object
+     */
+    private Object mResultObject;
+
+
+    /**
+     *<br> Description: 构造函数
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:34
+     *
+     * @param code int
+     * @param message String
+     * @param throwable Throwable
+     */
+    public ApiException(int code, String message, Throwable throwable) {
+        super(message, throwable);
+        this.mCode = code;
+    }
+
+    /**
+     *<br> Description: 构造函数
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:34
+     *
+     * @param code int
+     * @param message String
+     */
+    public ApiException(int code, String message) {
+        super(message);
+        this.mCode = code;
+    }
+
+    /**
+     *<br> Description: 构造函数
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:34
+     *
+     * @param code int
+     * @param throwable Throwable
+     */
+    public ApiException(int code, Throwable throwable) {
+        super(throwable);
+        this.mCode = code;
+    }
+
+    /**
+     *<br> Description: 构造函数
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:34
+     *
+     * @param code int
+     * @param throwable Throwable
+     * @param url String
+     */
+    public ApiException(int code, Throwable throwable, String url) {
+        super(throwable);
+        this.mCode = code;
+        this.mUrl = url;
+    }
+
+    /**
+     *<br> Description: 构造函数
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/6/13 15:34
+     *
+     * @param code int
+     * @param message String
+     * @param resultObject Object
+     */
+    public ApiException(int code, String message, Object resultObject) {
+        super(message);
+        this.mCode = code;
+        this.mResultObject = resultObject;
+    }
+
+    /**
+     *<br> Description: 构造函数
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/9/18 18:33
+     */
+    public ApiException(Response<?> response) {
+        super(getMessage(response));
+        this.mCode = response.code();
+        this.message = response.message();
+    }
+
+
+    public int getCode() {
+        return mCode;
+    }
+
+    public void setCode(int code) {
+        this.mCode = code;
+    }
+
+    @Override
+    public String getMessage() {
+        if (TextUtils.isEmpty(message)) {
+            if (TextUtils.isEmpty(super.getMessage())) {
+                return "Unknown Error!";
+            } else {
+                return super.getMessage();
+            }
+        } else {
+            return message;
+        }
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public String getUrl() {
+        return mUrl;
+    }
+
+    public void setUrl(String url) {
+        this.mUrl = url;
+    }
+
+    public Object getResultObject() {
+        return mResultObject;
+    }
+
+    public void setResultObject(Object resultObject) {
+        this.mResultObject = resultObject;
+    }
+
+    /**
+     *<br> Description: 获取Response信息
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/9/18 18:34
+     *
+     * @param response Response
+     * @return String
+     */
+    private static String getMessage(Response<?> response) {
+        if (response == null) {
+            return "Response is null!!! ";
+        }
+        return "HTTP " + response.code() + " " + response.message();
+    }
+}

+ 19 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/extra/DefaultCallAdapter.java

@@ -0,0 +1,19 @@
+package com.paisheng.lib.network.extra;
+
+import com.paisheng.lib.network.CallAdapter;
+import com.paisheng.lib.network.RequestCall;
+
+/**
+ * @author: liaoshengjian
+ * @Filename:
+ * @Description:
+ * @Copyright: Copyright (c) 2016 Tuandai Inc. All rights reserved.
+ * @date: 2017/11/6 10:47
+ */
+public class DefaultCallAdapter<T> implements CallAdapter<T> {
+
+    @Override
+    public T adapt(RequestCall requestCall) {
+        return (T) requestCall;
+    }
+}

+ 133 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/extra/ServiceMethod.java

@@ -0,0 +1,133 @@
+package com.paisheng.lib.network.extra;
+
+import com.paisheng.lib.network.RequestCall;
+import com.paisheng.lib.network.Smart;
+import com.paisheng.lib.network.http.GET;
+import com.paisheng.lib.network.http.POST;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * @author: liaoshengjian
+ * @Filename:
+ * @Description:
+ * @Copyright: Copyright (c) 2016 Tuandai Inc. All rights reserved.
+ * @date: 2017/11/6 09:51
+ */
+public final class ServiceMethod<T> {
+
+    private _Smart mSmart;
+    private Method mMethod;
+
+    private RequestCall mRequestCall;
+
+    public ServiceMethod(Builder builder) {
+        this.mSmart = builder.mSmart;
+        this.mMethod = builder.mMethod;
+        this.mRequestCall = builder.mRequestCall;
+    }
+
+    @SuppressWarnings("unchecked")
+    public T adapt() {
+        return (T) this.mRequestCall
+                .converter(this.mSmart.getSmartConverter())
+                .adapt(this.mSmart.getCallAdapter());
+    }
+
+
+    static final class Builder {
+        private _Smart mSmart;
+        private Method mMethod;
+        private Object[] mArgs;
+
+        private RequestCall mRequestCall;
+
+        public Builder(_Smart smart, Method method, Object[] args) {
+            this.mSmart = smart;
+            this.mMethod = method;
+            this.mArgs = args;
+        }
+
+        public ServiceMethod build() {
+
+            Annotation[] methodAnnotations = mMethod.getAnnotations();
+            if (methodAnnotations.length != 1) {
+                throw new IllegalArgumentException("At least one annotation from POST or GET");
+            }
+            if (mArgs.length > 1) {
+                throw new IllegalArgumentException("params support only one now, String or HashMap");
+            }
+//            if (mSmart.getSmartConverter() == null) {
+//                throw new IllegalArgumentException("You must set SmartConverter");
+//            }
+            if (mSmart.getCallAdapter() == null) {
+                throw new IllegalArgumentException("You must set CallAdapter");
+            }
+
+            //参数
+            String params = "";
+            if (mArgs.length == 1) {
+                Object object = mArgs[0];
+                if (object instanceof String) {
+                    params = (String) object;
+                } else if (object instanceof HashMap) {
+                    params = hashMapToJson((HashMap) object);
+                }
+            }
+
+            //http请求
+            String mUrl = mSmart.getBaseUrl();
+            Annotation annotation = methodAnnotations[0];
+            if (annotation instanceof POST) {
+                mUrl += ((POST) annotation).value();
+                mRequestCall = Smart.post(mUrl).addJson(params);
+
+            } else if (annotation instanceof GET) {
+                mUrl += ((GET) annotation).value();
+                mRequestCall = Smart.get(mUrl);
+
+            } else {
+                throw new IllegalArgumentException("Annotation must from POST or GET");
+            }
+
+            if (mSmart.getSmartConverter() != null) {
+
+                //IConverter
+                Type returnType = mMethod.getGenericReturnType();
+                if (returnType instanceof ParameterizedType) {
+                    Type cls = ((ParameterizedType) returnType).getActualTypeArguments()[0];
+                    //Observable<HttpResult<UserInfo>>
+                    if (cls instanceof ParameterizedType) {
+                        cls = ((ParameterizedType) cls).getActualTypeArguments()[0];
+                    }
+                    //Observable<UserInfo>
+                    mSmart.getSmartConverter().setType(cls);
+                }
+            }
+
+            return new ServiceMethod(this);
+        }
+    }
+
+    /**把数据源HashMap转换成json
+     * @param map
+     */
+    public static String hashMapToJson(HashMap map) {
+        String string = "{";
+        for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
+            Map.Entry e = (Map.Entry) it.next();
+            string += "'" + e.getKey() + "':";
+            string += "'" + e.getValue() + "',";
+        }
+        string = string.substring(0, string.lastIndexOf(","));
+        string += "}";
+        return string;
+    }
+
+}

+ 107 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/extra/_Smart.java

@@ -0,0 +1,107 @@
+package com.paisheng.lib.network.extra;
+
+import com.paisheng.lib.network.CallAdapter;
+import com.paisheng.lib.network.Smart;
+import com.paisheng.lib.network.converter.SmartConverter;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * @author: liaoshengjian
+ * @Filename:
+ * @Description:
+ * @Copyright: Copyright (c) 2016 Tuandai Inc. All rights reserved.
+ * @date: 2017/11/6 09:26
+ */
+public final class _Smart {
+
+    private SmartConverter mSmartConverter;
+    private CallAdapter mCallAdapter;
+    private String mBaseUrl = "";
+    private Smart.Builder mBuilder;
+
+    private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();
+
+    public _Smart(Smart.Builder builder) {
+        refreshBuilder(builder);
+    }
+
+    public void refreshBuilder(Smart.Builder builder) {
+        this.mBuilder = builder;
+        this.mSmartConverter = builder.getSmartConverter();
+        this.mCallAdapter = builder.getCallAdapter();
+        this.mBaseUrl = builder.getBaseUrl();
+
+        if (serviceMethodCache.size() > 0) {
+            serviceMethodCache.clear();
+        }
+    }
+
+    public Smart.Builder getBuilder() {
+        return this.mBuilder;
+    }
+
+
+    public SmartConverter getSmartConverter() {
+        return mSmartConverter;
+    }
+    public CallAdapter getCallAdapter() {
+        return mCallAdapter;
+    }
+    public String getBaseUrl() {
+        return mBaseUrl;
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> T create(final Class<T> service) {
+        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service},
+                new InvocationHandler() {
+
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args)
+                            throws Throwable {
+                        ServiceMethod serviceMethod = loadServiceMethod(method, args);
+                        return  serviceMethod.adapt();
+                    }
+                });
+
+    }
+
+    private ServiceMethod loadServiceMethod(Method method, Object[] args) {
+        ServiceMethod result;
+        synchronized (serviceMethodCache) {
+            result = serviceMethodCache.get(method);
+            if (result == null) {
+                result = new ServiceMethod.Builder(this, method, args).build();
+                serviceMethodCache.put(method, result);
+            }
+        }
+        return result;
+    }
+
+
+
+
+//    public static final class Builder {
+//
+//        private SmartConverter mSmartConverter;
+//        private CallAdapter mCallAdapter;
+//
+//        public Builder(SmartConverter converter, CallAdapter callAdapter) {
+//            this.mSmartConverter = converter;
+//            this.mCallAdapter = callAdapter;
+//        }
+//        public Builder() {
+//            this.mCallAdapter = new DefaultCallAdapter();
+//        }
+//
+//        public _Smart build() {
+//            return new _Smart(this);
+//        }
+//
+//    }
+}

+ 39 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/http/GET.java

@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2013 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.paisheng.lib.network.http;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import okhttp3.HttpUrl;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/** Make a GET request. */
+@Documented
+@Target(METHOD)
+@Retention(RUNTIME)
+public @interface GET {
+  /**
+   * A relative or absolute path, or full URL of the endpoint. This value is optional if the first
+   * parameter of the method is annotated with {@link Url @Url}.
+   * <p>
+   * See {@linkplain retrofit2.Retrofit.Builder#baseUrl(HttpUrl) base URL} for details of how
+   * this is resolved against a base URL to create the full endpoint URL.
+   */
+  String value() default "";
+}

+ 39 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/http/POST.java

@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2013 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.paisheng.lib.network.http;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import okhttp3.HttpUrl;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/** Make a POST request. */
+@Documented
+@Target(METHOD)
+@Retention(RUNTIME)
+public @interface POST {
+  /**
+   * A relative or absolute path, or full URL of the endpoint. This value is optional if the first
+   * parameter of the method is annotated with {@link Url @Url}.
+   * <p>
+   * See {@linkplain retrofit2.Retrofit.Builder#baseUrl(HttpUrl) base URL} for details of how
+   * this is resolved against a base URL to create the full endpoint URL.
+   */
+  String value() default "";
+}

+ 294 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/interceptor/CustomLoggingInterceptor.java

@@ -0,0 +1,294 @@
+package com.paisheng.lib.network.interceptor;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.nio.charset.Charset;
+import java.nio.charset.UnsupportedCharsetException;
+import java.util.concurrent.TimeUnit;
+
+import okhttp3.Connection;
+import okhttp3.Headers;
+import okhttp3.Interceptor;
+import okhttp3.MediaType;
+import okhttp3.MultipartBody;
+import okhttp3.Protocol;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+import okhttp3.internal.http.HttpHeaders;
+import okhttp3.internal.platform.Platform;
+import okio.Buffer;
+import okio.BufferedSource;
+
+import static okhttp3.internal.platform.Platform.INFO;
+
+/**
+ * <br> ClassName:   CustomLoggingInterceptor
+ * <br> Description: HttpLoggingInterceptor 改进版
+ *                      1、修复MultiPartBody 8M文件打印导致的OOM
+ *                      2、兼容Unicode中文乱码
+ *                      3、OkHttp 3.9
+ * <br>
+ * <br> Author:      liaoshengjian
+ * <br> Date:        2017/11/3 10:33
+ */
+public class CustomLoggingInterceptor implements Interceptor {
+    private static final Charset UTF8 = Charset.forName("UTF-8");
+
+    public enum Level {
+        /** No logs. */
+        NONE,
+        /**
+         * Logs request and response lines.
+         *
+         * <p>Example:
+         * <pre>{@code
+         * --> POST /greeting http/1.1 (3-byte body)
+         *
+         * <-- 200 OK (22ms, 6-byte body)
+         * }</pre>
+         */
+        BASIC,
+        /**
+         * Logs request and response lines and their respective headers.
+         *
+         * <p>Example:
+         * <pre>{@code
+         * --> POST /greeting http/1.1
+         * Host: example.com
+         * Content-Type: plain/text
+         * Content-Length: 3
+         * --> END POST
+         *
+         * <-- 200 OK (22ms)
+         * Content-Type: plain/text
+         * Content-Length: 6
+         * <-- END HTTP
+         * }</pre>
+         */
+        HEADERS,
+        /**
+         * Logs request and response lines and their respective headers and bodies (if present).
+         *
+         * <p>Example:
+         * <pre>{@code
+         * --> POST /greeting http/1.1
+         * Host: example.com
+         * Content-Type: plain/text
+         * Content-Length: 3
+         *
+         * Hi?
+         * --> END GET
+         *
+         * <-- 200 OK (22ms)
+         * Content-Type: plain/text
+         * Content-Length: 6
+         *
+         * Hello!
+         * <-- END HTTP
+         * }</pre>
+         */
+        BODY
+    }
+
+    public interface Logger {
+        void log(String message);
+
+        /** A {@link Logger} defaults output appropriate for the current platform. */
+        Logger DEFAULT = new Logger() {
+            @Override public void log(String message) {
+                Platform.get().log(INFO, message, null);
+            }
+        };
+    }
+
+    public CustomLoggingInterceptor() {
+        this(Logger.DEFAULT);
+    }
+
+    public CustomLoggingInterceptor(Logger logger) {
+        this.logger = logger;
+    }
+
+    private final Logger logger;
+
+    private volatile Level level = Level.NONE;
+
+    /** Change the level at which this interceptor logs. */
+    public CustomLoggingInterceptor setLevel(Level level) {
+        if (level == null) throw new NullPointerException("level == null. Use Level.NONE instead.");
+        this.level = level;
+        return this;
+    }
+
+    public Level getLevel() {
+        return level;
+    }
+
+    @Override public Response intercept(Chain chain) throws IOException {
+        Level level = this.level;
+
+        Request request = chain.request();
+        if (level == Level.NONE) {
+            return chain.proceed(request);
+        }
+
+        boolean logBody = level == Level.BODY;
+        boolean logHeaders = logBody || level == Level.HEADERS;
+
+        RequestBody requestBody = request.body();
+        boolean hasRequestBody = requestBody != null;
+
+        Connection connection = chain.connection();
+        Protocol protocol = connection != null ? connection.protocol() : Protocol.HTTP_1_1;
+        String requestStartMessage = "--> " + request.method() + ' ' + request.url() + ' ' + protocol;
+        if (!logHeaders && hasRequestBody) {
+            requestStartMessage += " (" + requestBody.contentLength() + "-byte body)";
+        }
+        logger.log(requestStartMessage);
+
+        if (logHeaders) {
+            if (hasRequestBody) {
+                // Request body headers are only present when installed as a network interceptor. Force
+                // them to be included (when available) so there values are known.
+                if (requestBody.contentType() != null) {
+                    logger.log("Content-Type: " + requestBody.contentType());
+                }
+                if (requestBody.contentLength() != -1) {
+                    logger.log("Content-Length: " + requestBody.contentLength());
+                }
+            }
+
+            Headers headers = request.headers();
+            for (int i = 0, count = headers.size(); i < count; i++) {
+                String name = headers.name(i);
+                // Skip headers from the request body as they are explicitly logged above.
+                if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) {
+                    logger.log(name + ": " + headers.value(i));
+                }
+            }
+
+            if (!logBody || !hasRequestBody) {
+                logger.log("--> END " + request.method());
+            } else if (bodyEncoded(request.headers())) {
+                logger.log("--> END " + request.method() + " (encoded body omitted)");
+            } else {
+                logger.log("");
+
+                //兼容MultipartBody,大文件打印
+                if (requestBody instanceof MultipartBody) {
+                    for (int i = 0; i < ((MultipartBody) requestBody).parts().size(); i++) {
+                        try {
+                            MultipartBody.Part requestPart = ((MultipartBody) requestBody).part(i);
+                            Field partHeaders = MultipartBody.Part.class.getDeclaredField("headers");
+                            partHeaders.setAccessible(true);
+                            logger.log(partHeaders.get(requestPart) + "");
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        }
+                    }
+                } else {
+                    Buffer buffer = new Buffer();
+                    requestBody.writeTo(buffer);
+
+                    Charset charset = UTF8;
+                    MediaType contentType = requestBody.contentType();
+                    if (contentType != null) {
+                        charset = contentType.charset(UTF8);
+                    }
+                    logger.log(buffer.readString(charset));
+                }
+                logger.log("--> END " + request.method()
+                        + " (" + requestBody.contentLength() + "-byte body)");
+            }
+        }
+
+        long startNs = System.nanoTime();
+        Response response = chain.proceed(request);
+        long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
+
+        ResponseBody responseBody = response.body();
+        long contentLength = responseBody.contentLength();
+        String bodySize = contentLength != -1 ? contentLength + "-byte" : "unknown-length";
+        logger.log("<-- " + response.code() + ' ' + response.message() + ' '
+                + response.request().url() + " (" + tookMs + "ms" + (!logHeaders ? ", "
+                + bodySize + " body" : "") + ')');
+
+        if (logHeaders) {
+            Headers headers = response.headers();
+            for (int i = 0, count = headers.size(); i < count; i++) {
+                logger.log(headers.name(i) + ": " + headers.value(i));
+            }
+
+            if (!logBody ||!HttpHeaders.hasBody(response)) {
+                logger.log("<-- END HTTP");
+            } else if (bodyEncoded(response.headers())) {
+                logger.log("<-- END HTTP (encoded body omitted)");
+            } else {
+                BufferedSource source = responseBody.source();
+                source.request(Long.MAX_VALUE); // Buffer the entire body.
+                Buffer buffer = source.buffer();
+
+                Charset charset = UTF8;
+                MediaType contentType = responseBody.contentType();
+                if (contentType != null) {
+                    try {
+                        charset = contentType.charset(UTF8);
+                    } catch (UnsupportedCharsetException e) {
+                        logger.log("");
+                        logger.log("Couldn't decode the response body; charset is likely malformed.");
+                        logger.log("<-- END HTTP");
+
+                        return response;
+                    }
+                }
+
+                //206表示这是一个文件content,不打印文件
+                if (contentLength != 0 && response.code() != 206) {
+                    logger.log("");
+                    /*logger.log(buffer.clone().readString(charset));*/
+                    logger.log(unicodeToUTF_8(buffer.clone().readString(charset)));
+                }
+
+                logger.log("<-- END HTTP (" + buffer.size() + "-byte body)");
+            }
+        }
+
+        return response;
+    }
+
+    /**
+     * Unicode 转 UTF-8
+     */
+    private String unicodeToUTF_8(String src) {
+        if (null == src) {
+            return null;
+        }
+        System.out.println("src: " + src);
+        StringBuilder out = new StringBuilder();
+        for (int i = 0; i < src.length();) {
+            char c = src.charAt(i);
+            if (i + 6 < src.length() && c == '\\' && src.charAt(i + 1) == 'u') {
+                String hex = src.substring(i + 2, i + 6);
+                try {
+                    out.append((char) Integer.parseInt(hex, 16));
+                } catch (NumberFormatException nfe) {
+                    nfe.fillInStackTrace();
+                }
+                i = i + 6;
+            } else {
+                out.append(src.charAt(i));
+                ++i;
+            }
+        }
+        return out.toString();
+
+    }
+
+
+    private boolean bodyEncoded(Headers headers) {
+        String contentEncoding = headers.get("Content-Encoding");
+        return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity");
+    }
+}

+ 123 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/interceptor/DNSTimeoutInterceptor.java

@@ -0,0 +1,123 @@
+package com.paisheng.lib.network.interceptor;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import okhttp3.Interceptor;
+import okhttp3.Request;
+import okhttp3.Response;
+
+/**
+ * Based on http://stackoverflow.com/questions/693997/how-to-set-httpresponse-timeout-for-android-in-java/31643186#31643186
+ * as per https://github.com/square/okhttp/issues/95
+ */
+public class DNSTimeoutInterceptor implements Interceptor {
+
+    private long mTimeoutMillis;
+
+    /**
+     *<br> Description: 构造函数,设置DNS超时时间
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/11/3 10:24
+     *
+     * @param timeoutMillis 毫秒
+     */
+    public DNSTimeoutInterceptor(long timeoutMillis) {
+        mTimeoutMillis = timeoutMillis;
+    }
+
+    @Override
+    public Response intercept(final Chain chain) throws IOException {
+        Request request = chain.request();
+        //Log.SplitTimer timer = (request.tag() instanceof RequestTag ? ((RequestTag) request.tag()).getTimer() : null);
+
+        // underlying call should timeout after 2 tries of 5s:  https://android.googlesource.com/platform/bionic/+/android-5.1.1_r38/libc/dns/include/resolv_private.h#137
+        // could use our own Dns implementation that falls back to public DNS servers:  https://garage.easytaxi.com/tag/dns-android-okhttp/
+        if (!DNSResolver.isDNSReachable(request.url().host(), mTimeoutMillis)) {
+            throw new UnknownHostException("DNS timeout");
+        }
+
+        return chain.proceed(request);
+    }
+
+    /**
+     *<br> Description: DNSResolver DNS解析
+     *<br> Author:      liaoshengjian
+     *<br> Date:        2017/11/3 10:25
+     */
+    private static class DNSResolver implements Runnable {
+        private String mDomain;
+        private InetAddress mAddress;
+
+        /**
+         *<br> Description: DNS解析是否成功
+         *<br> Author:      liaoshengjian
+         *<br> Date:        2017/11/3 10:26
+         *
+         * @param domain 域名
+         * @param timeoutMillis 超时时间(毫秒)
+         * @return boolean 解析结果
+         */
+        public static boolean isDNSReachable(String domain, long timeoutMillis) {
+            try {
+                DNSResolver dnsRes = new DNSResolver(domain);
+
+                Thread t = new Thread(dnsRes, "DNSResolver");
+                t.start();
+                t.join(timeoutMillis);
+                return dnsRes.get() != null;
+            } catch (Exception e) {
+                return false;
+            }
+        }
+
+        /**
+         *<br> Description: 构造函数
+         *<br> Author:      liaoshengjian
+         *<br> Date:        2017/11/3 10:27
+         *
+         * @param domain 域名
+         */
+        public DNSResolver(String domain) {
+            this.mDomain = domain;
+        }
+
+        /**
+         *<br> Description: 启动线程
+         *<br> Author:      liaoshengjian
+         *<br> Date:        2017/11/3 10:28
+         */
+        public void run() {
+            try {
+                InetAddress addr = InetAddress.getByName(mDomain);
+                set(addr);
+            } catch (UnknownHostException e) {
+                e.printStackTrace();
+            }
+        }
+
+        /**
+         *<br> Description: 设置解析的ip信息
+         *<br> Author:      liaoshengjian
+         *<br> Date:        2017/11/3 10:28
+         *
+         * @param inetAddr IP信息对象
+         */
+        public synchronized void set(InetAddress inetAddr) {
+            this.mAddress = inetAddr;
+        }
+
+        /**
+         *<br> Description: 获取IP信息对象
+         *<br> Author:      liaoshengjian
+         *<br> Date:        2017/11/3 10:28
+         *
+         * @return InetAddress IP信息对象
+         */
+        public synchronized InetAddress get() {
+            return mAddress;
+        }
+    }
+
+}

+ 50 - 0
ps_lib_smart/src/main/java/com/paisheng/lib/network/interceptor/NotIOExceptionInterceptor.java

@@ -0,0 +1,50 @@
+package com.paisheng.lib.network.interceptor;
+
+import com.paisheng.lib.network.Smart;
+
+import java.io.IOException;
+
+import okhttp3.Interceptor;
+import okhttp3.Response;
+
+/**
+ * okhttp AsyncCall only catch the IOException, other exception will occur crash,this Interceptor impl can transform all exception to IOException
+ * <p>
+ * see : https://github.com/square/okhttp/issues/3477
+ */
+public class NotIOExceptionInterceptor implements Interceptor {
+    private static final String TAG = "Smart.OkHttp3";
+
+    private CatchListener mListener;
+
+    public NotIOExceptionInterceptor() {
+    }
+
+    public NotIOExceptionInterceptor(CatchListener listener) {
+        this.mListener = listener;
+    }
+
+    @Override
+    public Response intercept(Chain chain) throws IOException {
+        try {
+            return chain.proceed(chain.request());
+        } catch (Throwable e) {
+            if (Smart.getDebugMode()) {
+                e.printStackTrace();
+            }
+
+            if (e instanceof IOException) {
+                throw e;
+            }
+
+            if (mListener != null) {
+                mListener.reportException();
+            }
+            throw new IOException(e);
+        }
+    }
+
+    private interface CatchListener {
+        void reportException();
+    }
+}

+ 3 - 0
ps_lib_smart/src/main/res/values/strings.xml

@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">ps_lib_smart</string>
+</resources>

+ 14 - 0
ps_lib_smart/src/test/java/com/paisheng/lib/network/ExampleUnitTest.java

@@ -0,0 +1,14 @@
+package com.paisheng.lib.network;
+
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+
+    public void addition_isCorrect() throws Exception {
+
+    }
+}