diff --git a/EasyHttp.apk b/EasyHttp.apk
index 21c7d91..d978ebe 100644
Binary files a/EasyHttp.apk and b/EasyHttp.apk differ
diff --git a/README.md b/README.md
index 0363bfe..216907a 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
# 网络请求框架
+> 码云地址:[Gitee](https://gitee.com/getActivity/EasyHttp)
+

[点击此处下载Demo](https://raw.githubusercontent.com/getActivity/EasyHttp/master/EasyHttp.apk)
@@ -15,7 +17,7 @@
}
dependencies {
- implementation 'com.hjq:http:6.0'
+ implementation 'com.hjq:http:6.5'
implementation 'com.squareup.okhttp3:okhttp:3.12.10'
implementation 'com.google.code.gson:gson:2.8.5'
}
@@ -29,10 +31,6 @@
-
-
-
-
#### 服务器配置
public class RequestServer implements IRequestServer {
@@ -71,7 +69,7 @@
//.addParam("token", "6666666")
// 添加全局请求头
//.addHeader("time", "20191030")
- // 启用配置
+ // 启用配置
.into();
> 上述是创建配置,更新配置可以使用
@@ -81,7 +79,8 @@
#### 配置接口
- public class LoginApi implements IRequestApi {
+ @Keep
+ public final class LoginApi implements IRequestApi {
@Override
public String getApi() {
@@ -204,19 +203,6 @@
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**
-dontwarn okio.**
-
- # 保护 IRequestApi 类字段名不被混淆
- -keepclassmembernames class * implements com.hjq.http.config.IRequestApi {
- ;
- }
- # 保护 Bean 类不被混淆(请注意修改包名路径)
- -keepclassmembernames class xxx.xxx.xxx.xxx.xxx.response.** {
- ;
- }
- # 保护模型类的字段不被混淆(请注意修改包名路径)
- -keepclassmembernames class xxx.xxx.xxx.xxx.xxx.xxx.HttpData {
- ;
- }
#### 作者的其他开源项目
diff --git a/app/build.gradle b/app/build.gradle
index 04a63e5..17b3afd 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -13,13 +13,14 @@ android {
applicationId "com.hjq.http.demo"
minSdkVersion 14
targetSdkVersion 28
- versionCode 60
- versionName "6.0"
- testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ versionCode 65
+ versionName "6.5"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
+
buildTypes {
release {
- minifyEnabled false
+ minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
@@ -33,13 +34,18 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation project(':library')
- implementation 'com.android.support:appcompat-v7:28.0.0'
- implementation 'com.android.support:design:28.0.0'
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+
+
+ implementation 'androidx.appcompat:appcompat:1.1.0'
+ implementation 'com.google.android.material:material:1.1.0'
// 标题栏:https://github.com/getActivity/TitleBar
implementation 'com.hjq:titlebar:6.0'
// 吐司工具类:https://github.com/getActivity/ToastUtils
- implementation 'com.hjq:toast:8.0'
+ implementation 'com.hjq:toast:8.2'
// 权限请求框架:https://github.com/getActivity/XXPermissions
implementation 'com.hjq:xxpermissions:6.2'
// Json 解析框架:https://github.com/google/gson
@@ -48,5 +54,5 @@ dependencies {
// 升级注意事项:https://www.jianshu.com/p/d12d0f536f55
implementation 'com.squareup.okhttp3:okhttp:3.12.10'
// 日志调试:https://github.com/getActivity/Logcat
- debugImplementation 'com.hjq:logcat:6.0'
+ debugImplementation 'com.hjq:logcat:6.5'
}
\ No newline at end of file
diff --git a/app/src/androidTest/assets/NoSpecification.json b/app/src/androidTest/assets/NoSpecification.json
new file mode 100644
index 0000000..9250950
--- /dev/null
+++ b/app/src/androidTest/assets/NoSpecification.json
@@ -0,0 +1,18 @@
+{
+ "listTest1" : [],
+ "listTest2" : {},
+ "listTest3" : "",
+ "booleanTest1" : 0,
+ "booleanTest2" : 1,
+ "booleanTest3" : null,
+ "booleanTest4" : "true",
+ "stringTest1" : null,
+ "stringTest2" : false,
+ "stringTest3" : 123,
+ "intTest1" : 2.2,
+ "intTest2" : null,
+ "intTest3" : "",
+ "longTest1" : 2.2,
+ "longTest2" : null,
+ "longTest3" : "22"
+}
\ No newline at end of file
diff --git a/app/src/androidTest/assets/Specification.json b/app/src/androidTest/assets/Specification.json
new file mode 100644
index 0000000..e17b311
--- /dev/null
+++ b/app/src/androidTest/assets/Specification.json
@@ -0,0 +1,18 @@
+{
+ "listTest1" : [1, 2],
+ "listTest2" : ["a", "b", "c"],
+ "listTest3" : [1, 2, 3],
+ "booleanTest1" : false,
+ "booleanTest2" : true,
+ "booleanTest3" : false,
+ "booleanTest4" : true,
+ "stringTest1" : null,
+ "stringTest2" : "",
+ "stringTest3" : "字符串",
+ "intTest1" : 1,
+ "intTest2" : 2,
+ "intTest3" : 3,
+ "longTest1" : 12580,
+ "longTest2" : 10086,
+ "longTest3" : 101000
+}
\ No newline at end of file
diff --git a/app/src/androidTest/java/com/hjq/http/test/JsonBean.java b/app/src/androidTest/java/com/hjq/http/test/JsonBean.java
new file mode 100644
index 0000000..0b4f790
--- /dev/null
+++ b/app/src/androidTest/java/com/hjq/http/test/JsonBean.java
@@ -0,0 +1,23 @@
+package com.hjq.http.test;
+
+import java.util.List;
+
+public class JsonBean {
+
+ private List listTest1;
+ private List listTest2;
+ private List listTest3;
+ private boolean booleanTest1;
+ private boolean booleanTest2;
+ private boolean booleanTest3;
+ private boolean booleanTest4;
+ private String stringTest1;
+ private String stringTest2;
+ private String stringTest3;
+ private int intTest1;
+ private int intTest2;
+ private int intTest3;
+ private long longTest1;
+ private long longTest2;
+ private long longTest3;
+}
\ No newline at end of file
diff --git a/app/src/androidTest/java/com/hjq/http/test/JsonUnitTest.java b/app/src/androidTest/java/com/hjq/http/test/JsonUnitTest.java
new file mode 100644
index 0000000..3089c78
--- /dev/null
+++ b/app/src/androidTest/java/com/hjq/http/test/JsonUnitTest.java
@@ -0,0 +1,88 @@
+package com.hjq.http.test;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.internal.bind.TypeAdapters;
+import com.hjq.http.demo.http.json.BooleanTypeAdapter;
+import com.hjq.http.demo.http.json.DoubleTypeAdapter;
+import com.hjq.http.demo.http.json.FloatTypeAdapter;
+import com.hjq.http.demo.http.json.IntegerTypeAdapter;
+import com.hjq.http.demo.http.json.ListTypeAdapter;
+import com.hjq.http.demo.http.json.LongTypeAdapter;
+import com.hjq.http.demo.http.json.StringTypeAdapter;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class JsonUnitTest {
+
+ private Gson mGson;
+
+ @Before
+ public void onTestBefore() {
+ mGson = new GsonBuilder()
+ .registerTypeAdapterFactory(TypeAdapters.newFactory(String.class, new StringTypeAdapter()))
+ .registerTypeAdapterFactory(TypeAdapters.newFactory(boolean.class, Boolean.class, new BooleanTypeAdapter()))
+ .registerTypeAdapterFactory(TypeAdapters.newFactory(int.class, Integer.class, new IntegerTypeAdapter()))
+ .registerTypeAdapterFactory(TypeAdapters.newFactory(long.class, Long.class, new LongTypeAdapter()))
+ .registerTypeAdapterFactory(TypeAdapters.newFactory(float.class, Float.class, new FloatTypeAdapter()))
+ .registerTypeAdapterFactory(TypeAdapters.newFactory(double.class, Double.class, new DoubleTypeAdapter()))
+ .registerTypeHierarchyAdapter(List.class, new ListTypeAdapter())
+ .create();
+ }
+
+ @Test
+ public void onSpecification() {
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ String json = getAssetsString(context, "Specification.json");
+ mGson.fromJson(json, JsonBean.class);
+ }
+
+ @Test
+ public void onNoSpecification() {
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ String json = getAssetsString(context, "NoSpecification.json");
+ mGson.fromJson(json, JsonBean.class);
+ }
+
+ @After
+ public void onTestAfter() {
+ mGson = null;
+ }
+
+ /**
+ * 获取资产目录下面文件的字符串
+ */
+ private static String getAssetsString(Context context, String file) {
+ try {
+ InputStream inputStream = context.getAssets().open(file);
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ byte[] buffer = new byte[512];
+ int length;
+ while ((length = inputStream.read(buffer)) != -1) {
+ outStream.write(buffer, 0, length);
+ }
+ outStream.close();
+ inputStream.close();
+ return outStream.toString();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index daabd5b..3ee8451 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -27,7 +27,7 @@
diff --git a/app/src/main/java/com/hjq/http/demo/BaseActivity.java b/app/src/main/java/com/hjq/http/demo/BaseActivity.java
index a43f6b8..da542d0 100644
--- a/app/src/main/java/com/hjq/http/demo/BaseActivity.java
+++ b/app/src/main/java/com/hjq/http/demo/BaseActivity.java
@@ -1,15 +1,21 @@
package com.hjq.http.demo;
import android.app.ProgressDialog;
-import android.support.v7.app.AppCompatActivity;
-import com.hjq.http.EasyHttp;
+import androidx.appcompat.app.AppCompatActivity;
+
import com.hjq.http.demo.http.model.HttpData;
import com.hjq.http.listener.OnHttpListener;
import com.hjq.toast.ToastUtils;
import okhttp3.Call;
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2019/05/19
+ * desc : 基类封装
+ */
public class BaseActivity extends AppCompatActivity implements OnHttpListener {
/** 加载对话框 */
@@ -75,10 +81,4 @@ public void onFail(Exception e) {
public void onEnd(Call call) {
hideDialog();
}
-
- @Override
- protected void onDestroy() {
- EasyHttp.cancel(this);
- super.onDestroy();
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/hjq/http/demo/MainActivity.java b/app/src/main/java/com/hjq/http/demo/MainActivity.java
index 1e49ed3..800ffad 100644
--- a/app/src/main/java/com/hjq/http/demo/MainActivity.java
+++ b/app/src/main/java/com/hjq/http/demo/MainActivity.java
@@ -9,11 +9,12 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.content.FileProvider;
import android.view.View;
import android.widget.ProgressBar;
+import androidx.core.content.ContextCompat;
+import androidx.core.content.FileProvider;
+
import com.hjq.http.EasyHttp;
import com.hjq.http.demo.http.model.HttpData;
import com.hjq.http.demo.http.request.SearchAuthorApi;
@@ -57,7 +58,6 @@ protected void onCreate(Bundle savedInstanceState) {
findViewById(R.id.btn_main_post).setOnClickListener(this);
findViewById(R.id.btn_main_update).setOnClickListener(this);
findViewById(R.id.btn_main_download).setOnClickListener(this);
-
requestPermission();
}
diff --git a/app/src/main/java/com/hjq/http/demo/MyApplication.java b/app/src/main/java/com/hjq/http/demo/MyApplication.java
index 461959c..0f8dcb6 100644
--- a/app/src/main/java/com/hjq/http/demo/MyApplication.java
+++ b/app/src/main/java/com/hjq/http/demo/MyApplication.java
@@ -28,13 +28,13 @@ public void onCreate() {
EasyConfig.with(new OkHttpClient())
// 是否打印日志
- .setLogEnabled(BuildConfig.DEBUG)
+ //.setLogEnabled(BuildConfig.DEBUG)
// 设置服务器配置
.setServer(server)
// 设置请求处理策略
- .setHandler(new RequestHandler())
+ .setHandler(new RequestHandler(this))
// 设置请求重试次数
- .setRetryCount(3)
+ .setRetryCount(1)
// 添加全局请求参数
//.addParam("token", "6666666")
// 添加全局请求头
diff --git a/app/src/main/java/com/hjq/http/demo/http/json/BooleanTypeAdapter.java b/app/src/main/java/com/hjq/http/demo/http/json/BooleanTypeAdapter.java
new file mode 100644
index 0000000..1ec35a9
--- /dev/null
+++ b/app/src/main/java/com/hjq/http/demo/http/json/BooleanTypeAdapter.java
@@ -0,0 +1,37 @@
+package com.hjq.http.demo.http.json;
+
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.IOException;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2020/05/05
+ * desc : boolean / Boolean 解析适配器 {@link com.google.gson.internal.bind.TypeAdapters#BOOLEAN}
+ */
+public class BooleanTypeAdapter extends TypeAdapter {
+
+ @Override
+ public Boolean read(JsonReader in) throws IOException {
+ switch (in.peek()) {
+ case NULL:
+ in.nextNull();
+ return null;
+ case STRING:
+ // 如果后台返回 "true" 或者 "TRUE",则默认处理为 true
+ return Boolean.parseBoolean(in.nextString());
+ case NUMBER:
+ // 如果这个后台返回是 1,则表示 true,否则表示 false
+ return in.nextInt() == 1;
+ default:
+ return in.nextBoolean();
+ }
+ }
+ @Override
+ public void write(JsonWriter out, Boolean value) throws IOException {
+ out.value(value);
+ }
+}
diff --git a/app/src/main/java/com/hjq/http/demo/http/json/DoubleTypeAdapter.java b/app/src/main/java/com/hjq/http/demo/http/json/DoubleTypeAdapter.java
new file mode 100644
index 0000000..8f125e5
--- /dev/null
+++ b/app/src/main/java/com/hjq/http/demo/http/json/DoubleTypeAdapter.java
@@ -0,0 +1,42 @@
+package com.hjq.http.demo.http.json;
+
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.IOException;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2020/05/05
+ * desc : double / Double 解析适配器 {@link com.google.gson.internal.bind.TypeAdapters#DOUBLE}
+ */
+public class DoubleTypeAdapter extends TypeAdapter {
+
+ @Override
+ public Number read(JsonReader in) throws IOException {
+ switch (in.peek()) {
+ case NULL:
+ in.nextNull();
+ return null;
+ case NUMBER:
+ // 如果后台返回数值,则按照正常逻辑解析
+ return in.nextDouble();
+ case STRING:
+ try {
+ return Double.parseDouble(in.nextString());
+ } catch (NumberFormatException e) {
+ // 如果是空字符串则会抛出这个异常
+ return 0;
+ }
+ default:
+ in.skipValue();
+ return 0;
+ }
+ }
+ @Override
+ public void write(JsonWriter out, Number value) throws IOException {
+ out.value(value);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hjq/http/demo/http/json/FloatTypeAdapter.java b/app/src/main/java/com/hjq/http/demo/http/json/FloatTypeAdapter.java
new file mode 100644
index 0000000..2edf49e
--- /dev/null
+++ b/app/src/main/java/com/hjq/http/demo/http/json/FloatTypeAdapter.java
@@ -0,0 +1,23 @@
+package com.hjq.http.demo.http.json;
+
+import com.google.gson.stream.JsonReader;
+
+import java.io.IOException;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2020/05/05
+ * desc : float / Float 解析适配器 {@link com.google.gson.internal.bind.TypeAdapters#FLOAT}
+ */
+public class FloatTypeAdapter extends DoubleTypeAdapter {
+
+ @Override
+ public Number read(JsonReader in) throws IOException {
+ Number number = super.read(in);
+ if (number != null) {
+ return number.floatValue();
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hjq/http/demo/http/json/IntegerTypeAdapter.java b/app/src/main/java/com/hjq/http/demo/http/json/IntegerTypeAdapter.java
new file mode 100644
index 0000000..11e18b9
--- /dev/null
+++ b/app/src/main/java/com/hjq/http/demo/http/json/IntegerTypeAdapter.java
@@ -0,0 +1,23 @@
+package com.hjq.http.demo.http.json;
+
+import com.google.gson.stream.JsonReader;
+
+import java.io.IOException;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2020/05/05
+ * desc : int / Integer 解析适配器 {@link com.google.gson.internal.bind.TypeAdapters#INTEGER}
+ */
+public class IntegerTypeAdapter extends DoubleTypeAdapter {
+
+ @Override
+ public Number read(JsonReader in) throws IOException {
+ Number number = super.read(in);
+ if (number != null) {
+ return number.intValue();
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hjq/http/demo/http/json/ListTypeAdapter.java b/app/src/main/java/com/hjq/http/demo/http/json/ListTypeAdapter.java
new file mode 100644
index 0000000..7de675b
--- /dev/null
+++ b/app/src/main/java/com/hjq/http/demo/http/json/ListTypeAdapter.java
@@ -0,0 +1,43 @@
+package com.hjq.http.demo.http.json;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2020/05/05
+ * desc : List 解析适配器
+ */
+public class ListTypeAdapter implements JsonDeserializer {
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public List deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+ // 如果这是一个数组
+ if (json.isJsonArray()) {
+ JsonArray array = json.getAsJsonArray();
+ // 获取 List 上的泛型
+ Type itemType = ((ParameterizedType) typeOfT).getActualTypeArguments()[0];
+ List list = new ArrayList();
+ for (int i = 0; i < array.size(); i++) {
+ JsonElement element = array.get(i);
+ // 解析 List 中的条目对象
+ Object item = context.deserialize(element, itemType);
+ list.add(item);
+ }
+ return list;
+ } else {
+ // 和接口类型不符,直接返回 null
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hjq/http/demo/http/json/LongTypeAdapter.java b/app/src/main/java/com/hjq/http/demo/http/json/LongTypeAdapter.java
new file mode 100644
index 0000000..5184952
--- /dev/null
+++ b/app/src/main/java/com/hjq/http/demo/http/json/LongTypeAdapter.java
@@ -0,0 +1,23 @@
+package com.hjq.http.demo.http.json;
+
+import com.google.gson.stream.JsonReader;
+
+import java.io.IOException;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2020/05/05
+ * desc : long / Long 解析适配器 {@link com.google.gson.internal.bind.TypeAdapters#LONG}
+ */
+public class LongTypeAdapter extends DoubleTypeAdapter {
+
+ @Override
+ public Number read(JsonReader in) throws IOException {
+ Number number = super.read(in);
+ if (number != null) {
+ return number.longValue();
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hjq/http/demo/http/json/StringTypeAdapter.java b/app/src/main/java/com/hjq/http/demo/http/json/StringTypeAdapter.java
new file mode 100644
index 0000000..7d90a27
--- /dev/null
+++ b/app/src/main/java/com/hjq/http/demo/http/json/StringTypeAdapter.java
@@ -0,0 +1,36 @@
+package com.hjq.http.demo.http.json;
+
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.IOException;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2020/05/05
+ * desc : String 解析适配器 {@link com.google.gson.internal.bind.TypeAdapters#STRING}
+ */
+public class StringTypeAdapter extends TypeAdapter {
+
+ @Override
+ public String read(JsonReader in) throws IOException {
+ switch (in.peek()) {
+ case NULL:
+ in.nextNull();
+ return null;
+ case BOOLEAN:
+ // 对于布尔类型比较特殊,需要做针对性处理
+ return Boolean.toString(in.nextBoolean());
+ default:
+ // 其他类型的数据直接按照字符串来处理
+ return in.nextString();
+ }
+ }
+
+ @Override
+ public void write(JsonWriter out, String value) throws IOException {
+ out.value(value);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hjq/http/demo/http/model/HttpData.java b/app/src/main/java/com/hjq/http/demo/http/model/HttpData.java
index bfc3316..a9a3c50 100644
--- a/app/src/main/java/com/hjq/http/demo/http/model/HttpData.java
+++ b/app/src/main/java/com/hjq/http/demo/http/model/HttpData.java
@@ -1,11 +1,14 @@
package com.hjq.http.demo.http.model;
+import androidx.annotation.Keep;
+
/**
* author : Android 轮子哥
* github : https://github.com/getActivity/EasyHttp
* time : 2019/05/19
* desc : 统一接口数据结构
*/
+@Keep
public class HttpData {
/** 返回码 */
diff --git a/app/src/main/java/com/hjq/http/demo/http/model/RequestHandler.java b/app/src/main/java/com/hjq/http/demo/http/model/RequestHandler.java
index 9040021..6e547af 100644
--- a/app/src/main/java/com/hjq/http/demo/http/model/RequestHandler.java
+++ b/app/src/main/java/com/hjq/http/demo/http/model/RequestHandler.java
@@ -1,16 +1,28 @@
package com.hjq.http.demo.http.model;
+import android.app.Application;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
+import androidx.lifecycle.LifecycleOwner;
+
import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
+import com.google.gson.internal.bind.TypeAdapters;
import com.hjq.http.EasyLog;
import com.hjq.http.config.IRequestHandler;
import com.hjq.http.demo.R;
+import com.hjq.http.demo.http.json.BooleanTypeAdapter;
+import com.hjq.http.demo.http.json.DoubleTypeAdapter;
+import com.hjq.http.demo.http.json.FloatTypeAdapter;
+import com.hjq.http.demo.http.json.IntegerTypeAdapter;
+import com.hjq.http.demo.http.json.ListTypeAdapter;
+import com.hjq.http.demo.http.json.LongTypeAdapter;
+import com.hjq.http.demo.http.json.StringTypeAdapter;
import com.hjq.http.exception.CancelException;
import com.hjq.http.exception.DataException;
import com.hjq.http.exception.HttpException;
@@ -29,6 +41,7 @@
import java.lang.reflect.Type;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
+import java.util.List;
import okhttp3.Response;
import okhttp3.ResponseBody;
@@ -41,13 +54,24 @@
*/
public final class RequestHandler implements IRequestHandler {
- private static final Gson GSON = new Gson();
+ private final Application mApplication;
+
+ private Gson mGson;
+
+ public RequestHandler(Application application) {
+ mApplication = application;
+ }
@Override
- public Object requestSucceed(Context context, Response response, Type type) throws Exception {
+ public Object requestSucceed(LifecycleOwner lifecycle, Response response, Type type) throws Exception {
+
+ if (Response.class.equals(type)) {
+ return response;
+ }
+
if (!response.isSuccessful()) {
// 返回响应异常
- throw new ResponseException(context.getString(R.string.http_server_error), response);
+ throw new ResponseException(mApplication.getString(R.string.http_server_error), response);
}
ResponseBody body = response.body();
@@ -55,10 +79,6 @@ public Object requestSucceed(Context context, Response response, Type type) thro
return null;
}
- if (Response.class.equals(type)) {
- return response;
- }
-
if (Bitmap.class.equals(type)) {
// 如果这是一个 Bitmap 对象
return BitmapFactory.decodeStream(body.byteStream());
@@ -69,7 +89,7 @@ public Object requestSucceed(Context context, Response response, Type type) thro
text = body.string();
} catch (IOException e) {
// 返回结果读取异常
- throw new DataException(context.getString(R.string.http_data_explain_error), e);
+ throw new DataException(mApplication.getString(R.string.http_data_explain_error), e);
}
// 打印这个 Json
@@ -84,22 +104,34 @@ public Object requestSucceed(Context context, Response response, Type type) thro
// 如果这是一个 JSONObject 对象
result = new JSONObject(text);
} catch (JSONException e) {
- throw new DataException(context.getString(R.string.http_data_explain_error), e);
+ throw new DataException(mApplication.getString(R.string.http_data_explain_error), e);
}
} else if (JSONArray.class.equals(type)) {
try {
// 如果这是一个 JSONArray 对象
result = new JSONArray(text);
}catch (JSONException e) {
- throw new DataException(context.getString(R.string.http_data_explain_error), e);
+ throw new DataException(mApplication.getString(R.string.http_data_explain_error), e);
}
} else {
try {
- result = GSON.fromJson(text, type);
+ if (mGson == null) {
+ // Json 容错处理
+ mGson = new GsonBuilder()
+ .registerTypeAdapterFactory(TypeAdapters.newFactory(String.class, new StringTypeAdapter()))
+ .registerTypeAdapterFactory(TypeAdapters.newFactory(boolean.class, Boolean.class, new BooleanTypeAdapter()))
+ .registerTypeAdapterFactory(TypeAdapters.newFactory(int.class, Integer.class, new IntegerTypeAdapter()))
+ .registerTypeAdapterFactory(TypeAdapters.newFactory(long.class, Long.class, new LongTypeAdapter()))
+ .registerTypeAdapterFactory(TypeAdapters.newFactory(float.class, Float.class, new FloatTypeAdapter()))
+ .registerTypeAdapterFactory(TypeAdapters.newFactory(double.class, Double.class, new DoubleTypeAdapter()))
+ .registerTypeHierarchyAdapter(List.class, new ListTypeAdapter())
+ .create();
+ }
+ result = mGson.fromJson(text, type);
} catch (JsonSyntaxException e) {
// 返回结果读取异常
- throw new DataException(context.getString(R.string.http_data_explain_error), e);
+ throw new DataException(mApplication.getString(R.string.http_data_explain_error), e);
}
if (result instanceof HttpData) {
@@ -109,7 +141,7 @@ public Object requestSucceed(Context context, Response response, Type type) thro
return result;
} else if (model.getCode() == 1001) {
// 代表登录失效,需要重新登录
- throw new TokenException(context.getString(R.string.http_account_error));
+ throw new TokenException(mApplication.getString(R.string.http_account_error));
} else {
// 代表执行失败
throw new ResultException(model.getMessage(), model);
@@ -120,7 +152,7 @@ public Object requestSucceed(Context context, Response response, Type type) thro
}
@Override
- public Exception requestFail(Context context, Exception e) {
+ public Exception requestFail(LifecycleOwner lifecycle, Exception e) {
// 判断这个异常是不是自己抛的
if (e instanceof HttpException) {
if (e instanceof TokenException) {
@@ -129,16 +161,16 @@ public Exception requestFail(Context context, Exception e) {
}
} else {
if (e instanceof SocketTimeoutException) {
- e = new TimeoutException(context.getString(R.string.http_server_out_time), e);
+ e = new TimeoutException(mApplication.getString(R.string.http_server_out_time), e);
} else if (e instanceof UnknownHostException) {
- NetworkInfo info = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
+ NetworkInfo info = ((ConnectivityManager) mApplication.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
// 判断网络是否连接
if (info != null && info.isConnected()) {
// 有连接就是服务器的问题
- e = new ServerException(context.getString(R.string.http_server_error), e);
+ e = new ServerException(mApplication.getString(R.string.http_server_error), e);
} else {
// 没有连接就是网络异常
- e = new NetworkException(context.getString(R.string.http_network_error), e);
+ e = new NetworkException(mApplication.getString(R.string.http_network_error), e);
}
} else if (e instanceof IOException) {
//e = new CancelException(context.getString(R.string.http_request_cancel), e);
diff --git a/app/src/main/java/com/hjq/http/demo/http/request/SearchAuthorApi.java b/app/src/main/java/com/hjq/http/demo/http/request/SearchAuthorApi.java
index 470ebe5..56901f3 100644
--- a/app/src/main/java/com/hjq/http/demo/http/request/SearchAuthorApi.java
+++ b/app/src/main/java/com/hjq/http/demo/http/request/SearchAuthorApi.java
@@ -1,5 +1,7 @@
package com.hjq.http.demo.http.request;
+import androidx.annotation.Keep;
+
import com.hjq.http.config.IRequestApi;
/**
@@ -8,7 +10,8 @@
* time : 2019/11/18
* desc : 按照作者昵称搜索文章
*/
-public class SearchAuthorApi implements IRequestApi {
+@Keep
+public final class SearchAuthorApi implements IRequestApi {
@Override
public String getApi() {
diff --git a/app/src/main/java/com/hjq/http/demo/http/request/SearchBlogsApi.java b/app/src/main/java/com/hjq/http/demo/http/request/SearchBlogsApi.java
index c5ac24a..e19ebf4 100644
--- a/app/src/main/java/com/hjq/http/demo/http/request/SearchBlogsApi.java
+++ b/app/src/main/java/com/hjq/http/demo/http/request/SearchBlogsApi.java
@@ -1,5 +1,7 @@
package com.hjq.http.demo.http.request;
+import androidx.annotation.Keep;
+
import com.hjq.http.annotation.HttpRename;
import com.hjq.http.config.IRequestApi;
@@ -9,7 +11,8 @@
* time : 2019/06/07
* desc : 搜索文章
*/
-public class SearchBlogsApi implements IRequestApi {
+@Keep
+public final class SearchBlogsApi implements IRequestApi {
@Override
public String getApi() {
diff --git a/app/src/main/java/com/hjq/http/demo/http/request/UpdateImageApi.java b/app/src/main/java/com/hjq/http/demo/http/request/UpdateImageApi.java
index f50af14..a01c62d 100644
--- a/app/src/main/java/com/hjq/http/demo/http/request/UpdateImageApi.java
+++ b/app/src/main/java/com/hjq/http/demo/http/request/UpdateImageApi.java
@@ -1,5 +1,7 @@
package com.hjq.http.demo.http.request;
+import androidx.annotation.Keep;
+
import com.hjq.http.config.IRequestApi;
import com.hjq.http.config.IRequestHost;
@@ -11,7 +13,8 @@
* time : 2019/12/14
* desc : 上传图片
*/
-public class UpdateImageApi implements IRequestHost, IRequestApi {
+@Keep
+public final class UpdateImageApi implements IRequestHost, IRequestApi {
@Override
public String getHost() {
diff --git a/app/src/main/java/com/hjq/http/demo/http/response/SearchBean.java b/app/src/main/java/com/hjq/http/demo/http/response/SearchBean.java
index a61da02..7ec3669 100644
--- a/app/src/main/java/com/hjq/http/demo/http/response/SearchBean.java
+++ b/app/src/main/java/com/hjq/http/demo/http/response/SearchBean.java
@@ -1,5 +1,7 @@
package com.hjq.http.demo.http.response;
+import androidx.annotation.Keep;
+
import java.util.List;
/**
@@ -8,7 +10,8 @@
* time : 2019/11/18
* desc : 搜索结果
*/
-public class SearchBean {
+@Keep
+public final class SearchBean {
/**
* curPage : 1
@@ -60,7 +63,7 @@ public static class DatasBean {
/**
* apkLink :
* audit : 1
- * author : 鸿洋
+ * author : 鸿洋
* chapterId : 249
* chapterName : 干货资源
* collect : false
diff --git a/gradle.properties b/gradle.properties
index 3530b01..07610dd 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -15,3 +15,8 @@ org.gradle.jvmargs=-Xmx1536m
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
+
+# 表示使用 AndroidX
+android.useAndroidX = true
+# 表示将第三方库迁移到 AndroidX
+android.enableJetifier = true
\ No newline at end of file
diff --git a/library/build.gradle b/library/build.gradle
index 9e60ecb..4d51484 100644
--- a/library/build.gradle
+++ b/library/build.gradle
@@ -13,21 +13,21 @@ android {
defaultConfig {
minSdkVersion 14
targetSdkVersion 26
- versionCode 60
- versionName "6.0"
+ versionCode 65
+ versionName "6.5"
}
}
dependencies {
implementation 'com.squareup.okhttp3:okhttp:3.12.10'
- implementation 'com.android.support:support-fragment:26.0.0'
+ implementation 'androidx.lifecycle:lifecycle-common:2.1.0'
}
publish {
userOrg = 'getactivity'
groupId = 'com.hjq'
artifactId = 'http'
- version = '6.0'
+ version = '6.5'
description = 'Easy-to-use network request framework'
website = "https://github.com/getActivity/EasyHttp"
}
diff --git a/library/src/main/java/com/hjq/http/EasyConfig.java b/library/src/main/java/com/hjq/http/EasyConfig.java
index 8534677..3ddf453 100644
--- a/library/src/main/java/com/hjq/http/EasyConfig.java
+++ b/library/src/main/java/com/hjq/http/EasyConfig.java
@@ -1,7 +1,9 @@
package com.hjq.http;
+import com.hjq.http.config.ILogStrategy;
import com.hjq.http.config.IRequestHandler;
import com.hjq.http.config.IRequestServer;
+import com.hjq.http.config.LogStrategy;
import com.hjq.http.config.RequestServer;
import java.net.MalformedURLException;
@@ -36,24 +38,44 @@ public static EasyConfig with(OkHttpClient client) {
return new EasyConfig(client);
}
- /** 服务器配置 */
+ /**
+ * 服务器配置
+ */
private IRequestServer mServer;
- /** 请求拦截器 */
+ /**
+ * 请求拦截器
+ */
private IRequestHandler mHandler;
-
- /** OkHttp 客户端 */
+ /**
+ * 日志打印策略
+ */
+ private ILogStrategy mLogStrategy;
+
+ /**
+ * OkHttp 客户端
+ */
private OkHttpClient mClient;
- /** 通用参数 */
+ /**
+ * 通用参数
+ */
private HashMap mParams;
- /** 通用请求头 */
+ /**
+ * 通用请求头
+ */
private HashMap mHeaders;
- /** 日志开关 */
+ /**
+ * 日志开关
+ */
private boolean mLogEnabled = true;
- /** 日志 TAG */
+ /**
+ * 日志 TAG
+ */
private String mLogTag = "EasyHttp";
- /** 重试次数 */
+ /**
+ * 重试次数
+ */
private int mRetryCount;
private EasyConfig(OkHttpClient client) {
@@ -101,6 +123,11 @@ public EasyConfig addParam(String key, String value) {
return this;
}
+ public EasyConfig setLogStrategy(ILogStrategy strategy) {
+ mLogStrategy = strategy;
+ return this;
+ }
+
public EasyConfig setLogEnabled(boolean enabled) {
mLogEnabled = enabled;
return this;
@@ -139,8 +166,12 @@ public HashMap getHeaders() {
return mHeaders;
}
+ public ILogStrategy getLogStrategy() {
+ return mLogStrategy;
+ }
+
public boolean isLogEnabled() {
- return mLogEnabled;
+ return mLogEnabled && mLogStrategy != null;
}
public String getLogTag() {
@@ -168,6 +199,9 @@ public void into() {
if (mHandler == null) {
throw new IllegalArgumentException("The object being processed by the request cannot be empty");
}
+ if (mLogStrategy == null) {
+ mLogStrategy = new LogStrategy();
+ }
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 58c6e08..b0ae4ee 100644
--- a/library/src/main/java/com/hjq/http/EasyHttp.java
+++ b/library/src/main/java/com/hjq/http/EasyHttp.java
@@ -1,10 +1,6 @@
package com.hjq.http;
-import android.app.Activity;
-import android.app.Dialog;
-import android.app.Fragment;
-import android.content.Context;
-import android.view.View;
+import androidx.lifecycle.LifecycleOwner;
import com.hjq.http.request.DownloadRequest;
import com.hjq.http.request.GetRequest;
@@ -24,82 +20,29 @@ public final class EasyHttp {
/**
* Get 请求
*/
- public static GetRequest get(Activity activity) {
- return new GetRequest(activity).tag(activity);
- }
-
- public static GetRequest get(Fragment fragment) {
- return new GetRequest(fragment.getActivity()).tag(fragment);
- }
-
- public static GetRequest get(android.support.v4.app.Fragment fragment) {
- return new GetRequest(fragment.getContext()).tag(fragment);
- }
-
- public static GetRequest get(Dialog dialog) {
- return new GetRequest(dialog.getContext()).tag(dialog);
- }
-
- public static GetRequest get(View view) {
- return new GetRequest(view.getContext()).tag(view);
- }
-
- public static GetRequest get(Context context) {
- return new GetRequest(context).tag(context);
+ public static GetRequest get(LifecycleOwner lifecycleOwner) {
+ return new GetRequest(lifecycleOwner).tag(lifecycleOwner);
}
/**
* Post 请求
*/
- public static PostRequest post(Activity activity) {
- return new PostRequest(activity).tag(activity);
- }
-
- public static PostRequest post(Fragment fragment) {
- return new PostRequest(fragment.getActivity()).tag(fragment);
- }
-
- public static PostRequest post(android.support.v4.app.Fragment fragment) {
- return new PostRequest(fragment.getContext()).tag(fragment);
- }
-
- public static PostRequest post(Dialog dialog) {
- return new PostRequest(dialog.getContext()).tag(dialog);
- }
-
- public static PostRequest post(View view) {
- return new PostRequest(view.getContext()).tag(view);
- }
-
- public static PostRequest post(Context context) {
- return new PostRequest(context).tag(context);
+ public static PostRequest post(LifecycleOwner lifecycleOwner) {
+ return new PostRequest(lifecycleOwner).tag(lifecycleOwner);
}
/**
* 下载请求
*/
- public static DownloadRequest download(Activity activity) {
- return new DownloadRequest(activity).tag(activity);
- }
-
- public static DownloadRequest download(Fragment fragment) {
- return new DownloadRequest(fragment.getActivity()).tag(fragment);
+ public static DownloadRequest download(LifecycleOwner lifecycleOwner) {
+ return new DownloadRequest(lifecycleOwner).tag(lifecycleOwner);
}
- public static DownloadRequest download(android.support.v4.app.Fragment fragment) {
- return new DownloadRequest(fragment.getContext()).tag(fragment);
- }
-
- public static DownloadRequest download(Dialog dialog) {
- return new DownloadRequest(dialog.getContext()).tag(dialog);
- }
-
- public static DownloadRequest download(View view) {
- return new DownloadRequest(view.getContext()).tag(view);
- }
-
- public static DownloadRequest download(Context context) {
- return new DownloadRequest(context).tag(context);
+ /**
+ * 取消请求
+ */
+ public static void cancel(LifecycleOwner lifecycleOwner) {
+ cancel(lifecycleOwner.getLifecycle().toString());
}
/**
diff --git a/library/src/main/java/com/hjq/http/EasyLog.java b/library/src/main/java/com/hjq/http/EasyLog.java
index 6d7e35f..e611a4a 100644
--- a/library/src/main/java/com/hjq/http/EasyLog.java
+++ b/library/src/main/java/com/hjq/http/EasyLog.java
@@ -1,147 +1,53 @@
-package com.hjq.http;
-
-import android.text.TextUtils;
-import android.util.Log;
-
-/**
- * author : Android 轮子哥
- * github : https://github.com/getActivity/EasyHttp
- * time : 2019/05/19
- * desc : 网络请求打印类
- */
-public final class EasyLog {
-
- /**
- * 日志开关
- */
- public static boolean isEnable() {
- return EasyConfig.getInstance().isLogEnabled();
- }
-
- /**
- * 打印日志
- */
- public static void print(String log) {
- if (isEnable()) {
- Log.d(EasyConfig.getInstance().getLogTag(), log != null ? log : "null");
- }
- }
-
- /**
- * 打印异常
- */
- public static void print(Throwable throwable) {
- if (EasyConfig.getInstance().isLogEnabled()) {
- Log.e(EasyConfig.getInstance().getLogTag(), throwable.getMessage(), throwable);
- }
- }
-
- /**
- * 打印键值对
- */
- public static void print(String key, String value) {
- print(key + " = " + value);
- }
-
- /**
- * 答应分割线
- */
- public static void print() {
- print("--------------------");
- }
-
- /**
- * 打印 Json
- */
- public static void json(String json) {
- if (isEnable()) {
- String text = stringToJSON(json);
- if (!TextUtils.isEmpty(text)) {
- // 打印 Json 数据最好换一行再打印会好看一点
- text = " \n" + text;
-
- int segmentSize = 3 * 1024;
- long length = text.length();
- if (length <= segmentSize ) {
- // 长度小于等于限制直接打印
- print(text);
- }else {
- // 循环分段打印日志
- while (text.length() > segmentSize ) {
- String logContent = text.substring(0, segmentSize );
- text = text.replace(logContent, "");
- print(logContent);
- }
-
- // 打印剩余日志
- print(text);
- }
- }
- }
- }
-
- /**
- * 将字符串格式化成JSON的格式
- */
- private static String stringToJSON(String strJson) {
- if (strJson == null) {
- return null;
- }
- // 计数tab的个数
- int tabNum = 0;
- StringBuilder builder = new StringBuilder();
- int length = strJson.length();
-
- char last = 0;
- for (int i = 0; i < length; i++) {
- char c = strJson.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 == ',') {
- builder.append(c).append("\n")
- .append(getSpaceOrTab(tabNum));
- } else if (c == ':') {
- if (i > 0 && strJson.charAt(i - 1) == '"') {
- builder.append(c).append(" ");
- } else {
- builder.append(c);
- }
- } else if (c == '[') {
- tabNum++;
- char next = strJson.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();
- }
-
- private static String getSpaceOrTab(int tabNum) {
- StringBuffer sb = new StringBuffer();
- for (int i = 0; i < tabNum; i++) {
- sb.append('\t');
- }
- return sb.toString();
- }
+package com.hjq.http;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2020/05/10
+ * desc : 日志打印类
+ */
+public final class EasyLog {
+
+ /**
+ * 打印分割线
+ */
+ public static void print() {
+ print("--------------------");
+ }
+
+ /**
+ * 打印日志
+ */
+ public static void print(String log) {
+ if (EasyConfig.getInstance().isLogEnabled()) {
+ EasyConfig.getInstance().getLogStrategy().print(log);
+ }
+ }
+
+ /**
+ * 打印 Json
+ */
+ public static void json(String json) {
+ if (EasyConfig.getInstance().isLogEnabled()) {
+ EasyConfig.getInstance().getLogStrategy().json(json);
+ }
+ }
+
+ /**
+ * 打印异常
+ */
+ public static void print(Throwable throwable) {
+ if (EasyConfig.getInstance().isLogEnabled()) {
+ EasyConfig.getInstance().getLogStrategy().print(throwable);
+ }
+ }
+
+ /**
+ * 打印键值对
+ */
+ public static void print(String key, String value) {
+ if (EasyConfig.getInstance().isLogEnabled()) {
+ EasyConfig.getInstance().getLogStrategy().print(key, value);
+ }
+ }
}
\ 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 8848067..d3c93df 100644
--- a/library/src/main/java/com/hjq/http/callback/BaseCallback.java
+++ b/library/src/main/java/com/hjq/http/callback/BaseCallback.java
@@ -1,7 +1,13 @@
package com.hjq.http.callback;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+
import com.hjq.http.EasyConfig;
+import com.hjq.http.EasyLog;
+import com.hjq.http.EasyUtils;
import com.hjq.http.model.CallProxy;
+import com.hjq.http.model.HttpLifecycle;
import java.io.IOException;
import java.net.SocketTimeoutException;
@@ -18,13 +24,23 @@
*/
public abstract class BaseCallback implements Callback {
- /** 请求任务对象 */
+ /**
+ * 请求任务对象
+ */
private CallProxy mCall;
- /** 当前重试次数 */
+ /**
+ * 当前重试次数
+ */
private int mRetryCount;
+ /**
+ * 生命周期管理
+ */
+ private LifecycleOwner mLifecycleOwner;
- BaseCallback(CallProxy call) {
+ BaseCallback(LifecycleOwner lifecycleOwner, CallProxy call) {
mCall = call;
+ mLifecycleOwner = lifecycleOwner;
+ HttpLifecycle.with(lifecycleOwner);
}
CallProxy getCall() {
@@ -48,15 +64,31 @@ public void onResponse(Call call, Response response) {
public void onFailure(Call call, IOException e) {
// 服务器请求超时重试
if (e instanceof SocketTimeoutException && mRetryCount < EasyConfig.getInstance().getRetryCount()) {
- mRetryCount++;
- Call newCall = call.clone();
- mCall.setCall(newCall);
- newCall.enqueue(this);
+ // 设置延迟 1 秒后重试该请求
+ EasyUtils.postDelayed(() -> {
+ // 前提是宿主还没有被销毁
+ if (isLifecycleActive()) {
+ mRetryCount++;
+ Call newCall = call.clone();
+ mCall.setCall(newCall);
+ newCall.enqueue(BaseCallback.this);
+ EasyLog.print("请求超时,正在延迟重试,重试次数:" + mRetryCount + "/" + EasyConfig.getInstance().getRetryCount());
+ } else {
+ EasyLog.print("宿主已被销毁,无法对请求进行重试");
+ }
+ }, 1000);
return;
}
onFailure(e);
}
+ /**
+ * 判断宿主是否处于活动状态
+ */
+ protected boolean isLifecycleActive() {
+ return mLifecycleOwner.getLifecycle().getCurrentState() != Lifecycle.State.DESTROYED;
+ }
+
protected abstract void onResponse(Response response) throws Exception;
protected abstract void onFailure(Exception e);
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 fd09c52..6b9feb5 100644
--- a/library/src/main/java/com/hjq/http/callback/DownloadCallback.java
+++ b/library/src/main/java/com/hjq/http/callback/DownloadCallback.java
@@ -2,6 +2,8 @@
import android.text.TextUtils;
+import androidx.lifecycle.LifecycleOwner;
+
import com.hjq.http.EasyLog;
import com.hjq.http.EasyUtils;
import com.hjq.http.exception.MD5Exception;
@@ -25,26 +27,39 @@
*/
public final class DownloadCallback extends BaseCallback {
- /** 文件 MD5 正则表达式 */
+ /**
+ * 文件 MD5 正则表达式
+ */
private static final String FILE_MD5_REGEX = "^[\\w]{32}$";
- /** 下载任务 */
+ /**
+ * 下载任务
+ */
private DownloadInfo mDownloadInfo;
- /** 保存的文件 */
+
+ /**
+ * 保存的文件
+ */
private File mFile;
- /** 校验的 MD5 */
+
+ /**
+ * 校验的 MD5
+ */
private String mMD5;
- /** 下载监听回调 */
+
+ /**
+ * 下载监听回调
+ */
private OnDownloadListener mListener;
- public DownloadCallback(CallProxy call, File file, String md5, OnDownloadListener listener) {
- super(call);
+ public DownloadCallback(LifecycleOwner lifecycleOwner, CallProxy call, File file, String md5, OnDownloadListener listener) {
+ super(lifecycleOwner, call);
mDownloadInfo = new DownloadInfo(file);
mFile = file;
mMD5 = md5;
mListener = listener;
- EasyUtils.runOnUiThread(mListener != null, () -> mListener.onStart(getCall()));
+ EasyUtils.runOnUiThread(mListener != null && isLifecycleActive(), () -> mListener.onStart(getCall()));
}
@Override
@@ -60,10 +75,9 @@ protected void onResponse(Response response) throws Exception {
}
EasyUtils.createFolder(mFile.getParentFile());
-
ResponseBody body = response.body();
if (body == null) {
- EasyUtils.runOnUiThread(mListener != null, () -> {
+ EasyUtils.runOnUiThread(mListener != null && isLifecycleActive(), () -> {
mListener.onError(mDownloadInfo, new NullBodyException("The response body is empty"));
mListener.onEnd(getCall());
});
@@ -73,7 +87,7 @@ protected void onResponse(Response response) throws Exception {
mDownloadInfo.setTotalLength(body.contentLength());
// 如果这个文件已经下载过,并且经过校验 MD5 是同一个文件的话,就直接回调下载成功监听
if (!TextUtils.isEmpty(mMD5) && mFile.exists() && mFile.isFile() && mMD5.equalsIgnoreCase(EasyUtils.getFileMD5(mFile))) {
- EasyUtils.runOnUiThread(mListener != null, () -> {
+ EasyUtils.runOnUiThread(mListener != null && isLifecycleActive(), () -> {
mDownloadInfo.setDownloadLength(mDownloadInfo.getTotalLength());
mListener.onComplete(mDownloadInfo);
mListener.onEnd(getCall());
@@ -90,7 +104,7 @@ protected void onResponse(Response response) throws Exception {
downloadSize += readLength;
outputStream.write(bytes, 0, readLength);
mDownloadInfo.setDownloadLength(downloadSize);
- EasyUtils.runOnUiThread(mListener != null, () -> mListener.onProgress(mDownloadInfo));
+ EasyUtils.runOnUiThread(mListener != null && isLifecycleActive(), () -> mListener.onProgress(mDownloadInfo));
EasyLog.print(mFile.getPath() + " 正在下载" +
",文件总字节:" + mDownloadInfo.getTotalLength() +
",已下载字节:" + mDownloadInfo.getDownloadLength() +
@@ -98,7 +112,7 @@ protected void onResponse(Response response) throws Exception {
}
outputStream.flush();
- EasyUtils.runOnUiThread(mListener != null, () -> {
+ EasyUtils.runOnUiThread(mListener != null && isLifecycleActive(), () -> {
String md5 = EasyUtils.getFileMD5(mDownloadInfo.getFile());
if (!TextUtils.isEmpty(mMD5) && !mMD5.equalsIgnoreCase(md5)) {
onFailure(new MD5Exception("MD5 verify failure", md5));
@@ -115,7 +129,7 @@ protected void onResponse(Response response) throws Exception {
@Override
protected void onFailure(final Exception e) {
EasyLog.print(e);
- EasyUtils.runOnUiThread(mListener != null, () -> {
+ EasyUtils.runOnUiThread(mListener != null && isLifecycleActive(), () -> {
mListener.onError(mDownloadInfo, e);
mListener.onEnd(getCall());
});
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 f695e2f..679c44f 100644
--- a/library/src/main/java/com/hjq/http/callback/NormalCallback.java
+++ b/library/src/main/java/com/hjq/http/callback/NormalCallback.java
@@ -1,6 +1,6 @@
package com.hjq.http.callback;
-import android.content.Context;
+import androidx.lifecycle.LifecycleOwner;
import com.hjq.http.EasyConfig;
import com.hjq.http.EasyLog;
@@ -21,17 +21,17 @@
*/
public final class NormalCallback extends BaseCallback {
- private Context mContext;
+ private LifecycleOwner mLifecycle;
private OnHttpListener mListener;
private long mRequestTime;
- public NormalCallback(Context context, CallProxy call, OnHttpListener listener) {
- super(call);
- mContext = context;
+ public NormalCallback(LifecycleOwner lifecycleOwner, CallProxy call, OnHttpListener listener) {
+ super(lifecycleOwner, call);
+ mLifecycle = lifecycleOwner;
mListener = listener;
mRequestTime = System.currentTimeMillis();
- EasyUtils.runOnUiThread(mListener != null, () -> mListener.onStart(call));
+ EasyUtils.runOnUiThread(mListener != null && isLifecycleActive(), () -> mListener.onStart(call));
}
@SuppressWarnings("unchecked")
@@ -48,8 +48,8 @@ protected void onResponse(Response response) throws Exception {
}
EasyLog.print("RequestTime:" + (System.currentTimeMillis() - mRequestTime) + " ms");
- final Object result = EasyConfig.getInstance().getHandler().requestSucceed(mContext, response, type);
- EasyUtils.runOnUiThread(mListener != null, () -> {
+ final Object result = EasyConfig.getInstance().getHandler().requestSucceed(mLifecycle, response, type);
+ EasyUtils.runOnUiThread(mListener != null && isLifecycleActive(), () -> {
mListener.onSucceed(result);
mListener.onEnd(getCall());
});
@@ -58,8 +58,8 @@ protected void onResponse(Response response) throws Exception {
@Override
protected void onFailure(Exception e) {
EasyLog.print(e);
- final Exception exception = EasyConfig.getInstance().getHandler().requestFail(mContext, e);
- EasyUtils.runOnUiThread(mListener != null, () -> {
+ final Exception exception = EasyConfig.getInstance().getHandler().requestFail(mLifecycle, e);
+ EasyUtils.runOnUiThread(mListener != null && isLifecycleActive(), () -> {
mListener.onFail(exception);
mListener.onEnd(getCall());
});
diff --git a/library/src/main/java/com/hjq/http/config/ILogStrategy.java b/library/src/main/java/com/hjq/http/config/ILogStrategy.java
new file mode 100644
index 0000000..f31c3bc
--- /dev/null
+++ b/library/src/main/java/com/hjq/http/config/ILogStrategy.java
@@ -0,0 +1,102 @@
+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(Throwable throwable);
+
+ /**
+ * 打印键值对
+ */
+ void print(String key, String value);
+
+ /**
+ * 将字符串格式化成JSON的格式
+ */
+ static String stringToJSON(String strJson) {
+ if (strJson == null) {
+ return null;
+ }
+ // 计数tab的个数
+ int tabNum = 0;
+ StringBuilder builder = new StringBuilder();
+ int length = strJson.length();
+
+ char last = 0;
+ for (int i = 0; i < length; i++) {
+ char c = strJson.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 == ',') {
+ builder.append(c).append("\n")
+ .append(getSpaceOrTab(tabNum));
+ } else if (c == ':') {
+ if (i > 0 && strJson.charAt(i - 1) == '"') {
+ builder.append(c).append(" ");
+ } else {
+ builder.append(c);
+ }
+ } else if (c == '[') {
+ tabNum++;
+ char next = strJson.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) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < tabNum; i++) {
+ sb.append('\t');
+ }
+ return sb.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 81b5451..9d851ba 100644
--- a/library/src/main/java/com/hjq/http/config/IRequestApi.java
+++ b/library/src/main/java/com/hjq/http/config/IRequestApi.java
@@ -11,5 +11,5 @@ public interface IRequestApi {
/**
* 请求接口
*/
- String getApi();
+ String getApi();
}
\ 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 ef74b89..57c8ac4 100644
--- a/library/src/main/java/com/hjq/http/config/IRequestHandler.java
+++ b/library/src/main/java/com/hjq/http/config/IRequestHandler.java
@@ -1,12 +1,9 @@
package com.hjq.http.config;
-import android.content.Context;
-
-import com.hjq.http.listener.OnHttpListener;
+import androidx.lifecycle.LifecycleOwner;
import java.lang.reflect.Type;
-import okhttp3.Call;
import okhttp3.Response;
/**
@@ -20,19 +17,20 @@ public interface IRequestHandler {
/**
* 请求成功时回调
*
- * @param context 上下文对象
- * @param response 响应对象
- * @param type 解析类型
- * @return 返回结果
- * @throws Exception 回调失败方法
+ * @param lifecycle 有生命周期的对象(例如 Activity、Fragment)
+ * @param response 响应对象
+ * @param type 解析类型
+ * @return 返回结果
+ * @throws Exception 回调失败方法
*/
- Object requestSucceed(Context context, Response response, Type type) throws Exception;
+ Object requestSucceed(LifecycleOwner lifecycle, Response response, Type type) throws Exception;
/**
* 请求失败
- * @param context 上下文对象
- * @param e 错误对象
- * @return 错误对象
+ *
+ * @param lifecycle 有生命周期的对象(例如 Activity、Fragment)
+ * @param e 错误对象
+ * @return 错误对象
*/
- Exception requestFail(Context context, Exception e);
+ Exception requestFail(LifecycleOwner lifecycle, Exception e);
}
\ 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
new file mode 100644
index 0000000..1a0ed0c
--- /dev/null
+++ b/library/src/main/java/com/hjq/http/config/LogStrategy.java
@@ -0,0 +1,56 @@
+package com.hjq.http.config;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.hjq.http.EasyConfig;
+
+/**
+ * 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(EasyConfig.getInstance().getLogTag(), log != null ? log : "null");
+ }
+
+ @Override
+ public void json(String json) {
+ String text = ILogStrategy.stringToJSON(json);
+ if (!TextUtils.isEmpty(text)) {
+ // 打印 Json 数据最好换一行再打印会好看一点
+ text = " \n" + text;
+
+ int segmentSize = 3 * 1024;
+ long length = text.length();
+ if (length <= segmentSize) {
+ // 长度小于等于限制直接打印
+ print(text);
+ } else {
+ // 循环分段打印日志
+ while (text.length() > segmentSize) {
+ String logContent = text.substring(0, segmentSize);
+ text = text.replace(logContent, "");
+ print(logContent);
+ }
+
+ // 打印剩余日志
+ print(text);
+ }
+ }
+ }
+
+ @Override
+ public void print(Throwable throwable) {
+ Log.e(EasyConfig.getInstance().getLogTag(), throwable.getMessage(), throwable);
+ }
+
+ @Override
+ public void print(String key, String value) {
+ print(key + " = " + value);
+ }
+}
\ 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/RequestApi.java
index 0c0cc9b..dd95335 100644
--- a/library/src/main/java/com/hjq/http/config/RequestApi.java
+++ b/library/src/main/java/com/hjq/http/config/RequestApi.java
@@ -10,7 +10,9 @@
*/
public final class RequestApi implements IRequestApi {
- /** 接口地址 */
+ /**
+ * 接口地址
+ */
@HttpIgnore
private String mApi;
diff --git a/library/src/main/java/com/hjq/http/config/RequestServer.java b/library/src/main/java/com/hjq/http/config/RequestServer.java
index a953f48..3586e7e 100644
--- a/library/src/main/java/com/hjq/http/config/RequestServer.java
+++ b/library/src/main/java/com/hjq/http/config/RequestServer.java
@@ -10,10 +10,15 @@
*/
public final class RequestServer implements IRequestServer {
- /** 主机地址 */
+ /**
+ * 主机地址
+ */
@HttpIgnore
private String mHost;
- /** 接口路径 */
+
+ /**
+ * 接口路径
+ */
@HttpIgnore
private String mPath;
diff --git a/library/src/main/java/com/hjq/http/model/DownloadInfo.java b/library/src/main/java/com/hjq/http/model/DownloadInfo.java
index c75ca9c..5dc5969 100644
--- a/library/src/main/java/com/hjq/http/model/DownloadInfo.java
+++ b/library/src/main/java/com/hjq/http/model/DownloadInfo.java
@@ -10,11 +10,19 @@
*/
public final class DownloadInfo {
- /** 文件对象 */
+ /**
+ * 文件对象
+ */
private final File mFile;
- /** 总字节数 */
+
+ /**
+ * 总字节数
+ */
private long mTotalLength;
- /** 已下载字节数 */
+
+ /**
+ * 已下载字节数
+ */
private long mDownloadLength;
public DownloadInfo(File 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 9419edd..d50d131 100644
--- a/library/src/main/java/com/hjq/http/model/HttpHeaders.java
+++ b/library/src/main/java/com/hjq/http/model/HttpHeaders.java
@@ -24,6 +24,15 @@ public void put(String key, String value) {
}
}
+ public void remove(String key) {
+ if (key != null) {
+ if (mHeaders == EasyConfig.getInstance().getHeaders()) {
+ mHeaders = new HashMap<>(mHeaders);
+ }
+ mHeaders.remove(key);
+ }
+ }
+
public String get(String key) {
return mHeaders.get(key);
}
diff --git a/library/src/main/java/com/hjq/http/model/HttpLifecycle.java b/library/src/main/java/com/hjq/http/model/HttpLifecycle.java
new file mode 100644
index 0000000..25cf659
--- /dev/null
+++ b/library/src/main/java/com/hjq/http/model/HttpLifecycle.java
@@ -0,0 +1,29 @@
+package com.hjq.http.model;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleEventObserver;
+import androidx.lifecycle.LifecycleOwner;
+
+import com.hjq.http.EasyHttp;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/EasyHttp
+ * time : 2020/04/05
+ * desc : 请求生命周期控制
+ */
+public class HttpLifecycle implements LifecycleEventObserver {
+
+ public static void with(LifecycleOwner lifecycleOwner) {
+ lifecycleOwner.getLifecycle().addObserver(new HttpLifecycle());
+ }
+
+ @Override
+ public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
+ if (event == Lifecycle.Event.ON_DESTROY) {
+ source.getLifecycle().removeObserver(this);
+ EasyHttp.cancel(source);
+ }
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/model/HttpParams.java b/library/src/main/java/com/hjq/http/model/HttpParams.java
index 9fee552..e037933 100644
--- a/library/src/main/java/com/hjq/http/model/HttpParams.java
+++ b/library/src/main/java/com/hjq/http/model/HttpParams.java
@@ -3,10 +3,13 @@
import com.hjq.http.EasyConfig;
import java.io.File;
+import java.io.InputStream;
import java.util.HashMap;
-import java.util.Map;
+import java.util.List;
import java.util.Set;
+import okhttp3.RequestBody;
+
/**
* author : Android 轮子哥
* github : https://github.com/getActivity/EasyHttp
@@ -24,9 +27,35 @@ public void put(String key, Object value) {
mParams = new HashMap<>(mParams);
}
mParams.put(key, value);
- if (value instanceof File) {
+ if (value instanceof File || value instanceof InputStream || value instanceof RequestBody) {
mMultipart = true;
+ } else if (value instanceof List) {
+ List list = (List) value;
+ if (!list.isEmpty()) {
+ // 判断一下这个集合装载的类型是不是 File
+ boolean isFileList = true;
+ for (Object object : list) {
+ if (!(object instanceof File)) {
+ isFileList = false;
+ break;
+ }
+ }
+ // 如果是的话就设置成上传多个文件
+ if (isFileList) {
+ // 标记成有流参数
+ mMultipart = true;
+ }
+ }
+ }
+ }
+ }
+
+ public void remove(String key) {
+ if (key != null) {
+ if (mParams == EasyConfig.getInstance().getParams()) {
+ mParams = new HashMap<>(mParams);
}
+ mParams.remove(key);
}
}
@@ -47,7 +76,7 @@ public HashMap getParams() {
}
/**
- * 是否有文件上传
+ * 是否有流参数
*/
public boolean isMultipart() {
return mMultipart;
diff --git a/library/src/main/java/com/hjq/http/model/UpdateBody.java b/library/src/main/java/com/hjq/http/model/UpdateBody.java
index 065da2f..fc7ac33 100644
--- a/library/src/main/java/com/hjq/http/model/UpdateBody.java
+++ b/library/src/main/java/com/hjq/http/model/UpdateBody.java
@@ -3,9 +3,15 @@
import com.hjq.http.EasyLog;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
+import java.net.FileNameMap;
+import java.net.URLConnection;
+import java.net.URLEncoder;
import okhttp3.MediaType;
+import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okio.Buffer;
import okio.BufferedSink;
@@ -20,36 +26,111 @@
*/
public final class UpdateBody extends RequestBody {
- private File mFile;
+ public static final MediaType MEDIA_TYPE = MediaType.parse("application/octet-stream");
- public UpdateBody(File file) {
- mFile = file;
+ /**
+ * 上传源
+ */
+ private final Source mSource;
+ /**
+ * 内容类型
+ */
+ private final MediaType mMediaType;
+ /**
+ * 内容名称
+ */
+ private final String mName;
+ /**
+ * 内容大小
+ */
+ private final long mLength;
+
+ public UpdateBody(File file) throws FileNotFoundException {
+ this(Okio.source(file), guessMimeType(file.getName()), file.getPath(), file.length());
+ }
+
+ public UpdateBody(InputStream inputStream, String name) throws IOException {
+ this(Okio.source(inputStream), MEDIA_TYPE, name, inputStream.available());
+ }
+
+ public UpdateBody(Source source, MediaType type, String name, long length) {
+ mSource = source;
+ mMediaType = type;
+ mName = name;
+ mLength = length;
}
@Override
public MediaType contentType() {
- return MediaType.parse("application/octet-stream");
+ return mMediaType;
}
@Override
public long contentLength() {
- return mFile.length();
+ return mLength;
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
- Source source = Okio.source(mFile);
Buffer buffer = new Buffer();
long totalLength = contentLength();
long updateLength = 0;
long readCount;
- while ((readCount = source.read(buffer, 2048)) != -1) {
+ while ((readCount = mSource.read(buffer, 2048)) != -1) {
sink.write(buffer, readCount);
updateLength += readCount;
- EasyLog.print(mFile.getPath() + " 正在上传" +
+ EasyLog.print(mName + " 正在上传" +
",文件总字节:" + totalLength +
",已上传字节:" + updateLength);
}
}
+
+ /**
+ * 根据文件名获取 MIME 类型
+ */
+ public static MediaType guessMimeType(String fileName) {
+ FileNameMap fileNameMap = URLConnection.getFileNameMap();
+ // 解决文件名中含有#号异常的问题
+ fileName = fileName.replace("#", "");
+ String contentType = fileNameMap.getContentTypeFor(fileName);
+ if (contentType == null) {
+ return MEDIA_TYPE;
+ }
+ MediaType type = MediaType.parse(contentType);
+ if (type == null) {
+ type = MEDIA_TYPE;
+ }
+ return type;
+ }
+
+
+ /**
+ * 根据 File 对象创建一个流媒体
+ */
+ public static MultipartBody.Part createPart(String key, File file) {
+ if (file.exists() && file.isFile()) {
+ try {
+ // 文件名必须不能带中文,所以这里要编码
+ return MultipartBody.Part.createFormData(key, URLEncoder.encode(file.getName()), new UpdateBody(file));
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ } else {
+ EasyLog.print("文件不存在,将被忽略上传:" + file.getPath());
+ }
+ return null;
+ }
+
+ /**
+ * 根据 InputStream 对象创建一个流媒体
+ */
+ public static MultipartBody.Part createPart(String key, InputStream inputStream) {
+ try {
+ return MultipartBody.Part.createFormData(key, null, new UpdateBody(inputStream, key));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
}
\ No newline at end of file
diff --git a/library/src/main/java/com/hjq/http/request/BaseRequest.java b/library/src/main/java/com/hjq/http/request/BaseRequest.java
index af0296b..d700f4f 100644
--- a/library/src/main/java/com/hjq/http/request/BaseRequest.java
+++ b/library/src/main/java/com/hjq/http/request/BaseRequest.java
@@ -1,9 +1,8 @@
package com.hjq.http.request;
-import android.content.Context;
+import androidx.lifecycle.LifecycleOwner;
import com.hjq.http.EasyConfig;
-import com.hjq.http.EasyLog;
import com.hjq.http.annotation.HttpHeader;
import com.hjq.http.annotation.HttpIgnore;
import com.hjq.http.annotation.HttpRename;
@@ -34,25 +33,39 @@
@SuppressWarnings("unchecked")
public abstract class BaseRequest {
- /** Http 客户端 */
+ /**
+ * Http 客户端
+ */
private OkHttpClient mClient = EasyConfig.getInstance().getClient();
- /** 请求主机配置 */
+ /**
+ * 请求主机配置
+ */
private IRequestHost mRequestHost = EasyConfig.getInstance().getServer();
- /** 请求路径配置 */
+ /**
+ * 请求路径配置
+ */
private IRequestPath mRequestPath = EasyConfig.getInstance().getServer();
- /** 参数提交类型 */
+ /**
+ * 参数提交类型
+ */
private IRequestType mRequestType = EasyConfig.getInstance().getServer();
- /** 请求接口配置 */
+ /**
+ * 请求接口配置
+ */
private IRequestApi mRequestApi;
- /** 请求的上下文 */
- private Context mContext;
- /** 请求标记 */
+ /**
+ * 请求的上下文
+ */
+ private LifecycleOwner mLifecycleOwner;
+ /**
+ * 请求标记
+ */
private String mTag;
- public BaseRequest(Context context) {
- mContext = context;
+ public BaseRequest(LifecycleOwner lifecycle) {
+ mLifecycleOwner = lifecycle;
}
public T api(Class extends IRequestApi> api) {
@@ -146,20 +159,10 @@ public Call create() {
// 允许访问私有字段
field.setAccessible(true);
- // 如果这个字段需要忽略,则进行忽略
- if (field.isAnnotationPresent(HttpIgnore.class)) {
- continue;
- }
-
try {
// 获取字段的对象
- Object object = field.get(mRequestApi);
-
- // 前提是这个字段对象不能为空(基本数据类型有默认的值,而对象默认的值为 null)
- if (object == null) {
- continue;
- }
+ Object value = field.get(mRequestApi);
// 获取字段的名称
String key;
@@ -169,22 +172,34 @@ public Call create() {
key = field.getName();
}
+ // 如果这个字段需要忽略,则进行忽略
+ // 前提是这个字段值不能为空(基本数据类型有默认的值,而对象默认的值为 null)
+ if (field.isAnnotationPresent(HttpIgnore.class) || value == null) {
+ if (field.isAnnotationPresent(HttpHeader.class)) {
+ headers.remove(key);
+ } else {
+ params.remove(key);
+ }
+ // 遍历下一个字段
+ continue;
+ }
+
// 如果这是一个请求头参数
if (field.isAnnotationPresent(HttpHeader.class)) {
- if (object instanceof Map) {
- Map map = ((Map) object);
+ if (value instanceof Map) {
+ Map map = ((Map) value);
for (Object o : map.keySet()) {
if (o != null && map.get(o) != null) {
headers.put(o.toString(), map.get(o).toString());
}
}
} else {
- headers.put(key, object.toString());
+ headers.put(key, value.toString());
}
} else {
// 如果这个是一个普通的参数
- if (object instanceof Map) {
- Map map = ((Map) object);
+ if (value instanceof Map) {
+ Map map = ((Map) value);
switch (type) {
case FORM:
for (Object o : map.keySet()) {
@@ -200,12 +215,12 @@ public Call create() {
break;
}
} else {
- params.put(key, object);
+ params.put(key, value);
}
}
} catch (IllegalAccessException e) {
- EasyLog.print(e);
+ e.printStackTrace();
}
}
@@ -213,8 +228,8 @@ public Call create() {
return mClient.newCall(create(url, mTag, params, headers, type));
}
- protected Context getContext() {
- return mContext;
+ public LifecycleOwner getLifecycleOwner() {
+ return mLifecycleOwner;
}
protected abstract Request create(String url, String tag, HttpParams params, HttpHeaders headers, BodyType type);
diff --git a/library/src/main/java/com/hjq/http/request/DownloadRequest.java b/library/src/main/java/com/hjq/http/request/DownloadRequest.java
index cd0c349..77a9af0 100644
--- a/library/src/main/java/com/hjq/http/request/DownloadRequest.java
+++ b/library/src/main/java/com/hjq/http/request/DownloadRequest.java
@@ -1,6 +1,6 @@
package com.hjq.http.request;
-import android.content.Context;
+import androidx.lifecycle.LifecycleOwner;
import com.hjq.http.callback.DownloadCallback;
import com.hjq.http.config.RequestApi;
@@ -24,20 +24,30 @@
*/
public final class DownloadRequest extends BaseRequest {
- /** 下载方式 */
+ /**
+ * 下载方式
+ */
private HttpMethod mMethod = HttpMethod.GET;
- /** 保存的文件 */
+ /**
+ * 保存的文件
+ */
private File mFile;
- /** 校验的 MD5 */
+ /**
+ * 校验的 MD5
+ */
private String mMD5;
- /** 下载监听回调 */
+ /**
+ * 下载监听回调
+ */
private OnDownloadListener mListener;
- /** 下载请求对象 */
+ /**
+ * 下载请求对象
+ */
private CallProxy mCallProxy;
- public DownloadRequest(Context context) {
- super(context);
+ public DownloadRequest(LifecycleOwner lifecycleOwner) {
+ super(lifecycleOwner);
}
/**
@@ -91,10 +101,10 @@ protected Request create(String url, String tag, HttpParams params, HttpHeaders
switch (mMethod) {
case GET:
// 如果这个下载请求方式是 Get
- return new GetRequest(getContext()).create(url, tag, params, headers, type);
+ return new GetRequest(getLifecycleOwner()).create(url, tag, params, headers, type);
case POST:
// 如果这个下载请求方式是 Post
- return new PostRequest(getContext()).create(url, tag, params, headers, type);
+ return new PostRequest(getLifecycleOwner()).create(url, tag, params, headers, type);
default:
throw new IllegalStateException("are you ok?");
}
@@ -106,7 +116,7 @@ protected Request create(String url, String tag, HttpParams params, HttpHeaders
public DownloadRequest start() {
mCallProxy = new CallProxy(create());
/** 下载回调对象 */
- mCallProxy.enqueue(new DownloadCallback(mCallProxy, mFile, mMD5, mListener));
+ mCallProxy.enqueue(new DownloadCallback(getLifecycleOwner(), mCallProxy, mFile, mMD5, mListener));
return this;
}
diff --git a/library/src/main/java/com/hjq/http/request/GetRequest.java b/library/src/main/java/com/hjq/http/request/GetRequest.java
index a7fb894..0ec9644 100644
--- a/library/src/main/java/com/hjq/http/request/GetRequest.java
+++ b/library/src/main/java/com/hjq/http/request/GetRequest.java
@@ -1,6 +1,6 @@
package com.hjq.http.request;
-import android.content.Context;
+import androidx.lifecycle.LifecycleOwner;
import com.hjq.http.EasyLog;
import com.hjq.http.callback.NormalCallback;
@@ -23,8 +23,8 @@ public final class GetRequest extends BaseRequest {
private CallProxy mCallProxy;
- public GetRequest(Context context) {
- super(context);
+ public GetRequest(LifecycleOwner lifecycleOwner) {
+ super(lifecycleOwner);
}
@Override
@@ -51,9 +51,8 @@ protected Request create(String url, String tag, HttpParams params, HttpHeaders
}
HttpUrl link = builder.build();
request.get().url(link);
- if (EasyLog.isEnable()) {
- EasyLog.print("GetUrl " + link);
- }
+
+ EasyLog.print("GetUrl", link.toString());
return request.build();
}
@@ -62,7 +61,7 @@ protected Request create(String url, String tag, HttpParams params, HttpHeaders
*/
public GetRequest request(OnHttpListener listener) {
mCallProxy = new CallProxy(create());
- mCallProxy.enqueue(new NormalCallback(getContext(), mCallProxy, listener));
+ mCallProxy.enqueue(new NormalCallback(getLifecycleOwner(), mCallProxy, listener));
return this;
}
diff --git a/library/src/main/java/com/hjq/http/request/PostRequest.java b/library/src/main/java/com/hjq/http/request/PostRequest.java
index 71257f4..85621ae 100644
--- a/library/src/main/java/com/hjq/http/request/PostRequest.java
+++ b/library/src/main/java/com/hjq/http/request/PostRequest.java
@@ -1,7 +1,8 @@
package com.hjq.http.request;
-import android.content.Context;
+import androidx.lifecycle.LifecycleOwner;
+import com.hjq.http.EasyConfig;
import com.hjq.http.EasyLog;
import com.hjq.http.callback.NormalCallback;
import com.hjq.http.listener.OnHttpListener;
@@ -13,7 +14,8 @@
import com.hjq.http.model.UpdateBody;
import java.io.File;
-import java.net.URLEncoder;
+import java.io.InputStream;
+import java.util.List;
import okhttp3.FormBody;
import okhttp3.MultipartBody;
@@ -30,8 +32,8 @@ public final class PostRequest extends BaseRequest {
private CallProxy mCallProxy;
- public PostRequest(Context context) {
- super(context);
+ public PostRequest(LifecycleOwner lifecycleOwner) {
+ super(lifecycleOwner);
}
@Override
@@ -56,18 +58,34 @@ protected Request create(String url, String tag, HttpParams params, HttpHeaders
if (!params.isEmpty()) {
for (String key : params.getNames()) {
Object object = params.get(key);
- // 如果这是一个文件
if (object instanceof File) {
- File file = (File) object;
- if (file.exists() && file.isFile()) {
- // 文件名必须不能带中文,所以这里要编码
- builder.addFormDataPart(key, URLEncoder.encode(file.getName()), new UpdateBody(file));
+ // 如果这是一个文件
+ MultipartBody.Part part = UpdateBody.createPart(key, (File) object);
+ if (part != null) {
+ builder.addPart(part);
+ }
+ } else if (object instanceof InputStream) {
+ // 如果这是一个输入流
+ MultipartBody.Part part = UpdateBody.createPart(key, (InputStream) object);
+ if (part != null) {
+ builder.addPart(part);
}
} else if (object instanceof RequestBody) {
+ // 如果这是一个自定义 RequestBody
builder.addFormDataPart(key, null, (RequestBody) object);
} else {
- // 如果这是一个参数
- builder.addFormDataPart(key, object.toString());
+ if (object instanceof List && isFileList((List) object)) {
+ // 上传文件列表
+ for (Object item : (List) object) {
+ MultipartBody.Part part = UpdateBody.createPart(key, (File) item);
+ if (part != null) {
+ builder.addPart(part);
+ }
+ }
+ } else {
+ // 如果这是一个普通参数
+ builder.addFormDataPart(key, object.toString());
+ }
}
}
}
@@ -91,8 +109,9 @@ protected Request create(String url, String tag, HttpParams params, HttpHeaders
}
request.post(body);
- if (EasyLog.isEnable()) {
- EasyLog.print("PostUrl " + url);
+ if (EasyConfig.getInstance().isLogEnabled()) {
+
+ EasyLog.print("PostUrl", url);
if (!headers.isEmpty() || !params.isEmpty()) {
EasyLog.print();
@@ -121,12 +140,28 @@ protected Request create(String url, String tag, HttpParams params, HttpHeaders
return request.build();
}
+ /**
+ * 判断一下这个集合装载的类型是不是 File
+ */
+ private boolean isFileList(List list) {
+ if (list != null && !list.isEmpty()) {
+ for (Object object : list) {
+ if (!(object instanceof File)) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
/**
* 执行请求
*/
public PostRequest request(OnHttpListener listener) {
mCallProxy = new CallProxy(create());
- mCallProxy.enqueue(new NormalCallback(getContext(), mCallProxy, listener));
+ mCallProxy.enqueue(new NormalCallback(getLifecycleOwner(), mCallProxy, listener));
return this;
}