+ +
+
+
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index dd4c74e..1f98522 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,38 +1,9 @@
-buildscript {
- repositories {
- // 阿里云云效仓库:https://maven.aliyun.com/mvn/guide
- maven { url 'https://maven.aliyun.com/repository/jcenter' }
- maven { url 'https://maven.aliyun.com/repository/google' }
- // 华为开源镜像:https://mirrors.huaweicloud.com/
- maven { url 'https://repo.huaweicloud.com/repository/maven/' }
- // JitPack 远程仓库:https://jitpack.io
- maven { url 'https://jitpack.io' }
- mavenCentral()
- google()
- // noinspection JcenterRepositoryObsolete
- jcenter()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:4.1.2'
- }
+plugins {
+ id 'com.android.application' version '8.13.2' apply false
+ id 'org.jetbrains.kotlin.android' version '2.2.21' apply false
+ id 'maven-publish'
}
-allprojects {
- repositories {
- maven { url 'https://maven.aliyun.com/repository/jcenter' }
- maven { url 'https://maven.aliyun.com/repository/google' }
- maven { url 'https://repo.huaweicloud.com/repository/maven/' }
- maven { url 'https://jitpack.io' }
- mavenCentral()
- google()
- // noinspection JcenterRepositoryObsolete
- jcenter()
- }
-
- // 将构建文件统一输出到项目根目录下的 build 文件夹
- setBuildDir(new File(rootDir, "build/${path.replaceAll(':', '/')}"))
-}
-
-task clean(type: Delete) {
+tasks.register('clean', Delete) {
delete rootProject.buildDir
}
\ No newline at end of file
diff --git a/common.gradle b/common.gradle
new file mode 100644
index 0000000..eb61a7e
--- /dev/null
+++ b/common.gradle
@@ -0,0 +1,36 @@
+// 通用配置
+android {
+
+ // 编译源码版本
+ compileSdk 34
+ defaultConfig {
+ // Android 版本适配指南:https://github.com/getActivity/AndroidVersionAdapter
+ targetSdk 34
+ versionCode 1350
+ versionName "13.5"
+ }
+
+ // 支持 Java JDK 8
+ compileOptions {
+ targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility JavaVersion.VERSION_1_8
+ }
+
+ // 读取 local.properties 文件配置
+ def properties = new Properties()
+ def localPropertiesFile = rootProject.file("local.properties")
+ if (localPropertiesFile.exists()) {
+ localPropertiesFile.withInputStream { inputStream ->
+ properties.load(inputStream)
+ }
+ }
+
+ String buildDirPath = properties.getProperty("build.dir")
+ if (buildDirPath != null && buildDirPath != "") {
+ // 将构建文件统一输出到指定的目录下
+ setBuildDir(new File(buildDirPath, rootProject.name + "/build/${path.replaceAll(':', '/')}"))
+ } else {
+ // 将构建文件统一输出到项目根目录下的 build 文件夹
+ setBuildDir(new File(rootDir, "build/${path.replaceAll(':', '/')}"))
+ }
+}
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 947be17..90726b0 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
-zipStoreBase = GRADLE_USER_HOME
-zipStorePath = wrapper/dists
-distributionBase = GRADLE_USER_HOME
-distributionPath = wrapper/dists
-distributionUrl = https\://services.gradle.org/distributions/gradle-6.5-all.zip
\ No newline at end of file
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-all.zip
\ No newline at end of file
diff --git a/jitpack.yml b/jitpack.yml
new file mode 100644
index 0000000..1e41e00
--- /dev/null
+++ b/jitpack.yml
@@ -0,0 +1,2 @@
+jdk:
+ - openjdk17
\ No newline at end of file
diff --git a/library/build.gradle b/library/build.gradle
index b17d142..778941b 100644
--- a/library/build.gradle
+++ b/library/build.gradle
@@ -1,12 +1,16 @@
apply plugin: 'com.android.library'
+apply plugin: 'maven-publish'
+apply from : '../common.gradle'
android {
- compileSdkVersion 29
+
+ namespace 'com.hjq.http'
defaultConfig {
- minSdkVersion 14
- versionCode 1000
- versionName "10.0"
+ minSdk 21
+
+ // 框架的混淆配置
+ consumerProguardFiles 'proguard-http.pro'
}
// 使用 JDK 1.8
@@ -15,53 +19,45 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
}
- android.libraryVariants.all { variant ->
- // aar 输出文件名配置
- variant.outputs.all { output ->
- outputFileName = "${rootProject.name}-${android.defaultConfig.versionName}.aar"
- }
+ packagingOptions {
+ // 剔除这个包下的所有文件(不会移除签名信息)
+ exclude 'META-INF/*******'
}
}
-afterEvaluate {
- // 排除 BuildConfig.class 和 R.class
- generateReleaseBuildConfig.enabled = false
- generateDebugBuildConfig.enabled = false
- generateReleaseResValues.enabled = false
- generateDebugResValues.enabled = false
-}
-
dependencies {
- // noinspection GradleDependency
- implementation 'com.squareup.okhttp3:okhttp:3.12.13'
+ // OkHttp 框架:https://github.com/square/okhttp
+ implementation 'com.squareup.okhttp3:okhttp:5.3.0'
// noinspection GradleDependency
implementation 'androidx.lifecycle:lifecycle-common:2.1.0'
// noinspection GradleDependency
implementation 'androidx.lifecycle:lifecycle-runtime:2.1.0'
}
-tasks.withType(Javadoc) {
- options.addStringOption('Xdoclint:none', '-quiet')
- options.addStringOption('encoding', 'UTF-8')
- options.addStringOption('charSet', 'UTF-8')
-}
-
-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
+publishing {
+ publications {
+ release(MavenPublication) {
+ groupId = 'com.github.getActivity'
+ artifactId = 'EasyHttp'
+ version = android.defaultConfig.versionName
+ // 指定 aar 文件的确切路径:https://cloud.tencent.com/developer/ask/sof/106381154
+ afterEvaluate {
+ artifact(bundleReleaseAar)
+ }
+
+ // 修正 Maven 插件在生成的 pom 文件没有携带 dependencies 信息的问题
+ // 用于测试 Gradle 命令:./gradlew publishToMavenLocal
+ // 相关 Github 地址:https://github.com/getActivity/XXPermissions/issues/371#issuecomment-3018735968
+ pom.withXml {
+ def dependencies = asNode().appendNode('dependencies')
+ configurations.implementation.allDependencies.each {
+ def dependency = dependencies.appendNode('dependency')
+ dependency.appendNode('groupId', it.group)
+ dependency.appendNode('artifactId', it.name)
+ dependency.appendNode('version', it.version)
+ dependency.appendNode('scope', 'runtime')
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/library/proguard-http.pro b/library/proguard-http.pro
new file mode 100644
index 0000000..7e23ccf
--- /dev/null
+++ b/library/proguard-http.pro
@@ -0,0 +1,7 @@
+# 不混淆实现 OnHttpListener 接口的类,必须要加上此规则,否则会导致泛型解析失败
+-keep class * implements com.hjq.http.listener.OnHttpListener {
+ *;
+}
+-keep class * extends com.hjq.http.model.ResponseClass {
+ *;
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/EasyConfig.java b/library/src/main/java/com/hjq/http/EasyConfig.java
index d17fd96..693d42c 100644
--- a/library/src/main/java/com/hjq/http/EasyConfig.java
+++ b/library/src/main/java/com/hjq/http/EasyConfig.java
@@ -1,16 +1,20 @@
package com.hjq.http;
-import com.hjq.http.config.ILogStrategy;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.hjq.http.config.IHttpCacheStrategy;
import com.hjq.http.config.IRequestHandler;
import com.hjq.http.config.IRequestInterceptor;
+import com.hjq.http.config.IHttpLogStrategy;
import com.hjq.http.config.IRequestServer;
-import com.hjq.http.config.LogStrategy;
-import com.hjq.http.config.RequestServer;
-
+import com.hjq.http.config.impl.DefaultHttpLogStrategy;
+import com.hjq.http.config.impl.DefaultNullCacheStrategy;
+import com.hjq.http.config.impl.SimpleRequestServer;
+import com.hjq.http.model.ThreadSchedulers;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
-
+import java.util.Map;
import okhttp3.OkHttpClient;
/**
@@ -46,16 +50,21 @@ public static EasyConfig with(OkHttpClient client) {
private IRequestHandler mHandler;
/** 请求拦截器 */
private IRequestInterceptor mInterceptor;
+ /** 请求缓存策略 */
+ private IHttpCacheStrategy mCacheStrategy;
/** 日志打印策略 */
- private ILogStrategy mLogStrategy;
+ private IHttpLogStrategy mLogStrategy;
/** OkHttp 客户端 */
private OkHttpClient mClient;
/** 通用参数 */
- private HashMap mParams;
+ private Map mParams;
/** 通用请求头 */
- private HashMap mHeaders;
+ private Map mHeaders;
+
+ /** 线程调度器 */
+ private ThreadSchedulers mThreadSchedulers = ThreadSchedulers.MAIN;
/** 日志开关 */
private boolean mLogEnabled = true;
@@ -73,44 +82,46 @@ private EasyConfig(OkHttpClient client) {
mHeaders = new HashMap();
}
- public EasyConfig setServer(String host) {
- return setServer(new RequestServer(host));
+ public EasyConfig setServer(@NonNull String host) {
+ return setServer(new SimpleRequestServer(host));
}
- public EasyConfig setServer(IRequestServer server) {
+ public EasyConfig setServer(@NonNull IRequestServer server) {
mServer = server;
return this;
}
- public EasyConfig setHandler(IRequestHandler handler) {
+ public EasyConfig setHandler(@NonNull IRequestHandler handler) {
mHandler = handler;
return this;
}
- public EasyConfig setInterceptor(IRequestInterceptor interceptor) {
+ public EasyConfig setInterceptor(@Nullable IRequestInterceptor interceptor) {
mInterceptor = interceptor;
return this;
}
- public EasyConfig setClient(OkHttpClient client) {
+ public EasyConfig setCacheStrategy(@NonNull IHttpCacheStrategy strategy) {
+ mCacheStrategy = strategy;
+ return this;
+ }
+
+ public EasyConfig setClient(@NonNull OkHttpClient client) {
mClient = client;
- if (mClient == null) {
- throw new IllegalArgumentException("The OkHttp client object cannot be empty");
- }
return this;
}
- public EasyConfig setParams(HashMap params) {
+ public EasyConfig setParams(Map params) {
if (params == null) {
- params = new HashMap();
+ params = new HashMap(10);
}
mParams = params;
return this;
}
- public EasyConfig setHeaders(HashMap headers) {
+ public EasyConfig setHeaders(Map headers) {
if (headers == null) {
- headers = new HashMap();
+ headers = new HashMap(10);
}
mHeaders = headers;
return this;
@@ -144,7 +155,16 @@ public EasyConfig removeParam(String key) {
return this;
}
- public EasyConfig setLogStrategy(ILogStrategy strategy) {
+ public EasyConfig setThreadSchedulers(@NonNull ThreadSchedulers schedulers) {
+ if (mThreadSchedulers == null) {
+ // 线程调度器不能为空
+ throw new NullPointerException("Thread schedulers cannot be empty");
+ }
+ mThreadSchedulers = schedulers;
+ return this;
+ }
+
+ public EasyConfig setLogStrategy(IHttpLogStrategy strategy) {
mLogStrategy = strategy;
return this;
}
@@ -185,6 +205,10 @@ public IRequestHandler getHandler() {
return mHandler;
}
+ public IHttpCacheStrategy getCacheStrategy() {
+ return mCacheStrategy;
+ }
+
public IRequestInterceptor getInterceptor() {
return mInterceptor;
}
@@ -193,15 +217,19 @@ public OkHttpClient getClient() {
return mClient;
}
- public HashMap getParams() {
+ public Map getParams() {
return mParams;
}
- public HashMap getHeaders() {
+ public Map getHeaders() {
return mHeaders;
}
- public ILogStrategy getLogStrategy() {
+ public ThreadSchedulers getThreadSchedulers() {
+ return mThreadSchedulers;
+ }
+
+ public IHttpLogStrategy getLogStrategy() {
return mLogStrategy;
}
@@ -223,27 +251,33 @@ public long getRetryTime() {
public void into() {
if (mClient == null) {
- throw new IllegalArgumentException("The OkHttp client object cannot be empty");
+ throw new IllegalArgumentException("Please set up the OkHttpClient object");
}
if (mServer == null) {
- throw new IllegalArgumentException("The host configuration cannot be empty");
+ throw new IllegalArgumentException("Please set up the RequestServer object");
}
if (mHandler == null) {
- throw new IllegalArgumentException("The object being processed by the request cannot be empty");
+ throw new IllegalArgumentException("Please set the RequestHandler object");
}
try {
// 校验主机和路径的 url 是否合法
- new URL(mServer.getHost() + mServer.getPath());
+ new URL(mServer.getHost());
} catch (MalformedURLException e) {
+ e.printStackTrace();
throw new IllegalArgumentException("The configured host path url address is not correct");
}
if (mLogStrategy == null) {
- mLogStrategy = new LogStrategy();
+ mLogStrategy = new DefaultHttpLogStrategy();
+ }
+
+ if (mCacheStrategy == null) {
+ mCacheStrategy = new DefaultNullCacheStrategy();
}
+
EasyConfig.setInstance(this);
}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/EasyHttp.java b/library/src/main/java/com/hjq/http/EasyHttp.java
index 452a2f1..6b4d209 100644
--- a/library/src/main/java/com/hjq/http/EasyHttp.java
+++ b/library/src/main/java/com/hjq/http/EasyHttp.java
@@ -1,8 +1,9 @@
package com.hjq.http;
+import android.text.TextUtils;
import androidx.lifecycle.LifecycleOwner;
-
-import com.hjq.http.request.DeleteRequest;
+import com.hjq.http.request.DeleteBodyRequest;
+import com.hjq.http.request.DeleteUrlRequest;
import com.hjq.http.request.DownloadRequest;
import com.hjq.http.request.GetRequest;
import com.hjq.http.request.HeadRequest;
@@ -11,7 +12,6 @@
import com.hjq.http.request.PostRequest;
import com.hjq.http.request.PutRequest;
import com.hjq.http.request.TraceRequest;
-
import okhttp3.Call;
import okhttp3.OkHttpClient;
@@ -61,15 +61,39 @@ public static HeadRequest head(LifecycleOwner lifecycleOwner) {
}
/**
- * Delete 请求
+ * Delete 请求(默认使用 Url 传递参数)
+ *
+ * @param lifecycleOwner 请传入 AppCompatActivity 或者 AndroidX.Fragment 子类
+ * 如需传入其他对象请参考以下两个类
+ * {@link com.hjq.http.lifecycle.ActivityLifecycle}
+ * {@link com.hjq.http.lifecycle.ApplicationLifecycle}
+ */
+ public static DeleteUrlRequest delete(LifecycleOwner lifecycleOwner) {
+ return deleteByUrl(lifecycleOwner);
+ }
+
+ /**
+ * Delete 请求(参数使用 Url 传递)
+ *
+ * @param lifecycleOwner 请传入 AppCompatActivity 或者 AndroidX.Fragment 子类
+ * 如需传入其他对象请参考以下两个类
+ * {@link com.hjq.http.lifecycle.ActivityLifecycle}
+ * {@link com.hjq.http.lifecycle.ApplicationLifecycle}
+ */
+ public static DeleteUrlRequest deleteByUrl(LifecycleOwner lifecycleOwner) {
+ return new DeleteUrlRequest(lifecycleOwner);
+ }
+
+ /**
+ * Delete 请求(参数使用 Body 传递)
*
* @param lifecycleOwner 请传入 AppCompatActivity 或者 AndroidX.Fragment 子类
* 如需传入其他对象请参考以下两个类
* {@link com.hjq.http.lifecycle.ActivityLifecycle}
* {@link com.hjq.http.lifecycle.ApplicationLifecycle}
*/
- public static DeleteRequest delete(LifecycleOwner lifecycleOwner) {
- return new DeleteRequest(lifecycleOwner);
+ public static DeleteBodyRequest deleteByBody(LifecycleOwner lifecycleOwner) {
+ return new DeleteBodyRequest(lifecycleOwner);
}
/**
@@ -133,16 +157,13 @@ public static DownloadRequest download(LifecycleOwner lifecycleOwner) {
}
/**
- * 取消请求
+ * 根据 TAG 取消请求任务
*/
- public static void cancel(LifecycleOwner lifecycleOwner) {
- cancel(String.valueOf(lifecycleOwner));
+ public static void cancelByTag(Object tag) {
+ cancelByTag(EasyUtils.getObjectTag(tag));
}
- /**
- * 根据 TAG 取消请求任务
- */
- public static void cancel(Object tag) {
+ public static void cancelByTag(String tag) {
if (tag == null) {
return;
}
@@ -151,23 +172,36 @@ public static void cancel(Object tag) {
// 清除排队等候的任务
for (Call call : client.dispatcher().queuedCalls()) {
- if (tag.equals(call.request().tag())) {
- call.cancel();
+ Object requestTag = call.request().tag();
+ if (requestTag == null) {
+ continue;
+ }
+ if (!TextUtils.equals(tag, String.valueOf(requestTag))) {
+ continue;
}
+ call.cancel();
}
// 清除正在执行的任务
for (Call call : client.dispatcher().runningCalls()) {
- if (tag.equals(call.request().tag())) {
- call.cancel();
+ Object requestTag = call.request().tag();
+ if (requestTag == null) {
+ continue;
+ }
+ if (!TextUtils.equals(tag, String.valueOf(requestTag))) {
+ continue;
}
+ call.cancel();
}
+
+ // 移除延迟发起的网络请求
+ EasyUtils.removeDelayedRunnable(tag.hashCode());
}
/**
* 清除所有请求任务
*/
- public static void cancel() {
+ public static void cancelAll() {
OkHttpClient client = EasyConfig.getInstance().getClient();
// 清除排队等候的任务
@@ -179,5 +213,8 @@ public static void cancel() {
for (Call call : client.dispatcher().runningCalls()) {
call.cancel();
}
+
+ // 移除延迟发起的网络请求
+ EasyUtils.removeAllRunnable();
}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/EasyLog.java b/library/src/main/java/com/hjq/http/EasyLog.java
index 5134acf..996cf16 100644
--- a/library/src/main/java/com/hjq/http/EasyLog.java
+++ b/library/src/main/java/com/hjq/http/EasyLog.java
@@ -1,5 +1,12 @@
package com.hjq.http;
+import com.hjq.http.request.HttpRequest;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
/**
* author : Android 轮子哥
* github : https://github.com/getActivity/EasyHttp
@@ -8,60 +15,77 @@
*/
public final class EasyLog {
+ /** 创建线程池来打印日志,解决出现大日志阻塞线程的情况 */
+ @SuppressWarnings("AlibabaThreadShouldSetName")
+ private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(1, 1,
+ 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(),
+ Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());
+
/**
* 打印分割线
*/
- public static void print() {
- print("----------------------------------------");
+ public static void printLine(HttpRequest> httpRequest) {
+ if (!EasyConfig.getInstance().isLogEnabled()) {
+ return;
+ }
+ EXECUTOR.execute(() -> EasyConfig.getInstance().getLogStrategy().printLine(getLogTag(httpRequest)));
}
/**
* 打印日志
*/
- public static void print(String log) {
+ public static void printLog(HttpRequest> httpRequest, String log) {
if (!EasyConfig.getInstance().isLogEnabled()) {
return;
}
- EasyConfig.getInstance().getLogStrategy().print(log);
+ EXECUTOR.execute(() -> EasyConfig.getInstance().getLogStrategy().printLog(getLogTag(httpRequest), log));
}
/**
* 打印 Json
*/
- public static void json(String json) {
+ public static void printJson(HttpRequest> httpRequest, String json) {
if (!EasyConfig.getInstance().isLogEnabled()) {
return;
}
- EasyConfig.getInstance().getLogStrategy().json(json);
+ EXECUTOR.execute(() -> EasyConfig.getInstance().getLogStrategy().printJson(getLogTag(httpRequest), json));
}
/**
* 打印键值对
*/
- public static void print(String key, String value) {
+ public static void printKeyValue(HttpRequest> httpRequest, String key, String value) {
if (!EasyConfig.getInstance().isLogEnabled()) {
return;
}
- EasyConfig.getInstance().getLogStrategy().print(key, value);
+ EXECUTOR.execute(() -> EasyConfig.getInstance().getLogStrategy().printKeyValue(getLogTag(httpRequest), key, value));
}
/**
* 打印异常
*/
- public static void print(Throwable throwable) {
+ public static void printThrowable(HttpRequest> httpRequest, Throwable throwable) {
if (!EasyConfig.getInstance().isLogEnabled()) {
return;
}
- EasyConfig.getInstance().getLogStrategy().print(throwable);
+ EXECUTOR.execute(() -> EasyConfig.getInstance().getLogStrategy().printThrowable(getLogTag(httpRequest), throwable));
}
/**
* 打印堆栈
*/
- public static void print(StackTraceElement[] stackTrace) {
+ public static void printStackTrace(HttpRequest> httpRequest, StackTraceElement[] stackTrace) {
if (!EasyConfig.getInstance().isLogEnabled()) {
return;
}
- EasyConfig.getInstance().getLogStrategy().print(stackTrace);
+ EXECUTOR.execute(() -> EasyConfig.getInstance().getLogStrategy().printStackTrace(getLogTag(httpRequest), stackTrace));
+ }
+
+ private static String getLogTag(HttpRequest> httpRequest) {
+ String logTag = EasyConfig.getInstance().getLogTag();
+ if (httpRequest == null) {
+ return logTag;
+ }
+ return logTag + " " + httpRequest.generateLogTag();
}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/EasyUtils.java b/library/src/main/java/com/hjq/http/EasyUtils.java
index bde0241..32bea8f 100644
--- a/library/src/main/java/com/hjq/http/EasyUtils.java
+++ b/library/src/main/java/com/hjq/http/EasyUtils.java
@@ -2,37 +2,46 @@
import android.os.Handler;
import android.os.Looper;
+import android.os.Message;
import android.text.TextUtils;
-
+import android.util.LruCache;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.hjq.http.annotation.HttpIgnore;
import com.hjq.http.annotation.HttpRename;
-import com.hjq.http.body.UpdateBody;
-import com.hjq.http.model.ContentType;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
+import com.hjq.http.body.WrapperRequestBody;
+import com.hjq.http.model.FileContentResolver;
+import com.hjq.http.model.ThreadSchedulers;
import java.io.Closeable;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Array;
import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
-import java.net.FileNameMap;
-import java.net.URLConnection;
+import java.lang.reflect.WildcardType;
import java.net.URLEncoder;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
-
-import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
/**
* author : Android 轮子哥
@@ -42,20 +51,84 @@
*/
public final class EasyUtils {
+ /** Handler 对象 */
private static final Handler HANDLER = new Handler(Looper.getMainLooper());
+ /** 反射字段缓存 */
+ private static final LruCache, List> CLASS_LIST_LRU_CACHE = new LruCache(30);
+
/**
* 在主线程中执行
*/
- public static void post(Runnable r) {
- HANDLER.post(r);
+ public static void runOnMainThread(Runnable runnable) {
+ HANDLER.post(runnable);
+ }
+
+ /**
+ * 在子线程中执行
+ */
+ public static void runOnIOThread(Runnable runnable) {
+ EasyConfig.getInstance().getClient().dispatcher().executorService().execute(runnable);
+ }
+
+ /**
+ * 运行在指定线程
+ */
+ public static void runOnAssignThread(ThreadSchedulers schedulers, Runnable runnable) {
+ switch (schedulers) {
+ case IO:
+ if (isMainThread()) {
+ runOnIOThread(runnable);
+ } else {
+ runnable.run();
+ }
+ break;
+ case MAIN:
+ default:
+ if (isMainThread()) {
+ runnable.run();
+ } else {
+ runOnMainThread(runnable);
+ }
+ break;
+ }
+ }
+
+ /**
+ * 延迟一段时间执行任务
+ */
+ public static void postDelayedRunnable(Runnable runnable, long delayMillis) {
+ HANDLER.postDelayed(runnable, delayMillis);
+ }
+
+ /**
+ * 延迟一段时间执行任务(带一定的标识,方便后续移除)
+ */
+ public static void postDelayedRunnable(Runnable runnable, int what, long delayMillis) {
+ Message msg = Message.obtain(HANDLER, runnable);
+ msg.what = what;
+ HANDLER.sendMessageDelayed(msg, delayMillis);
+ }
+
+ /**
+ * 移除指定的消息
+ */
+ public static void removeDelayedRunnable(int what) {
+ HANDLER.removeMessages(what);
+ }
+
+ /**
+ * 移除所有消息
+ */
+ public static void removeAllRunnable() {
+ HANDLER.removeCallbacksAndMessages(null);
}
/**
- * 延迟一段时间执行
+ * 判断当前是否为主线程
*/
- public static void postDelayed(Runnable r, long delayMillis) {
- HANDLER.postDelayed(r, delayMillis);
+ public static boolean isMainThread() {
+ return Looper.getMainLooper() == Looper.myLooper();
}
/**
@@ -67,11 +140,18 @@ public static void closeStream(Closeable closeable) {
}
try {
closeable.close();
- } catch (Exception e) {
- EasyLog.print(e);
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
}
}
+ /**
+ * 判断对象是否为数组类型
+ */
+ public static boolean isArrayType(Object object) {
+ return object.getClass().isArray();
+ }
+
/**
* 判断对象是否为 Bean 类
*/
@@ -79,6 +159,12 @@ public static boolean isBeanType(Object object) {
if (object == null) {
return false;
}
+ if (object instanceof Enum) {
+ return false;
+ }
+ if (isArrayType(object)) {
+ return false;
+ }
// Number:Long、Integer、Short、Double、Float、Byte
// CharSequence:String、StringBuilder、StringBuilder
return !(object instanceof Number || object instanceof CharSequence || object instanceof Boolean ||
@@ -89,11 +175,15 @@ public static boolean isBeanType(Object object) {
/**
* 判断是否包含存在流参数
*/
- public static boolean isMultipart(List fields) {
+ public static boolean isMultipartParameter(List fields) {
for (Field field : fields) {
// 允许访问私有字段
field.setAccessible(true);
+ if (EasyUtils.isConstantField(field)) {
+ continue;
+ }
+
// 获取对象的类型
Class> clazz = field.getType();
@@ -107,18 +197,21 @@ public static boolean isMultipart(List fields) {
temp = interfaces[i];
}
- // 判断类型是否是 List
if (List.class.equals(temp)) {
- Type[] actualTypeArguments = ((ParameterizedType) field.getGenericType()).getActualTypeArguments();
- if (actualTypeArguments.length == 1 && File.class.equals(actualTypeArguments[0])) {
+ // 如果实现了 List 接口,则取第一个位置的泛型
+ if (isMultipartClass(getFieldGenericType(field, 0))) {
+ return true;
+ }
+ } else if (Map.class.equals(temp)) {
+ // 如果实现了 Map 接口,则取第二个位置的泛型
+ if (isMultipartClass(getFieldGenericType(field, 1))) {
return true;
}
}
}
do {
- if (File.class.equals(clazz) || InputStream.class.equals(clazz)
- || RequestBody.class.equals(clazz) || MultipartBody.Part.class.equals(clazz)) {
+ if (isMultipartClass(clazz)) {
return true;
}
// 获取对象的父类类型
@@ -129,37 +222,53 @@ public static boolean isMultipart(List fields) {
}
/**
- * 判断一下这个集合装载的类型是不是 File
+ * 获取字段中携带的泛型类型
+ *
+ * @param field 字段对象
+ * @param position 泛型的位置
*/
- public static boolean isFileList(List> list) {
- if (list == null || list.isEmpty()) {
- return false;
+ public static Type getFieldGenericType(Field field, int position) {
+ Type type = field.getGenericType();
+ if (!(type instanceof ParameterizedType)) {
+ return null;
+ }
+
+ // 获取泛型数组
+ Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
+ // 如果泛型的位置超过数组的长度,证明这个位置的泛型不存在
+ if (position>= actualTypeArguments.length) {
+ return null;
}
- for (Object object : list) {
- if (!(object instanceof File)) {
- return false;
+ // 获取指定位置上的泛型
+ Type actualType = actualTypeArguments[position];
+ // 如果这是一个通配符类型
+ if (actualType instanceof WildcardType) {
+ // 获取上界通配符
+ Type[] upperBounds = ((WildcardType) actualType).getUpperBounds();
+ if (upperBounds.length == 0) {
+ return null;
}
+ return upperBounds[0];
}
- return true;
+ return actualType;
}
/**
- * 判断对象或者集合是否为空
+ * 判断 Type 是否为流类型
*/
- public static boolean isEmpty(Object object) {
- if (object == null) {
- return true;
- }
-
- if (object instanceof List && ((List>) object).isEmpty()) {
- return true;
+ public static boolean isMultipartClass(Type type) {
+ if (type == null) {
+ return false;
}
- return object instanceof Map && ((Map, ?>) object).isEmpty();
+ return File.class.equals(type) || FileContentResolver.class.equals(type) || InputStream.class.equals(type)
+ || RequestBody.class.equals(type) || MultipartBody.Part.class.equals(type);
}
/**
* 将 List 集合转 JsonArray 对象
+ *
+ * Github issue 地址:https://github.com/getActivity/EasyHttp/issues/164
*/
public static JSONArray listToJsonArray(List> list) {
JSONArray jsonArray = new JSONArray();
@@ -168,18 +277,10 @@ public static JSONArray listToJsonArray(List> list) {
}
for (Object value : list) {
- if (isEmpty(value)) {
+ if (value == null) {
continue;
}
- if (value instanceof List) {
- jsonArray.put(listToJsonArray(((List>) value)));
- } else if (value instanceof Map) {
- jsonArray.put(mapToJsonObject(((Map, ?>) value)));
- } else if (isBeanType(value)) {
- jsonArray.put(mapToJsonObject(beanToHashMap(value)));
- } else {
- jsonArray.put(value);
- }
+ jsonArray.put(convertObject(value));
}
return jsonArray;
}
@@ -196,26 +297,31 @@ public static JSONObject mapToJsonObject(Map, ?> map) {
Set> keySet = map.keySet();
for (Object key : keySet) {
Object value = map.get(key);
- if (isEmpty(value)) {
+ if (value == null) {
continue;
}
try {
- if (value instanceof List) {
- jsonObject.put(String.valueOf(key), listToJsonArray(((List>) value)));
- } else if (value instanceof Map) {
- jsonObject.put(String.valueOf(key), mapToJsonObject(((Map, ?>) value)));
- } else if (isBeanType(value)) {
- jsonObject.put(String.valueOf(key), mapToJsonObject(beanToHashMap(value)));
- } else {
- jsonObject.put(String.valueOf(key), value);
- }
+ jsonObject.put(String.valueOf(key), convertObject(value));
} catch (JSONException e) {
- EasyLog.print(e);
+ e.printStackTrace();
}
}
return jsonObject;
}
+ /**
+ * 将数组转换成 List 集合
+ */
+ public static List arrayToList(Object array) {
+ List list = new ArrayList();
+ int length = Array.getLength(array);
+ for (int i = 0; i < length; i++) { + Object element = Array.get(array, i); + list.add(element); + } + return list; + } + /** * 将 Bean 类转成 HashMap 对象 */ @@ -223,6 +329,9 @@ public static HashMap beanToHashMap(Object object) {
if (object == null) {
return null;
}
+ if (object instanceof Enum) {
+ return null;
+ }
Field[] fields = object.getClass().getDeclaredFields();
HashMap data = new HashMap(fields.length);
@@ -230,21 +339,25 @@ public static HashMap beanToHashMap(Object object) {
// 允许访问私有字段
field.setAccessible(true);
- try {
+ if (EasyUtils.isConstantField(field)) {
+ continue;
+ }
+ try {
// 获取字段的对象
Object value = field.get(object);
// 前提是这个字段值不能为空(基本数据类型有默认的值,而对象默认的值为 null)
// 又或者这个字段需要忽略,则进行忽略
- if (isEmpty(value) || field.isAnnotationPresent(HttpIgnore.class)) {
+ if (value == null || field.isAnnotationPresent(HttpIgnore.class)) {
continue;
}
// 获取字段的名称
String key;
- if (field.isAnnotationPresent(HttpRename.class)) {
- key = field.getAnnotation(HttpRename.class).value();
+ HttpRename annotation = field.getAnnotation(HttpRename.class);
+ if (annotation != null) {
+ key = annotation.value();
} else {
key = field.getName();
// 如果是内部类则会出现一个字段名为 this0ドル 的外部类对象,会导致无限递归,这里要忽略掉,如果使用静态内部类则不会出现这个问题
@@ -254,18 +367,10 @@ public static HashMap beanToHashMap(Object object) {
}
}
- if (value instanceof List) {
- data.put(key, listToJsonArray(((List>) value)));
- } else if (value instanceof Map) {
- data.put(key, mapToJsonObject(((Map, ?>) value)));
- } else if (isBeanType(value)) {
- data.put(key, beanToHashMap(value));
- } else {
- data.put(key, value);
- }
+ data.put(key, convertObject(value));
} catch (IllegalAccessException e) {
- EasyLog.print(e);
+ e.printStackTrace();
}
}
@@ -273,26 +378,74 @@ public static HashMap beanToHashMap(Object object) {
}
/**
- * 获取对象反射类型
+ * 对象转换
+ */
+ public static Object convertObject(Object object) {
+ if (object instanceof List) {
+ // 如果这是一个 List 参数
+ return listToJsonArray(((List>) object));
+ } else if (object instanceof Map) {
+ // 如果这是一个 Map 参数
+ return mapToJsonObject(((Map, ?>) object));
+ } else if (object instanceof Enum) {
+ // 如果这是一个枚举的参数
+ return String.valueOf(object);
+ } else if (isArrayType(object)) {
+ // 如果这是一个数组参数,需要放在判断 Bean 类前面
+ return listToJsonArray(arrayToList(object));
+ } else if (isBeanType(object)) {
+ // 如果这是一个 Bean 参数
+ return mapToJsonObject(beanToHashMap(object));
+ } else {
+ // 如果这是一个普通的参数
+ return object;
+ }
+ }
+
+ /**
+ * 获取对象上面的泛型
*/
- public static Type getReflectType(Object object) {
+ public static Type getGenericType(Object object) {
if (object == null) {
return Void.class;
}
- Type[] types = object.getClass().getGenericInterfaces();
- if (types.length> 0) {
- // 如果这个监听对象是直接实现了接口
- return ((ParameterizedType) types[0]).getActualTypeArguments()[0];
+ // 获取接口上面的泛型
+ Type[] genericInterfaces = object.getClass().getGenericInterfaces();
+ if (genericInterfaces.length> 0) {
+ for (Type genericInterface : genericInterfaces) {
+ // java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
+ if (!(genericInterface instanceof ParameterizedType)) {
+ continue;
+ }
+ // 如果这个对象是直接实现了接口,并且携带了泛型
+ ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
+ return parameterizedType.getActualTypeArguments()[0];
+ }
+ }
+
+ // 获取父类上面的泛型
+ Type genericSuperclass = object.getClass().getGenericSuperclass();
+ if (!(genericSuperclass instanceof ParameterizedType)) {
+ return Void.class;
+ }
+
+ Type[] actualTypeArguments = ((ParameterizedType) genericSuperclass).getActualTypeArguments();
+ if (actualTypeArguments.length == 0) {
+ return Void.class;
}
- // 如果这个监听对象是通过类继承
- return ((ParameterizedType) object.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
+ // 如果这个对象是通过类继承,并且携带了泛型
+ return actualTypeArguments[0];
}
/**
* 获取进度百分比
*/
public static int getProgressProgress(long totalByte, long currentByte) {
+ if (totalByte <= 0) { + // 返回 -1 表示无法获取进度 + return -1; + } // 计算百分比,这里踩了两个坑 // 当文件很大的时候:字节数 * 100 会超过 int 最大值,计算结果会变成负数 // 还有需要注意的是,long 除以 long 等于 long,这里的字节数除以总字节数应该要 double 类型的 @@ -315,45 +468,170 @@ public static String encodeString(String text) { } /** - * 根据文件名获取 MIME 类型 + * 格式化 Json 字符串 */ - public static MediaType guessMimeType(String fileName) { - FileNameMap fileNameMap = URLConnection.getFileNameMap(); - // 解决文件名中含有#号异常的问题 - fileName = fileName.replace("#", ""); - String contentType = fileNameMap.getContentTypeFor(fileName); - if (contentType == null) { - return ContentType.STREAM; + @SuppressWarnings("AlibabaUndefineMagicConstant") + @Nullable + public static String formatJson(@Nullable String json) { + if (json == null) { + return null; } - MediaType type = MediaType.parse(contentType); - if (type == null) { - type = ContentType.STREAM; + + try { + if (json.startsWith("{")) { + return unescapeJson(new JSONObject(json).toString(4)); + } else if (json.startsWith("[")) { + return unescapeJson(new JSONArray(json).toString(4)); + } + } catch (JSONException e) { + e.printStackTrace(); } - return type; + return json; } /** - * 根据 File 对象创建一个流媒体 + * 去除 Json 中非必要的字符转义 */ - public static MultipartBody.Part createPart(String key, File file) { - try { - // 文件名必须不能带中文,所以这里要编码 - return MultipartBody.Part.createFormData(key, encodeString(file.getName()), new UpdateBody(file)); - } catch (FileNotFoundException e) { - EasyLog.print("文件不存在,将被忽略上传:" + key + " = " + file.getPath()); + @NonNull + public static String unescapeJson(String json) { + if (TextUtils.isEmpty(json)) { + return ""; + } + // https://github.com/getActivity/EasyHttp/issues/67 + return json.replace("\\/", "/"); + } + + /** + * 获取对象的唯一标记 + */ + @Nullable + public static String getObjectTag(Object object) { + if (object == null) { return null; } + return String.valueOf(object); } /** - * 根据 InputStream 对象创建一个流媒体 + * 创建文件夹 */ - public static MultipartBody.Part createPart(String key, InputStream inputStream) { + @SuppressWarnings("ResultOfMethodCallIgnored") + public static void createFolder(File targetFolder) { + if (targetFolder.exists()) { + if (targetFolder.isDirectory()) { + return; + } + targetFolder.delete(); + } + targetFolder.mkdirs(); + } + + /** + * 获取文件的 md5 + */ + public static String getFileMd5(InputStream inputStream) { + if (inputStream == null) { + return ""; + } + DigestInputStream digestInputStream = null; try { - return MultipartBody.Part.createFormData(key, null, new UpdateBody(inputStream, key)); - } catch (IOException e) { - EasyLog.print(e); - return null; + MessageDigest messageDigest = MessageDigest.getInstance("MD5"); + digestInputStream = new DigestInputStream(inputStream, messageDigest); + byte[] buffer = new byte[1024 * 256]; + while (true) { + if (digestInputStream.read(buffer) <= 0) { + break; + } + } + messageDigest = digestInputStream.getMessageDigest(); + byte[] md5 = messageDigest.digest(); + StringBuilder sb = new StringBuilder(); + for (byte b : md5) { + sb.append(String.format("%02X", b)); + } + return sb.toString().toLowerCase(); + } catch (NoSuchAlgorithmException | IOException e) { + e.printStackTrace(); + } finally { + EasyUtils.closeStream(inputStream); + EasyUtils.closeStream(digestInputStream); + } + return null; + } + + /** + * 打开文件的输入流 + */ + public static InputStream openFileInputStream(File file) throws FileNotFoundException { + if (file instanceof FileContentResolver) { + return ((FileContentResolver) file).openInputStream(); + } + return new FileInputStream(file); + } + + /** + * 打开文件的输出流 + */ + public static OutputStream openFileOutputStream(File file) throws FileNotFoundException { + return openFileOutputStream(file, false); + } + + /** + * 打开文件的输出流 + * + * @param file 文件对象 + * @param append 是否追加内容 + */ + public static OutputStream openFileOutputStream(File file, boolean append) throws FileNotFoundException { + if (file instanceof FileContentResolver) { + return ((FileContentResolver) file).openOutputStream(append); + } + return new FileOutputStream(file, append); + } + + /** + * 判断一个字段是否是常量字段 + */ + public static boolean isConstantField(Field field) { + int modifiers = field.getModifiers(); + // 如果这是一个常量字段,则直接忽略掉,例如 Parcelable 接口中的 CREATOR 字段 + // https://github.com/getActivity/EasyHttp/issues/112 + return Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers); + } + + /** + * 获取类的所有字段 + */ + public static List getAllFields(Class> originalClass) {
+ List fields = CLASS_LIST_LRU_CACHE.get(originalClass);
+ if (fields != null) {
+ return fields;
+ }
+
+ fields = new ArrayList();
+ Class> clazz = originalClass;
+ do {
+ Field[] declaredFields = clazz.getDeclaredFields();
+ fields.addAll(0, Arrays.asList(declaredFields));
+ // 遍历获取父类的字段
+ clazz = clazz.getSuperclass();
+ } while (clazz != null && !Object.class.equals(clazz));
+
+ CLASS_LIST_LRU_CACHE.put(originalClass, fields);
+
+ return fields;
+ }
+
+ /**
+ * 寻找真实的 RequestBody
+ */
+ public static RequestBody findRealRequestBody(RequestBody body) {
+ while (true) {
+ if (body instanceof WrapperRequestBody) {
+ body = ((WrapperRequestBody) body).getRequestBody();
+ } else {
+ return body;
+ }
}
}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/body/CustomTypeRequestBody.java b/library/src/main/java/com/hjq/http/body/CustomTypeRequestBody.java
new file mode 100644
index 0000000..fe3406d
--- /dev/null
+++ b/library/src/main/java/com/hjq/http/body/CustomTypeRequestBody.java
@@ -0,0 +1,35 @@
+package com.hjq.http.body;
+
+import okhttp3.MediaType;
+import okhttp3.RequestBody;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2022年09月17日
+ * desc : 支持自定义 Content-Type 的 RequestBody
+ */
+public class CustomTypeRequestBody extends WrapperRequestBody {
+
+ /** 内容类型 */
+ private MediaType mContentType;
+
+ public CustomTypeRequestBody(RequestBody body) {
+ super(body);
+ }
+
+ @Override
+ public MediaType contentType() {
+ if (mContentType != null) {
+ return mContentType;
+ }
+ return super.contentType();
+ }
+
+ /**
+ * 设置内容的类型
+ */
+ public void setContentType(MediaType type) {
+ mContentType = type;
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/body/JsonBody.java b/library/src/main/java/com/hjq/http/body/JsonRequestBody.java
similarity index 75%
rename from library/src/main/java/com/hjq/http/body/JsonBody.java
rename to library/src/main/java/com/hjq/http/body/JsonRequestBody.java
index 5ba1497..76ff442 100644
--- a/library/src/main/java/com/hjq/http/body/JsonBody.java
+++ b/library/src/main/java/com/hjq/http/body/JsonRequestBody.java
@@ -1,19 +1,16 @@
package com.hjq.http.body;
import androidx.annotation.NonNull;
-
+import com.hjq.http.EasyUtils;
import com.hjq.http.model.ContentType;
-
-import org.json.JSONArray;
-import org.json.JSONObject;
-
import java.io.IOException;
import java.util.List;
import java.util.Map;
-
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okio.BufferedSink;
+import org.json.JSONArray;
+import org.json.JSONObject;
/**
* author : Android 轮子哥
@@ -21,32 +18,32 @@
* time : 2019年12月28日
* desc : Json 参数提交
*/
-public class JsonBody extends RequestBody {
+public class JsonRequestBody extends RequestBody {
/** Json 数据 */
private final String mJson;
/** 字节数组 */
private final byte[] mBytes;
- public JsonBody(Map, ?> map) {
+ public JsonRequestBody(Map, ?> map) {
this(new JSONObject(map));
}
- public JsonBody(List> list) {
+ public JsonRequestBody(List> list) {
this(new JSONArray(list));
}
- public JsonBody(JSONObject jsonObject) {
- mJson = jsonObject.toString();
+ public JsonRequestBody(JSONObject jsonObject) {
+ mJson = EasyUtils.unescapeJson(jsonObject.toString());
mBytes = mJson.getBytes();
}
- public JsonBody(JSONArray jsonArray) {
- mJson = jsonArray.toString();
+ public JsonRequestBody(JSONArray jsonArray) {
+ mJson = EasyUtils.unescapeJson(jsonArray.toString());
mBytes = mJson.getBytes();
}
- public JsonBody(String json) {
+ public JsonRequestBody(String json) {
mJson = json;
mBytes = mJson.getBytes();
}
@@ -67,16 +64,17 @@ public void writeTo(BufferedSink sink) throws IOException {
sink.write(mBytes, 0, mBytes.length);
}
- /**
- * 获取 Json 字符串
- */
- public String getJson() {
+ @NonNull
+ @Override
+ public String toString() {
return mJson;
}
+ /**
+ * 获取 Json 字符串
+ */
@NonNull
- @Override
- public String toString() {
+ public String getJson() {
return mJson;
}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/body/ProgressBody.java b/library/src/main/java/com/hjq/http/body/ProgressBody.java
deleted file mode 100644
index 040e770..0000000
--- a/library/src/main/java/com/hjq/http/body/ProgressBody.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package com.hjq.http.body;
-
-import androidx.lifecycle.LifecycleOwner;
-
-import com.hjq.http.EasyLog;
-import com.hjq.http.EasyUtils;
-import com.hjq.http.lifecycle.HttpLifecycleManager;
-import com.hjq.http.listener.OnUpdateListener;
-
-import java.io.IOException;
-
-import okhttp3.MediaType;
-import okhttp3.RequestBody;
-import okio.Buffer;
-import okio.BufferedSink;
-import okio.ForwardingSink;
-import okio.Okio;
-import okio.Sink;
-
-/**
- * author : Android 轮子哥
- * github : https://github.com/getActivity/EasyHttp
- * time : 2020年08月15日
- * desc : RequestBody 包装类(用于获取上传进度)
- */
-public class ProgressBody extends RequestBody {
-
- private final RequestBody mRequestBody;
- private final OnUpdateListener> mListener;
- private final LifecycleOwner mLifecycleOwner;
-
- /** 总字节数 */
- private long mTotalByte;
- /** 已上传字节数 */
- private long mUpdateByte;
- /** 上传进度值 */
- private int mUpdateProgress;
-
- public ProgressBody(RequestBody body, LifecycleOwner lifecycleOwner, OnUpdateListener> listener) {
- mRequestBody = body;
- mLifecycleOwner = lifecycleOwner;
- mListener = listener;
- }
-
- @Override
- public MediaType contentType() {
- return mRequestBody.contentType();
- }
-
- @Override
- public long contentLength() throws IOException {
- return mRequestBody.contentLength();
- }
-
- @Override
- public void writeTo(BufferedSink sink) throws IOException {
- mTotalByte = contentLength();
- sink = Okio.buffer(new WrapperSink(sink));
- mRequestBody.writeTo(sink);
- sink.flush();
- }
-
- private class WrapperSink extends ForwardingSink {
-
- public WrapperSink(Sink delegate) {
- super(delegate);
- }
-
- @Override
- public void write(Buffer source, long byteCount) throws IOException {
- super.write(source, byteCount);
- mUpdateByte += byteCount;
- EasyUtils.post(() -> {
- if (mListener != null && HttpLifecycleManager.isLifecycleActive(mLifecycleOwner)) {
- mListener.onByte(mTotalByte, mUpdateByte);
- }
- int progress = EasyUtils.getProgressProgress(mTotalByte, mUpdateByte);
- // 只有上传进度发生改变的时候才回调此方法,避免引起不必要的 View 重绘
- if (progress != mUpdateProgress) {
- mUpdateProgress = progress;
- if (mListener != null && HttpLifecycleManager.isLifecycleActive(mLifecycleOwner)) {
- mListener.onProgress(progress);
- }
- EasyLog.print("正在进行上传" + ",总字节:" + mTotalByte +
- ",已上传:" + mUpdateByte + ",进度:" + progress + "%");
- }
- });
- }
- }
-}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/body/ProgressMonitorRequestBody.java b/library/src/main/java/com/hjq/http/body/ProgressMonitorRequestBody.java
new file mode 100644
index 0000000..85a63c1
--- /dev/null
+++ b/library/src/main/java/com/hjq/http/body/ProgressMonitorRequestBody.java
@@ -0,0 +1,87 @@
+package com.hjq.http.body;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.LifecycleOwner;
+import com.hjq.http.EasyLog;
+import com.hjq.http.EasyUtils;
+import com.hjq.http.lifecycle.HttpLifecycleManager;
+import com.hjq.http.listener.OnUpdateListener;
+import com.hjq.http.request.HttpRequest;
+import java.io.IOException;
+import okhttp3.RequestBody;
+import okio.Buffer;
+import okio.BufferedSink;
+import okio.ForwardingSink;
+import okio.Okio;
+import okio.Sink;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2020年08月15日
+ * desc : RequestBody 包装类(用于获取上传进度)
+ */
+public class ProgressMonitorRequestBody extends WrapperRequestBody {
+
+ private final HttpRequest> mHttpRequest;
+ private final OnUpdateListener> mListener;
+ private final LifecycleOwner mLifecycleOwner;
+
+ /** 总字节数 */
+ private long mTotalByte;
+ /** 已上传字节数 */
+ private long mUpdateByte;
+ /** 上传进度值 */
+ private int mUpdateProgress;
+
+ public ProgressMonitorRequestBody(HttpRequest> httpRequest, RequestBody body, LifecycleOwner lifecycleOwner, OnUpdateListener> listener) {
+ super(body);
+ mHttpRequest = httpRequest;
+ mLifecycleOwner = lifecycleOwner;
+ mListener = listener;
+ }
+
+ @Override
+ public void writeTo(@NonNull BufferedSink sink) throws IOException {
+ mTotalByte = contentLength();
+ sink = Okio.buffer(new WrapperSink(sink));
+ getRequestBody().writeTo(sink);
+ sink.flush();
+ }
+
+ private class WrapperSink extends ForwardingSink {
+
+ public WrapperSink(Sink delegate) {
+ super(delegate);
+ }
+
+ @Override
+ public void write(Buffer source, long byteCount) throws IOException {
+ super.write(source, byteCount);
+ mUpdateByte += byteCount;
+
+ EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), ProgressMonitorRequestBody.this::dispatchUpdateByteChangeCallback);
+ }
+ }
+
+ private void dispatchUpdateByteChangeCallback() {
+ if (mListener != null && HttpLifecycleManager.isLifecycleActive(mLifecycleOwner)) {
+ mListener.onUpdateByteChange(mTotalByte, mUpdateByte);
+ }
+
+ int currentProgress = EasyUtils.getProgressProgress(mTotalByte, mUpdateByte);
+ // 只有上传进度发生改变的时候才回调此方法,避免引起不必要的 View 重绘
+ if (currentProgress == mUpdateProgress) {
+ return;
+ }
+
+ mUpdateProgress = currentProgress;
+ if (mListener != null && HttpLifecycleManager.isLifecycleActive(mLifecycleOwner)) {
+ mListener.onUpdateProgressChange(currentProgress);
+ }
+
+ EasyLog.printLog(mHttpRequest, "Update progress change, uploaded: " +
+ mUpdateByte + " / " + mTotalByte +
+ ", progress: " + currentProgress + "%");
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/body/StringBody.java b/library/src/main/java/com/hjq/http/body/TextRequestBody.java
similarity index 89%
rename from library/src/main/java/com/hjq/http/body/StringBody.java
rename to library/src/main/java/com/hjq/http/body/TextRequestBody.java
index 7aec7e6..ad1f48d 100644
--- a/library/src/main/java/com/hjq/http/body/StringBody.java
+++ b/library/src/main/java/com/hjq/http/body/TextRequestBody.java
@@ -1,11 +1,8 @@
package com.hjq.http.body;
import androidx.annotation.NonNull;
-
import com.hjq.http.model.ContentType;
-
import java.io.IOException;
-
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okio.BufferedSink;
@@ -16,7 +13,7 @@
* time : 2020年10月26日
* desc : 文本参数提交
*/
-public class StringBody extends RequestBody {
+public class TextRequestBody extends RequestBody {
/** 字符串数据 */
private final String mText;
@@ -24,11 +21,11 @@ public class StringBody extends RequestBody {
/** 字节数组 */
private final byte[] mBytes;
- public StringBody() {
+ public TextRequestBody() {
this("");
}
- public StringBody(String text) {
+ public TextRequestBody(String text) {
mText = text;
mBytes = mText.getBytes();
}
diff --git a/library/src/main/java/com/hjq/http/body/UpdateBody.java b/library/src/main/java/com/hjq/http/body/UpdateStreamRequestBody.java
similarity index 54%
rename from library/src/main/java/com/hjq/http/body/UpdateBody.java
rename to library/src/main/java/com/hjq/http/body/UpdateStreamRequestBody.java
index 32e6669..d30b1d9 100644
--- a/library/src/main/java/com/hjq/http/body/UpdateBody.java
+++ b/library/src/main/java/com/hjq/http/body/UpdateStreamRequestBody.java
@@ -1,13 +1,11 @@
package com.hjq.http.body;
-import com.hjq.http.EasyUtils;
import com.hjq.http.model.ContentType;
-
+import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okio.BufferedSink;
@@ -20,7 +18,7 @@
* time : 2019年12月14日
* desc : 上传文件流
*/
-public class UpdateBody extends RequestBody {
+public class UpdateStreamRequestBody extends RequestBody implements Closeable {
/** 上传源 */
private final Source mSource;
@@ -29,23 +27,23 @@ public class UpdateBody extends RequestBody {
private final MediaType mMediaType;
/** 内容名称 */
- private final String mName;
+ private final String mKeyName;
/** 内容大小 */
private final long mLength;
- public UpdateBody(File file) throws FileNotFoundException {
- this(Okio.source(file), EasyUtils.guessMimeType(file.getName()), file.getName(), file.length());
+ public UpdateStreamRequestBody(File file) throws FileNotFoundException {
+ this(Okio.source(file), ContentType.guessMimeType(file.getName()), file.getName(), file.length());
}
- public UpdateBody(InputStream inputStream, String name) throws IOException {
+ public UpdateStreamRequestBody(InputStream inputStream, String name) throws IOException {
this(Okio.source(inputStream), ContentType.STREAM, name, inputStream.available());
}
- public UpdateBody(Source source, MediaType type, String name, long length) {
+ public UpdateStreamRequestBody(Source source, MediaType type, String name, long length) {
mSource = source;
mMediaType = type;
- mName = name;
+ mKeyName = name;
mLength = length;
}
@@ -56,19 +54,28 @@ public MediaType contentType() {
@Override
public long contentLength() {
+ if (mLength == 0) {
+ // 如果不能获取到大小,则返回 -1,参考 RequestBody.contentLength 方法实现
+ return -1;
+ }
return mLength;
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
- try {
- sink.writeAll(mSource);
- } finally {
- EasyUtils.closeStream(mSource);
- }
+ sink.writeAll(mSource);
+ }
+
+ /**
+ * {@link Closeable}
+ */
+ @Override
+ public void close() throws IOException {
+ // 关闭文件流
+ mSource.close();
}
- public String getName() {
- return mName;
+ public String getKeyName() {
+ return mKeyName;
}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/body/WrapperRequestBody.java b/library/src/main/java/com/hjq/http/body/WrapperRequestBody.java
new file mode 100644
index 0000000..fc68f44
--- /dev/null
+++ b/library/src/main/java/com/hjq/http/body/WrapperRequestBody.java
@@ -0,0 +1,44 @@
+package com.hjq.http.body;
+
+import androidx.annotation.NonNull;
+import java.io.IOException;
+import okhttp3.MediaType;
+import okhttp3.RequestBody;
+import okio.BufferedSink;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2022年09月17日
+ * desc : RequestBody 包装类
+ */
+public class WrapperRequestBody extends RequestBody {
+
+ private final RequestBody mRequestBody;
+
+ public WrapperRequestBody(RequestBody body) {
+ mRequestBody = body;
+ }
+
+ @Override
+ public long contentLength() throws IOException {
+ return mRequestBody.contentLength();
+ }
+
+ @Override
+ public MediaType contentType() {
+ return mRequestBody.contentType();
+ }
+
+ @Override
+ public void writeTo(@NonNull BufferedSink sink) throws IOException {
+ mRequestBody.writeTo(sink);
+ }
+
+ /**
+ * 获取当前的 RequestBody
+ */
+ public RequestBody getRequestBody() {
+ return mRequestBody;
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/callback/BaseCallback.java b/library/src/main/java/com/hjq/http/callback/BaseCallback.java
index 983f87d..20b882b 100644
--- a/library/src/main/java/com/hjq/http/callback/BaseCallback.java
+++ b/library/src/main/java/com/hjq/http/callback/BaseCallback.java
@@ -1,17 +1,20 @@
package com.hjq.http.callback;
+import androidx.annotation.NonNull;
import com.hjq.http.EasyConfig;
import com.hjq.http.EasyLog;
import com.hjq.http.EasyUtils;
import com.hjq.http.lifecycle.HttpLifecycleManager;
import com.hjq.http.model.CallProxy;
-import com.hjq.http.request.BaseRequest;
-
+import com.hjq.http.model.ThreadSchedulers;
+import com.hjq.http.request.HttpRequest;
+import java.io.Closeable;
import java.io.IOException;
import java.net.SocketTimeoutException;
-
import okhttp3.Call;
import okhttp3.Callback;
+import okhttp3.Request;
+import okhttp3.RequestBody;
import okhttp3.Response;
/**
@@ -23,85 +26,122 @@
public abstract class BaseCallback implements Callback {
/** 请求配置 */
- private final BaseRequest> mBaseRequest;
+ private final HttpRequest> mHttpRequest;
+
+ /** 请求任务对象创建工厂 */
+ private CallProxy.Factory mCallProxyFactory;
/** 请求任务对象 */
- private CallProxy mCall;
+ private CallProxy mCallProxy;
/** 当前重试次数 */
private int mRetryCount;
- public BaseCallback(BaseRequest> request) {
- mBaseRequest = request;
- HttpLifecycleManager.bind(mBaseRequest.getLifecycleOwner());
+ public BaseCallback(@NonNull HttpRequest> request) {
+ mHttpRequest = request;
+ // Lifecycle addObserver 需要在主线程中执行,所以这里要做一下线程转换
+ EasyUtils.runOnAssignThread(ThreadSchedulers.MAIN,
+ () -> HttpLifecycleManager.register(mHttpRequest.getLifecycleOwner()));
}
- public BaseCallback setCall(CallProxy call) {
- mCall = call;
+ public BaseCallback setCallProxyFactory(CallProxy.Factory factory) {
+ mCallProxyFactory = factory;
return this;
}
public void start() {
- mCall.enqueue(this);
- onStart(mCall);
+ onStart();
+ mCallProxy = mCallProxyFactory.create();
+ try {
+ mCallProxy.enqueue(this);
+ } catch (Throwable throwable) {
+ onHttpFailure(throwable);
+ }
}
- protected CallProxy getCall() {
- return mCall;
+ protected CallProxy getCallProxy() {
+ return mCallProxy;
}
@Override
- public void onResponse(Call call, Response response) {
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
try {
// 收到响应
- onResponse(response);
- } catch (Exception e) {
+ onHttpResponse(response);
+ } catch (Throwable throwable) {
// 回调失败
- onFailure(e);
+ onHttpFailure(throwable);
} finally {
- // 关闭响应
- response.close();
+ // 关闭请求体
+ closeRequest(response.request());
+ // 关闭响应体
+ closeResponse(response);
}
}
@Override
- public void onFailure(Call call, IOException e) {
+ public void onFailure(@NonNull Call call, @NonNull IOException e) {
// 服务器请求超时重试
if (e instanceof SocketTimeoutException && mRetryCount < EasyConfig.getInstance().getRetryCount()) { // 设置延迟 N 秒后重试该请求 - EasyUtils.postDelayed(() -> {
+ EasyUtils.postDelayedRunnable(() -> {
// 前提是宿主还没有被销毁
- if (!HttpLifecycleManager.isLifecycleActive(mBaseRequest.getLifecycleOwner())) {
- EasyLog.print("宿主已被销毁,无法对请求进行重试");
+ if (!HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) {
+ // 宿主已被销毁,请求无法进行
+ EasyLog.printLog(mHttpRequest, "LifecycleOwner has been destroyed and the request cannot be made");
return;
}
mRetryCount++;
Call newCall = call.clone();
- mCall.setCall(newCall);
+ mCallProxy.setRealCall(newCall);
newCall.enqueue(BaseCallback.this);
- EasyLog.print("请求超时,正在延迟重试,重试次数:" + mRetryCount + "/" + EasyConfig.getInstance().getRetryCount());
+ // 请求超时,正在执行延迟重试
+ EasyLog.printLog(mHttpRequest, "The request timed out, a delayed retry is being performed, the number of retries: " +
+ mRetryCount + " / " + EasyConfig.getInstance().getRetryCount());
}, EasyConfig.getInstance().getRetryTime());
return;
}
- onFailure(e);
+ onHttpFailure(e);
}
/**
* 请求开始
*/
- protected abstract void onStart(Call call);
+ protected abstract void onStart();
/**
* 请求成功
*/
- protected abstract void onResponse(Response response) throws Exception;
+ protected abstract void onHttpResponse(Response response) throws Throwable;
/**
* 请求失败
*/
- protected abstract void onFailure(Exception e);
+ protected abstract void onHttpFailure(Throwable e);
+
+ /**
+ * 关闭请求体
+ */
+ protected void closeRequest(Request request) {
+ RequestBody body = request.body();
+ if (body == null) {
+ return;
+ }
+ if (!(body instanceof Closeable)) {
+ return;
+ }
+ // 手动关闭文件流,避免内存泄漏
+ EasyUtils.closeStream(((Closeable) body));
+ }
+
+ /**
+ * 关闭响应体
+ */
+ protected void closeResponse(Response response) {
+ EasyUtils.closeStream(response);
+ }
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/callback/DownloadCallback.java b/library/src/main/java/com/hjq/http/callback/DownloadCallback.java
index f10db1d..1f6c1a1 100644
--- a/library/src/main/java/com/hjq/http/callback/DownloadCallback.java
+++ b/library/src/main/java/com/hjq/http/callback/DownloadCallback.java
@@ -1,21 +1,24 @@
package com.hjq.http.callback;
import android.text.TextUtils;
-
+import androidx.annotation.NonNull;
import com.hjq.http.EasyLog;
import com.hjq.http.EasyUtils;
-import com.hjq.http.exception.MD5Exception;
+import com.hjq.http.config.IRequestInterceptor;
+import com.hjq.http.exception.FileMd5Exception;
import com.hjq.http.exception.NullBodyException;
+import com.hjq.http.exception.ResponseException;
import com.hjq.http.lifecycle.HttpLifecycleManager;
import com.hjq.http.listener.OnDownloadListener;
-import com.hjq.http.model.FileWrapper;
-import com.hjq.http.request.BaseRequest;
-
+import com.hjq.http.model.CallProxy;
+import com.hjq.http.request.HttpRequest;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;
-
+import java.util.concurrent.atomic.AtomicLong;
import okhttp3.Call;
+import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
@@ -28,13 +31,15 @@
public final class DownloadCallback extends BaseCallback {
/** 请求配置 */
- private final BaseRequest> mBaseRequest;
+ @NonNull
+ private final HttpRequest> mHttpRequest;
/** 文件 MD5 正则表达式 */
private static final String FILE_MD5_REGEX = "^[\\w]{32}$";
/** 保存的文件 */
- private FileWrapper mFile;
+ @NonNull
+ private final File mFile;
/** 校验的 MD5 */
private String mMd5;
@@ -43,22 +48,21 @@ public final class DownloadCallback extends BaseCallback {
private OnDownloadListener mListener;
/** 下载总字节 */
- private long mTotalByte;
+ private final AtomicLong mTotalByte = new AtomicLong();
/** 已下载字节 */
- private long mDownloadByte;
+ private final AtomicLong mDownloadByte = new AtomicLong();
/** 下载进度 */
private int mDownloadProgress;
- public DownloadCallback(BaseRequest> request) {
- super(request);
- mBaseRequest = request;
- }
+ /** 断点续传开关 */
+ private boolean mResumableTransfer;
- public DownloadCallback setFile(FileWrapper file) {
+ public DownloadCallback(@NonNull HttpRequest> request, @NonNull File file) {
+ super(request);
+ mHttpRequest = request;
mFile = file;
- return this;
}
public DownloadCallback setMd5(String md5) {
@@ -71,108 +75,197 @@ public DownloadCallback setListener(OnDownloadListener listener) {
return this;
}
+ public DownloadCallback setResumableTransfer(boolean resumableTransfer) {
+ mResumableTransfer = resumableTransfer;
+ return this;
+ }
+
@Override
- protected void onStart(Call call) {
- EasyUtils.post(() -> {
- if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mBaseRequest.getLifecycleOwner())) {
- return;
- }
- mListener.onStart(mFile);
- });
+ protected void onStart() {
+ EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), this::dispatchDownloadStartCallback);
}
@Override
- protected void onResponse(Response response) throws Exception {
+ protected void onHttpResponse(Response response) throws Throwable {
+ // 打印请求耗时时间
+ EasyLog.printLog(mHttpRequest, "RequestConsuming:" +
+ (response.receivedResponseAtMillis() - response.sentRequestAtMillis()) + " ms");
+
+ // 响应码 416 表示请求的范围不符合要求,这通常发生在使用断点续传(Range请求头)时,服务器无法满足请求的范围条件,造成这个问题的原因可能有以下几种:
+ // 1. 范围请求错误:请求头中的Range字段可能设置了无效的范围。请确保你设置的范围是有效的,且在文件范围内
+ // 2. 服务器不支持范围请求:有些服务器不支持范围请求,尤其是对于静态文件服务。在这种情况下,服务器可能会返回 416 错误
+ // 3. 服务器不允许断点续传:即使服务器支持范围请求,也可能配置为不允许断点续传。这可能是出于性能或其他原因的考虑
+ if (response.code() == 416 && !TextUtils.isEmpty(response.request().header("Range"))) {
+ Request request = response.request().newBuilder().removeHeader("Range").build();
+ CallProxy callProxy = getCallProxy();
+ Call newCall = mHttpRequest.getRequestHttpClient().getOkHttpClient().newCall(request);
+ callProxy.setRealCall(newCall);
+ Response newResponse = callProxy.execute();
+ // 打印请求耗时时间
+ EasyLog.printLog(mHttpRequest, "The response status code is 416" +
+ ", response message: " + response.message() + ", require special treatment" +
+ ", re-initiate a new request,new request consuming:" +
+ (newResponse.receivedResponseAtMillis() - newResponse.sentRequestAtMillis()) + " ms");
+ // 替换之前的 Response 对象
+ response = newResponse;
+ }
+
+ IRequestInterceptor interceptor = mHttpRequest.getRequestInterceptor();
+ if (interceptor != null) {
+ response = interceptor.interceptResponse(mHttpRequest, response);
+ }
+
+ if (!response.isSuccessful()) {
+ throw new ResponseException("The request failed, response code: " +
+ response.code() + ", response message: " + response.message(), response);
+ }
+
// 如果没有指定文件的 md5 值
if (mMd5 == null) {
// 获取响应头中的文件 MD5 值
String md5 = response.header("Content-MD5");
// 这个 md5 值必须是文件的 md5 值
- if (!TextUtils.isEmpty(md5) && md5.matches(FILE_MD5_REGEX)) {
+ if (md5 != null && md5.matches(FILE_MD5_REGEX)) {
mMd5 = md5;
}
}
+ // 如果这个文件已经下载过,并且经过校验 MD5 是同一个文件的话,就直接回调下载成功监听
+ if (verifyFileMd5()) {
+ // 文件已存在,跳过下载
+ EasyLog.printLog(mHttpRequest, mFile.getPath() + " download file already exists, skip request");
+ EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), () -> dispatchDownloadSuccessCallback(true));
+ return;
+ }
+
+ // 当前是否支持断点续传
+ boolean supportResumableTransfer = false;
+ long fileLength = mFile.length();
+ // 当前必须开启了断点续传的开关
+ if (mResumableTransfer && response.code() == 206 && fileLength> 0) {
+ // 获取 Accept-Ranges 字段的值
+ String acceptRanges = response.header("Accept-Ranges");
+ String contentRange = response.header("Content-Range");
+ // 若能够找到 Content-Range,则表明服务器支持断点续传
+ // 有些服务器还会返回 Accept-Ranges,输出结果 Accept-Ranges: bytes,说明服务器支持按字节下载
+ if (acceptRanges != null && !acceptRanges.isEmpty()) {
+ // Accept-Ranges:bytes
+ supportResumableTransfer = "bytes".equalsIgnoreCase(acceptRanges);
+ } else if (contentRange != null && !contentRange.isEmpty()) {
+ // Content-Range: bytes 7897088-221048888/221048889
+ supportResumableTransfer = contentRange.matches("bytes\\s+\\d+-\\d+/\\d+");
+ }
+ }
+
File parentFile = mFile.getParentFile();
if (parentFile != null) {
- FileWrapper.createFolder(parentFile);
+ EasyUtils.createFolder(parentFile);
}
ResponseBody body = response.body();
if (body == null) {
throw new NullBodyException("The response body is empty");
}
- mTotalByte = body.contentLength();
- if (mTotalByte < 0) { - mTotalByte = 0; - } - // 如果这个文件已经下载过,并且经过校验 MD5 是同一个文件的话,就直接回调下载成功监听 - if (!TextUtils.isEmpty(mMd5) && mFile.isFile() && - mMd5.equalsIgnoreCase(FileWrapper.getFileMd5(mFile.getInputStream()))) { - EasyUtils.post(() -> {
- if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mBaseRequest.getLifecycleOwner())) {
- return;
- }
- mListener.onComplete(mFile);
- mListener.onEnd(mFile);
- });
- return;
+ mTotalByte.set(body.contentLength());
+ if (mTotalByte.get() < 0) { + mTotalByte.set(0); } int readLength; - mDownloadByte = 0; + mDownloadByte.set(0); byte[] bytes = new byte[8192]; - InputStream inputStream = body.byteStream(); - OutputStream outputStream = mFile.getOutputStream(); - while ((readLength = inputStream.read(bytes)) != -1) { - mDownloadByte += readLength; - outputStream.write(bytes, 0, readLength); - EasyUtils.post(() -> {
- if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mBaseRequest.getLifecycleOwner())) {
- return;
- }
- mListener.onByte(mFile, mTotalByte, mDownloadByte);
- int progress = EasyUtils.getProgressProgress(mTotalByte, mDownloadByte);
- // 只有下载进度发生改变的时候才回调此方法,避免引起不必要的 View 重绘
- if (progress != mDownloadProgress) {
- mDownloadProgress = progress;
- mListener.onProgress(mFile, mDownloadProgress);
- EasyLog.print(mFile.getPath() + " 正在下载,总字节:" + mTotalByte +
- ",已下载:" + mDownloadByte + ",进度:" + progress + " %");
- }
- });
- }
- EasyUtils.closeStream(inputStream);
- EasyUtils.closeStream(outputStream);
-
- String md5 = FileWrapper.getFileMd5(mFile.getInputStream());
- if (!TextUtils.isEmpty(mMd5) && !mMd5.equalsIgnoreCase(md5)) {
+ InputStream responeInputStream = body.byteStream();
+
+ OutputStream fileOutputStream = EasyUtils.openFileOutputStream(mFile, supportResumableTransfer);
+ if (supportResumableTransfer) {
+ mDownloadByte.addAndGet(fileLength);
+ // 有一些响应头没有返回 Content-Length 请求头,会导致总字节数为 0
+ if (mTotalByte.get()> 0) {
+ mTotalByte.addAndGet(fileLength);
+ }
+ EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), this::dispatchDownloadByteChangeCallback);
+ }
+
+ while ((readLength = responeInputStream.read(bytes)) != -1) {
+ mDownloadByte.addAndGet(readLength);
+ fileOutputStream.write(bytes, 0, readLength);
+ EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), this::dispatchDownloadByteChangeCallback);
+ }
+
+ // 刷新 IO 缓冲区
+ fileOutputStream.flush();
+
+ EasyUtils.closeStream(responeInputStream);
+ EasyUtils.closeStream(fileOutputStream);
+ EasyUtils.closeStream(response);
+
+ String md5 = EasyUtils.getFileMd5(EasyUtils.openFileInputStream(mFile));
+ if (mMd5 != null && !mMd5.isEmpty() && !mMd5.equalsIgnoreCase(md5)) {
// 文件 MD5 值校验失败
- throw new MD5Exception("MD5 verify failure", md5);
+ throw new FileMd5Exception("File md5 hash verify failure", md5);
}
- EasyUtils.post(() -> {
- if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mBaseRequest.getLifecycleOwner())) {
- return;
- }
- mListener.onComplete(mFile);
- mListener.onEnd(mFile);
- });
+ EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), () -> dispatchDownloadSuccessCallback(false));
}
@Override
- protected void onFailure(final Exception e) {
+ protected void onHttpFailure(final Throwable throwable) {
+ EasyLog.printThrowable(mHttpRequest, throwable);
// 打印错误堆栈
- final Exception exception = mBaseRequest.getRequestHandler().requestFail(
- mBaseRequest.getLifecycleOwner(), mBaseRequest.getRequestApi(), e);
- EasyLog.print(exception);
+ final Throwable finalThrowable = mHttpRequest.getRequestHandler().downloadFail(mHttpRequest, throwable);
+ if (finalThrowable != throwable) {
+ EasyLog.printThrowable(mHttpRequest, finalThrowable);
+ }
+ EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), () -> dispatchDownloadFailCallback(finalThrowable));
+ }
- EasyUtils.post(() -> {
- if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mBaseRequest.getLifecycleOwner())) {
- return;
- }
- mListener.onError(mFile, exception);
- mListener.onEnd(mFile);
- });
+ public boolean verifyFileMd5() {
+ try {
+ return mFile.isFile() && mMd5 != null && !mMd5.isEmpty() &&
+ mMd5.equalsIgnoreCase(EasyUtils.getFileMd5(EasyUtils.openFileInputStream(mFile)));
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ public void dispatchDownloadStartCallback() {
+ if (mListener != null && HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) {
+ mListener.onDownloadStart(mFile);
+ }
+ EasyLog.printLog(mHttpRequest, "Download file start, file path = " + mFile.getPath());
+ }
+
+ public void dispatchDownloadByteChangeCallback() {
+ if (mListener != null && HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) {
+ mListener.onDownloadByteChange(mFile, mTotalByte.get(), mDownloadByte.get());
+ }
+ int currentProgress = EasyUtils.getProgressProgress(mTotalByte.get(), mDownloadByte.get());
+ // 只有下载进度发生改变的时候才回调此方法,避免引起不必要的 View 重绘
+ if (currentProgress == mDownloadProgress) {
+ return;
+ }
+ mDownloadProgress = currentProgress;
+ if (mListener != null && HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) {
+ mListener.onDownloadProgressChange(mFile, mDownloadProgress);
+ }
+ EasyLog.printLog(mHttpRequest, "Download file progress change, downloaded: " + mDownloadByte + " / " + mTotalByte +
+ ", progress: " + currentProgress + " %" + ", file path = " + mFile.getPath());
+ }
+
+ public void dispatchDownloadSuccessCallback(boolean cache) {
+ if (mListener != null && HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) {
+ mListener.onDownloadSuccess(mFile, cache);
+ mListener.onDownloadEnd(mFile);
+ }
+ EasyLog.printLog(mHttpRequest, "Download file success, file path = " + mFile.getPath());
+ }
+
+ public void dispatchDownloadFailCallback(Throwable throwable) {
+ if (mListener != null && HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) {
+ mListener.onDownloadFail(mFile, throwable);
+ mListener.onDownloadEnd(mFile);
+ }
+ EasyLog.printLog(mHttpRequest, "Download file fail, file path = " + mFile.getPath());
}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/callback/NormalCallback.java b/library/src/main/java/com/hjq/http/callback/NormalCallback.java
index 0adf3de..c83befb 100644
--- a/library/src/main/java/com/hjq/http/callback/NormalCallback.java
+++ b/library/src/main/java/com/hjq/http/callback/NormalCallback.java
@@ -1,16 +1,18 @@
package com.hjq.http.callback;
+import androidx.annotation.NonNull;
import com.hjq.http.EasyLog;
import com.hjq.http.EasyUtils;
+import com.hjq.http.config.IRequestInterceptor;
import com.hjq.http.lifecycle.HttpLifecycleManager;
import com.hjq.http.listener.OnHttpListener;
import com.hjq.http.model.CacheMode;
-import com.hjq.http.request.BaseRequest;
-
+import com.hjq.http.request.HttpRequest;
import java.io.IOException;
-
-import okhttp3.Call;
+import java.io.InputStream;
+import java.lang.reflect.Type;
import okhttp3.Response;
+import okhttp3.ResponseBody;
/**
* author : Android 轮子哥
@@ -22,33 +24,41 @@
public final class NormalCallback extends BaseCallback {
/** 请求配置 */
- private final BaseRequest mBaseRequest;
-
+ private final HttpRequest mHttpRequest;
/** 接口回调 */
private OnHttpListener mListener;
+ /** 解析类型 */
+ private Type mReflectType;
- public NormalCallback(BaseRequest request) {
+ public NormalCallback(@NonNull HttpRequest request) {
super(request);
- mBaseRequest = request;
+ mHttpRequest = request;
}
public NormalCallback setListener(OnHttpListener listener) {
mListener = listener;
+ mReflectType = mHttpRequest.getRequestHandler().getGenericType(mListener);
+ return this;
+ }
+
+ public NormalCallback setReflectType(Type reflectType) {
+ mReflectType = reflectType;
return this;
}
@Override
public void start() {
- CacheMode cacheMode = mBaseRequest.getRequestCache().getMode();
- if (cacheMode != CacheMode.USE_CACHE_ONLY && cacheMode != CacheMode.USE_CACHE_FIRST) {
+ CacheMode cacheMode = mHttpRequest.getRequestCacheConfig().getCacheMode();
+ if (cacheMode != CacheMode.USE_CACHE_ONLY &&
+ cacheMode != CacheMode.USE_CACHE_FIRST) {
super.start();
return;
}
try {
- Object result = mBaseRequest.getRequestHandler().readCache(mBaseRequest.getLifecycleOwner(),
- mBaseRequest.getRequestApi(), EasyUtils.getReflectType(mListener));
- EasyLog.print("ReadCache result:" + result);
+ Object result = mHttpRequest.getHttpCacheStrategy().readCache(mHttpRequest,
+ mReflectType, mHttpRequest.getRequestCacheConfig().getCacheTime());
+ EasyLog.printLog(mHttpRequest, "ReadCache result:" + result);
// 如果没有缓存,就请求网络
if (result == null) {
@@ -57,19 +67,15 @@ public void start() {
}
// 读取缓存成功
- EasyUtils.post(() -> {
- if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mBaseRequest.getLifecycleOwner())) {
- return;
- }
- mListener.onStart(getCall());
- mListener.onSucceed(result, true);
- mListener.onEnd(getCall());
+ EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), () -> {
+ dispatchHttpStartCallback();
+ dispatchHttpSuccessCallback(result, true);
});
// 如果当前模式是先读缓存再写请求
if (cacheMode == CacheMode.USE_CACHE_FIRST) {
- EasyUtils.postDelayed(() -> {
- if (!HttpLifecycleManager.isLifecycleActive(mBaseRequest.getLifecycleOwner())) {
+ EasyUtils.postDelayedRunnable(() -> {
+ if (!HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) {
return;
}
// 将回调置为空,避免出现两次回调
@@ -78,91 +84,108 @@ public void start() {
}, 1);
}
- } catch (Throwable throwable) {
- EasyLog.print("ReadCache error");
- EasyLog.print(throwable);
+ } catch (Throwable cacheThrowable) {
+ EasyLog.printLog(mHttpRequest, "ReadCache error");
+ EasyLog.printThrowable(mHttpRequest, cacheThrowable);
super.start();
}
}
@Override
- protected void onStart(Call call) {
- EasyUtils.post(() -> {
- if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mBaseRequest.getLifecycleOwner())) {
- return;
- }
- mListener.onStart(call);
- });
+ protected void onStart() {
+ EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), this::dispatchHttpStartCallback);
}
@Override
- protected void onResponse(Response response) throws Exception {
+ protected void onHttpResponse(Response response) throws Throwable {
// 打印请求耗时时间
- EasyLog.print("RequestConsuming:" +
+ EasyLog.printLog(mHttpRequest, "RequestConsuming:" +
(response.receivedResponseAtMillis() - response.sentRequestAtMillis()) + " ms");
+ IRequestInterceptor interceptor = mHttpRequest.getRequestInterceptor();
+ if (interceptor != null) {
+ response = interceptor.interceptResponse(mHttpRequest, response);
+ }
+
// 解析 Bean 类对象
- final Object result = mBaseRequest.getRequestHandler().requestSucceed(
- mBaseRequest.getLifecycleOwner(), mBaseRequest.getRequestApi(),
- response, EasyUtils.getReflectType(mListener));
+ final Object result = mHttpRequest.getRequestHandler().requestSuccess(
+ mHttpRequest, response, mReflectType);
- CacheMode cacheMode = mBaseRequest.getRequestCache().getMode();
- if (cacheMode == CacheMode.USE_CACHE_ONLY || cacheMode == CacheMode.USE_CACHE_FIRST) {
+ CacheMode cacheMode = mHttpRequest.getRequestCacheConfig().getCacheMode();
+ if (cacheMode == CacheMode.USE_CACHE_ONLY ||
+ cacheMode == CacheMode.USE_CACHE_FIRST ||
+ cacheMode == CacheMode.USE_CACHE_AFTER_FAILURE) {
try {
- boolean writeSucceed = mBaseRequest.getRequestHandler().writeCache(mBaseRequest.getLifecycleOwner(),
- mBaseRequest.getRequestApi(), response, result);
- EasyLog.print("WriteCache result:" + writeSucceed);
- } catch (Throwable throwable) {
- EasyLog.print("WriteCache error");
- EasyLog.print(throwable);
+ boolean writeCacheResult = mHttpRequest.getHttpCacheStrategy().writeCache(mHttpRequest, response, result);
+ EasyLog.printLog(mHttpRequest, "write cache result:" + writeCacheResult);
+ } catch (Throwable cacheThrowable) {
+ EasyLog.printLog(mHttpRequest, "write cache error");
+ EasyLog.printThrowable(mHttpRequest, cacheThrowable);
}
}
- EasyUtils.post(() -> {
- if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mBaseRequest.getLifecycleOwner())) {
- return;
- }
- mListener.onSucceed(result, false);
- mListener.onEnd(getCall());
- });
+ EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), () -> dispatchHttpSuccessCallback(result, false));
}
@Override
- protected void onFailure(Exception e) {
+ protected void onHttpFailure(Throwable throwable) {
+ // 打印错误堆栈
+ EasyLog.printThrowable(mHttpRequest, throwable);
// 如果设置了只在网络请求失败才去读缓存
- if (e instanceof IOException && mBaseRequest.getRequestCache().getMode() == CacheMode.USE_CACHE_AFTER_FAILURE) {
+ if (throwable instanceof IOException && mHttpRequest.getRequestCacheConfig().getCacheMode() == CacheMode.USE_CACHE_AFTER_FAILURE) {
try {
- Object result = mBaseRequest.getRequestHandler().readCache(mBaseRequest.getLifecycleOwner(),
- mBaseRequest.getRequestApi(), EasyUtils.getReflectType(mListener));
- EasyLog.print("ReadCache result:" + result);
+ Object result = mHttpRequest.getHttpCacheStrategy().readCache(mHttpRequest,
+ mReflectType, mHttpRequest.getRequestCacheConfig().getCacheTime());
+ EasyLog.printLog(mHttpRequest, "ReadCache result:" + result);
if (result != null) {
- EasyUtils.post(() -> {
- if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mBaseRequest.getLifecycleOwner())) {
- return;
- }
- mListener.onSucceed(result, true);
- mListener.onEnd(getCall());
- });
+ EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), () -> dispatchHttpSuccessCallback(result, true));
return;
}
- } catch (Throwable throwable) {
- EasyLog.print("ReadCache error");
- EasyLog.print(throwable);
+ } catch (Throwable cacheThrowable) {
+ EasyLog.printLog(mHttpRequest, "ReadCache error");
+ EasyLog.printThrowable(mHttpRequest, cacheThrowable);
}
}
- final Exception exception = mBaseRequest.getRequestHandler().requestFail(
- mBaseRequest.getLifecycleOwner(), mBaseRequest.getRequestApi(), e);
+ final Throwable finalThrowable = mHttpRequest.getRequestHandler().requestFail(mHttpRequest, throwable);
+ if (finalThrowable != throwable) {
+ EasyLog.printThrowable(mHttpRequest, finalThrowable);
+ }
+
+ EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), () -> dispatchHttpFailCallback(finalThrowable));
+ }
- // 打印错误堆栈
- EasyLog.print(exception);
+ private void dispatchHttpStartCallback() {
+ if (mListener != null && HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) {
+ mListener.onHttpStart(mHttpRequest.getRequestApi());
+ }
+ EasyLog.printLog(mHttpRequest, "Http request start");
+ }
- EasyUtils.post(() -> {
- if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mBaseRequest.getLifecycleOwner())) {
- return;
- }
- mListener.onFail(exception);
- mListener.onEnd(getCall());
- });
+ private void dispatchHttpSuccessCallback(@NonNull Object result, boolean cache) {
+ if (mListener != null && HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) {
+ mListener.onHttpSuccess(result, cache);
+ mListener.onHttpEnd(mHttpRequest.getRequestApi());
+ }
+ EasyLog.printLog(mHttpRequest, "Http request success");
+ }
+
+ private void dispatchHttpFailCallback(Throwable throwable) {
+ if (mListener != null && HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) {
+ mListener.onHttpFail(throwable);
+ mListener.onHttpEnd(mHttpRequest.getRequestApi());
+ }
+ EasyLog.printLog(mHttpRequest, "Http request fail");
+ }
+
+ @Override
+ protected void closeResponse(Response response) {
+ if (Response.class.equals(mReflectType) ||
+ ResponseBody.class.equals(mReflectType) ||
+ InputStream.class.equals(mReflectType)) {
+ // 如果反射是这几个类型,则不关闭 Response,否则会导致拉取不到里面的流
+ return;
+ }
+ super.closeResponse(response);
}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/config/IHttpCacheStrategy.java b/library/src/main/java/com/hjq/http/config/IHttpCacheStrategy.java
new file mode 100644
index 0000000..1fd0b1c
--- /dev/null
+++ b/library/src/main/java/com/hjq/http/config/IHttpCacheStrategy.java
@@ -0,0 +1,50 @@
+package com.hjq.http.config;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.hjq.http.request.HttpRequest;
+import java.lang.reflect.Type;
+import okhttp3.Response;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2025年03月23日
+ * desc : 请求缓存策略
+ */
+public interface IHttpCacheStrategy {
+
+ /**
+ * 读取缓存
+ *
+ * @param httpRequest 请求接口对象
+ * @param type 解析类型
+ * @param cacheTime 缓存的有效期(以毫秒为单位)
+ * @return 返回新的请求对象
+ */
+ @Nullable
+ Object readCache(@NonNull HttpRequest> httpRequest, @NonNull Type type, long cacheTime) throws Throwable;
+
+ /**
+ * 写入缓存
+ *
+ * @param httpRequest 请求接口对象
+ * @param response 响应对象
+ * @param result 请求结果对象
+ * @return 缓存写入结果
+ */
+ boolean writeCache(@NonNull HttpRequest> httpRequest, @NonNull Response response, @NonNull Object result) throws Throwable;
+
+ /**
+ * 删除缓存
+ *
+ * @param httpRequest 请求接口对象
+ * @return 删除缓存的结果
+ */
+ boolean deleteCache(@NonNull HttpRequest> httpRequest);
+
+ /**
+ * 清空缓存
+ */
+ void clearCache();
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/config/IHttpLogStrategy.java b/library/src/main/java/com/hjq/http/config/IHttpLogStrategy.java
new file mode 100644
index 0000000..e5f3acd
--- /dev/null
+++ b/library/src/main/java/com/hjq/http/config/IHttpLogStrategy.java
@@ -0,0 +1,45 @@
+package com.hjq.http.config;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2020年04月24日
+ * desc : 日志打印策略
+ */
+public interface IHttpLogStrategy {
+
+ /**
+ * 打印分割线
+ */
+ default void printLine(@NonNull String tag) {
+ printLog(tag, "----------------------------------------");
+ }
+
+ /**
+ * 打印日志
+ */
+ void printLog(@NonNull String tag, @Nullable String message);
+
+ /**
+ * 打印 Json
+ */
+ void printJson(@NonNull String tag, @Nullable String json);
+
+ /**
+ * 打印键值对
+ */
+ void printKeyValue(@NonNull String tag, @Nullable String key, @Nullable String value);
+
+ /**
+ * 打印异常
+ */
+ void printThrowable(@NonNull String tag, @Nullable Throwable throwable);
+
+ /**
+ * 打印堆栈
+ */
+ void printStackTrace(@NonNull String tag, @Nullable StackTraceElement[] stackTrace);
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/config/IHttpPostBodyStrategy.java b/library/src/main/java/com/hjq/http/config/IHttpPostBodyStrategy.java
new file mode 100644
index 0000000..f528e29
--- /dev/null
+++ b/library/src/main/java/com/hjq/http/config/IHttpPostBodyStrategy.java
@@ -0,0 +1,24 @@
+package com.hjq.http.config;
+
+import com.hjq.http.model.HttpParams;
+import com.hjq.http.request.HttpRequest;
+import okhttp3.RequestBody;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2023年09月23日
+ * desc : 请求 Body 策略接口
+ */
+public interface IHttpPostBodyStrategy {
+
+ /**
+ * 添加参数
+ */
+ void addParams(HttpParams params, String key, Object value);
+
+ /**
+ * 创建 RequestBody
+ */
+ RequestBody createRequestBody(HttpRequest> httpRequest, HttpParams params);
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/config/ILogStrategy.java b/library/src/main/java/com/hjq/http/config/ILogStrategy.java
deleted file mode 100644
index 4d04004..0000000
--- a/library/src/main/java/com/hjq/http/config/ILogStrategy.java
+++ /dev/null
@@ -1,130 +0,0 @@
-package com.hjq.http.config;
-
-/**
- * author : Android 轮子哥
- * github : https://github.com/getActivity/EasyHttp
- * time : 2020年04月24日
- * desc : 日志打印策略
- */
-public interface ILogStrategy {
-
- /**
- * 打印分割线
- */
- default void print() {
- print("--------------------");
- }
-
- /**
- * 打印日志
- */
- void print(String log);
-
- /**
- * 打印 Json
- */
- void json(String json);
-
- /**
- * 打印键值对
- */
- void print(String key, String value);
-
- /**
- * 打印异常
- */
- void print(Throwable throwable);
-
- /**
- * 打印堆栈
- */
- void print(StackTraceElement[] stackTrace);
-
- /**
- * 将字符串格式化成 JSON 格式
- */
- static String formatJson(String json) {
- if (json == null) {
- return "";
- }
- // 计数tab的个数
- int tabNum = 0;
- StringBuilder builder = new StringBuilder();
- int length = json.length();
-
- char last = 0;
- for (int i = 0; i < length; i++) { - char c = json.charAt(i); - if (c == '{') { - tabNum++; - builder.append(c).append("\n") - .append(getSpaceOrTab(tabNum)); - } else if (c == '}') { - tabNum--; - builder.append("\n") - .append(getSpaceOrTab(tabNum)) - .append(c); - } else if (c == ',') { - // 是否格式化处理 - boolean formatFlag = true; - // 获取冒号最后所在位置 - int colonIndex = json.lastIndexOf(":", i); - // 再获取引号最后所在位置 - int quoteIndex = json.lastIndexOf(":\"", i); - if (colonIndex != -1) { - if (quoteIndex == colonIndex) { - // {"code":"12.0101.0122651.00,300.200000,210428,,,,,,10002,,01"} - if (json.charAt(i - 1) != '"') { - formatFlag = false; - } - } - } - - if (formatFlag) { - builder.append(c).append("\n").append(getSpaceOrTab(tabNum)); - } else { - builder.append(c); - } - - } else if (c == ':') { - if (i> 0 && json.charAt(i - 1) == '"') {
- builder.append(" ").append(c).append(" ");
- } else {
- builder.append(c);
- }
- } else if (c == '[') {
- tabNum++;
- char next = json.charAt(i + 1);
- if (next == ']') {
- builder.append(c);
- } else {
- builder.append(c).append("\n")
- .append(getSpaceOrTab(tabNum));
- }
- } else if (c == ']') {
- tabNum--;
- if (last == '[') {
- builder.append(c);
- } else {
- builder.append("\n").append(getSpaceOrTab(tabNum)).append(c);
- }
- } else {
- builder.append(c);
- }
- last = c;
- }
-
- return builder.toString();
- }
-
- /**
- * 创建对应数量的制表符
- */
- static String getSpaceOrTab(int tabNum) {
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < tabNum; i++) { - builder.append('\t'); - } - return builder.toString(); - } -} \ No newline at end of file diff --git a/library/src/main/java/com/hjq/http/config/IRequestApi.java b/library/src/main/java/com/hjq/http/config/IRequestApi.java index 9d851ba..0e8f479 100644 --- a/library/src/main/java/com/hjq/http/config/IRequestApi.java +++ b/library/src/main/java/com/hjq/http/config/IRequestApi.java @@ -1,15 +1,18 @@ -package com.hjq.http.config; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/EasyHttp - * time : 2019/05/19 - * desc : 请求接口配置 - */ -public interface IRequestApi { - - /** - * 请求接口 - */ - String getApi(); +package com.hjq.http.config; + +import androidx.annotation.NonNull; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/EasyHttp + * time : 2019/05/19 + * desc : 请求接口配置 + */ +public interface IRequestApi { + + /** + * 请求接口 + */ + @NonNull + String getApi(); } \ No newline at end of file diff --git a/library/src/main/java/com/hjq/http/config/IRequestBodyType.java b/library/src/main/java/com/hjq/http/config/IRequestBodyType.java new file mode 100644 index 0000000..b4c2b70 --- /dev/null +++ b/library/src/main/java/com/hjq/http/config/IRequestBodyType.java @@ -0,0 +1,18 @@ +package com.hjq.http.config; + +import androidx.annotation.NonNull; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/EasyHttp + * time : 2020/01/01 + * desc : 请求体类型配置 + */ +public interface IRequestBodyType { + + /** + * 获取参数的提交类型 + */ + @NonNull + IHttpPostBodyStrategy getBodyType(); +} \ No newline at end of file diff --git a/library/src/main/java/com/hjq/http/config/IRequestCache.java b/library/src/main/java/com/hjq/http/config/IRequestCache.java deleted file mode 100644 index 77256e1..0000000 --- a/library/src/main/java/com/hjq/http/config/IRequestCache.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.hjq.http.config; - -import com.hjq.http.model.CacheMode; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/EasyHttp - * time : 2021/05/22 - * desc : 请求缓存配置 - */ -public interface IRequestCache { - - /** - * 接口缓存方式 - */ - CacheMode getMode(); -} \ No newline at end of file diff --git a/library/src/main/java/com/hjq/http/config/IRequestCacheConfig.java b/library/src/main/java/com/hjq/http/config/IRequestCacheConfig.java new file mode 100644 index 0000000..bdd9dc2 --- /dev/null +++ b/library/src/main/java/com/hjq/http/config/IRequestCacheConfig.java @@ -0,0 +1,25 @@ +package com.hjq.http.config; + +import androidx.annotation.NonNull; + +import com.hjq.http.model.CacheMode; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/EasyHttp + * time : 2021/05/22 + * desc : 请求缓存配置 + */ +public interface IRequestCacheConfig { + + /** + * 获取缓存的模式 + */ + @NonNull + CacheMode getCacheMode(); + + /** + * 获取缓存的有效时长(以毫秒为单位) + */ + long getCacheTime(); +} \ No newline at end of file diff --git a/library/src/main/java/com/hjq/http/config/IRequestHandler.java b/library/src/main/java/com/hjq/http/config/IRequestHandler.java index 922cb4d..0191f6f 100644 --- a/library/src/main/java/com/hjq/http/config/IRequestHandler.java +++ b/library/src/main/java/com/hjq/http/config/IRequestHandler.java @@ -1,75 +1,58 @@ package com.hjq.http.config; -import androidx.lifecycle.LifecycleOwner; - +import androidx.annotation.NonNull; +import com.hjq.http.EasyUtils; +import com.hjq.http.request.HttpRequest; import java.lang.reflect.Type; - -import okhttp3.Request; import okhttp3.Response; /** * author : Android 轮子哥 * github : https://github.com/getActivity/EasyHttp * time : 2019/11/25 - * desc : 请求处理器 + * desc : 请求处理策略 */ public interface IRequestHandler { /** - * 请求开始 + * 请求成功 * - * @param lifecycle 有生命周期的对象(例如 Activity、Fragment) - * @param api 请求接口对象 - * @param request 请求对象 - * @return 返回新的请求对象 - */ - default Request requestStart(LifecycleOwner lifecycle, IRequestApi api, Request request) { - return request; - } - - /** - * 请求成功时回调 - * - * @param lifecycle 有生命周期的对象(例如 Activity、Fragment) - * @param api 请求接口对象 + * @param httpRequest 请求接口对象 * @param response 响应对象 * @param type 解析类型 * @return 返回结果 * - * @throws Exception 如果抛出则回调失败 + * @throws Throwable 如果抛出则回调失败 */ - Object requestSucceed(LifecycleOwner lifecycle, IRequestApi api, Response response, Type type) throws Exception; + @NonNull + Object requestSuccess(@NonNull HttpRequest> httpRequest, @NonNull Response response, @NonNull Type type) throws Throwable;
/**
* 请求失败
*
- * @param lifecycle 有生命周期的对象(例如 Activity、Fragment)
- * @param api 请求接口对象
+ * @param httpRequest 请求接口对象
* @param e 错误对象
* @return 错误对象
*/
- Exception requestFail(LifecycleOwner lifecycle, IRequestApi api, Exception e);
+ @NonNull
+ Throwable requestFail(@NonNull HttpRequest> httpRequest, @NonNull Throwable e);
/**
- * 读取缓存
+ * 下载失败
*
- * @param lifecycle 有生命周期的对象(例如 Activity、Fragment)
- * @param api 请求接口对象
- * @return 返回新的请求对象
+ * @param httpRequest 请求接口对象
+ * @param throwable 错误对象
+ * @return 错误对象
*/
- default Object readCache(LifecycleOwner lifecycle, IRequestApi api, Type type) throws Throwable {
- return null;
+ @NonNull
+ default Throwable downloadFail(@NonNull HttpRequest> httpRequest, @NonNull Throwable throwable) {
+ return requestFail(httpRequest, throwable);
}
/**
- * 写入缓存
- *
- * @param lifecycle 有生命周期的对象(例如 Activity、Fragment)
- * @param api 请求接口对象
- * @param result 请求结果对象
- * @return 缓存写入结果
+ * 解析泛型
*/
- default boolean writeCache(LifecycleOwner lifecycle, IRequestApi api, Response response, Object result) throws Throwable {
- return false;
+ default Type getGenericType(Object object) {
+ return EasyUtils.getGenericType(object);
}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/config/IRequestHost.java b/library/src/main/java/com/hjq/http/config/IRequestHost.java
index 531bd4a..aa8e75a 100644
--- a/library/src/main/java/com/hjq/http/config/IRequestHost.java
+++ b/library/src/main/java/com/hjq/http/config/IRequestHost.java
@@ -1,15 +1,18 @@
-package com.hjq.http.config;
-
-/**
- * author : Android 轮子哥
- * github : https://github.com/getActivity/EasyHttp
- * time : 2019年05月19日
- * desc : 请求主机配置
- */
-public interface IRequestHost {
-
- /**
- * 主机地址
- */
- String getHost();
+package com.hjq.http.config;
+
+import androidx.annotation.NonNull;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2019年05月19日
+ * desc : 请求主机配置
+ */
+public interface IRequestHost {
+
+ /**
+ * 主机地址
+ */
+ @NonNull
+ String getHost();
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/config/IRequestClient.java b/library/src/main/java/com/hjq/http/config/IRequestHttpClient.java
similarity index 72%
rename from library/src/main/java/com/hjq/http/config/IRequestClient.java
rename to library/src/main/java/com/hjq/http/config/IRequestHttpClient.java
index 10049d1..a51888f 100644
--- a/library/src/main/java/com/hjq/http/config/IRequestClient.java
+++ b/library/src/main/java/com/hjq/http/config/IRequestHttpClient.java
@@ -1,5 +1,7 @@
package com.hjq.http.config;
+import androidx.annotation.NonNull;
+
import com.hjq.http.EasyConfig;
import okhttp3.OkHttpClient;
@@ -10,12 +12,13 @@
* time : 2021年03月02日
* desc : OkHttpClient 配置
*/
-public interface IRequestClient {
+public interface IRequestHttpClient {
/**
* 获取 OkHttpClient
*/
- default OkHttpClient getClient() {
+ @NonNull
+ default OkHttpClient getOkHttpClient() {
return EasyConfig.getInstance().getClient();
}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/config/IRequestInterceptor.java b/library/src/main/java/com/hjq/http/config/IRequestInterceptor.java
index ddac168..d41fa39 100644
--- a/library/src/main/java/com/hjq/http/config/IRequestInterceptor.java
+++ b/library/src/main/java/com/hjq/http/config/IRequestInterceptor.java
@@ -1,7 +1,13 @@
package com.hjq.http.config;
+import androidx.annotation.NonNull;
+
import com.hjq.http.model.HttpHeaders;
import com.hjq.http.model.HttpParams;
+import com.hjq.http.request.HttpRequest;
+
+import okhttp3.Request;
+import okhttp3.Response;
/**
* author : Android 轮子哥
@@ -14,9 +20,33 @@ public interface IRequestInterceptor {
/**
* 拦截参数
*
- * @param api 接口对象
+ * @param httpRequest 接口对象
* @param params 请求参数
* @param headers 请求头参数
*/
- void interceptArguments(IRequestApi api, HttpParams params, HttpHeaders headers);
+ default void interceptArguments(@NonNull HttpRequest> httpRequest, @NonNull HttpParams params, @NonNull HttpHeaders headers) {}
+
+ /**
+ * 拦截请求头
+ *
+ * @param httpRequest 接口对象
+ * @param request 请求头对象
+ * @return 返回新的请求头
+ */
+ @NonNull
+ default Request interceptRequest(@NonNull HttpRequest> httpRequest, @NonNull Request request) {
+ return request;
+ }
+
+ /**
+ * 拦截器响应头
+ *
+ * @param httpRequest 接口对象
+ * @param response 响应头对象
+ * @return 返回新的响应头
+ */
+ @NonNull
+ default Response interceptResponse(HttpRequest> httpRequest, Response response) {
+ return response;
+ }
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/config/IRequestPath.java b/library/src/main/java/com/hjq/http/config/IRequestPath.java
deleted file mode 100644
index 612ba0b..0000000
--- a/library/src/main/java/com/hjq/http/config/IRequestPath.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.hjq.http.config;
-
-/**
- * author : Android 轮子哥
- * github : https://github.com/getActivity/EasyHttp
- * time : 2019年05月19日
- * desc : 请求路径配置
- */
-public interface IRequestPath {
-
- /**
- * 路径地址
- */
- String getPath();
-}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/config/IRequestServer.java b/library/src/main/java/com/hjq/http/config/IRequestServer.java
index b20fd43..c2f83c4 100644
--- a/library/src/main/java/com/hjq/http/config/IRequestServer.java
+++ b/library/src/main/java/com/hjq/http/config/IRequestServer.java
@@ -1,6 +1,8 @@
package com.hjq.http.config;
-import com.hjq.http.model.BodyType;
+import androidx.annotation.NonNull;
+
+import com.hjq.http.model.RequestBodyType;
import com.hjq.http.model.CacheMode;
/**
@@ -10,24 +12,25 @@
* desc : 请求服务配置
*/
public interface IRequestServer extends
- IRequestHost, IRequestPath, IRequestClient,
- IRequestType, IRequestCache {
+ IRequestHost, IRequestHttpClient,
+ IRequestBodyType, IRequestCacheConfig {
+ @NonNull
@Override
- default BodyType getType() {
+ default IHttpPostBodyStrategy getBodyType() {
// 默认以表单的方式提交
- return BodyType.FORM;
+ return RequestBodyType.FORM;
}
+ @NonNull
@Override
- default CacheMode getMode() {
+ default CacheMode getCacheMode() {
// 默认的缓存方式
return CacheMode.DEFAULT;
}
@Override
- default String getPath() {
- // 服务器路径可填可不填
- return "";
+ default long getCacheTime() {
+ return Long.MAX_VALUE;
}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/config/IRequestType.java b/library/src/main/java/com/hjq/http/config/IRequestType.java
deleted file mode 100644
index 4b5f248..0000000
--- a/library/src/main/java/com/hjq/http/config/IRequestType.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.hjq.http.config;
-
-import com.hjq.http.model.BodyType;
-
-/**
- * author : Android 轮子哥
- * github : https://github.com/getActivity/EasyHttp
- * time : 2020年01月01日
- * desc : 请求接口配置
- */
-public interface IRequestType {
-
- /**
- * 参数提交类型
- */
- BodyType getType();
-}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/config/LogStrategy.java b/library/src/main/java/com/hjq/http/config/LogStrategy.java
deleted file mode 100644
index 035ac04..0000000
--- a/library/src/main/java/com/hjq/http/config/LogStrategy.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package com.hjq.http.config;
-
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.hjq.http.EasyConfig;
-import com.hjq.http.EasyHttp;
-
-/**
- * author : Android 轮子哥
- * github : https://github.com/getActivity/EasyHttp
- * time : 2020年04月24日
- * desc : 网络请求日志打印默认实现
- */
-public final class LogStrategy implements ILogStrategy {
-
- @Override
- public void print(String log) {
- // 这里解释一下,为什么不用 Log.d,而用 Log.i,因为 Log.d 在魅族 16th 手机上面无法输出日志
- Log.i(EasyConfig.getInstance().getLogTag(), log != null ? log : "null");
- }
-
- @Override
- public void json(String json) {
- String text = ILogStrategy.formatJson(json);
- if (TextUtils.isEmpty(text)) {
- return;
- }
-
- // 打印 Json 数据最好换一行再打印会好看一点
- text = " \n" + text;
-
- int segmentSize = 3 * 1024;
- long length = text.length();
- if (length <= segmentSize) { - // 长度小于等于限制直接打印 - print(text); - return; - } - - // 循环分段打印日志 - while (text.length()> segmentSize) {
- String logContent = text.substring(0, segmentSize);
- text = text.replace(logContent, "");
- print(logContent);
- }
-
- // 打印剩余日志
- print(text);
- }
-
- @Override
- public void print(String key, String value) {
- print(key + " = " + value);
- }
-
- @Override
- public void print(Throwable throwable) {
- Log.e(EasyConfig.getInstance().getLogTag(), throwable.getMessage(), throwable);
- }
-
- @Override
- public void print(StackTraceElement[] stackTrace) {
- for (StackTraceElement element : stackTrace) {
- // 获取代码行数
- int lineNumber = element.getLineNumber();
- // 获取类的全路径
- String className = element.getClassName();
- if (lineNumber <= 0 || className.startsWith(EasyHttp.class.getPackage().getName())) { - continue; - } - - print("RequestCode = (" + element.getFileName() + ":" + lineNumber + ") "); - break; - } - } -} \ No newline at end of file diff --git a/library/src/main/java/com/hjq/http/config/impl/DefaultHttpLogStrategy.java b/library/src/main/java/com/hjq/http/config/impl/DefaultHttpLogStrategy.java new file mode 100644 index 0000000..4e5d338 --- /dev/null +++ b/library/src/main/java/com/hjq/http/config/impl/DefaultHttpLogStrategy.java @@ -0,0 +1,87 @@ +package com.hjq.http.config.impl; + +import android.text.TextUtils; +import android.util.Log; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.hjq.http.EasyUtils; +import com.hjq.http.config.IHttpLogStrategy; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/EasyHttp + * time : 2020/04/24 + * desc : 网络请求日志打印默认实现 + */ +public final class DefaultHttpLogStrategy implements IHttpLogStrategy { + + @Override + public void printLog(@NonNull String tag, @Nullable String message) { + // 这里解释一下,为什么不用 Log.d,而用 Log.i,因为 Log.d 在魅族 16th 手机上面无法输出日志 + Log.i(tag, message != null ? message : "null"); + } + + @Override + public void printJson(@NonNull String tag, @Nullable String json) { + String text = EasyUtils.formatJson(json); + if (TextUtils.isEmpty(text)) { + return; + } + + // 打印 Json 数据最好换一行再打印会好看一点 + text = " \n" + text; + + // 测试了一些设备,日志限制的长度大概在 3700 ~ 3800 + // 为了保险起见,这里最大长度设置成 3600 + int segmentSize = 3600; + long length = text.length(); + if (length <= segmentSize) { + // 长度小于等于限制直接打印 + printLog(tag, text); + return; + } + + // 循环分段打印日志 + while (text.length()> segmentSize) {
+ String logContent = text.substring(0, segmentSize);
+ text = text.replace(logContent, "");
+ printLog(tag, logContent);
+ }
+
+ // 打印剩余日志
+ printLog(tag, text);
+ }
+
+ @Override
+ public void printKeyValue(@NonNull String tag, @Nullable String key, @Nullable String value) {
+ printLog(tag, key + " = " + value);
+ }
+
+ @Override
+ public void printThrowable(@NonNull String tag, @Nullable Throwable throwable) {
+ if (throwable == null) {
+ Log.e(tag, "An empty throwable object appears");
+ return;
+ }
+ Log.e(tag, throwable.getMessage(), throwable);
+ }
+
+ @Override
+ public void printStackTrace(@NonNull String tag, @Nullable StackTraceElement[] stackTrace) {
+ if (stackTrace == null) {
+ return;
+ }
+ for (StackTraceElement element : stackTrace) {
+ // 获取代码行数
+ int lineNumber = element.getLineNumber();
+ // 获取类的全路径
+ String className = element.getClassName();
+ if (lineNumber <= 0 || className.startsWith("com.hjq.http")) { + continue; + } + + printLog(tag, "RequestCode = (" + element.getFileName() + ":" + lineNumber + ") "); + break; + } + } +} \ No newline at end of file diff --git a/library/src/main/java/com/hjq/http/config/impl/DefaultNullCacheStrategy.java b/library/src/main/java/com/hjq/http/config/impl/DefaultNullCacheStrategy.java new file mode 100644 index 0000000..c70ff65 --- /dev/null +++ b/library/src/main/java/com/hjq/http/config/impl/DefaultNullCacheStrategy.java @@ -0,0 +1,36 @@ +package com.hjq.http.config.impl; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.hjq.http.config.IHttpCacheStrategy; +import com.hjq.http.request.HttpRequest; +import java.lang.reflect.Type; +import okhttp3.Response; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/EasyHttp + * time : 2025/03/23 + * desc : 请求缓存默认实现的空策略 + */ +public class DefaultNullCacheStrategy implements IHttpCacheStrategy { + + @Nullable + @Override + public Object readCache(@NonNull HttpRequest> httpRequest, @NonNull Type type, long cacheTime) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public boolean writeCache(@NonNull HttpRequest> httpRequest, @NonNull Response response, @NonNull Object result) throws Throwable {
+ return false;
+ }
+
+ @Override
+ public boolean deleteCache(@NonNull HttpRequest> httpRequest) {
+ return false;
+ }
+
+ @Override
+ public void clearCache() {}
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/config/impl/HttpPostFormBodyStrategy.java b/library/src/main/java/com/hjq/http/config/impl/HttpPostFormBodyStrategy.java
new file mode 100644
index 0000000..c80ea3a
--- /dev/null
+++ b/library/src/main/java/com/hjq/http/config/impl/HttpPostFormBodyStrategy.java
@@ -0,0 +1,185 @@
+package com.hjq.http.config.impl;
+
+import android.text.TextUtils;
+import com.hjq.http.EasyLog;
+import com.hjq.http.body.UpdateStreamRequestBody;
+import com.hjq.http.config.IHttpPostBodyStrategy;
+import com.hjq.http.model.FileContentResolver;
+import com.hjq.http.model.HttpParams;
+import com.hjq.http.request.HttpRequest;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+import okhttp3.FormBody;
+import okhttp3.MultipartBody;
+import okhttp3.RequestBody;
+import okio.Okio;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2023年09月23日
+ * desc : RequestBody 表单策略实现接口
+ */
+public class HttpPostFormBodyStrategy implements IHttpPostBodyStrategy {
+
+ @Override
+ public void addParams(HttpParams params, String key, Object value) {
+ // 表单提交
+ params.put(key, value);
+ }
+
+ @Override
+ public RequestBody createRequestBody(HttpRequest> httpRequest, HttpParams params) {
+ if (!params.isEmpty() && params.isMultipart()) {
+ return createMultipartRequestBody(httpRequest, params);
+ }
+ return createFormRequestBody(params);
+ }
+
+ public RequestBody createFormRequestBody(HttpParams params) {
+ FormBody.Builder bodyBuilder = new FormBody.Builder();
+ if (params.isEmpty()) {
+ return bodyBuilder.build();
+ }
+
+ for (String key : params.getKeys()) {
+ Object value = params.get(key);
+
+ if (!(value instanceof List)) {
+ bodyBuilder.add(key, String.valueOf(value));
+ continue;
+ }
+
+ List> list = (List>) value;
+ for (Object itemValue : list) {
+ if (itemValue == null) {
+ continue;
+ }
+ bodyBuilder.add(key, String.valueOf(itemValue));
+ }
+ }
+ return bodyBuilder.build();
+ }
+
+ public RequestBody createMultipartRequestBody(HttpRequest> httpRequest, HttpParams params) {
+ MultipartBody.Builder bodyBuilder = new MultipartBody.Builder();
+ bodyBuilder.setType(MultipartBody.FORM);
+ for (String key : params.getKeys()) {
+ Object value = params.get(key);
+
+ if (value instanceof Map) {
+ // 如果这是一个 Map 集合
+ Map, ?> map = ((Map, ?>) value);
+ for (Object itemKey : map.keySet()) {
+ if (itemKey == null) {
+ continue;
+ }
+ Object itemValue = map.get(itemKey);
+ if (itemValue == null) {
+ continue;
+ }
+ addFormData(httpRequest, bodyBuilder, String.valueOf(itemKey), itemValue);
+ }
+ continue;
+ }
+
+ if (value instanceof List) {
+ // 如果这是一个 List 集合
+ List> list = (List>) value;
+ for (Object itemValue : list) {
+ if (itemValue == null) {
+ continue;
+ }
+ addFormData(httpRequest, bodyBuilder, key, itemValue);
+ }
+ continue;
+ }
+
+ addFormData(httpRequest, bodyBuilder, key, value);
+ }
+
+ try {
+ return bodyBuilder.build();
+ } catch (IllegalStateException ignored) {
+ // 如果参数为空则会抛出异常:Multipart body must have at least one part.
+ return new FormBody.Builder().build();
+ }
+ }
+
+ /**
+ * 添加参数
+ */
+ private void addFormData(HttpRequest> httpRequest, MultipartBody.Builder bodyBuilder, String key, Object object) {
+ if (object instanceof File) {
+ // 如果这是一个 File 对象
+ File file = (File) object;
+ String fileName = null;
+ if (file instanceof FileContentResolver) {
+ fileName = ((FileContentResolver) file).getFileName();
+ }
+ if (TextUtils.isEmpty(fileName)) {
+ fileName = file.getName();
+ }
+
+ try {
+ MultipartBody.Part part;
+ if (file instanceof FileContentResolver) {
+ FileContentResolver fileContentResolver = (FileContentResolver) file;
+ InputStream inputStream = fileContentResolver.openInputStream();
+ part = MultipartBody.Part.createFormData(key, fileName, new UpdateStreamRequestBody(
+ Okio.source(inputStream), fileContentResolver.getContentType(),
+ fileName, inputStream.available()));
+ } else {
+ part = MultipartBody.Part.createFormData(key, fileName, new UpdateStreamRequestBody(file));
+ }
+ bodyBuilder.addPart(part);
+ } catch (FileNotFoundException e) {
+ // 文件不存在,将被忽略上传
+ EasyLog.printLog(httpRequest, "File does not exist, will be ignored upload: " +
+ key + " = " + file.getPath());
+ } catch (IOException e) {
+ EasyLog.printThrowable(httpRequest, e);
+ // 文件流读取失败,将被忽略上传
+ EasyLog.printLog(httpRequest, "File stream reading failed and will be ignored upload: " +
+ key + " = " + file.getPath());
+ }
+ return;
+ }
+
+ if (object instanceof InputStream) {
+ // 如果这是一个 InputStream 对象
+ InputStream inputStream = (InputStream) object;
+ try {
+ bodyBuilder.addPart(MultipartBody.Part.createFormData(key, null, new UpdateStreamRequestBody(inputStream, key)));
+ } catch (IOException e) {
+ EasyLog.printThrowable(httpRequest, e);
+ }
+ return;
+ }
+
+ if (object instanceof RequestBody) {
+ // 如果这是一个自定义的 RequestBody 对象
+ RequestBody requestBody = (RequestBody) object;
+ if (requestBody instanceof UpdateStreamRequestBody) {
+ bodyBuilder.addPart(MultipartBody.Part.createFormData(key,
+ ((UpdateStreamRequestBody) requestBody).getKeyName(), requestBody));
+ } else {
+ bodyBuilder.addPart(MultipartBody.Part.createFormData(key, null, requestBody));
+ }
+ return;
+ }
+
+ if (object instanceof MultipartBody.Part) {
+ // 如果这是一个自定义的 MultipartBody.Part 对象
+ bodyBuilder.addPart((MultipartBody.Part) object);
+ return;
+ }
+
+ // 如果这是一个普通参数
+ bodyBuilder.addFormDataPart(key, String.valueOf(object));
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/config/impl/HttpPostJsonBodyStrategy.java b/library/src/main/java/com/hjq/http/config/impl/HttpPostJsonBodyStrategy.java
new file mode 100644
index 0000000..1b31987
--- /dev/null
+++ b/library/src/main/java/com/hjq/http/config/impl/HttpPostJsonBodyStrategy.java
@@ -0,0 +1,28 @@
+package com.hjq.http.config.impl;
+
+import com.hjq.http.EasyUtils;
+import com.hjq.http.body.JsonRequestBody;
+import com.hjq.http.config.IHttpPostBodyStrategy;
+import com.hjq.http.model.HttpParams;
+import com.hjq.http.request.HttpRequest;
+import okhttp3.RequestBody;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2023年09月23日
+ * desc : RequestBody Json 策略实现接口
+ */
+public class HttpPostJsonBodyStrategy implements IHttpPostBodyStrategy {
+
+ @Override
+ public void addParams(HttpParams params, String key, Object value) {
+ // Json 提交
+ params.put(key, EasyUtils.convertObject(value));
+ }
+
+ @Override
+ public RequestBody createRequestBody(HttpRequest> httpRequest, HttpParams params) {
+ return new JsonRequestBody(params.getMap());
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/config/impl/SimpleDownloadRequestApi.java b/library/src/main/java/com/hjq/http/config/impl/SimpleDownloadRequestApi.java
new file mode 100644
index 0000000..0ff9a98
--- /dev/null
+++ b/library/src/main/java/com/hjq/http/config/impl/SimpleDownloadRequestApi.java
@@ -0,0 +1,14 @@
+package com.hjq.http.config.impl;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2021年12月09日
+ * desc : 下载请求接口简单配置类
+ */
+public final class SimpleDownloadRequestApi extends SimpleRequestApi {
+
+ public SimpleDownloadRequestApi(String api) {
+ super(api);
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/config/RequestApi.java b/library/src/main/java/com/hjq/http/config/impl/SimpleRequestApi.java
similarity index 66%
rename from library/src/main/java/com/hjq/http/config/RequestApi.java
rename to library/src/main/java/com/hjq/http/config/impl/SimpleRequestApi.java
index 2588d94..9411517 100644
--- a/library/src/main/java/com/hjq/http/config/RequestApi.java
+++ b/library/src/main/java/com/hjq/http/config/impl/SimpleRequestApi.java
@@ -1,6 +1,8 @@
-package com.hjq.http.config;
+package com.hjq.http.config.impl;
+import androidx.annotation.NonNull;
import com.hjq.http.annotation.HttpIgnore;
+import com.hjq.http.config.IRequestApi;
/**
* author : Android 轮子哥
@@ -8,21 +10,23 @@
* time : 2019年05月19日
* desc : 请求接口简单配置类
*/
-public final class RequestApi implements IRequestApi {
+public class SimpleRequestApi implements IRequestApi {
/** 接口地址 */
@HttpIgnore
private final String mApi;
- public RequestApi(String api) {
+ public SimpleRequestApi(String api) {
mApi = api;
}
+ @NonNull
@Override
public String getApi() {
return mApi;
}
+ @NonNull
@Override
public String toString() {
return mApi;
diff --git a/library/src/main/java/com/hjq/http/config/RequestServer.java b/library/src/main/java/com/hjq/http/config/impl/SimpleRequestServer.java
similarity index 51%
rename from library/src/main/java/com/hjq/http/config/RequestServer.java
rename to library/src/main/java/com/hjq/http/config/impl/SimpleRequestServer.java
index 3541b55..5d2d2ae 100644
--- a/library/src/main/java/com/hjq/http/config/RequestServer.java
+++ b/library/src/main/java/com/hjq/http/config/impl/SimpleRequestServer.java
@@ -1,6 +1,8 @@
-package com.hjq.http.config;
+package com.hjq.http.config.impl;
+import androidx.annotation.NonNull;
import com.hjq.http.annotation.HttpIgnore;
+import com.hjq.http.config.IRequestServer;
/**
* author : Android 轮子哥
@@ -8,37 +10,25 @@
* time : 2019年05月19日
* desc : 服务器简单配置
*/
-public final class RequestServer implements IRequestServer {
+public final class SimpleRequestServer implements IRequestServer {
/** 主机地址 */
@HttpIgnore
private final String mHost;
- /** 接口路径 */
- @HttpIgnore
- private final String mPath;
-
- public RequestServer(String host) {
- this(host, "");
- }
-
- public RequestServer(String host, String path) {
+ public SimpleRequestServer(String host) {
mHost = host;
- mPath = path;
}
+ @NonNull
@Override
public String getHost() {
return mHost;
}
- @Override
- public String getPath() {
- return mPath;
- }
-
+ @NonNull
@Override
public String toString() {
- return mHost + mPath;
+ return mHost;
}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/config/impl/SimpleRequestUrl.java b/library/src/main/java/com/hjq/http/config/impl/SimpleRequestUrl.java
new file mode 100644
index 0000000..a6a68b0
--- /dev/null
+++ b/library/src/main/java/com/hjq/http/config/impl/SimpleRequestUrl.java
@@ -0,0 +1,77 @@
+package com.hjq.http.config.impl;
+
+import androidx.annotation.NonNull;
+import com.hjq.http.annotation.HttpIgnore;
+import com.hjq.http.config.IRequestApi;
+import com.hjq.http.config.IRequestServer;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2022年03月03日
+ * desc : 请求 url 简单配置类
+ */
+public final class SimpleRequestUrl implements IRequestServer, IRequestApi {
+
+ /** 主机地址 */
+ @HttpIgnore
+ private final String mHost;
+
+ /** 接口地址 */
+ @HttpIgnore
+ private final String mApi;
+
+ public SimpleRequestUrl(String url) {
+ URI uri = null;
+ try {
+ uri = new URI(url);
+ } catch (URISyntaxException e) {
+ e.printStackTrace();
+ }
+
+ if (uri == null) {
+ // 如果解析失败的情况下,就直接把 url 充当 host,api 则什么都不设置
+ mHost = url;
+ mApi = "";
+ return;
+ }
+
+ StringBuilder hostBuilder = new StringBuilder();
+ if (uri.getScheme() != null) {
+ hostBuilder.append(uri.getScheme()).append("://");
+ }
+ if (uri.getPort() != -1) {
+ hostBuilder.append(uri.getHost()).append(":").append(uri.getPort());
+ } else {
+ hostBuilder.append(uri.getHost());
+ }
+
+ mHost = hostBuilder.toString();
+ mApi = url.replace(mHost, "");
+ }
+
+ public SimpleRequestUrl(String host, String api) {
+ mHost = host;
+ mApi = api;
+ }
+
+ @NonNull
+ @Override
+ public String getHost() {
+ return mHost;
+ }
+
+ @NonNull
+ @Override
+ public String getApi() {
+ return mApi;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return mHost + mApi;
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/exception/FileMd5Exception.java b/library/src/main/java/com/hjq/http/exception/FileMd5Exception.java
new file mode 100644
index 0000000..3dc4574
--- /dev/null
+++ b/library/src/main/java/com/hjq/http/exception/FileMd5Exception.java
@@ -0,0 +1,21 @@
+package com.hjq.http.exception;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2019年11月16日
+ * desc : MD5 校验异常
+ */
+public final class FileMd5Exception extends HttpException {
+
+ private final String mMd5;
+
+ public FileMd5Exception(String message, String md5) {
+ super(message);
+ mMd5 = md5;
+ }
+
+ public String getMd5() {
+ return mMd5;
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/exception/HttpException.java b/library/src/main/java/com/hjq/http/exception/HttpException.java
index 2a0d2ae..c3eab48 100644
--- a/library/src/main/java/com/hjq/http/exception/HttpException.java
+++ b/library/src/main/java/com/hjq/http/exception/HttpException.java
@@ -1,5 +1,8 @@
package com.hjq.http.exception;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
/**
* author : Android 轮子哥
* github : https://github.com/getActivity/EasyHttp
@@ -8,7 +11,8 @@
*/
public class HttpException extends Exception {
- private final String mMessage;
+ private String mMessage;
+ private Throwable mThrowable;
public HttpException(String message) {
super(message);
@@ -18,6 +22,11 @@ public HttpException(String message) {
public HttpException(String message, Throwable cause) {
super(message, cause);
mMessage = message;
+ mThrowable = cause;
+ }
+
+ public void setMessage(String message) {
+ mMessage = message;
}
/**
@@ -27,4 +36,22 @@ public HttpException(String message, Throwable cause) {
public String getMessage() {
return mMessage;
}
+
+ @NonNull
+ @Override
+ public StackTraceElement[] getStackTrace() {
+ if (mThrowable != null) {
+ return mThrowable.getStackTrace();
+ }
+ return super.getStackTrace();
+ }
+
+ @Nullable
+ @Override
+ public synchronized Throwable getCause() {
+ if (mThrowable != null) {
+ return mThrowable.getCause();
+ }
+ return super.getCause();
+ }
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/exception/MD5Exception.java b/library/src/main/java/com/hjq/http/exception/MD5Exception.java
deleted file mode 100644
index f83a415..0000000
--- a/library/src/main/java/com/hjq/http/exception/MD5Exception.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.hjq.http.exception;
-
-/**
- * author : Android 轮子哥
- * github : https://github.com/getActivity/EasyHttp
- * time : 2019年11月16日
- * desc : MD5 校验异常
- */
-public final class MD5Exception extends HttpException {
-
- private final String mMD5;
-
- public MD5Exception(String message, String md5) {
- super(message);
- mMD5 = md5;
- }
-
- public String getMD5() {
- return mMD5;
- }
-}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/exception/ResultException.java b/library/src/main/java/com/hjq/http/exception/ResultException.java
deleted file mode 100644
index 398e0ae..0000000
--- a/library/src/main/java/com/hjq/http/exception/ResultException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.hjq.http.exception;
-
-/**
- * author : Android 轮子哥
- * github : https://github.com/getActivity/EasyHttp
- * time : 2019年06月25日
- * desc : 返回结果异常
- */
-public final class ResultException extends HttpException {
-
- private final Object mData;
-
- public ResultException(String message, Object data) {
- super(message);
- mData = data;
- }
-
- public ResultException(String message, Throwable cause, Object data) {
- super(message, cause);
- mData = data;
- }
-
- public Object getData() {
- return mData;
- }
-}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/exception/UnknownException.java b/library/src/main/java/com/hjq/http/exception/UnknownException.java
deleted file mode 100644
index 4eedb53..0000000
--- a/library/src/main/java/com/hjq/http/exception/UnknownException.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.hjq.http.exception;
-
-/**
- * author : Android 轮子哥
- * github : https://github.com/getActivity/EasyHttp
- * time : 2019年12月01日
- * desc : 未知异常
- */
-public final class UnknownException extends HttpException {
-
- public UnknownException(String message) {
- super(message);
- }
-
- public UnknownException(String message, Throwable cause) {
- super(message, cause);
- }
-}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/lifecycle/ActivityLifecycle.java b/library/src/main/java/com/hjq/http/lifecycle/ActivityLifecycle.java
index 19bf5ea..7eb6992 100644
--- a/library/src/main/java/com/hjq/http/lifecycle/ActivityLifecycle.java
+++ b/library/src/main/java/com/hjq/http/lifecycle/ActivityLifecycle.java
@@ -25,18 +25,20 @@ public final class ActivityLifecycle implements
private Activity mActivity;
- public ActivityLifecycle(Activity activity) {
+ public ActivityLifecycle(@NonNull Activity activity) {
mActivity = activity;
if (mActivity instanceof LifecycleOwner) {
((LifecycleOwner) mActivity).getLifecycle().addObserver(this);
- } else {
- if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.Q) {
- mActivity.registerActivityLifecycleCallbacks(this);
- } else {
- mActivity.getApplication().registerActivityLifecycleCallbacks(this);
- }
+ return;
}
+
+ if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.Q) {
+ mActivity.registerActivityLifecycleCallbacks(this);
+ return;
+ }
+
+ mActivity.getApplication().registerActivityLifecycleCallbacks(this);
}
/**
diff --git a/library/src/main/java/com/hjq/http/lifecycle/ApplicationLifecycle.java b/library/src/main/java/com/hjq/http/lifecycle/ApplicationLifecycle.java
index ecb8b72..d412389 100644
--- a/library/src/main/java/com/hjq/http/lifecycle/ApplicationLifecycle.java
+++ b/library/src/main/java/com/hjq/http/lifecycle/ApplicationLifecycle.java
@@ -13,8 +13,16 @@
*/
public final class ApplicationLifecycle implements LifecycleOwner {
+ private static final ApplicationLifecycle INSTANCE = new ApplicationLifecycle();
+
+ public static ApplicationLifecycle getInstance() {
+ return INSTANCE;
+ }
+
private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
+ private ApplicationLifecycle() {}
+
@NonNull
@Override
public Lifecycle getLifecycle() {
diff --git a/library/src/main/java/com/hjq/http/lifecycle/HttpLifecycleManager.java b/library/src/main/java/com/hjq/http/lifecycle/HttpLifecycleManager.java
index eb60896..0812c03 100644
--- a/library/src/main/java/com/hjq/http/lifecycle/HttpLifecycleManager.java
+++ b/library/src/main/java/com/hjq/http/lifecycle/HttpLifecycleManager.java
@@ -4,7 +4,6 @@
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleEventObserver;
import androidx.lifecycle.LifecycleOwner;
-
import com.hjq.http.EasyHttp;
/**
@@ -18,7 +17,7 @@ public final class HttpLifecycleManager implements LifecycleEventObserver {
/**
* 绑定组件的生命周期
*/
- public static void bind(LifecycleOwner lifecycleOwner) {
+ public static void register(LifecycleOwner lifecycleOwner) {
lifecycleOwner.getLifecycle().addObserver(new HttpLifecycleManager());
}
@@ -38,6 +37,6 @@ public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Ev
// 移除监听
source.getLifecycle().removeObserver(this);
// 取消请求
- EasyHttp.cancel(source);
+ EasyHttp.cancelByTag(source);
}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/lifecycle/LifecycleAppFragment.java b/library/src/main/java/com/hjq/http/lifecycle/LifecycleAppFragment.java
new file mode 100644
index 0000000..5b68818
--- /dev/null
+++ b/library/src/main/java/com/hjq/http/lifecycle/LifecycleAppFragment.java
@@ -0,0 +1,64 @@
+package com.hjq.http.lifecycle;
+
+import android.app.Fragment;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2021年11月22日
+ * desc : Fragment 生命周期管理基类
+ */
+@SuppressWarnings("deprecation")
+public class LifecycleAppFragment extends Fragment implements LifecycleOwner {
+
+ private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
+
+ @NonNull
+ @Override
+ public Lifecycle getLifecycle() {
+ return mLifecycle;
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ mLifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public void onStart() {
+ mLifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START);
+ super.onStart();
+ }
+
+ @Override
+ public void onResume() {
+ mLifecycle.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ mLifecycle.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
+ super.onPause();
+ }
+
+ @Override
+ public void onStop() {
+ mLifecycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+ super.onStop();
+ }
+
+ @Override
+ public void onDestroy() {
+ mLifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
+ super.onDestroy();
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/lifecycle/LifecycleService.java b/library/src/main/java/com/hjq/http/lifecycle/LifecycleService.java
index ffb427a..43cf6db 100644
--- a/library/src/main/java/com/hjq/http/lifecycle/LifecycleService.java
+++ b/library/src/main/java/com/hjq/http/lifecycle/LifecycleService.java
@@ -1,8 +1,11 @@
package com.hjq.http.lifecycle;
import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
@@ -25,13 +28,36 @@ public Lifecycle getLifecycle() {
@Override
public void onCreate() {
- super.onCreate();
mLifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+ super.onCreate();
+ }
+
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ mLifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START);
+ return null;
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public void onStart(Intent intent, int startId) {
+ mLifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START);
+ super.onStart(intent, startId);
+ }
+
+ /**
+ * 为什么不在这个方法里面派发 Lifecycle 回调?因为它最后会调用 {@link #onStart(Intent, int)}
+ */
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
- super.onDestroy();
+ mLifecycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
mLifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
+ super.onDestroy();
}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/listener/HttpCallback.java b/library/src/main/java/com/hjq/http/listener/HttpCallback.java
deleted file mode 100644
index b922052..0000000
--- a/library/src/main/java/com/hjq/http/listener/HttpCallback.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package com.hjq.http.listener;
-
-import okhttp3.Call;
-
-/**
- * author : Android 轮子哥
- * github : https://github.com/getActivity/EasyHttp
- * time : 2019年05月19日
- * desc : 请求回调包装类
- */
-@SuppressWarnings({"rawtypes", "unchecked"})
-public class HttpCallback implements OnHttpListener {
-
- private final OnHttpListener mListener;
-
- public HttpCallback(OnHttpListener listener) {
- mListener = listener;
- }
-
- @Override
- public void onStart(Call call) {
- if (mListener == null) {
- return;
- }
- mListener.onStart(call);
- }
-
- @Override
- public void onSucceed(T result, boolean cache) {
- onSucceed(result);
- }
-
- @Override
- public void onSucceed(T result) {
- if (mListener == null) {
- return;
- }
- mListener.onSucceed(result);
- }
-
- @Override
- public void onFail(Exception e) {
- if (mListener == null) {
- return;
- }
- mListener.onFail(e);
- }
-
- @Override
- public void onEnd(Call call) {
- if (mListener == null) {
- return;
- }
- mListener.onEnd(call);
- }
-}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/listener/HttpCallbackProxy.java b/library/src/main/java/com/hjq/http/listener/HttpCallbackProxy.java
new file mode 100644
index 0000000..70811bc
--- /dev/null
+++ b/library/src/main/java/com/hjq/http/listener/HttpCallbackProxy.java
@@ -0,0 +1,65 @@
+package com.hjq.http.listener;
+
+import androidx.annotation.NonNull;
+import com.hjq.http.config.IRequestApi;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2019年05月19日
+ * desc : 请求回调代理类
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+public class HttpCallbackProxy implements OnHttpListener {
+
+ private final OnHttpListener mSourceListener;
+
+ public HttpCallbackProxy(OnHttpListener listener) {
+ mSourceListener = listener;
+ }
+
+ @Override
+ public void onHttpStart(@NonNull IRequestApi api) {
+ if (mSourceListener == null) {
+ return;
+ }
+ mSourceListener.onHttpStart(api);
+ }
+
+ @Override
+ public void onHttpSuccess(@NonNull T result, boolean cache) {
+ // 这里解释一下,为什么不那么写
+ // 这是因为回调原有的监听器的 onHttpSuccess(T result, boolean cache) 方法,
+ // 最终它只会回调原有监听器的 onHttpSuccess(T result) 方法
+ // 这样会导致当前类的 onHttpSuccess(T result) 方法没有被回调
+ // if (mListener == null) {
+ // return;
+ // }
+ // mListener.onHttpSuccess(result, cache);
+ onHttpSuccess(result);
+ }
+
+ @Override
+ public void onHttpSuccess(@NonNull T result) {
+ if (mSourceListener == null) {
+ return;
+ }
+ mSourceListener.onHttpSuccess(result);
+ }
+
+ @Override
+ public void onHttpFail(@NonNull Throwable throwable) {
+ if (mSourceListener == null) {
+ return;
+ }
+ mSourceListener.onHttpFail(throwable);
+ }
+
+ @Override
+ public void onHttpEnd(@NonNull IRequestApi api) {
+ if (mSourceListener == null) {
+ return;
+ }
+ mSourceListener.onHttpEnd(api);
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/listener/OnDownloadListener.java b/library/src/main/java/com/hjq/http/listener/OnDownloadListener.java
index 3b1b476..16365e8 100644
--- a/library/src/main/java/com/hjq/http/listener/OnDownloadListener.java
+++ b/library/src/main/java/com/hjq/http/listener/OnDownloadListener.java
@@ -1,5 +1,6 @@
package com.hjq.http.listener;
+import androidx.annotation.NonNull;
import java.io.File;
/**
@@ -13,7 +14,7 @@ public interface OnDownloadListener {
/**
* 下载开始
*/
- void onStart(File file);
+ default void onDownloadStart(@NonNull File file) {}
/**
* 下载字节改变
@@ -21,27 +22,36 @@ public interface OnDownloadListener {
* @param totalByte 总字节数
* @param downloadByte 已下载字节数
*/
- default void onByte(File file, long totalByte, long downloadByte) {}
+ default void onDownloadByteChange(@NonNull File file, long totalByte, long downloadByte) {}
/**
* 下载进度改变
*
* @param progress 下载进度值(0-100)
*/
- void onProgress(File file, int progress);
+ void onDownloadProgressChange(@NonNull File file, int progress);
/**
- * 下载完成
+ * 下载成功
+ *
+ * @param cache 是否是通过缓存下载成功的
+ */
+ default void onDownloadSuccess(@NonNull File file, boolean cache) {
+ onDownloadSuccess(file);
+ }
+
+ /**
+ * 下载成功
*/
- void onComplete(File file);
+ void onDownloadSuccess(@NonNull File file);
/**
- * 下载出错
+ * 下载失败
*/
- void onError(File file, Exception e);
+ void onDownloadFail(@NonNull File file, @NonNull Throwable throwable);
/**
* 下载结束
*/
- void onEnd(File file);
+ default void onDownloadEnd(@NonNull File file) {}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/listener/OnHttpListener.java b/library/src/main/java/com/hjq/http/listener/OnHttpListener.java
index dccbaa7..0449fe8 100644
--- a/library/src/main/java/com/hjq/http/listener/OnHttpListener.java
+++ b/library/src/main/java/com/hjq/http/listener/OnHttpListener.java
@@ -1,6 +1,7 @@
package com.hjq.http.listener;
-import okhttp3.Call;
+import androidx.annotation.NonNull;
+import com.hjq.http.config.IRequestApi;
/**
* author : Android 轮子哥
@@ -13,29 +14,29 @@ public interface OnHttpListener {
/**
* 请求开始
*/
- default void onStart(Call call) {}
+ default void onHttpStart(@NonNull IRequestApi api) {}
/**
* 请求成功
*
* @param cache 是否是通过缓存请求成功的
*/
- default void onSucceed(T result, boolean cache) {
- onSucceed(result);
+ default void onHttpSuccess(@NonNull T result, boolean cache) {
+ onHttpSuccess(result);
}
/**
* 请求成功
*/
- void onSucceed(T result);
+ void onHttpSuccess(@NonNull T result);
/**
* 请求出错
*/
- void onFail(Exception e);
+ void onHttpFail(@NonNull Throwable throwable);
/**
* 请求结束
*/
- default void onEnd(Call call) {}
+ default void onHttpEnd(@NonNull IRequestApi api) {}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/listener/OnUpdateListener.java b/library/src/main/java/com/hjq/http/listener/OnUpdateListener.java
index 294d088..28c1cc3 100644
--- a/library/src/main/java/com/hjq/http/listener/OnUpdateListener.java
+++ b/library/src/main/java/com/hjq/http/listener/OnUpdateListener.java
@@ -1,5 +1,8 @@
package com.hjq.http.listener;
+import androidx.annotation.NonNull;
+import com.hjq.http.config.IRequestApi;
+
/**
* author : Android 轮子哥
* github : https://github.com/getActivity/EasyHttp
@@ -8,18 +11,72 @@
*/
public interface OnUpdateListener extends OnHttpListener {
+ /**
+ * 请求开始
+ */
+ @Override
+ default void onHttpStart(@NonNull IRequestApi api) {
+ onUpdateStart(api);
+ }
+
+ /**
+ * 请求成功
+ */
+ @Override
+ default void onHttpSuccess(@NonNull T result) {
+ onUpdateSuccess(result);
+ }
+
+ /**
+ * 请求出错
+ */
+ @Override
+ default void onHttpFail(@NonNull Throwable throwable) {
+ onUpdateFail(throwable);
+ }
+
+ /**
+ * 请求结束
+ */
+ @Override
+ default void onHttpEnd(@NonNull IRequestApi api) {
+ onUpdateEnd(api);
+ }
+
+ /* --------------------------------------------------------------- */
+
+ /**
+ * 上传开始
+ */
+ default void onUpdateStart(@NonNull IRequestApi api) {}
+
/**
* 上传字节改变
*
* @param totalByte 总字节数
* @param updateByte 已上传字节数
*/
- default void onByte(long totalByte, long updateByte) {}
+ default void onUpdateByteChange(long totalByte, long updateByte) {}
/**
* 上传进度改变
*
* @param progress 上传进度值(0-100)
*/
- void onProgress(int progress);
+ void onUpdateProgressChange(int progress);
+
+ /**
+ * 上传成功
+ */
+ void onUpdateSuccess(@NonNull T result);
+
+ /**
+ * 上传出错
+ */
+ void onUpdateFail(@NonNull Throwable throwable);
+
+ /**
+ * 上传结束
+ */
+ default void onUpdateEnd(@NonNull IRequestApi api) {}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/model/BodyType.java b/library/src/main/java/com/hjq/http/model/BodyType.java
deleted file mode 100644
index 6aa3626..0000000
--- a/library/src/main/java/com/hjq/http/model/BodyType.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.hjq.http.model;
-
-/**
- * author : Android 轮子哥
- * github : https://github.com/getActivity/EasyHttp
- * time : 2019年12月18日
- * desc : 参数提交方式
- */
-public enum BodyType {
-
- /**
- * 表单提交
- */
- FORM,
-
- /**
- * JSON 提交
- */
- JSON
-}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/model/CacheMode.java b/library/src/main/java/com/hjq/http/model/CacheMode.java
index f41f462..67382f6 100644
--- a/library/src/main/java/com/hjq/http/model/CacheMode.java
+++ b/library/src/main/java/com/hjq/http/model/CacheMode.java
@@ -9,7 +9,7 @@
public enum CacheMode {
/**
- * 默认(按照 Http 协议来缓存)
+ * 默认(按照 Http 协议来缓存,目前 OkHttp 只支持 Get 请求缓存,不支持 Post 请求缓存)
*/
DEFAULT,
@@ -21,16 +21,16 @@ public enum CacheMode {
/**
* 只使用缓存
*
- * 有缓存的情况下:读取缓存 -> 回调成功
- * 无缓存的情况下:请求网络 -> 写入缓存 -> 回调成功
+ * 已有缓存的情况下:读取缓存 -> 回调成功
+ * 没有缓存的情况下:请求网络 -> 写入缓存 -> 回调成功
*/
USE_CACHE_ONLY,
/**
* 优先使用缓存
*
- * 有缓存的情况下:先读缓存 —> 回调成功 —> 请求网络 —> 刷新缓存
- * 无缓存的情况下:请求网络 -> 写入缓存 -> 回调成功
+ * 已有缓存的情况下:读取缓存 —> 回调成功 —> 请求网络 —> 刷新缓存
+ * 没有缓存的情况下:请求网络 -> 写入缓存 -> 回调成功
*/
USE_CACHE_FIRST,
diff --git a/library/src/main/java/com/hjq/http/model/CallProxy.java b/library/src/main/java/com/hjq/http/model/CallProxy.java
index 5e2c433..2c729d9 100644
--- a/library/src/main/java/com/hjq/http/model/CallProxy.java
+++ b/library/src/main/java/com/hjq/http/model/CallProxy.java
@@ -1,7 +1,10 @@
package com.hjq.http.model;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import java.io.IOException;
-
+import kotlin.jvm.functions.Function0;
+import kotlin.reflect.KClass;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Request;
@@ -16,77 +19,90 @@
*/
public final class CallProxy implements Call {
- private Call mCall;
+ private Call mRealCall;
+
+ public CallProxy(@NonNull Call realCall) {
+ mRealCall = realCall;
+ }
- public CallProxy(Call call) {
- mCall = call;
+ public void setRealCall(@NonNull Call call) {
+ mRealCall = call;
}
- public void setCall(Call call) {
- mCall = call;
+ public Call getRealCall() {
+ return mRealCall;
}
+ @NonNull
@Override
public Request request() {
- if (mCall == null) {
- return null;
- }
- return mCall.request();
+ return mRealCall.request();
}
+ @NonNull
@Override
public Response execute() throws IOException {
- if (mCall == null) {
- return null;
- }
- return mCall.execute();
+ return mRealCall.execute();
}
@Override
- public void enqueue(Callback responseCallback) {
- if (mCall == null) {
- return;
- }
- mCall.enqueue(responseCallback);
+ public void enqueue(@NonNull Callback responseCallback) {
+ mRealCall.enqueue(responseCallback);
}
@Override
public void cancel() {
- if (mCall == null) {
- return;
- }
- mCall.cancel();
+ mRealCall.cancel();
}
@Override
public boolean isExecuted() {
- if (mCall == null) {
- return false;
- }
- return mCall.isExecuted();
+ return mRealCall.isExecuted();
}
@Override
public boolean isCanceled() {
- if (mCall == null) {
- return false;
- }
- return mCall.isCanceled();
+ return mRealCall.isCanceled();
}
+ @NonNull
@Override
public Timeout timeout() {
- if (mCall == null) {
- return null;
- }
- return mCall.timeout();
+ return mRealCall.timeout();
}
+ @NonNull
@Override
public Call clone() {
- if (mCall == null) {
- return null;
- }
- return mCall.clone();
+ return mRealCall.clone();
+ }
+
+ @Nullable
+ @Override
+ public T tag(@NonNull KClass type) {
+ return mRealCall.tag(type);
+ }
+
+ @Nullable
+ @Override
+ public T tag(@NonNull Class extends T> type) {
+ return mRealCall.tag(type);
+ }
+
+ @NonNull
+ @Override
+ public T tag(@NonNull KClass type, @NonNull Function0 extends T> computeIfAbsent) {
+ return mRealCall.tag(type, computeIfAbsent);
+ }
+
+ @NonNull
+ @Override
+ public T tag(@NonNull Class type, @NonNull Function0 extends T> computeIfAbsent) {
+ return mRealCall.tag(type, computeIfAbsent);
+ }
+
+ public interface Factory {
+
+ CallProxy create();
}
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/model/ContentType.java b/library/src/main/java/com/hjq/http/model/ContentType.java
index 99925dd..99fc459 100644
--- a/library/src/main/java/com/hjq/http/model/ContentType.java
+++ b/library/src/main/java/com/hjq/http/model/ContentType.java
@@ -1,5 +1,10 @@
package com.hjq.http.model;
+import android.text.TextUtils;
+
+import java.net.FileNameMap;
+import java.net.URLConnection;
+
import okhttp3.MediaType;
/**
@@ -10,6 +15,9 @@
*/
public final class ContentType {
+ /** Http 请求 key */
+ public static final String HTTP_HEAD_KEY = "Content-Type";
+
/** 字节流 */
public static final MediaType STREAM = MediaType.parse("application/octet-stream");
@@ -18,4 +26,25 @@ public final class ContentType {
/** 纯文本 */
public static final MediaType TEXT = MediaType.parse("text/plain; charset=utf-8");
+
+ /**
+ * 根据文件名获取 MIME 类型
+ */
+ public static MediaType guessMimeType(String fileName) {
+ if (TextUtils.isEmpty(fileName)) {
+ return STREAM;
+ }
+ FileNameMap fileNameMap = URLConnection.getFileNameMap();
+ // 解决文件名中含有#号异常的问题
+ fileName = fileName.replace("#", "");
+ String contentType = fileNameMap.getContentTypeFor(fileName);
+ if (contentType == null) {
+ return STREAM;
+ }
+ MediaType type = MediaType.parse(contentType);
+ if (type == null) {
+ type = STREAM;
+ }
+ return type;
+ }
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/model/FileContentResolver.java b/library/src/main/java/com/hjq/http/model/FileContentResolver.java
index 555b6dc..4f99e6a 100644
--- a/library/src/main/java/com/hjq/http/model/FileContentResolver.java
+++ b/library/src/main/java/com/hjq/http/model/FileContentResolver.java
@@ -1,17 +1,22 @@
package com.hjq.http.model;
import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
import android.net.Uri;
-
+import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-
+import com.hjq.http.EasyUtils;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
+import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import okhttp3.MediaType;
+import okhttp3.RequestBody;
/**
* author : Android 轮子哥
@@ -19,30 +24,95 @@
* time : 2021年04月18日
* desc : 文件内容解析器
*/
-public class FileContentResolver extends FileWrapper {
+public class FileContentResolver extends File {
private final ContentResolver mContentResolver;
- private final Uri mFileUri;
+ private final Uri mContentUri;
+
+ private MediaType mContentType;
+ private String mFileName;
+
+ public FileContentResolver(Context context, Uri uri) {
+ this(context.getContentResolver(), uri);
+ }
+
+ public FileContentResolver(ContentResolver resolver, Uri uri) {
+ this(resolver, uri, null);
+ }
+
+ public FileContentResolver(Context context, Uri uri, String fileName) {
+ this(context.getContentResolver(), uri, fileName);
+ }
- public FileContentResolver(@NonNull ContentResolver resolver, Uri uri) {
- super(new File(uri.toString()));
+ public FileContentResolver(ContentResolver resolver, Uri uri, String fileName) {
+ super(new File(uri.toString()).getPath());
mContentResolver = resolver;
- mFileUri = uri;
+ // 请注意这个 uri 需要通过 ContentResolver.insert 方法生成的,并且没有经过修改的,否则会导致文件流读取失败
+ // 经过测试,ContentResolver.insert 生成的 uri 类型为 Uri.HierarchicalUri 这个内部类的
+ mContentUri = uri;
+ if (!TextUtils.isEmpty(fileName)) {
+ mFileName = fileName;
+ mContentType = ContentType.guessMimeType(fileName);
+ } else {
+ mFileName = getName();
+ mContentType = ContentType.STREAM;
+ }
}
- @Override
- public InputStream getInputStream() throws FileNotFoundException {
- return mContentResolver.openInputStream(mFileUri);
+ /**
+ * 设置真实的文件名(用于 {@link okhttp3.MultipartBody.Builder#addFormDataPart(String, String, RequestBody)} 方法中的 fileName 属性)
+ */
+ public void setFileName(String fileName) {
+ mFileName = fileName;
}
- @Override
- public OutputStream getOutputStream() throws FileNotFoundException {
- return mContentResolver.openOutputStream(mFileUri);
+ /**
+ * 获取真实的文件名
+ */
+ public String getFileName() {
+ return mFileName;
+ }
+
+ /**
+ * 设置内容类型(用于 {@link RequestBody#contentType()} 方法)
+ */
+ public void setContentType(MediaType type) {
+ mContentType = type;
+ }
+
+ /**
+ * 获取内容类型
+ */
+ public MediaType getContentType() {
+ return mContentType;
+ }
+
+ /**
+ * 获取内容的 uri
+ */
+ public Uri getContentUri() {
+ return mContentUri;
+ }
+
+ /**
+ * 打开文件输入流
+ */
+ public InputStream openInputStream() throws FileNotFoundException {
+ return mContentResolver.openInputStream(mContentUri);
+ }
+
+ /**
+ * 打开文件输出流
+ */
+ public OutputStream openOutputStream(boolean append) throws FileNotFoundException {
+ // w:写入模式,如果文件存在则覆盖,如果文件不存在则创建
+ // wa:追加模式,如果文件存在则追加到文件末尾,如果文件不存在则创建
+ return mContentResolver.openOutputStream(mContentUri, append ? "wa" : "w");
}
@Override
public boolean delete() {
- return mContentResolver.delete(mFileUri, null, null)> 0;
+ return mContentResolver.delete(mContentUri, null, null)> 0;
}
@Override
@@ -57,17 +127,37 @@ public boolean isHidden() {
@Override
public long length() {
+ InputStream inputStream = null;
+ try {
+ inputStream = openInputStream();
+ if (inputStream != null) {
+ return inputStream.available();
+ }
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ EasyUtils.closeStream(inputStream);
+ }
return 0;
}
@Override
public boolean exists() {
- return true;
+ Cursor cursor = null;
+ try {
+ // 通过 Cursor 来验证文件 Uri 是否存在
+ cursor = mContentResolver.query(mContentUri, null, null, null, null);
+ return cursor != null && cursor.getCount() != 0;
+ } finally {
+ EasyUtils.closeStream(cursor);
+ }
}
@Override
public boolean isFile() {
- return true;
+ return exists();
}
@Override
diff --git a/library/src/main/java/com/hjq/http/model/FileWrapper.java b/library/src/main/java/com/hjq/http/model/FileWrapper.java
deleted file mode 100644
index d1f0d83..0000000
--- a/library/src/main/java/com/hjq/http/model/FileWrapper.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package com.hjq.http.model;
-
-import androidx.annotation.NonNull;
-
-import com.hjq.http.EasyLog;
-import com.hjq.http.EasyUtils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.DigestInputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-/**
- * author : Android 轮子哥
- * github : https://github.com/getActivity/EasyHttp
- * time : 2021年04月18日
- * desc : File 包装类
- */
-public class FileWrapper extends File {
-
- public FileWrapper(@NonNull File file) {
- super(file.getPath());
- }
-
- /**
- * 获取文件的输出流
- */
- public OutputStream getOutputStream() throws FileNotFoundException {
- return new FileOutputStream(this);
- }
-
- /**
- * 获取文件的输入流
- */
- public InputStream getInputStream() throws FileNotFoundException {
- return new FileInputStream(this);
- }
-
- /**
- * 创建文件夹
- */
- public static boolean createFolder(File targetFolder) {
- if (targetFolder.exists()) {
- if (targetFolder.isDirectory()) {
- return true;
- }
- // noinspection ResultOfMethodCallIgnored
- targetFolder.delete();
- }
- return targetFolder.mkdirs();
- }
-
- /**
- * 获取文件的 md5
- */
- public static String getFileMd5(InputStream inputStream) {
- if (inputStream == null) {
- return "";
- }
- DigestInputStream digestInputStream = null;
- try {
- MessageDigest messageDigest = MessageDigest.getInstance("MD5");
- digestInputStream = new DigestInputStream(inputStream, messageDigest);
- byte[] buffer = new byte[1024 * 256];
- while (true) {
- if (!(digestInputStream.read(buffer)> 0)) {
- break;
- }
- }
- messageDigest = digestInputStream.getMessageDigest();
- byte[] md5 = messageDigest.digest();
- StringBuilder sb = new StringBuilder();
- for (byte b : md5) {
- sb.append(String.format("%02X", b));
- }
- return sb.toString().toLowerCase();
- } catch (NoSuchAlgorithmException | IOException e) {
- EasyLog.print(e);
- } finally {
- EasyUtils.closeStream(digestInputStream);
- }
- return null;
- }
-}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/model/HttpHeaders.java b/library/src/main/java/com/hjq/http/model/HttpHeaders.java
index d1d8c67..1dc255d 100644
--- a/library/src/main/java/com/hjq/http/model/HttpHeaders.java
+++ b/library/src/main/java/com/hjq/http/model/HttpHeaders.java
@@ -1,56 +1,52 @@
-package com.hjq.http.model;
-
-import com.hjq.http.EasyConfig;
-
-import java.util.HashMap;
-import java.util.Set;
-
-/**
- * author : Android 轮子哥
- * github : https://github.com/getActivity/EasyHttp
- * time : 2019年07月20日
- * desc : 请求头封装
- */
-public final class HttpHeaders {
-
- /** 请求头存放集合 */
- private HashMap mHeaders = EasyConfig.getInstance().getHeaders();
-
- public void put(String key, String value) {
- if (key == null || value == null) {
- return;
- }
-
- if (mHeaders == EasyConfig.getInstance().getHeaders()) {
- mHeaders = new HashMap(mHeaders);
- }
- mHeaders.put(key, value);
- }
-
- public void remove(String key) {
- if (key == null) {
- return;
- }
-
- if (mHeaders == EasyConfig.getInstance().getHeaders()) {
- mHeaders = new HashMap(mHeaders);
- }
- mHeaders.remove(key);
- }
-
- public String get(String key) {
- return mHeaders.get(key);
- }
-
- public boolean isEmpty() {
- return mHeaders == null || mHeaders.isEmpty();
- }
-
- public Set getNames() {
- return mHeaders.keySet();
- }
-
- public HashMap getHeaders() {
- return mHeaders;
- }
+package com.hjq.http.model;
+
+import com.hjq.http.EasyConfig;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2019年07月20日
+ * desc : 请求头封装
+ */
+public final class HttpHeaders {
+
+ /** 请求头存放集合 */
+ private final Map