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) + ![](EasyHttp.jpg) [点击此处下载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 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; }