diff --git a/EasyHttp.apk b/EasyHttp.apk index a82c03b..2ea264c 100644 Binary files a/EasyHttp.apk and b/EasyHttp.apk differ diff --git a/EasyHttp.jpg b/EasyHttp.jpg index 9b3fed7..fd756ba 100644 Binary files a/EasyHttp.jpg and b/EasyHttp.jpg differ diff --git a/HelpDoc.md b/HelpDoc.md index 5ed841f..a7acaf7 100644 --- a/HelpDoc.md +++ b/HelpDoc.md @@ -1,10 +1,266 @@ +# 目录 + +> 文档大致分为三类 + +* [集成文档](#集成文档) + +* [使用文档](#使用文档) + +* [疑难解答](#疑难解答) + +# 集成文档 + +#### 配置权限 + +```xml + + + + + + +``` + +#### 关于 Http 明文请求 + +> Android P 限制了明文流量的网络请求,非加密的流量请求都会被系统禁止掉。 +如果当前应用的请求是 http 请求,而非 https ,这样就会导系统禁止当前应用进行该请求,如果 WebView 的 url 用 http 协议,同样会出现加载失败,https 不受影响 + +> 在 res 下新建一个 xml 目录,然后创建一个名为:network_security_config.xml 文件 ,该文件内容如下 + +```xml + + + + +``` + +> 然后在 AndroidManifest.xml application 标签内应用上面的xml配置 + +```xml + +``` + +#### 服务器配置 + +```java +public class RequestServer implements IRequestServer { + + @Override + public String getHost() { + return "https://www.baidu.com/"; + } + + @Override + public String getPath() { + return "api/"; + } + + @Override + public BodyType getType() { + // 参数以 Json 格式提交(默认是表单) + return BodyType.JSON; + } +} +``` + +#### 框架初始化 + +> 需要配置请求结果处理,具体封装可以参考 [RequestHandler](app/src/main/java/com/hjq/http/demo/http/model/RequestHandler.java) + +```java +OkHttpClient okHttpClient = new OkHttpClient.Builder() + .build(); + +EasyConfig.with(okHttpClient) + // 是否打印日志 + .setLogEnabled(BuildConfig.DEBUG) + // 设置服务器配置 + .setServer(server) + // 设置请求处理策略 + .setHandler(new RequestHandler()) + // 设置请求重试次数 + .setRetryCount(3) + // 添加全局请求参数 + //.addParam("token", "6666666") + // 添加全局请求头 + //.addHeader("time", "20191030") + // 启用配置 + .into(); +``` + +> 上述是创建配置,更新配置可以使用 + +```java +EasyConfig.getInstance() + .addParam("token", data.getData().getToken()); +``` + +# 使用文档 + +#### 配置接口 + +```java +public final class LoginApi implements IRequestApi { + + @Override + public String getApi() { + return "user/login"; + } + + /** 用户名 */ + private String userName; + + /** 登录密码 */ + private String password; + + public LoginApi setUserName(String userName) { + this.userName = userName; + return this; + } + + public LoginApi setPassword(String password) { + this.password = password; + return this; + } +} +``` + +* 可为这个类的字段加上一些注解 + + * @HttpHeader:标记这个字段是一个请求头参数 + + * @HttpIgnore:标记这个字段不会被发送给后台 + + * @HttpRename:重新定义这个字段发送给后台的参数名称 + +* 可在这个类实现一些接口 + + * implements IRequestHost:实现这个接口之后可以重新指定这个请求的主机地址 + + * implements IRequestPath:实现这个接口之后可以重新指定这个请求的接口路径 + + * implements IRequestType:实现这个接口之后可以重新指定这个请求的提交方式 + +#### 发起请求 + +> 需要配置请求状态及生命周期处理,具体封装可以参考 [BaseActivity](app/src/main/java/com/hjq/http/demo/BaseActivity.java) + +```java +EasyHttp.post(this) + .api(new LoginApi() + .setUserName("Android 轮子哥") + .setPassword("123456")) + .request(new HttpCallback>(activity) { + + @Override + public void onSucceed(HttpData data) { + ToastUtils.show("登录成功"); + } + }); +``` + +#### 上传文件 + +```java +EasyHttp.post(this) + .api(new UpdateImageApi(file)) + .request(new OnUpdateListener() { + + @Override + public void onStart(Call call) { + mProgressBar.setVisibility(View.VISIBLE); + } + + @Override + public void onUpdate(long totalByte, long updateByte, int progress) { + mProgressBar.setProgress(progress); + } + + @Override + public void onSucceed(Void result) { + ToastUtils.show("上传成功"); + } + + @Override + public void onFail(Exception e) { + ToastUtils.show("上传失败"); + } + + @Override + public void onEnd(Call call) { + mProgressBar.setVisibility(View.GONE); + } + }); +``` + +#### 下载文件 + +> 下载缓存策略:在指定下载文件 md5 或者后台有返回 md5 的情况下,下载框架默认开启下载缓存模式,如果这个文件已经存在手机中,并且经过 md5 校验文件完整,框架就不会重复下载,而是直接回调下载监听。减轻服务器压力,减少用户等待时间。 + +```java +EasyHttp.download(this) + .method(HttpMethod.GET) + .file(new File(Environment.getExternalStorageDirectory(), "微信.apk")) + //.url("https://qd.myapp.com/myapp/qqteam/AndroidQQ/mobileqq_android.apk") + .url("http://dldir1.qq.com/weixin/android/weixin708android1540.apk") + .md5("2E8BDD7686474A7BC4A51ADC3667CABF") + .listener(new OnDownloadListener() { + + @Override + public void onStart(File file) { + mProgressBar.setVisibility(View.VISIBLE); + } + + @Override + public void onProgress(File file, long totalByte, long downloadByte, int progress) { + mProgressBar.setProgress(progress); + } + + @Override + public void onComplete(File file) { + ToastUtils.show("下载完成:" + file.getPath()); + installApk(MainActivity.this, file); + } + + @Override + public void onError(File file, Exception e) { + ToastUtils.show("下载出错:" + e.getMessage()); + } + + @Override + public void onEnd(File file) { + mProgressBar.setVisibility(View.GONE); + } + + }).start(); +``` + +#### 同步请求 + +```java +try { + HttpData data = EasyHttp.post(MainActivity.this) + .api(new SearchBlogsApi() + .setKeyword("搬砖不再有")) + .execute(new DataClass>() {}); + ToastUtils.show("请求成功,请看日志"); +} catch (Exception e) { + e.printStackTrace(); + ToastUtils.show(e.getMessage()); +} +``` + +# 疑难解答 + #### 如何添加全局参数? ```java // 添加全局请求参数 -EasyConfig.getInstance().addHeader("token", "abc"); -// 添加全局请求头 EasyConfig.getInstance().addParam("token", "abc"); +// 添加全局请求头 +EasyConfig.getInstance().addHeader("token", "abc"); ``` #### 如何定义全局的动态参数? @@ -149,14 +405,29 @@ public class XxxServer implements IRequestServer { } ``` -* 当然也支持对某个接口进行单独配置,和上个问题雷同,这里略过 +* 当然也支持对某个接口进行单独配置 + +```java +public final class XxxApi implements IRequestApi, IRequestType { + + @Override + public String getApi() { + return "xxx/xxxx"; + } + + @Override + public BodyType getType() { + return BodyType.JSON; + } +} +``` * 表单和 Json 方式提交的优缺点对比 | 场景 | 表单方式 | Json 方式 | | :----: | :------: | :-----: | -| 参数嵌套 | 不支持 | 支持 | -| 文件上传 | 支持 | 不支持 | +| 参数嵌套 | 不支持 | 支持 | +| 文件上传 | 支持 | 不支持 | #### 如何忽略某个参数? @@ -264,11 +535,97 @@ public final class XxxApi implements IRequestApi { #### 如何设置超时重试? ```java +// 设置请求重试次数 EasyConfig.getInstance().setRetryCount(3); +// 设置请求重试时间 +EasyConfig.getInstance().setRetryTime(1000); +``` + +#### 如何设置请求超时时间? + +* 全局配置(所有接口都生效) + +```java +OkHttpClient.Builder builder = new OkHttpClient.Builder(); +builder.readTimeout(5000, TimeUnit.MILLISECONDS); +builder.writeTimeout(5000, TimeUnit.MILLISECONDS); +builder.connectTimeout(5000, TimeUnit.MILLISECONDS); +EasyConfig.with(builder.build()) + .into(); +``` + +* 局部配置(只在某个请求生效) + +```java +OkHttpClient.Builder builder = EasyConfig.getInstance().getClient().newBuilder(); +builder.readTimeout(5000, TimeUnit.MILLISECONDS); +builder.writeTimeout(5000, TimeUnit.MILLISECONDS); +builder.connectTimeout(5000, TimeUnit.MILLISECONDS); + +EasyHttp.post(this) + .api(new XxxApi()) + .client(builder.build()) + .request(new HttpCallback>(this) { + + @Override + public void onSucceed(HttpData data) { + + } + }); ``` #### 如何设置不打印日志? ```java EasyConfig.getInstance().setLogEnabled(false); +``` + +#### 框架指定只能传入 LifecycleOwner,我想传入其他对象怎么办? + +* 其中 AppCompatActivity 和 AndroidX.Fragment 都是 LifecycleOwner 子类的,这个是毋庸置疑的 + +* 但是你如果传入的是 Activity 对象,并非 AppCompatActivity 对象,那么你可以这样写 + +```java +EasyHttp.post(new ActivityLifecycle(this)) + .api(new XxxApi()) + .request(new HttpCallback>(this) { + + @Override + public void onSucceed(HttpData result) { + + } + }); +``` + +* 如果以上条件都不满足,但是你就是想在某个地方请求网络,那么你可以这样写 + +```java +EasyHttp.post(new ApplicationLifecycle()) + .api(new XxxApi()) + .tag("abc") + .request(new HttpCallback>(this) { + + @Override + public void onSucceed(HttpData result) { + + } + }); +``` + +* 但是你需要注意,传入 ApplicationLifecycle 将意味着框架无法自动把控请求的生命周期 + +* 如果你采用了这样的写法,那么为了避免内存泄漏或者崩溃的事情发生 + +* 需要你在请求的时候设置对应的 Tag,然后在恰当的时机手动取消请求(一般都在销毁或者退出的时候取消请求) + +#### 如何取消已发起的请求? + +```java +// 取消和这个 LifecycleOwner 关联的请求 +EasyHttp.cancel(LifecycleOwner lifecycleOwner); +// 取消指定 Tag 标记的请求 +EasyHttp.cancel(Object tag); +// 取消所有请求 +EasyHttp.cancel(); ``` \ No newline at end of file diff --git a/README.md b/README.md index aff36ab..68a5348 100644 --- a/README.md +++ b/README.md @@ -8,225 +8,71 @@ #### Gradle 集成 - android { - // 支持 JDK 1.8 - compileOptions { - targetCompatibility JavaVersion.VERSION_1_8 - sourceCompatibility JavaVersion.VERSION_1_8 - } - } - - dependencies { - implementation 'com.hjq:http:8.2' - implementation 'com.squareup.okhttp3:okhttp:3.12.10' - implementation 'com.google.code.gson:gson:2.8.5' +```groovy +android { + // 支持 JDK 1.8 + compileOptions { + targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_1_8 } +} + +dependencies { + implementation 'com.hjq:http:8.6' + implementation 'com.squareup.okhttp3:okhttp:3.12.12' + implementation 'com.google.code.gson:gson:2.8.5' +} +``` + +#### 具体用法[请点击这里查看](HelpDoc.md) + +#### 不同网络请求框架之间的对比 -#### 配置权限 - - - - - - - - -#### 服务器配置 - - public class RequestServer implements IRequestServer { - - @Override - public String getHost() { - return "https://www.baidu.com/"; - } - - @Override - public String getPath() { - return "api/"; - } - - @Override - public BodyType getType() { - // 参数以 Json 格式提交(默认是表单) - return BodyType.JSON; - } - } - -#### 初始化 - -> 需要配置请求结果处理,具体封装可以参考 [RequestHandler](https://github.com/getActivity/EasyHttp/blob/master/app/src/main/java/com/hjq/http/demo/http/model/RequestHandler.java) - - EasyConfig.with(new OkHttpClient()) - // 是否打印日志 - .setLogEnabled(BuildConfig.DEBUG) - // 设置服务器配置 - .setServer(server) - // 设置请求处理策略 - .setHandler(new RequestHandler()) - // 设置请求重试次数 - .setRetryCount(3) - // 添加全局请求参数 - //.addParam("token", "6666666") - // 添加全局请求头 - //.addHeader("time", "20191030") - // 启用配置 - .into(); - -> 上述是创建配置,更新配置可以使用 - - EasyConfig.getInstance() - .addParam("token", data.getData().getToken()); - -#### 配置接口 - - public final class LoginApi implements IRequestApi { - - @Override - public String getApi() { - return "user/login"; - } - - /** 用户名 */ - private String userName; - - /** 登录密码 */ - private String password; - - public LoginApi setUserName(String userName) { - this.userName = userName; - return this; - } - - public LoginApi setPassword(String password) { - this.password = password; - return this; - } - } - -* 可为这个类的字段加上一些注解 - - * @HttpHeader:标记这个字段是一个请求头参数 - - * @HttpIgnore:标记这个字段不会被发送给后台 - - * @HttpRename:重新定义这个字段发送给后台的参数名称 - -* 可在这个类实现一些接口 - - * implements IRequestHost:实现这个接口之后可以重新指定这个请求的主机地址 - - * implements IRequestPath:实现这个接口之后可以重新指定这个请求的接口路径 - - * implements IRequestType:实现这个接口之后可以重新指定这个请求的提交方式 - -* 具体用法可以[点击这里查看](HelpDoc.md) - -#### 发起请求 - -> 需要配置请求状态及生命周期处理,具体封装可以参考 [BaseActivity](https://github.com/getActivity/EasyHttp/blob/master/app/src/main/java/com/hjq/http/demo/BaseActivity.java) - - EasyHttp.post(this) - .api(new LoginApi() - .setUserName("Android 轮子哥") - .setPassword("123456")) - .request(new HttpCallback>(activity) { - - @Override - public void onSucceed(HttpData data) { - ToastUtils.show("登录成功"); - } - }); - -#### 下载文件 - -> 下载缓存策略:在指定下载文件 md5 或者后台有返回 md5 的情况下,下载框架默认开启下载缓存模式,如果这个文件已经存在手机中,并且经过 md5 校验文件完整,框架就不会重复下载,而是直接回调下载监听。减轻服务器压力,减少用户等待时间。 - - EasyHttp.download(this) - .method(HttpMethod.GET) - .file(new File(Environment.getExternalStorageDirectory(), "微信.apk")) - .url("http://dldir1.qq.com/weixin/android/weixin708android1540.apk") - .md5("2E8BDD7686474A7BC4A51ADC3667CABF") - .listener(new OnDownloadListener() { - - @Override - public void onStart(Call call) { - mProgressBar.setVisibility(View.VISIBLE); - ToastUtils.show("下载开始"); - } - - @Override - public void onProgress(DownloadInfo info) { - mProgressBar.setProgress(info.getDownloadProgress()); - } - - @Override - public void onComplete(DownloadInfo info) { - ToastUtils.show("下载完成:" + info.getFile().getPath()); - installApk(MainActivity.this, info.getFile()); - } - - @Override - public void onError(DownloadInfo info, Exception e) { - ToastUtils.show("下载出错:" + e.getMessage()); - } - - @Override - public void onEnd(Call call) { - mProgressBar.setVisibility(View.GONE); - ToastUtils.show("下载结束"); - } - - }).start(); +| 功能 | [EasyHttp](https://github.com/getActivity/EasyHttp) | [Retrofit](https://github.com/square/retrofit) | [OkGo](https://github.com/jeasonlzy/okhttp-OkGo) | +| :----: | :------: | :-----: | :-----: | +| 动态 Host | 支持 | 不支持 | 支持 | +| 全局参数 | 支持 | 不支持 | 支持 | +| 超时重试 | 支持 | 不支持 | 支持 | +| 极速下载 | 支持 | 不支持 | 不支持 | +| 下载校验 | 支持 | 不支持 | 不支持 | +| 注解数量 | 3 个 | 25 个 | 0 个 | +| 上传文件类型 | File / InputStream | RequestBody | File | +| 批量上传文件 | 支持 | 不支持 | 支持 | +| 上传进度监听 | 支持 | 不支持 | 支持 | +| Json 参数提交 | 支持 | 支持 | 支持 | +| 请求生命周期 | 自动管控 | 需要封装 | 需要封装 | +| 参数传值方式 | 字段名 + 字段值 | 方法参数名 + 方法参数值 | 定义 key 和 value | +| 参数灵活性 | 不强制传入 | 强制全部传入 | 不强制传入 | +| 框架维护状态 | 维护中 | 维护中 | 停止维护 | -#### 关于 Http 明文请求 - -> Android P 限制了明文流量的网络请求,非加密的流量请求都会被系统禁止掉。 -如果当前应用的请求是 http 请求,而非 https ,这样就会导系统禁止当前应用进行该请求,如果 WebView 的 url 用 http 协议,同样会出现加载失败,https 不受影响 - -> 在 res 下新建一个 xml 目录,然后创建一个名为:network_security_config.xml 文件 ,该文件内容如下 +* Retrofit 在我看来并不是那么好用,因为很多常用的功能实现起来比较麻烦,动态 Host 要写拦截器,日志打印要写拦截器,就连最常用的添加全局参数也要写拦截器,一个拦截器意味着要写很多代码,如果写得不够严谨还有可能出现 Bug,从而影响整个 OkHttp 请求流程,我经常在想这些功能能不能都用一句代码搞定,因为我觉得这些功能是框架设计的时候应该考虑的,这便是我做这个框架的初心。 - - - - +* 本框架采用了 OOP 思想,一个请求代表一个对象,通过类的继承和实现的特性来实现接口的动态化,几乎涵盖接口开发中所有的功能,使用起来非常简单灵活。 -> 然后在 AndroidManifest.xml application 标签内应用上面的xml配置 +* 有很多人觉得写一个接口类很麻烦,这个点确实有点麻烦,但是这块的付出是有收获的,从前期开发的效率考虑:OkGo > EasyHttp > Retrofit,但是从后期维护的效率考虑:EasyHttp > Retrofit > OkGo,之所以比较这三个框架,是因为框架的设计思想不同,但是我始终认为 EasyHttp 才是最好的设计,所以我创造了它。 - +* 前期开发和后期维护哪个更重要?我觉得都重要,但是如果两者之间有利益冲突,我会毫不犹豫选择后期维护,因为前期开发占据的是小头,后期的持续维护才是大头。 #### 混淆规则 - - # OkHttp3 - -keepattributes Signature - -keepattributes *Annotation* - -keep class okhttp3.** { *; } - -keep interface okhttp3.** { *; } - -dontwarn okhttp3.** - -dontwarn okio.** - - # 不混淆这个包下的字段名 - -keepclassmembernames class com.hjq.http.demo.http.** { - ; - } - -#### 对比 Retrofit - -| 功能 | Retrofit 框架 | EasyHttp 框架 | -| :----: | :------: | :-----: | -| 动态 Host | 不支持 | 支持 | -| 全局参数 | 不支持 | 支持 | -| 动态参数 | 不支持 | 支持 | -| 超时重试 | 不支持 | 支持 | -| 极速下载 | 不支持 | 支持 | -| 下载校验 | 不支持 | 支持 | -| 注解数量 | 25 个 | 3 个 | -| 上传文件 | RequestBody | File / InputStream | -| 生命周期 | 需要封装 | 自动管控 | + +```groovy +# OkHttp3 +-keepattributes Signature +-keepattributes *Annotation* +-keep class okhttp3.** { *; } +-keep interface okhttp3.** { *; } +-dontwarn okhttp3.** +-dontwarn okio.** + +# 不混淆这个包下的字段名 +-keepclassmembernames class com.hjq.http.demo.http.** { + ; +} +``` #### 作者的其他开源项目 -* 架构工程:[AndroidProject](https://github.com/getActivity/AndroidProject) +* 安卓架构:[AndroidProject](https://github.com/getActivity/AndroidProject) * 日志框架:[Logcat](https://github.com/getActivity/Logcat) @@ -240,12 +86,6 @@ * 悬浮窗框架:[XToast](https://github.com/getActivity/XToast) -#### 特别感谢 - -[张鸿洋](https://github.com/hongyangAndroid) - -[WanAndroid](https://www.wanandroid.com/) - #### Android技术讨论Q群:78797078 #### 如果您觉得我的开源库帮你节省了大量的开发时间,请扫描下方的二维码随意打赏,要是能打赏个 10.24 :monkey_face:就太:thumbsup:了。您的支持将鼓励我继续创作:octocat: @@ -254,6 +94,12 @@ #### [点击查看捐赠列表](https://github.com/getActivity/Donate) +#### 特别感谢 + +[张鸿洋](https://github.com/hongyangAndroid) + +[WanAndroid](https://www.wanandroid.com/) + ## License ```text diff --git a/app/build.gradle b/app/build.gradle index 6208f60..c1058c2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,7 +1,11 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 28 + compileSdkVersion 30 + + lintOptions { + abortOnError false + } // 支持 JDK 1.8 compileOptions { @@ -12,9 +16,9 @@ android { defaultConfig { applicationId "com.hjq.http.demo" minSdkVersion 14 - targetSdkVersion 28 - versionCode 82 - versionName "8.2" + targetSdkVersion 30 + versionCode 86 + versionName "8.6" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -24,35 +28,37 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } release { - minifyEnabled true + minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { + // 依赖 libs 目录下所有的 jar 和 aar 包 implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation fileTree(include: ['*.aar'], dir: 'libs') + implementation project(':library') testImplementation 'junit:junit:4.13' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'com.google.android.material:material:1.1.0' + // 谷歌兼容库:https://developer.android.google.cn/jetpack/androidx/releases/appcompat?hl=zh-cn + implementation 'androidx.appcompat:appcompat:1.3.0-alpha02' // 标题栏:https://github.com/getActivity/TitleBar - implementation 'com.hjq:titlebar:6.5' + implementation 'com.hjq:titlebar:8.0' // 吐司工具类:https://github.com/getActivity/ToastUtils - implementation 'com.hjq:toast:8.6' + implementation 'com.hjq:toast:8.8' // 权限请求框架:https://github.com/getActivity/XXPermissions - implementation 'com.hjq:xxpermissions:6.5' + implementation 'com.hjq:xxpermissions:8.8' // Json 解析框架:https://github.com/google/gson implementation 'com.google.code.gson:gson:2.8.5' // OkHttp 框架:https://github.com/square/okhttp // 升级注意事项:https://www.jianshu.com/p/d12d0f536f55 - implementation 'com.squareup.okhttp3:okhttp:3.12.10' + implementation 'com.squareup.okhttp3:okhttp:3.12.12' // 日志调试:https://github.com/getActivity/Logcat - debugImplementation 'com.hjq:logcat:8.2' + debugImplementation 'com.hjq:logcat:8.6' } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3ee8451..a4991a7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -10,6 +10,7 @@ + @@ -23,6 +24,7 @@ android:networkSecurityConfig="@xml/network_security_config" android:roundIcon="@mipmap/ic_launcher_round" android:theme="@style/AppTheme" + tools:ignore="LockedOrientationActivity" tools:targetApi="n"> 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 10de53e..2f748b4 100644 --- a/app/src/main/java/com/hjq/http/demo/MainActivity.java +++ b/app/src/main/java/com/hjq/http/demo/MainActivity.java @@ -16,7 +16,6 @@ import androidx.core.content.FileProvider; import com.hjq.http.EasyHttp; -import com.hjq.http.EasyLog; import com.hjq.http.demo.http.model.HttpData; import com.hjq.http.demo.http.request.SearchAuthorApi; import com.hjq.http.demo.http.request.SearchBlogsApi; @@ -24,7 +23,8 @@ import com.hjq.http.demo.http.response.SearchBean; import com.hjq.http.listener.HttpCallback; import com.hjq.http.listener.OnDownloadListener; -import com.hjq.http.model.DownloadInfo; +import com.hjq.http.listener.OnUpdateListener; +import com.hjq.http.model.DataClass; import com.hjq.http.model.HttpMethod; import com.hjq.permissions.OnPermission; import com.hjq.permissions.Permission; @@ -57,6 +57,7 @@ protected void onCreate(Bundle savedInstanceState) { findViewById(R.id.btn_main_get).setOnClickListener(this); findViewById(R.id.btn_main_post).setOnClickListener(this); + findViewById(R.id.btn_main_exec).setOnClickListener(this); findViewById(R.id.btn_main_update).setOnClickListener(this); findViewById(R.id.btn_main_download).setOnClickListener(this); requestPermission(); @@ -64,7 +65,7 @@ protected void onCreate(Bundle savedInstanceState) { private void requestPermission() { XXPermissions.with(this) - .permission(Permission.Group.STORAGE) + .permission(Permission.MANAGE_EXTERNAL_STORAGE) .request(this); } @@ -78,10 +79,10 @@ public void hasPermission(List granted, boolean all) { } @Override - public void noPermission(List denied, boolean quick) { - if (quick) { + public void noPermission(List denied, boolean never) { + if (never) { ToastUtils.show("授权失败,请手动授予权限"); - XXPermissions.startPermissionActivity(this, true); + XXPermissions.startPermissionActivity(this, denied); } else { ToastUtils.show("请先授予权限"); requestPermission(); @@ -91,7 +92,7 @@ public void noPermission(List denied, boolean quick) { @Override protected void onRestart() { super.onRestart(); - if (XXPermissions.hasPermission(this, Permission.Group.STORAGE)) { + if (XXPermissions.hasPermission(this, Permission.MANAGE_EXTERNAL_STORAGE)) { hasPermission(null, true); } else { requestPermission(); @@ -109,7 +110,7 @@ public void onClick(View v) { @Override public void onSucceed(HttpData result) { - ToastUtils.show("请求成功,请看日志"); + ToastUtils.show("Get 请求成功,请看日志"); } }); break; @@ -121,26 +122,73 @@ public void onSucceed(HttpData result) { @Override public void onSucceed(HttpData result) { - ToastUtils.show("请求成功,请看日志"); + ToastUtils.show("Post 请求成功,请看日志"); } }); break; + case R.id.btn_main_exec: + // 在主线程中不能做耗时操作 + new Thread(() -> { + runOnUiThread(this::showDialog); + try { + HttpData data = EasyHttp.post(MainActivity.this) + .api(new SearchBlogsApi() + .setKeyword("搬砖不再有")) + .execute(new DataClass>() {}); + ToastUtils.show("同步请求成功,请看日志"); + } catch (Exception e) { + e.printStackTrace(); + ToastUtils.show(e.getMessage()); + } + runOnUiThread(this::hideDialog); + }).start(); + break; case R.id.btn_main_update: + if (mProgressBar.getVisibility() == View.VISIBLE) { + ToastUtils.show("当前正在上传或者下载,请等待完成之后再进行操作"); + return; + } + File file = new File(Environment.getExternalStorageDirectory(), getString(R.string.app_name) + ".png"); // 生成图片到本地 - drawableToFile(ContextCompat.getDrawable(this, R.mipmap.ic_launcher), file); + drawableToFile(ContextCompat.getDrawable(this, R.drawable.bg_material), file); EasyHttp.post(this) .api(new UpdateImageApi(file)) - .request(new HttpCallback(this) { + .request(new OnUpdateListener() { + + @Override + public void onStart(Call call) { + mProgressBar.setVisibility(View.VISIBLE); + } + + @Override + public void onUpdate(long totalByte, long updateByte, int progress) { + mProgressBar.setProgress(progress); + } @Override - public void onSucceed(String result) { + + public void onSucceed(Void result) { ToastUtils.show("上传成功"); } + + @Override + public void onFail(Exception e) { + ToastUtils.show("上传失败"); + } + + @Override + public void onEnd(Call call) { + mProgressBar.setVisibility(View.GONE); + } }); break; case R.id.btn_main_download: + if (mProgressBar.getVisibility() == View.VISIBLE) { + ToastUtils.show("当前正在上传或者下载,请等待完成之后再进行操作"); + return; + } EasyHttp.download(this) .method(HttpMethod.GET) .file(new File(Environment.getExternalStorageDirectory(), "微信.apk")) @@ -150,31 +198,29 @@ public void onSucceed(String result) { .listener(new OnDownloadListener() { @Override - public void onStart(Call call) { + public void onStart(File file) { mProgressBar.setVisibility(View.VISIBLE); - ToastUtils.show("下载开始"); } @Override - public void onProgress(DownloadInfo info) { - mProgressBar.setProgress(info.getDownloadProgress()); + public void onProgress(File file, long totalByte, long downloadByte, int progress) { + mProgressBar.setProgress(progress); } @Override - public void onComplete(DownloadInfo info) { - ToastUtils.show("下载完成:" + info.getFile().getPath()); - installApk(MainActivity.this, info.getFile()); + public void onComplete(File file) { + ToastUtils.show("下载完成:" + file.getPath()); + installApk(MainActivity.this, file); } @Override - public void onError(DownloadInfo info, Exception e) { + public void onError(File file, Exception e) { ToastUtils.show("下载出错:" + e.getMessage()); } @Override - public void onEnd(Call call) { + public void onEnd(File file) { mProgressBar.setVisibility(View.GONE); - ToastUtils.show("下载结束"); } }).start(); @@ -193,8 +239,8 @@ private void installApk(final Context context, final File file) { .permission(Permission.REQUEST_INSTALL_PACKAGES) .request(new OnPermission() { @Override - public void hasPermission(List granted, boolean isAll) { - if (isAll) { + public void hasPermission(List granted, boolean all) { + if (all) { Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); Uri uri; @@ -240,7 +286,7 @@ private void drawableToFile(Drawable drawable, File file) { ((BitmapDrawable) drawable).getBitmap().compress(Bitmap.CompressFormat.PNG, 100, out); out.close(); } catch (IOException e) { - EasyLog.print(e); + e.printStackTrace(); } } } \ No newline at end of file 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 3474fe7..b6322f4 100644 --- a/app/src/main/java/com/hjq/http/demo/MyApplication.java +++ b/app/src/main/java/com/hjq/http/demo/MyApplication.java @@ -29,7 +29,10 @@ public void onCreate() { server = new ReleaseServer(); } - EasyConfig.with(new OkHttpClient()) + OkHttpClient okHttpClient = new OkHttpClient.Builder() + .build(); + + EasyConfig.with(okHttpClient) // 是否打印日志 //.setLogEnabled(BuildConfig.DEBUG) // 设置服务器配置 @@ -45,6 +48,8 @@ public void intercept(String url, String tag, HttpParams params, HttpHeaders hea }) // 设置请求重试次数 .setRetryCount(1) + // 设置请求重试时间 + .setRetryTime(1000) // 添加全局请求参数 //.addParam("token", "6666666") // 添加全局请求头 diff --git a/app/src/main/java/com/hjq/http/demo/http/model/HttpListData.java b/app/src/main/java/com/hjq/http/demo/http/model/HttpListData.java new file mode 100644 index 0000000..ce66bfa --- /dev/null +++ b/app/src/main/java/com/hjq/http/demo/http/model/HttpListData.java @@ -0,0 +1,47 @@ +package com.hjq.http.demo.http.model; + +import java.util.List; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/EasyHttp + * time : 2020/10/07 + * desc : 统一接口列表数据结构 + */ +public class HttpListData extends HttpData> { + + public static class ListBean { + + /** 当前页码 */ + private int pageIndex; + /** 页大小 */ + private int pageSize; + /** 总数量 */ + private int totalNumber; + /** 数据 */ + private List items; + + /** + * 判断是否是最后一页 + */ + public boolean isLastPage() { + return Math.ceil((float) totalNumber / pageSize) <= pageIndex; + } + + public int getTotalNumber() { + return totalNumber; + } + + public int getPageIndex() { + return pageIndex; + } + + public int getPageSize() { + return pageSize; + } + + public List getItems() { + return items; + } + } +} \ No newline at end of file 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 3dc1f63..90d9ab2 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 @@ -2,8 +2,6 @@ import android.app.Application; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.net.ConnectivityManager; import android.net.NetworkInfo; @@ -38,11 +36,13 @@ import org.json.JSONObject; import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.Type; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.util.List; +import okhttp3.Headers; import okhttp3.Response; import okhttp3.ResponseBody; @@ -74,14 +74,17 @@ public Object requestSucceed(LifecycleOwner lifecycle, Response response, Type t throw new ResponseException(mApplication.getString(R.string.http_response_error) + ",responseCode:" + response.code() + ",message:" + response.message(), response); } + if (Headers.class.equals(type)) { + return response.headers(); + } + ResponseBody body = response.body(); if (body == null) { return null; } - if (Bitmap.class.equals(type)) { - // 如果这是一个 Bitmap 对象 - return BitmapFactory.decodeStream(body.byteStream()); + if (InputStream.class.equals(type)) { + return body.byteStream(); } String text; 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 ea21fa3..2e09357 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,9 +1,11 @@ package com.hjq.http.demo.http.request; import com.hjq.http.config.IRequestApi; -import com.hjq.http.config.IRequestHost; +import com.hjq.http.config.IRequestServer; import java.io.File; +import java.util.ArrayList; +import java.util.List; /** * author : Android 轮子哥 @@ -11,7 +13,7 @@ * time : 2019/12/14 * desc : 上传图片 */ -public final class UpdateImageApi implements IRequestHost, IRequestApi { +public final class UpdateImageApi implements IRequestServer, IRequestApi { @Override public String getHost() { @@ -26,12 +28,15 @@ public String getApi() { /** 本地图片 */ private File image; + private List files = new ArrayList<>(); + public UpdateImageApi(File image) { this.image = image; } public UpdateImageApi setImage(File image) { this.image = image; + files.add(image); return this; } } \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_material.jpg b/app/src/main/res/drawable/bg_material.jpg new file mode 100644 index 0000000..b0a3f67 Binary files /dev/null and b/app/src/main/res/drawable/bg_material.jpg differ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 378754c..06f772b 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -34,14 +34,21 @@ android:id="@+id/btn_main_get" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="Get请求" /> + android:text="Get 请求" />