Sfoglia il codice sorgente

Merge remote-tracking branch 'origin/master'

yanyi 4 anni fa
parent
commit
375cc4525e
26 ha cambiato i file con 1020 aggiunte e 262 eliminazioni
  1. 36 7
      README.md
  2. 13 7
      app/build.gradle
  3. 2 2
      app/src/androidTest/java/com/benyanyi/okhttp/ExampleInstrumentedTest.java
  4. 1 1
      app/src/main/AndroidManifest.xml
  5. 473 0
      app/src/main/java/com/mylove/okhttp/DateUtil.java
  6. 31 30
      app/src/main/java/com/mylove/okhttp/DownloadActivity.java
  7. 101 27
      app/src/main/java/com/mylove/okhttp/MainActivity.java
  8. 3 1
      build.gradle
  9. 2 0
      gradle.properties
  10. 16 68
      okhttplib/bintrayUpload.gradle
  11. 11 11
      okhttplib/build.gradle
  12. 2 2
      okhttplib/src/androidTest/java/com/benyanyi/okhttp/ExampleInstrumentedTest.java
  13. 11 0
      okhttplib/src/main/java/com/benyanyi/okhttp/OkHttpUtil.java
  14. 4 10
      okhttplib/src/main/java/com/benyanyi/okhttp/call/BeanCall.java
  15. 4 3
      okhttplib/src/main/java/com/benyanyi/okhttp/call/ListCall.java
  16. 2 3
      okhttplib/src/main/java/com/benyanyi/okhttp/call/ObjectCall.java
  17. 10 11
      okhttplib/src/main/java/com/benyanyi/okhttp/download/DownloadCall.java
  18. 114 49
      okhttplib/src/main/java/com/benyanyi/okhttp/download/DownloadManager.java
  19. 1 1
      okhttplib/src/main/java/com/benyanyi/okhttp/download/FileUtil.java
  20. 40 0
      okhttplib/src/main/java/com/benyanyi/okhttp/type/ApiDns.java
  21. 13 5
      okhttplib/src/main/java/com/benyanyi/okhttp/type/Client.java
  22. 11 2
      okhttplib/src/main/java/com/benyanyi/okhttp/type/HttpConfig.java
  23. 81 15
      okhttplib/src/main/java/com/benyanyi/okhttp/type/HttpRequest.java
  24. 21 2
      okhttplib/src/main/java/com/benyanyi/okhttp/type/RequestType.java
  25. 1 1
      okhttplib/src/main/java/com/benyanyi/okhttp/type/SslConfig.java
  26. 16 4
      okhttplib/src/main/java/com/benyanyi/okhttp/util/CacheUtils.java

+ 36 - 7
README.md

@@ -1,12 +1,41 @@
-# OkHttp
-## OkHttp请求封装
-### 使用方法
+# BaseOkHttp
 
-module 下添加
+![](https://img.shields.io/badge/okhttplib-2.0.6-green)
+
+
+OkHttp请求封装
+----------
+使用
+---
+### 根目录下build.gradle添加Maven地址
+~~~
+repositories {
+        maven {
+            url "http://maven.benyanyi.com/nexus/content/repositories/mylove/"
+        }
+    }
+~~~
+### module 下添加
+~~~
+implementation 'com.yanyi.benyanyi:okhttplib:2.0.7'
+~~~
+
+或者
+~~~
+<dependency>
+  <groupId>com.yanyi.benyanyi</groupId>
+  <artifactId>okhttplib</artifactId>
+  <version>2.0.7</version>
+  <type>aar</type>
+</dependency>
+~~~
 
-`implementation 'com.yanyi.benyanyi:okhttplib:2.0.2'` 
-  
 ### 更新记录
+* 2021-03-13(2.0.7) 多文件上传
+* 2020-10-24(2.0.6) 修复缓存文件android6.0时磁盘未挂载时报错问题
+* 2020-09-25(2.0.5) 更改超时时间,添加下载文件覆盖
+* 2020-05-16(2.0.4) 修复上传大文件时OOM问题
+* 2020-05-16(2.0.3) 将android support转成androidx支持,并将Maven库存放到个人服务器上
 * 2020-01-16(2.0.2) 修复解析崩溃问题,优化返回方法
 * 2019-08-26(2.0.1) 优化下载
 * 2019-08-20(2.0.0) 升级2.x版本,重构代码,优化方法,使调用更简洁明了
@@ -19,7 +48,7 @@ module 下添加
 * 2018-09-14(1.0.8) 优化下载文件方法
 * 2018-09-12(1.0.7) 修复下载文件回调没返回问题,下载路径都是根目录下
 
-<br/>
+---
 若在使用过程中出现什么问题,可以联系作者<br/>
 作者:演绎<br/>
 QQ:1541612424<br/>

+ 13 - 7
app/build.gradle

@@ -1,14 +1,14 @@
 apply plugin: 'com.android.application'
 
 android {
-    compileSdkVersion 28
+    compileSdkVersion 29
     defaultConfig {
         applicationId "com.mylove.okhttp"
         minSdkVersion 15
-        targetSdkVersion 28
+        targetSdkVersion 29
         versionCode 1
         versionName "1.0"
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
 
 
         multiDexEnabled true
@@ -25,20 +25,26 @@ android {
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
         }
     }
+
+    lintOptions {
+        abortOnError false
+    }
+
 }
 
 dependencies {
     implementation fileTree(include: ['*.jar'], dir: 'libs')
-    implementation 'com.android.support:appcompat-v7:28.0.0'
+    implementation 'androidx.appcompat:appcompat:1.0.0'
     testImplementation 'junit:junit:4.12'
-    androidTestImplementation 'com.android.support.test:runner:1.0.2'
-    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
-    implementation project(':okhttplib')
+    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
+//    implementation project(':okhttplib')
     implementation 'com.yanyi.benyanyi:loglib:1.0.3'
     implementation 'com.yanyi.benyanyi:permissionlib:1.0.9'
     implementation 'io.reactivex.rxjava2:rxjava:2.2.10'
     implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
     implementation 'org.greenrobot:eventbus:3.1.1'
     implementation 'com.google.code.gson:gson:2.8.6'
+    implementation project(path: ':okhttplib')
 
 }

+ 2 - 2
app/src/androidTest/java/com/benyanyi/okhttp/ExampleInstrumentedTest.java

@@ -1,8 +1,8 @@
 package com.benyanyi.okhttp;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;

+ 1 - 1
app/src/main/AndroidManifest.xml

@@ -29,7 +29,7 @@
         </activity>
         <!-- 定义FileProvider -->
         <provider
-            android:name="android.support.v4.content.FileProvider"
+            android:name="androidx.core.content.FileProvider"
             android:authorities="com.mylove.okhttp.fileProvider"
             android:exported="false"
             android:grantUriPermissions="true">

+ 473 - 0
app/src/main/java/com/mylove/okhttp/DateUtil.java

@@ -0,0 +1,473 @@
+package com.mylove.okhttp;
+
+import android.annotation.SuppressLint;
+
+import java.text.DateFormat;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * @author Administrator
+ * @date 2020/04/22 10:00
+ * @email ben@yanyi.red
+ * @overview
+ */
+public class DateUtil {
+    /**
+     * 时间戳转换成日期格式字符串
+     * seconds 精确到秒的字符串
+     */
+    public static String timeStamp2Date(String seconds, String format) {
+        if (seconds == null || seconds.isEmpty() || seconds.equals("null")) {
+            return "";
+        }
+        if (format.isEmpty())
+            format = "yyyy-MM-dd HH:mm:ss";
+        SimpleDateFormat sdf = new SimpleDateFormat(format);
+        return sdf.format(new Date(Long.valueOf(seconds + "000")));
+    }
+
+    /**
+     * 计算两个日期型的时间相差多少时间
+     */
+    @SuppressLint("SimpleDateFormat")
+    public static String twoDateDistance(String endDates) {
+        endDates = timeStamp2Date(endDates, "yyyy-MM-dd HH:mm:ss");
+        SimpleDateFormat dfs = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String date = dfs.format(new Date());
+        Date startDate = null;
+        try {
+            startDate = dfs.parse(date);
+            Date endDate = dfs.parse(endDates);
+            if (startDate == null || endDate == null) {
+                return null;
+            }
+            long timeLong = (endDate.getTime() - startDate.getTime()) / -1000;// 单位毫秒,除以1000转换成秒
+            // 单位秒
+            long month = 60 * 60 * 24 * 7 * 4;// 月
+            long week = 60 * 60 * 24 * 7;// 周
+            long day = 60 * 60 * 24;// 天
+            long hour = 60 * 60;// 小时
+            long minute = 60; // 分钟
+            long second = 1; // 秒
+
+            if (timeLong < minute) {
+                return timeLong / second + "秒前";
+            } else if (timeLong < hour) {
+                timeLong = timeLong / minute;
+                return timeLong + "分钟前";
+            } else if (timeLong < day) {
+                timeLong = timeLong / hour;
+                return timeLong + "小时前";
+            } else if (timeLong < week) {
+                timeLong = timeLong / day;
+                return timeLong + "天前";
+            } else if (timeLong < month) {
+                timeLong = timeLong / week;
+                return timeLong + "周前";
+            } else {
+                // 如果超过几周则显示日期
+                String str = new String(endDates);
+                String date1[] = str.split(" ");
+                return date1[0];
+            }
+        } catch (Exception e) {//
+            e.printStackTrace();
+        }
+        return dfs.format(startDate);
+
+    }
+
+    public static boolean timeCompare(String startTime, String endTime) {
+        try {
+            DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+            Date startDate = df.parse(startTime);
+            Date endDate = df.parse(endTime);
+            long diff = endDate.getTime() - startDate.getTime();
+            long min = diff / (1000 * 60);
+            if (min < 1)
+                return false;
+            else
+                return true;
+        } catch (Exception e) {
+        }
+        return true;
+    }
+
+    /**
+     * 时间戳转换成日期格式字符串
+     *
+     * @param seconds 精确到秒的字符串
+     */
+    public static String timeStamp2Date(String seconds) {
+        if (seconds == null || seconds.isEmpty() || seconds.equals("null")) {
+            return "";
+        }
+        String format = "yyyy-MM-dd";
+        SimpleDateFormat sdf = new SimpleDateFormat(format);
+        return sdf.format(new Date(Long.valueOf(seconds + "000")));
+    }
+
+    /**
+     * 日期格式字符串转换成时间戳
+     *
+     * @param format 如:yyyy-MM-dd HH:mm:ss
+     */
+    public static String date2TimeStamp(String date_str, String format) {
+        // 2016-03-13 03:29:00
+        try {
+            SimpleDateFormat sdf = new SimpleDateFormat(format);
+            return String.valueOf(sdf.parse(date_str).getTime() / 1000);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return "";
+    }
+
+    /**
+     * 取得当前时间戳(精确到秒)
+     */
+    public static String timeStamp() {
+        long time = System.currentTimeMillis();
+        String t = String.valueOf(time / 1000);
+        return t;
+    }
+
+    /**
+     * 得到当前的时间
+     *
+     * @return 例如:2015-01-06 22:56
+     */
+    public static String getStringDate() {
+        Date currentTime = new Date();
+        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String dateString = formatter.format(currentTime);
+        return dateString;
+    }
+
+    /**
+     * 得到当前的时间
+     */
+    public static String getStringDate(String format) {
+        Date currentTime = new Date();
+        SimpleDateFormat formatter = new SimpleDateFormat(format);
+        String dateString = formatter.format(currentTime);
+        return dateString;
+    }
+
+    /**
+     * 计算时间差
+     */
+    public static String TimeDifference(String startTime, String endTime, String format) {
+        String str = "";
+        try {
+            SimpleDateFormat sd = new SimpleDateFormat(format);
+            long nd = 1000 * 24 * 60 * 60;// 一天的毫秒数
+            long nh = 1000 * 60 * 60;// 一小时的毫秒数
+            long nm = 1000 * 60;// 一分钟的毫秒数
+            long ns = 1000;// 一秒钟的毫秒数long diff;try {
+            // 获得两个时间的毫秒时间差异
+            long diff = sd.parse(endTime).getTime() - sd.parse(startTime).getTime();
+            long day = diff / nd;// 计算差多少天
+            long hour = diff % nd / nh;// 计算差多少小时
+            long min = diff % nd % nh / nm;// 计算差多少分钟
+            long sec = diff % nd % nh % nm / ns;// 计算差多少秒//输出结果
+            if (day > 0) {
+                str += day + "天";
+            }
+            if (hour > 0) {
+                str += hour + "小时";
+            }
+            if (min > 0) {
+                str += min + "分钟";
+            }
+            if (sec > 0) {
+                str += sec + "秒";
+            }
+        } catch (Exception e) {
+        }
+        return str;
+    }
+
+    /**
+     * 计算时间差
+     */
+    public static String TimeDifference(String startTime, String endTime) {
+        String str = "剩余";
+        try {
+            SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            long nd = 1000 * 24 * 60 * 60;// 一天的毫秒数
+            long nh = 1000 * 60 * 60;// 一小时的毫秒数
+            long nm = 1000 * 60;// 一分钟的毫秒数
+            long ns = 1000;// 一秒钟的毫秒数
+            // 获得两个时间的毫秒时间差异
+            Date start = new Date();
+            Date end = new Date();
+            start = sd.parse(timeStamp2Date(startTime, ""));
+            end = sd.parse(timeStamp2Date(endTime, ""));
+            long diff = end.getTime() - start.getTime();
+            long day = diff / nd;// 计算差多少天
+            long hour = diff % nd / nh;// 计算差多少小时
+            long min = diff % nd % nh / nm;// 计算差多少分钟
+            long sec = diff % nd % nh % nm / ns;// 计算差多少秒
+            if (day > 0) {
+                str += day + "天";
+            }
+            if (hour > 0) {
+                str += hour + "小时";
+            }
+            if (min > 0) {
+                str += min + "分钟";
+            }
+            if (sec > 0) {
+                str += sec + "秒";
+            }
+            if (diff < 0) {
+                str = "已过期";
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return str;
+    }
+
+    /**
+     * 结束日期是否大于开始日期
+     *
+     * @param startTime 开始时间 为时间戳类型
+     * @param endTime   结束时间 为时间戳类型
+     * @return true 表示为真
+     */
+    public static boolean TimeComparison(String startTime, String endTime) {
+        boolean mAfter = true;
+        try {
+            SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            long nd = 1000 * 24 * 60 * 60;// 一天的毫秒数
+            long nh = 1000 * 60 * 60;// 一小时的毫秒数
+            long nm = 1000 * 60;// 一分钟的毫秒数
+            long ns = 1000;// 一秒钟的毫秒数
+            // 获得两个时间的毫秒时间差异
+            Date start = new Date();
+            Date end = new Date();
+            start = sd.parse(timeStamp2Date(startTime, ""));
+            end = sd.parse(timeStamp2Date(endTime, ""));
+            long diff = end.getTime() - start.getTime();
+            long sec = diff % nd % nh % nm / ns;// 计算差多少秒
+            if (sec < 0) {
+                mAfter = false;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return mAfter;
+    }
+
+    public static String TimeDifferenceDay(String startTime, String endTime) {
+        String str = "";
+        try {
+            SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd:mm:ss");
+            long nd = 1000 * 24 * 60 * 60;// 一天的毫秒数
+            long nh = 1000 * 60 * 60;// 一小时的毫秒数
+            // 获得两个时间的毫秒时间差异
+            long diff = sd.parse(endTime).getTime() - sd.parse(startTime).getTime();
+            long day = diff / nd;// 计算差多少天
+            long hour = diff % nd / nh;// 计算差多少小时
+            if (day > 0) {
+                str += day + "天";
+            }
+            if (hour > 0) {
+                str += hour + "小时";
+            }
+            if (diff < 0) {
+                str = "已过期";
+            }
+        } catch (Exception e) {
+        }
+        return str;
+    }
+
+    /**
+     * 获取现在时间
+     *
+     * @return 返回短时间字符串格式yyyy-MM-dd
+     */
+    public static String getStringDateShort() {
+        Date currentTime = new Date();
+        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
+        String dateString = formatter.format(currentTime);
+        return dateString;
+    }
+
+    /**
+     * 获取时间 小时:分;秒 HH:mm:ss
+     */
+    public static String getTimeShort() {
+        SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
+        Date currentTime = new Date();
+        String dateString = formatter.format(currentTime);
+        return dateString;
+    }
+
+    /**
+     * 将长时间格式字符串转换为时间 yyyy-MM-dd HH:mm:ss
+     */
+    public static Date strToDateLong(String strDate) {
+        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        ParsePosition pos = new ParsePosition(0);
+        Date strtodate = formatter.parse(strDate, pos);
+        return strtodate;
+    }
+
+    /**
+     * 将长时间格式时间转换为字符串 yyyy-MM-dd HH:mm:ss
+     */
+    public static String dateToStrLong(Date dateDate) {
+        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String dateString = formatter.format(dateDate);
+        return dateString;
+    }
+
+    /**
+     * 将短时间格式时间转换为字符串 yyyy-MM-dd
+     */
+    public static String dateToStr(Date dateDate) {
+        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
+        String dateString = formatter.format(dateDate);
+        return dateString;
+    }
+
+    /**
+     * 将短时间格式字符串转换为时间 yyyy-MM-dd
+     */
+    public static Date strToDate(String strDate) {
+        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
+        ParsePosition pos = new ParsePosition(0);
+        Date strtodate = formatter.parse(strDate, pos);
+        return strtodate;
+    }
+
+    /**
+     * 得到现在时间
+     */
+    public static Date getNow() {
+        Date currentTime = new Date();
+        return currentTime;
+    }
+
+    /**
+     * 提取一个月中的最后一天
+     */
+    public static Date getLastDate(long day) {
+        Date date = new Date();
+        long date_3_hm = date.getTime() - 3600000 * 34 * day;
+        Date date_3_hm_date = new Date(date_3_hm);
+        return date_3_hm_date;
+    }
+
+    /**
+     * 得到现在时间
+     *
+     * @return 字符串 yyyyMMdd HHmmss
+     */
+    public static String getStringToday() {
+        Date currentTime = new Date();
+        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd HHmmss");
+        String dateString = formatter.format(currentTime);
+        return dateString;
+    }
+
+    /**
+     * 得到现在小时
+     */
+    public static String getHour() {
+        Date currentTime = new Date();
+        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String dateString = formatter.format(currentTime);
+        String hour;
+        hour = dateString.substring(11, 13);
+        return hour;
+    }
+
+    /**
+     * 得到现在分钟
+     */
+    public static String getTime() {
+        Date currentTime = new Date();
+        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String dateString = formatter.format(currentTime);
+        String min;
+        min = dateString.substring(14, 16);
+        return min;
+    }
+
+    /**
+     * 根据用户传入的时间表示格式,返回当前时间的格式 如果是yyyyMMdd,注意字母y不能大写。
+     */
+    public static String getUserDate(String sformat) {
+        Date currentTime = new Date();
+        SimpleDateFormat formatter = new SimpleDateFormat(sformat);
+        String dateString = formatter.format(currentTime);
+        return dateString;
+    }
+
+    /**
+     * @param strDate 当前时间
+     * @param pattern 时间格式
+     */
+    public static Date strToDate(String strDate, String pattern) {
+        SimpleDateFormat formatter = new SimpleDateFormat(pattern);
+        ParsePosition pos = new ParsePosition(0);
+        Date strtodate = formatter.parse(strDate, pos);
+        return strtodate;
+    }
+
+    /**
+     * @param strDate 时间
+     * @param pattern 时间格式
+     */
+    public static String getDate(String strDate, String pattern) {
+        Date currentTime = strToDate(strDate, pattern);
+        SimpleDateFormat format1 = new SimpleDateFormat(pattern);
+        String dateString = format1.format(currentTime);
+        return dateString;
+    }
+
+    /**
+     * 判断星期几
+     */
+    public static String getWeek(String date) {
+        String week = "";
+        @SuppressLint("SimpleDateFormat")
+        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
+        Calendar c = Calendar.getInstance();
+        try {
+            c.setTime(format.parse(date));
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        if (c.get(Calendar.DAY_OF_WEEK) == 1) {
+            week += "天";
+        }
+        if (c.get(Calendar.DAY_OF_WEEK) == 2) {
+            week += "一";
+        }
+        if (c.get(Calendar.DAY_OF_WEEK) == 3) {
+            week += "二";
+        }
+        if (c.get(Calendar.DAY_OF_WEEK) == 4) {
+            week += "三";
+        }
+        if (c.get(Calendar.DAY_OF_WEEK) == 5) {
+            week += "四";
+        }
+        if (c.get(Calendar.DAY_OF_WEEK) == 6) {
+            week += "五";
+        }
+        if (c.get(Calendar.DAY_OF_WEEK) == 7) {
+            week += "六";
+        }
+        return week;
+    }
+}

+ 31 - 30
app/src/main/java/com/mylove/okhttp/DownloadActivity.java

@@ -2,14 +2,17 @@ package com.mylove.okhttp;
 
 import android.content.Context;
 import android.os.Bundle;
-import android.support.annotation.IdRes;
-import android.support.v7.app.AppCompatActivity;
 import android.view.View;
 import android.widget.Button;
 import android.widget.ProgressBar;
 
+import androidx.annotation.IdRes;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.benyanyi.loglib.Jlog;
 import com.benyanyi.okhttp.OkHttpUtil;
 import com.benyanyi.okhttp.download.DownloadInfo;
+import com.benyanyi.okhttp.listener.OnDownLoadObserver;
 
 import org.greenrobot.eventbus.EventBus;
 import org.greenrobot.eventbus.Subscribe;
@@ -25,7 +28,7 @@ public class DownloadActivity extends AppCompatActivity implements View.OnClickL
     private Button downloadBtn1, downloadBtn2, downloadBtn3;
     private Button cancelBtn1, cancelBtn2, cancelBtn3;
     private ProgressBar progress1, progress2, progress3;
-    String url2 = "https://go.ziwanyouxi.com/ad/=ITOwgjM/28092";
+    String url = "http://apitest.yanyi.online/app/xiangyou.apk";
 
     private Context mContext;
 
@@ -58,11 +61,9 @@ public class DownloadActivity extends AppCompatActivity implements View.OnClickL
 
     @Override
     public void onClick(View v) {
-        String url1 = "http://www.yanyi.red/bluetooth/ios.pdf";
-        String url3 = "https://go.ziwanyouxi.com/ad/=MDO3kjM/29783";
         switch (v.getId()) {
             case R.id.main_btn_down1:
-                AppContext.getDownloadManager().start(url2);
+//                AppContext.getDownloadManager().start(url2);
                 break;
 //            case R.id.main_btn_down1:
 //                OkHttpUtil.getInstance(mContext).url(url1).download().start(new OnDownLoadObserver() {
@@ -80,26 +81,26 @@ public class DownloadActivity extends AppCompatActivity implements View.OnClickL
 //                    }
 //                });
 //                break;
-//            case R.id.main_btn_down2:
-//                OkHttpUtil.getInstance(mContext).url(url2).download(".apk").start(new OnDownLoadObserver() {
-//                    @Override
-//                    public void onNext(DownloadInfo downloadInfo) {
-//                        JLog.v(downloadInfo.getProgress());
-//                        progress2.setMax((int) downloadInfo.getTotal());
-//                        progress2.setProgress((int) downloadInfo.getProgress());
-//                    }
-//
-//                    @Override
-//                    public void onError(Throwable e) {
-//
-//                    }
-//
-//                    @Override
-//                    public void onComplete() {
-//
-//                    }
-//                });
-//                break;
+            case R.id.main_btn_down2:
+                OkHttpUtil.getInstance(mContext).url(url).download(true).start(new OnDownLoadObserver() {
+                    @Override
+                    public void onNext(DownloadInfo downloadInfo) {
+                        Jlog.v(downloadInfo.getProgress());
+                        progress2.setMax((int) downloadInfo.getTotal());
+                        progress2.setProgress((int) downloadInfo.getProgress());
+                    }
+
+                    @Override
+                    public void onError(Throwable e) {
+
+                    }
+
+                    @Override
+                    public void onComplete() {
+
+                    }
+                });
+                break;
 //            case R.id.main_btn_down3:
 //                OkHttpUtil.getInstance(mContext).url(url3).download(".apk").start(new OnDownLoadObserver() {
 //                    @Override
@@ -121,14 +122,14 @@ public class DownloadActivity extends AppCompatActivity implements View.OnClickL
 //                });
 //                break;
             case R.id.main_btn_cancel1:
-                AppContext.getDownloadManager().cancel(url2);
+//                AppContext.getDownloadManager().cancel(url);
 //                OkHttpUtil.getInstance(mContext).url(url1).download().cancel();
                 break;
             case R.id.main_btn_cancel2:
-                OkHttpUtil.getInstance(mContext).url(url2).download().pause();
+                OkHttpUtil.getInstance(mContext).url(url).download(true).pause();
                 break;
             case R.id.main_btn_cancel3:
-                OkHttpUtil.getInstance(mContext).url(url3).download().cancel();
+                OkHttpUtil.getInstance(mContext).url(url).download(true).cancel();
                 break;
             default:
                 break;
@@ -138,7 +139,7 @@ public class DownloadActivity extends AppCompatActivity implements View.OnClickL
     @Subscribe(threadMode = ThreadMode.MAIN)
     public void download(DownloadInfo downloadInfo) {
         if (downloadInfo != null) {
-            if (downloadInfo.getUrl().equals(url2)) {
+            if (downloadInfo.getUrl().equals(url)) {
                 progress1.setMax((int) downloadInfo.getTotal());
                 progress1.setProgress((int) downloadInfo.getProgress());
             }

+ 101 - 27
app/src/main/java/com/mylove/okhttp/MainActivity.java

@@ -1,25 +1,26 @@
 package com.mylove.okhttp;
 
+import android.Manifest;
 import android.content.Intent;
 import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.v7.app.AppCompatActivity;
 import android.view.View;
 
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
 import com.benyanyi.loglib.Jlog;
 import com.benyanyi.okhttp.OkHttpUtil;
+import com.benyanyi.okhttp.download.DownloadInfo;
+import com.benyanyi.okhttp.listener.OnDownLoadObserver;
 import com.benyanyi.okhttp.listener.OnOkHttpListener;
 import com.benyanyi.permissionlib.PermissionCallBack;
 import com.benyanyi.permissionlib.PermissionHelper;
 import com.benyanyi.permissionlib.PermissionType;
 import com.benyanyi.permissionlib.msg.FailureMsg;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParser;
 
 import java.util.HashMap;
 import java.util.Map;
 
-
 /**
  * @author myLove
  * @time 2017/11/16 19:18
@@ -34,8 +35,61 @@ public class MainActivity extends AppCompatActivity {
         super.onCreate(savedInstanceState);
 //        setContentView(R.layout.notification_contentview);
         setContentView(R.layout.activity_main);
-        init();
+        findViewById(R.id.but).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                PermissionHelper.getInstance(MainActivity.this).hasPermission(0x11, new PermissionCallBack() {
+                    @Override
+                    public void onPermissionSuccess(int permissionCode) {
+                        Intent intent = new Intent(MainActivity.this, DownloadActivity.class);
+                        startActivity(intent);
+                    }
+
+                    @Override
+                    public void onPermissionFailure(FailureMsg failureMsg) {
+
+                    }
+
+                    @Override
+                    public void onPermissionComplete(int permissionCode) {
+
+                    }
+                }, Manifest.permission.WRITE_EXTERNAL_STORAGE);
+            }
+        });
+        OkHttpUtil.getInstance(this).url("http://apitest.yanyi.online/app/update").getText(null).async(new OnOkHttpListener<Object>() {
+            @Override
+            public void onCompleted() {
 
+            }
+
+            @Override
+            public void onSuccess(Object message) {
+                Jlog.d(message);
+            }
+
+            @Override
+            public void onFailure(Throwable t) {
+
+            }
+        });
+//        init();
+//        PermissionHelper.getInstance(this).hasPermission(0x11, new PermissionCallBack() {
+//            @Override
+//            public void onPermissionSuccess(int permissionCode) {
+//                downloadAPK();
+//            }
+//
+//            @Override
+//            public void onPermissionFailure(FailureMsg failureMsg) {
+//
+//            }
+//
+//            @Override
+//            public void onPermissionComplete(int permissionCode) {
+//
+//            }
+//        }, Manifest.permission.WRITE_EXTERNAL_STORAGE);
 //        permissionHelper = new PermissionHelper(this, strings);
 //        permissionHelper.hasPermission(new PermissionHelper.OnPermissionListener() {
 //            @Override
@@ -49,17 +103,17 @@ public class MainActivity extends AppCompatActivity {
 //
 //            }
 //        });
-        String str = "<html></html>";
-        try {
-//            JSONObject jsonObject = new JSONObject(str);
-            JsonElement jsonParser = JsonParser.parseString(str);
-            Jlog.v(jsonParser.isJsonArray());
-            Jlog.v(jsonParser.isJsonObject());
-            Jlog.v(jsonParser.isJsonPrimitive());
-            Jlog.v(jsonParser.isJsonNull());
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
+//        String str = "<html></html>";
+//        try {
+////            JSONObject jsonObject = new JSONObject(str);
+//            JsonElement jsonParser = JsonParser.parseString(str);
+//            Jlog.v(jsonParser.isJsonArray());
+//            Jlog.v(jsonParser.isJsonObject());
+//            Jlog.v(jsonParser.isJsonPrimitive());
+//            Jlog.v(jsonParser.isJsonNull());
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//        }
     }
 
     public void onClick(View view) {
@@ -83,15 +137,17 @@ public class MainActivity extends AppCompatActivity {
     }
 
     private void init() {
-        String url = "http://qxu1590330342.my3w.com/taihao/SimpleAlarm/update.json";
-        Map<Object, Object> oMap = new HashMap<>();
-        oMap.put("UserID", "110");
-        oMap.put("TypeID", "1");
-        oMap.put("Mac", "");
-        oMap.put("CodeID", "");
-        oMap.put("Status", "");
-        oMap.put("DataSet", "");
-        OkHttpUtil.getInstance(this).url(url, true).postText(oMap).async(new OnOkHttpListener<Object>() {
+        String url = "http://test.zhcjrwang.com/api/Test/Index";
+        Map<Object, Object> headerMap = new HashMap<>();
+        headerMap.put("app_key", "XiangKeLai");
+        String timeStamp = DateUtil.timeStamp();
+        headerMap.put("timestamp", timeStamp);
+        headerMap.put("sign", ("XiangKeLai1" + timeStamp).toUpperCase());
+        Jlog.v(headerMap);
+        Map<Object, Object> bodyMap = new HashMap<>();
+        bodyMap.put("data", "+fAFfMCGsMZqeS+gyrZm1A==");
+        Jlog.v(bodyMap);
+        OkHttpUtil.getInstance(this).url(url, true).postText(headerMap, bodyMap).async(new OnOkHttpListener<Object>() {
             @Override
             public void onCompleted() {
 
@@ -111,9 +167,27 @@ public class MainActivity extends AppCompatActivity {
 
     private void downloadAPK() {
 //        JLog.init(true);
-        String url = "http://qxu1590330342.my3w.com/bluetooth/dectector/dectector.apk";
+        String url = "http://apitest.yanyi.online/app/xiangyou.apk";
 //        String filePath = "/dectector/dfu/";
 ////        String filePath = Environment.getExternalStorageDirectory().toString() + "/dectector/dfu/";
+        OkHttpUtil.getInstance(this).url(url).download(true).start(new OnDownLoadObserver() {
+            @Override
+            public void onComplete() {
+
+            }
+
+            @Override
+            public void onNext(DownloadInfo downloadInfo) {
+                super.onNext(downloadInfo);
+                Jlog.v(downloadInfo);
+            }
+
+            @Override
+            public void onError(Throwable e) {
+                super.onError(e);
+                Jlog.e(e.getMessage());
+            }
+        });
 //        OkHttpUtil.getInstance(this).downloadFile(url).downloads(filePath, new OnDownloadCallBack() {
 //            @Override
 //            public void onDownloading(int progress) {

+ 3 - 1
build.gradle

@@ -5,11 +5,12 @@ buildscript {
     repositories {
         google()
         jcenter()
+        maven { url 'http://maven.benyanyi.com/nexus/content/repositories/mylove/' }
     }
     dependencies {
         classpath 'com.android.tools.build:gradle:3.2.0'
         classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0'
-        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.3'
+//        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.3'
 //        classpath 'com.novoda:bintray-release:0.8.0'
 
         // NOTE: Do not place your application dependencies here; they belong
@@ -21,6 +22,7 @@ allprojects {
     repositories {
         google()
         jcenter()
+        maven { url 'http://maven.benyanyi.com/nexus/content/repositories/mylove/' }
         //解决中文乱码问题
         tasks.withType(Javadoc) { //兼容中文
             options.addStringOption('Xdoclint:none', '-quiet')

+ 2 - 0
gradle.properties

@@ -9,6 +9,8 @@
 
 # Specifies the JVM arguments used for the daemon process.
 # The setting is particularly useful for tweaking memory settings.
+android.enableJetifier=true
+android.useAndroidX=true
 org.gradle.jvmargs=-Xmx1536m
 
 # When configured, Gradle will run in incubating parallel mode.

+ 16 - 68
okhttplib/bintrayUpload.gradle

@@ -1,80 +1,28 @@
-// 这里添加下面两行代码。
-apply plugin: 'com.github.dcendents.android-maven'
-apply plugin: 'com.jfrog.bintray'
+apply plugin: 'maven-publish'
+apply plugin: 'maven'
 
-// 定义两个链接,下面会用到。
-def siteUrl = 'http://www.yanyis.space/yanyi/baseokhttp' // 项目主页。
-def gitUrl = 'http://www.yanyis.space/yanyi/baseokhttp.git' // Git仓库的url。
+Properties properties = new Properties()
+properties.load(project.rootProject.file('local.properties').newDataInputStream())
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            repository(url: properties.getProperty("POM_URL")) {
+                authentication(userName: properties.getProperty("nexus.user"), password: properties.getProperty("nexus.password"))
+            }
 
-group = "com.yanyi.benyanyi"// 唯一包名,比如compile 'com.ansen.http:okhttpencapsulation:1.0.1'中的com.ansen.http就是这里配置的。
-version = "2.0.2" //项目引用的版本号,比如compile 'com.ansen.http:okhttpencapsulation:1.0.1'中的1.0.1就是这里配置的。
-install {
-    repositories.mavenInstaller {
-        // This generates POM.xml with proper parameters
-        pom {
-            project {
-                packaging 'aar'
-                // Add your description here
-                name 'okhttp3 + rxjava2'
-                url siteUrl
-                // Set your license
+            pom.groupId = properties.getProperty("POM_GROUP_ID")
+            pom.artifactId = properties.getProperty("POM_ATRIFACT_ID")
+            pom.version = properties.getProperty("POM_VERSION")
+
+            pom.project {
                 licenses {
                     license {
                         name 'The Apache Software License, Version 2.0'
                         url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                     }
                 }
-                developers {
-                    developer {
-                        id 'benyanyi'     //填写的一些基本信息
-                        name 'benyanyi'
-                        email 'git@yanyi.red'
-                    }
-                }
-                scm {
-                    connection gitUrl
-                    developerConnection gitUrl
-                    url siteUrl
-                }
             }
         }
     }
 }
-task sourcesJar(type: Jar) {
-    from android.sourceSets.main.java.srcDirs
-    classifier = 'sources'
-}
-task javadoc(type: Javadoc) {
-    source = android.sourceSets.main.java.srcDirs
-    classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
-}
-task javadocJar(type: Jar, dependsOn: javadoc) {
-    classifier = 'javadoc'
-    from javadoc.destinationDir
-}
-artifacts {
-    archives javadocJar
-    archives sourcesJar
-}
-Properties properties = new Properties()
-properties.load(project.rootProject.file('local.properties').newDataInputStream())
-bintray {
-    user = properties.getProperty("bintray.user")
-    key = properties.getProperty("bintray.apikey")
-    configurations = ['archives']
-    pkg {
-        repo = "OkHttpLib"
-        name = "OkHttpLib"   //发布到JCenter上的项目名字
-        websiteUrl = siteUrl
-        vcsUrl = gitUrl
-        licenses = ["Apache-2.0"]
-        publish = true
-    }
-}
-javadoc { //jav doc采用utf-8编码否则会报“GBK的不可映射字符”错误
-    options{
-        encoding "UTF-8"
-        charSet 'UTF-8'
-    }
-}
-//gradlew bintrayUpload    terminal中输入
+//gradlew uploadArchives    terminal中输入

+ 11 - 11
okhttplib/build.gradle

@@ -1,17 +1,17 @@
 apply plugin: 'com.android.library'
 
 android {
-    compileSdkVersion 28
+    compileSdkVersion 29
 
 
 
     defaultConfig {
         minSdkVersion 14
-        targetSdkVersion 28
+        targetSdkVersion 29
         versionCode 1
         versionName "1.0"
 
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
 
     }
 
@@ -25,18 +25,18 @@ android {
 
 dependencies {
     implementation fileTree(include: ['*.jar'], dir: 'libs')
-    implementation 'com.android.support:appcompat-v7:28.0.0'
+    implementation 'androidx.appcompat:appcompat:1.0.0'
     testImplementation 'junit:junit:4.12'
-    androidTestImplementation 'com.android.support.test:runner:1.0.2'
-    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
-    implementation 'com.squareup.okhttp3:okhttp:4.0.0'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
+    implementation 'com.squareup.okhttp3:okhttp:4.5.0'
     //网络请求log 拦截器
-    implementation 'com.squareup.okhttp3:logging-interceptor:4.0.0'
-    implementation 'com.squareup.okio:okio:2.2.2'
+    implementation 'com.squareup.okhttp3:logging-interceptor:4.5.0'
+    implementation 'com.squareup.okio:okio:2.6.0'
     implementation 'io.reactivex.rxjava2:rxjava:2.2.10'
     implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
-    implementation 'com.google.code.gson:gson:2.8.5'
-    implementation 'com.squareup.retrofit2:retrofit:2.5.0'
+    implementation 'com.google.code.gson:gson:2.8.6'
+//    implementation 'com.squareup.retrofit2:retrofit:2.5.0'
     api files('lib/android-xml-json.jar')
     implementation 'org.greenrobot:eventbus:3.1.1'
 

+ 2 - 2
okhttplib/src/androidTest/java/com/benyanyi/okhttp/ExampleInstrumentedTest.java

@@ -1,8 +1,8 @@
 package com.benyanyi.okhttp;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;

+ 11 - 0
okhttplib/src/main/java/com/benyanyi/okhttp/OkHttpUtil.java

@@ -4,6 +4,9 @@ import android.content.Context;
 
 import com.benyanyi.okhttp.type.HttpRequest;
 import com.benyanyi.okhttp.type.RequestType;
+import com.benyanyi.okhttp.util.FormatUtil;
+
+import java.io.File;
 
 /**
  * @author YanYi
@@ -16,6 +19,8 @@ public class OkHttpUtil {
     private static OkHttpUtil instance;
     private static Context mContext;
 
+    public static File cacheFile;
+
     public static OkHttpUtil getInstance(Context context) {
         if (instance == null) {
             instance = new OkHttpUtil();
@@ -25,10 +30,16 @@ public class OkHttpUtil {
     }
 
     public RequestType url(String url) {
+        if (FormatUtil.isEmpty(url) || (url.indexOf("https:") != 0 && url.indexOf("http:") != 0)) {
+            throw new RuntimeException("url is not http or https");
+        }
         return new HttpRequest(mContext, url, false);
     }
 
     public RequestType url(String url, boolean isCache) {
+        if (FormatUtil.isEmpty(url) || (url.indexOf("https:") != 0 && url.indexOf("http:") != 0)) {
+            throw new RuntimeException("url is not http or https");
+        }
         return new HttpRequest(mContext, url, isCache);
     }
 

+ 4 - 10
okhttplib/src/main/java/com/benyanyi/okhttp/call/BeanCall.java

@@ -10,8 +10,6 @@ import com.benyanyi.okhttp.util.Internet;
 import com.benyanyi.okhttp.util.InternetBean;
 import com.benyanyi.okhttp.util.OkHttpLog;
 import com.google.gson.Gson;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParser;
 
 import org.json.XML;
 
@@ -163,7 +161,7 @@ class BeanCall<T> {
         String str = message;
         OkHttpLog.d(str);
         String html = "<!DOCTYPE HTML>";
-        if (FormatUtil.isNotEmpty(str) && !str.toUpperCase().contains(html)) {
+        if (FormatUtil.isNotEmpty(str) && !str.toUpperCase().contains(html) && isCache) {
             if (FormatUtil.isNotEmpty(mCacheName)) {
                 CacheUtils.getInstance(context).setCacheToLocalJson(mCacheName, str);
             }
@@ -210,13 +208,9 @@ class BeanCall<T> {
             }
         }
         try {
-            JsonParser jsonParser = new JsonParser();
-            JsonElement parse = jsonParser.parse(str);
-            if (parse.isJsonObject()) {
-                t = new Gson().fromJson(str, tClass);
-            } else {
-                t = new Gson().fromJson(str, tClass);
-            }
+//            JsonParser jsonParser = new JsonParser();
+//            JsonElement parse = jsonParser.parse(str);
+            t = new Gson().fromJson(str, tClass);
         } catch (Exception e) {
             OkHttpLog.e(e.getMessage());
         }

+ 4 - 3
okhttplib/src/main/java/com/benyanyi/okhttp/call/ListCall.java

@@ -166,7 +166,7 @@ class ListCall<T> {
         String str = message;
         OkHttpLog.d(str);
         String html = "<!DOCTYPE HTML>";
-        if (FormatUtil.isNotEmpty(str) && !str.toUpperCase().contains(html)) {
+        if (FormatUtil.isNotEmpty(str) && !str.toUpperCase().contains(html) && isCache) {
             if (FormatUtil.isNotEmpty(mCacheName)) {
                 CacheUtils.getInstance(context).setCacheToLocalJson(mCacheName, str);
             }
@@ -212,8 +212,9 @@ class ListCall<T> {
             }
         }
         try {
-            JsonParser jsonParser = new JsonParser();
-            JsonElement parse = jsonParser.parse(str);
+//            JsonParser jsonParser = new JsonParser();
+//            JsonElement parse = jsonParser.parse(str);
+            JsonElement parse = JsonParser.parseString(str);
             Gson gson = new Gson();
             if (parse.isJsonArray()) {
                 JsonArray array = parse.getAsJsonArray();

+ 2 - 3
okhttplib/src/main/java/com/benyanyi/okhttp/call/ObjectCall.java

@@ -157,7 +157,7 @@ class ObjectCall {
         }
         OkHttpLog.d(str);
         String html = "<!DOCTYPE HTML>";
-        if (FormatUtil.isNotEmpty(str) && !str.toUpperCase().contains(html)) {
+        if (FormatUtil.isNotEmpty(str) && !str.toUpperCase().contains(html) && isCache) {
             if (FormatUtil.isNotEmpty(mCacheName)) {
                 CacheUtils.getInstance(context).setCacheToLocalJson(mCacheName, str);
             }
@@ -178,10 +178,9 @@ class ObjectCall {
     private void dataReturn(String message, ObservableEmitter<Object> emitter) {
         if (isCache) {
             emitter.onNext(CacheUtils.getInstance(context).getCacheToLocalJson(mCacheName));
-            emitter.onComplete();
         } else {
             emitter.onNext(dataProcessing(message));
-            emitter.onComplete();
         }
+        emitter.onComplete();
     }
 }

+ 10 - 11
okhttplib/src/main/java/com/benyanyi/okhttp/download/DownloadCall.java

@@ -3,7 +3,6 @@ package com.benyanyi.okhttp.download;
 import android.content.Context;
 
 import com.benyanyi.okhttp.listener.OnDownLoadObserver;
-import com.benyanyi.okhttp.util.FormatUtil;
 
 /**
  * @author YanYi
@@ -16,29 +15,23 @@ public class DownloadCall implements DownloadConfig {
     private Context mContext;
     private String url;
     private String suffix;
+    private boolean isCover;
 
     private DownloadCall(Builder builder) {
         this.mContext = builder.mContext;
         this.url = builder.url;
         this.suffix = builder.suffix;
+        this.isCover = builder.isCover;
     }
 
     @Override
     public void start() {
-        if (FormatUtil.isEmpty(this.suffix)) {
-            DownloadManager.getInstance(mContext).download(url);
-        } else {
-            DownloadManager.getInstance(mContext).download(url, suffix);
-        }
+        DownloadManager.getInstance(mContext).download(url, isCover, suffix);
     }
 
     @Override
     public void start(OnDownLoadObserver onDownLoadObserver) {
-        if (FormatUtil.isEmpty(this.suffix)) {
-            DownloadManager.getInstance(mContext).download(url, onDownLoadObserver);
-        } else {
-            DownloadManager.getInstance(mContext).download(url, suffix, onDownLoadObserver);
-        }
+        DownloadManager.getInstance(mContext).download(url, isCover, suffix, onDownLoadObserver);
     }
 
     @Override
@@ -66,6 +59,7 @@ public class DownloadCall implements DownloadConfig {
         private Context mContext;
         private String url;
         private String suffix;
+        private boolean isCover;
 
         public Builder setContext(Context mContext) {
             this.mContext = mContext;
@@ -82,6 +76,11 @@ public class DownloadCall implements DownloadConfig {
             return this;
         }
 
+        public Builder setCover(boolean cover) {
+            isCover = cover;
+            return this;
+        }
+
         public DownloadConfig builder() {
             return new DownloadCall(this);
         }

+ 114 - 49
okhttplib/src/main/java/com/benyanyi/okhttp/download/DownloadManager.java

@@ -1,8 +1,10 @@
 package com.benyanyi.okhttp.download;
 
 import android.content.Context;
+import android.content.SharedPreferences;
 
 import com.benyanyi.okhttp.listener.OnDownLoadObserver;
+import com.benyanyi.okhttp.util.OkHttpLog;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -44,7 +46,22 @@ class DownloadManager {
     private OkHttpClient mClient;
     private Context mContext;
 
-    private String suffix = "";//文件名后缀
+    /**
+     * 文件名后缀
+     */
+    private String suffix = "";
+    /**
+     * 是否覆盖之前的文件
+     */
+    private boolean isCover = false;
+
+    /**
+     * 是否为下载 false 为继续下载
+     */
+    private boolean isStart = true;
+
+    private SharedPreferences sharedPreferences;
+    private SharedPreferences.Editor oEditor;
 
     /**
      * 获得一个单例类
@@ -69,6 +86,8 @@ class DownloadManager {
         downCalls = new HashMap<>();
         downInfos = new HashMap<>();
         mClient = new OkHttpClient.Builder().build();
+        sharedPreferences = mContext.getSharedPreferences("benyanyi", Context.MODE_PRIVATE);
+        oEditor = sharedPreferences.edit();
         this.mContext = mContext;
     }
 
@@ -95,23 +114,29 @@ class DownloadManager {
     /**
      * 开始下载
      *
-     * @param suffix 文件名后缀
-     * @param url    下载请求的网址
+     * @param url     下载请求的网址
+     * @param isCover 是否覆盖之前的文件
+     * @param suffix  文件名后缀
      */
-    void download(String url, String suffix) {
+    void download(String url, boolean isCover, String suffix) {
+        this.isCover = isCover;
         this.suffix = suffix;
+        this.isStart = this.sharedPreferences.getBoolean("download_start", true);
         this.download(url);
     }
 
     /**
      * 开始下载
      *
-     * @param suffix             文件名后缀
      * @param url                下载请求的网址
+     * @param isCover            是否覆盖之前的文件
+     * @param suffix             文件名后缀
      * @param onDownLoadObserver 用来回调的接口
      */
-    void download(String url, String suffix, OnDownLoadObserver onDownLoadObserver) {
+    void download(String url, boolean isCover, String suffix, OnDownLoadObserver onDownLoadObserver) {
+        this.isCover = isCover;
         this.suffix = suffix;
+        this.isStart = this.sharedPreferences.getBoolean("download_start", true);
         this.download(url, onDownLoadObserver);
     }
 
@@ -120,7 +145,7 @@ class DownloadManager {
      *
      * @param url 下载请求的网址
      */
-    void download(String url) {
+    private void download(String url) {
         DownloadObserver observer = new DownloadObserver();
         observer.setContext(mContext);
         Observable.just(url)
@@ -159,7 +184,7 @@ class DownloadManager {
                 .subscribe(observer);
     }
 
-    void download(String url, OnDownLoadObserver onDownLoadObserver) {
+    private void download(String url, OnDownLoadObserver onDownLoadObserver) {
         Observable.just(url)
                 //call的map已经有了,就证明正在下载,则这次不下载
                 .filter(new Predicate<String>() {
@@ -204,6 +229,9 @@ class DownloadManager {
     void pause(String url) {
         remove(url);
         downInfos.remove(url);
+        oEditor.putBoolean("download_start", false);
+        oEditor.apply();
+        oEditor.commit();
     }
 
     /**
@@ -229,6 +257,9 @@ class DownloadManager {
             }
         }
         downInfos.remove(url);
+        oEditor.putBoolean("download_start", true);
+        oEditor.apply();
+        oEditor.commit();
     }
 
     private void remove(String url) {
@@ -261,24 +292,33 @@ class DownloadManager {
         long downloadLength = 0, contentLength = downloadInfo.getTotal();
         File file = new File(savePath, fileName);
         if (file.exists()) {
-            //找到了文件,代表已经下载过,则获取其长度
-            downloadLength = file.length();
-        }
-        //之前下载过,需要重新来一个文件
-        int i = 1;
-        while (downloadLength >= contentLength) {
-            int dotIndex = fileName.lastIndexOf(".");
-            String fileNameOther;
-            if (dotIndex == -1) {
-                fileNameOther = fileName + "(" + i + ")";
+            if (isStart && isCover) {
+                file.delete();
             } else {
-                fileNameOther = fileName.substring(0, dotIndex)
-                        + "(" + i + ")" + fileName.substring(dotIndex);
+                //找到了文件,代表已经下载过,则获取其长度
+                downloadLength = file.length();
+                oEditor.putBoolean("download_start", true);
+                oEditor.apply();
+                oEditor.commit();
+            }
+        }
+        if (!isCover) {
+            //之前下载过,需要重新来一个文件
+            int i = 1;
+            while (downloadLength >= contentLength) {
+                int dotIndex = fileName.lastIndexOf(".");
+                String fileNameOther;
+                if (dotIndex == -1) {
+                    fileNameOther = fileName + "(" + i + ")";
+                } else {
+                    fileNameOther = fileName.substring(0, dotIndex)
+                            + "(" + i + ")" + fileName.substring(dotIndex);
+                }
+                File newFile = new File(savePath, fileNameOther);
+                file = newFile;
+                downloadLength = newFile.length();
+                i++;
             }
-            File newFile = new File(savePath, fileNameOther);
-            file = newFile;
-            downloadLength = newFile.length();
-            i++;
         }
         //设置改变过的文件名/大小
         downloadInfo.setProgress(downloadLength);
@@ -295,7 +335,7 @@ class DownloadManager {
         }
 
         @Override
-        public void subscribe(ObservableEmitter<DownloadInfo> e) throws Exception {
+        public void subscribe(ObservableEmitter<DownloadInfo> e) {
             String url = downloadInfo.getUrl();
             //已经下载好的长度
             long downloadLength = downloadInfo.getProgress();
@@ -313,32 +353,57 @@ class DownloadManager {
             //把这个添加到call里,方便取消
             downCalls.put(url, call);
             downInfos.put(url, downloadInfo);
-            Response response = call.execute();
-            String savePath = FileUtil.isExistDir(mContext.getPackageName());
-            File file = new File(savePath, downloadInfo.getFileName());
-            InputStream is = null;
-            FileOutputStream fileOutputStream = null;
+            Response response;
             try {
-                is = response.body().byteStream();
-                fileOutputStream = new FileOutputStream(file, true);
-                //缓冲数组2kB
-                byte[] buffer = new byte[2048];
-                int len;
-                while ((len = is.read(buffer)) != -1) {
-                    fileOutputStream.write(buffer, 0, len);
-                    downloadLength += len;
-                    downloadInfo.setProgress(downloadLength);
-                    downInfos.put(url, downloadInfo);
-                    e.onNext(downloadInfo);
+                response = call.execute();
+                String savePath = FileUtil.isExistDir(mContext.getPackageName());
+                File file = new File(savePath, downloadInfo.getFileName());
+                InputStream is = null;
+                FileOutputStream fileOutputStream = null;
+                try {
+                    if (response.body() == null) {
+                        e.onError(new Throwable("下载文件为空"));
+                    } else {
+                        is = response.body().byteStream();
+                        fileOutputStream = new FileOutputStream(file, true);
+                        //缓冲数组2kB
+                        byte[] buffer = new byte[2048];
+                        int len;
+                        while ((len = is.read(buffer)) != -1) {
+                            fileOutputStream.write(buffer, 0, len);
+                            downloadLength += len;
+                            downloadInfo.setDownloadStatus(DownloadInfo.DOWNLOAD_ING);
+                            downloadInfo.setProgress(downloadLength);
+                            downInfos.put(url, downloadInfo);
+                            e.onNext(downloadInfo);
+                        }
+                        downloadInfo.setFile(file);
+                        fileOutputStream.flush();
+                        downCalls.remove(url);
+                        downloadInfo.setDownloadStatus(DownloadInfo.DOWNLOAD_OVER);
+                        oEditor.putBoolean("download_start", true);
+                        oEditor.apply();
+                        oEditor.commit();
+                        //关闭IO流
+                        IoUtil.closeAll(is, fileOutputStream);
+                        e.onComplete();//完成
+                    }
+                } catch (Exception ex) {
+                    if (ex.getMessage() != null && ex.getMessage().equals("Software caused connection abort")) {
+                        OkHttpLog.e("网络断开");
+                    }
+                    pause(url);
+                    downloadInfo.setDownloadStatus(DownloadInfo.DOWNLOAD_ERROR);
+                    e.onError(ex);
+//                } finally {
+                    //关闭IO流
+//                    IoUtil.closeAll(is, fileOutputStream);
                 }
-                downloadInfo.setFile(file);
-                fileOutputStream.flush();
-                downCalls.remove(url);
-            } finally {
-                //关闭IO流
-                IoUtil.closeAll(is, fileOutputStream);
+            } catch (IOException ex) {
+                pause(url);
+                downloadInfo.setDownloadStatus(DownloadInfo.DOWNLOAD_ERROR);
+                e.onError(ex);
             }
-            e.onComplete();//完成
         }
     }
 
@@ -405,7 +470,7 @@ class DownloadManager {
                 .build();
         try {
             Response response = mClient.newCall(request).execute();
-            if (response != null && response.isSuccessful()) {
+            if (response.isSuccessful()) {
                 long contentLength = response.body().contentLength();
                 response.close();
                 return contentLength == 0 ? DownloadInfo.TOTAL_ERROR : contentLength;

+ 1 - 1
okhttplib/src/main/java/com/benyanyi/okhttp/download/FileUtil.java

@@ -2,7 +2,7 @@ package com.benyanyi.okhttp.download;
 
 import android.graphics.Bitmap;
 import android.os.Environment;
-import android.support.annotation.NonNull;
+import androidx.annotation.NonNull;
 
 import com.benyanyi.okhttp.util.FormatUtil;
 

+ 40 - 0
okhttplib/src/main/java/com/benyanyi/okhttp/type/ApiDns.java

@@ -0,0 +1,40 @@
+package com.benyanyi.okhttp.type;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+
+import okhttp3.Dns;
+
+/**
+ * @author YanYi
+ * @date 2020/10/9 14:21
+ * @email ben@yanyi.red
+ * @overview
+ */
+class ApiDns implements Dns {
+    @NotNull
+    @Override
+    public List<InetAddress> lookup(@NotNull String hostname) throws UnknownHostException {
+        try {
+            List<InetAddress> mInetAddressesList = new ArrayList<>();
+            InetAddress[] mInetAddresses = InetAddress.getAllByName(hostname);
+            for (InetAddress address : mInetAddresses) {
+                if (address instanceof Inet4Address) {
+                    mInetAddressesList.add(0, address);
+                } else {
+                    mInetAddressesList.add(address);
+                }
+            }
+            return mInetAddressesList;
+        } catch (NullPointerException var4) {
+            UnknownHostException unknownHostException = new UnknownHostException("Broken system behaviour");
+            unknownHostException.initCause(var4);
+            throw unknownHostException;
+        }
+    }
+}

+ 13 - 5
okhttplib/src/main/java/com/benyanyi/okhttp/type/Client.java

@@ -2,9 +2,11 @@ package com.benyanyi.okhttp.type;
 
 import android.content.Context;
 
+import java.util.Collections;
 import java.util.concurrent.TimeUnit;
 
 import okhttp3.OkHttpClient;
+import okhttp3.Protocol;
 
 /**
  * @author YanYi
@@ -14,21 +16,27 @@ import okhttp3.OkHttpClient;
  */
 class Client {
 
-    static OkHttpClient getClient(Context context, boolean isCache) {
+    static OkHttpClient getClient(Context context, boolean isCache, boolean isFile) {
         OkHttpClient.Builder builder = new OkHttpClient.Builder();
-        builder.addNetworkInterceptor(HttpConfig.HTTP_LOGGING_INTERCEPTOR);
+        if (isFile) {
+            builder.addNetworkInterceptor(HttpConfig.HTTP_LOGGING_INTERCEPTOR_FILE);
+        } else {
+            builder.addNetworkInterceptor(HttpConfig.HTTP_LOGGING_INTERCEPTOR);
+        }
         if (isCache) {
             builder.addInterceptor(new CacheInterceptor(context))
 //                .addNetworkInterceptor(CacheInterceptor())
 //                .addInterceptor(HttpConfig.httpInterception())
                     .cache(HttpConfig.privateCache(context));
         }
-        return builder.connectTimeout(10, TimeUnit.SECONDS)
-                .writeTimeout(10, TimeUnit.SECONDS)
-                .readTimeout(10, TimeUnit.SECONDS)
+        return builder.connectTimeout(60, TimeUnit.SECONDS)
+                .writeTimeout(60, TimeUnit.SECONDS)
+                .readTimeout(60, TimeUnit.SECONDS)
+                .protocols(Collections.singletonList(Protocol.HTTP_1_1))
                 //支持HTTPS请求,跳过证书验证
                 .sslSocketFactory(SslConfig.createSSLSocketFactory(), SslConfig.getTrustManager())
                 .hostnameVerifier(HttpConfig.verifier())
+                .dns(new ApiDns())
                 .build();
     }
 

+ 11 - 2
okhttplib/src/main/java/com/benyanyi/okhttp/type/HttpConfig.java

@@ -1,7 +1,8 @@
 package com.benyanyi.okhttp.type;
 
 import android.content.Context;
-import android.support.annotation.NonNull;
+
+import androidx.annotation.NonNull;
 
 import com.benyanyi.okhttp.util.OkHttpLog;
 
@@ -26,11 +27,19 @@ class HttpConfig {
                 }
             }).setLevel(HttpLoggingInterceptor.Level.BODY);
 
+    static HttpLoggingInterceptor HTTP_LOGGING_INTERCEPTOR_FILE =
+            new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
+                @Override
+                public void log(@NonNull String message) {
+                    OkHttpLog.d(message);
+                }
+            }).setLevel(HttpLoggingInterceptor.Level.HEADERS);
+
     /**
      * 设置缓存路径,以及缓存文件大小
      */
     static okhttp3.Cache privateCache(Context mContext) {
-        return new okhttp3.Cache(mContext.getCacheDir(), 1024 * 1024);
+        return new okhttp3.Cache(mContext.getCacheDir(), 10240 * 10240);
     }
 //    fun verifier(): HostnameVerifier
 //

+ 81 - 15
okhttplib/src/main/java/com/benyanyi/okhttp/type/HttpRequest.java

@@ -2,15 +2,16 @@ package com.benyanyi.okhttp.type;
 
 import android.content.Context;
 
-import com.benyanyi.okhttp.download.DownloadCall;
-import com.benyanyi.okhttp.download.DownloadConfig;
 import com.benyanyi.okhttp.call.HttpCall;
 import com.benyanyi.okhttp.call.RequestConfig;
+import com.benyanyi.okhttp.download.DownloadCall;
+import com.benyanyi.okhttp.download.DownloadConfig;
 import com.benyanyi.okhttp.util.FormatUtil;
 import com.benyanyi.okhttp.util.OkHttpLog;
 import com.google.gson.Gson;
 
 import java.io.File;
+import java.util.List;
 import java.util.Map;
 
 import okhttp3.FormBody;
@@ -60,17 +61,22 @@ public class HttpRequest implements RequestType {
     }
 
     @Override
-    public RequestConfig postText(Map<Object, Object> map) {
+    public RequestConfig postText(Map<Object, Object> headerMap, Map<Object, Object> bodyMap) {
         FormBody.Builder builder = new FormBody.Builder();
-        if (FormatUtil.isMapNotEmpty(map)) {
-            mCacheName = mCacheName + map.toString();
-            for (Map.Entry<Object, Object> entry : map.entrySet()) {
+        if (FormatUtil.isMapNotEmpty(bodyMap)) {
+            mCacheName = mCacheName + bodyMap.toString();
+            for (Map.Entry<Object, Object> entry : bodyMap.entrySet()) {
                 builder.add(entry.getKey().toString(), entry.getValue().toString());
             }
         }
         FormBody build = builder.build();
-        request = new Request.Builder()
-                .url(url)
+        Request.Builder builder1 = new Request.Builder();
+        if (FormatUtil.isMapNotEmpty(headerMap)) {
+            for (Map.Entry<Object, Object> entry : headerMap.entrySet()) {
+                builder1.addHeader(entry.getKey().toString(), entry.getValue().toString());
+            }
+        }
+        request = builder1.url(url)
                 .post(build)
                 .build();
         return send();
@@ -139,9 +145,11 @@ public class HttpRequest implements RequestType {
         if (FormatUtil.isMapNotEmpty(map)) {
             for (Map.Entry<Object, Object> entry : map.entrySet()) {
                 File file = new File(entry.getValue().toString());
-                int indexOf = entry.getValue().toString().indexOf("/");
-                RequestBody requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
-                String fileName = entry.getKey().toString().substring(indexOf + 1);
+//                int indexOf = entry.getValue().toString().indexOf("/");
+//                RequestBody requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
+                RequestBody requestBody = RequestBody.create(file, MediaType.parse("multipart/form-data"));
+//                String fileName = entry.getKey().toString().substring(indexOf + 1);
+                String fileName = entry.getKey().toString();
                 builder.addFormDataPart(entry.getKey().toString(), fileName, requestBody);
             }
         }
@@ -150,25 +158,83 @@ public class HttpRequest implements RequestType {
                 .url(url)
                 .post(multipartBody)
                 .build();
-        return send();
+        return send(true);
+    }
+
+    @Override
+    public RequestConfig file(String... filePaths) {
+        MultipartBody.Builder builder = new MultipartBody.Builder()
+                .setType(MultipartBody.FORM);
+        if (filePaths != null && filePaths.length > 0) {
+            for (String str : filePaths) {
+                File file = new File(str);
+//                int indexOf = entry.getValue().toString().indexOf("/");
+//                RequestBody requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
+                RequestBody requestBody = RequestBody.create(file, MediaType.parse("multipart/form-data"));
+//                String fileName = entry.getKey().toString().substring(indexOf + 1);
+                builder.addFormDataPart("file", file.getName(), requestBody);
+            }
+        }
+        MultipartBody multipartBody = builder.build();
+        request = new Request.Builder()
+                .url(url)
+                .post(multipartBody)
+                .build();
+        return send(true);
+    }
+
+    @Override
+    public RequestConfig file(List<String> filePaths) {
+        MultipartBody.Builder builder = new MultipartBody.Builder()
+                .setType(MultipartBody.FORM);
+        if (filePaths != null && filePaths.size() > 0) {
+            for (String str : filePaths) {
+                File file = new File(str);
+//                int indexOf = entry.getValue().toString().indexOf("/");
+//                RequestBody requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
+                RequestBody requestBody = RequestBody.create(file, MediaType.parse("multipart/form-data"));
+//                String fileName = entry.getKey().toString().substring(indexOf + 1);
+                builder.addFormDataPart("file", file.getName(), requestBody);
+            }
+        }
+        MultipartBody multipartBody = builder.build();
+        request = new Request.Builder()
+                .url(url)
+                .post(multipartBody)
+                .build();
+        return send(true);
     }
 
     @Override
     public DownloadConfig download() {
-        return new DownloadCall.Builder().setContext(context).setUrl(url).builder();
+        return new DownloadCall.Builder().setContext(context).setUrl(url).setCover(false).setSuffix("").builder();
+    }
+
+    @Override
+    public DownloadConfig download(boolean isCover) {
+        return new DownloadCall.Builder().setContext(context).setUrl(url).setCover(isCover).setSuffix("").builder();
     }
 
     @Override
     public DownloadConfig download(String suffix) {
-        return new DownloadCall.Builder().setContext(context).setUrl(url).setSuffix(suffix).builder();
+        return new DownloadCall.Builder().setContext(context).setUrl(url).setCover(false).setSuffix(suffix).builder();
+    }
+
+    @Override
+    public DownloadConfig download(boolean isCover, String suffix) {
+        return new DownloadCall.Builder().setContext(context).setUrl(url).setCover(isCover).setSuffix(suffix).builder();
     }
 
     private RequestConfig send() {
+        return send(false);
+    }
+
+    private RequestConfig send(boolean isFile) {
         return new HttpCall.Builder()
                 .setCache(isCache)
                 .setCacheUrl(mCacheName)
                 .setContext(context)
-                .setHttpClient(Client.getClient(context, isCache))
+                .setHttpClient(Client.getClient(context, isCache, isFile))
                 .setRequest(request)
                 .builder();
     }

+ 21 - 2
okhttplib/src/main/java/com/benyanyi/okhttp/type/RequestType.java

@@ -1,8 +1,9 @@
 package com.benyanyi.okhttp.type;
 
-import com.benyanyi.okhttp.download.DownloadConfig;
 import com.benyanyi.okhttp.call.RequestConfig;
+import com.benyanyi.okhttp.download.DownloadConfig;
 
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -15,7 +16,7 @@ public interface RequestType {
 
     RequestConfig getText(Map<Object, Object> map);
 
-    RequestConfig postText(Map<Object, Object> map);
+    RequestConfig postText(Map<Object, Object> headerMap, Map<Object, Object> bodyMap);
 
     RequestConfig postJson(Map<Object, Object> map);
 
@@ -25,8 +26,26 @@ public interface RequestType {
 
     RequestConfig file(Map<Object, Object> map);
 
+    RequestConfig file(String... filePaths);
+
+    RequestConfig file(List<String> filePaths);
+
     DownloadConfig download();
 
+    /**
+     * @param isCover 是否覆盖之前的文件
+     */
+    DownloadConfig download(boolean isCover);
+
+    /**
+     * @param suffix 文件名后缀
+     */
     DownloadConfig download(String suffix);
 
+    /**
+     * @param isCover 是否覆盖之前的文件
+     * @param suffix  文件名后缀
+     */
+    DownloadConfig download(boolean isCover, String suffix);
+
 }

+ 1 - 1
okhttplib/src/main/java/com/benyanyi/okhttp/type/SslConfig.java

@@ -33,7 +33,7 @@ class SslConfig {
 
         SSLSocketFactory sslSocketFactory = null;
         try {
-            SSLContext sslContext = SSLContext.getInstance("SSL");
+            SSLContext sslContext = SSLContext.getInstance("TLS");
             sslContext.init(null, new TrustManager[]{getTrustManager()}, new SecureRandom());
             sslSocketFactory = sslContext.getSocketFactory();
         } catch (Exception e) {

+ 16 - 4
okhttplib/src/main/java/com/benyanyi/okhttp/util/CacheUtils.java

@@ -4,6 +4,8 @@ import android.annotation.SuppressLint;
 import android.content.Context;
 import android.os.Environment;
 
+import com.benyanyi.okhttp.OkHttpUtil;
+
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.File;
@@ -27,11 +29,21 @@ public final class CacheUtils {
             instance = new CacheUtils();
             mContext = context;
         }
-        File dir = mContext.getExternalFilesDir(null);
-        if (dir != null && !dir.exists() && Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
-            dir.mkdirs();
+        if (OkHttpUtil.cacheFile == null) {
+            File dir = mContext.getExternalFilesDir(null);
+            if (dir != null && !dir.exists() && Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+                dir.mkdirs();
+            } else {
+                dir = mContext.getFilesDir();
+                if (!dir.exists()) {
+                    dir.mkdirs();
+                }
+            }
+            realFile = dir;
+        } else {
+            realFile = OkHttpUtil.cacheFile;
         }
-        realFile = dir;
+
         return instance;
     }