Giter VIP home page Giter VIP logo

easyhttp's Introduction

简单易用的网络框架

集成步骤

  • 如果你的项目 Gradle 配置是在 7.0 以下,需要在 build.gradle 文件中加入
allprojects {
    repositories {
        // JitPack 远程仓库:https://jitpack.io
        maven { url 'https://jitpack.io' }
    }
}
  • 如果你的 Gradle 配置是 7.0 及以上,则需要在 settings.gradle 文件中加入
dependencyResolutionManagement {
    repositories {
        // JitPack 远程仓库:https://jitpack.io
        maven { url 'https://jitpack.io' }
    }
}
  • 配置完远程仓库后,在项目 app 模块下的 build.gradle 文件中加入远程依赖
android {
    // 支持 JDK 1.8
    compileOptions {
        targetCompatibility JavaVersion.VERSION_1_8
        sourceCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    // 网络请求框架:https://github.com/getActivity/EasyHttp
    implementation 'com.github.getActivity:EasyHttp:12.8'
    // OkHttp 框架:https://github.com/square/okhttp
    // noinspection GradleDependency
    implementation 'com.squareup.okhttp3:okhttp:3.12.13'
}
  • 需要注意的是:由于使用了 Lifecycle 特性,目前框架只支持 AndroidX 的项目集成

框架混淆规则

  • OkHttp3 框架混淆规则
# OkHttp3 框架混淆规则
-keepattributes Signature
-keepattributes *Annotation*
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**
-dontwarn okio.**
  • EasyHttp 框架混淆规则
# EasyHttp 框架混淆规则
-keep class com.hjq.http.** {*;}
  • 不混淆实现 OnHttpListener 接口的类
# 必须要加上此规则,否则会导致泛型解析失败
-keep public class * implements com.hjq.http.listener.OnHttpListener {
    *;
}
  • 不混淆某个包下的 Bean 类
# 必须要加上此规则,否则可能会导致 Bean 类的字段无法解析成后台返回的字段,xxx 请替换成对应包名
-keep class com.xxx.xxx.xxx.xxx.** {
    <fields>;
}
  • 以上混淆规则,可以在主模块的 proguard-rules.pro 文件中加入

不同网络请求框架之间的对比

功能或细节 EasyHttp Retrofit OkGo
对应版本 12.8 2.9.0 3.0.4
issues 数
aar 包大小 95 KB 123 KB 131 KB
minSdk 要求 API 14+ API 21+ API 14+
配置多域名
动态 Host
全局参数
日志打印
超时重试
配置 Http 缓存
下载文件校验
极速下载
下载断点续传
上传进度监听
Json 参数提交
Json 日志打印格式化
请求代码定位
延迟发起请求
分区存储适配
上传文件类型 File / FileContentResolver
InputStream / RequestBody
RequestBody File
批量上传文件
请求生命周期 自动管控 需要封装 需要封装
参数传值方式 字段名 + 字段值 参数名 + 参数值 定义 Key + Value
框架灵活性
框架学习成本
API 记忆成本
接口维护成本
框架维护状态 维护中 维护中 停止维护
  • Retrofit 在我看来并不是那么好用,因为很多常用的功能实现起来比较麻烦,动态 Host 要写拦截器,日志打印要写拦截器,就连最常用的添加全局参数也要写拦截器,一个拦截器意味着要写很多代码,如果写得不够严谨还有可能出现 Bug,从而影响整个 OkHttp 请求流程,我经常在想这些功能能不能都用一句代码搞定,因为我觉得这些功能是设计框架的时候本应该考虑的,这便是我做这个框架的初心。

  • OkGo 其实也存在一些弊端,例如会把参数的 key 引用放到外层去,这样会引发一些问题:

    1. Key 管理问题:这个 key 可能会在外层被使用很多次,这样参数的 key 管理就会变得不可控,后续接口改动可能会出现漏改的风险,尽管这种情况比较少见,但是也不容忽视,而 EasyHttp 没有这个问题,因为 EasyHttp 不会将参数 key 值放置到外层中去。

    2. 接口参数注释的问题:站在代码的规范角度上讲,我们应该在代码中注明参数的含义及作用,如果一旦将 key 放到外层,那么每一处调用的地方都需要写一遍注释,而 EasyHttp 是将参数字段化,只需要写一次注释到字段上即可。

    3. 接口信息完整信息展示:使用 OkGo 请求网络,只能在调用的地方看到传递的接口参数,而一些被其他地方引用的参数,我们无法很直观的看到,只能通过追踪代码或者查看文档来得知,而 EasyHttp 将一个接口的信息全部通过一个类来管理的,这个类其实就相当于一个接口文档。

    4. 接口的动态化配置:除了接口的参数之外,一个接口还有可能单独配置 OkHttpClient 对象、参数的提交方式、接口响应处理方式等,这些用 OkGo 是可以实现,但是每个地方都要写一次,而 EasyHttp 可以直接在 API 类中配置,真正做到一劳永逸。

  • EasyHttp 采用了 OOP **,一个请求代表一个对象,通过类继承和实现的特性来对接口进行动态化配置,几乎涵盖接口开发中所有的功能,使用起来非常简单灵活。而 Retrofit 采用的是注解方式,缺点是灵活性极低,因为注解上面只能放常量,也就会限定你在注解上面的一切参数只能是事先定义好的,这对接口的动态化配置极不利的。

  • 有很多人觉得写一个接口类很麻烦,关于这个问题我后面已经想到一个好方案了,大家可以将 Api 类和 Bean 类写在一起,这样大家就不需要多写一个类了,具体写法示例如下:

public final class XxxApi implements IRequestApi {

    @Override
    public String getApi() {
        return "xxx/xxx";
    }

    private int xxx;

    public XxxApi setXxx(int xxx) {
        this.xxx = xxx;
        return this;
    }

    ......

    public final static class Bean {

        private int xyz;

        public int getXyz() {
            return xyz;
        }

        ......
    }
}
  • 是不是很机智?这样不仅很好地解决了这一问题,还能将一个接口所有的信息都包裹在这个类中,非常直观,一览如云,妥妥的一箭双雕。

生命周期自动管控介绍

  • 框架可以自动管控请求的生命周期,无需第三方调用者封装和适配,这块其实用到 Jetpack 中的一个 Lifecycle 特性,框架将网络请求和 LifecycleOwner 绑定在一起,当 LifecycleOwner 触发 destroy 时,框架会对绑定的网络请求进行 cancel 处理。相比较传统的写法,这种方式更加简单快捷,同时有 Lifecycle 特性的加持,灵活也会更高,因为我们完全不需要关心请求的主体是 Activity 还是 Fragment,又或者是其他类型的对象。

  • 但并不是完全没有缺点,因为 Lifecycle 特性是 AndroidX 包中的新特性,如果需要使用的话,当前项目必须基于 AndroidX 库开发的才可以集成。

  • 都说代码是最好的老师,具体实现的代码如下:

public final class HttpLifecycleManager implements LifecycleEventObserver {

    /**
     * 绑定组件的生命周期
     */
    public static void register(LifecycleOwner lifecycleOwner) {
        lifecycleOwner.getLifecycle().addObserver(new HttpLifecycleManager());
    }

    @Override
    public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
        if (event != Lifecycle.Event.ON_DESTROY) {
            return;
        }

        // 移除监听
        source.getLifecycle().removeObserver(this);
        // 取消请求
        EasyHttp.cancelByTag(source);
    }
}

极速下载功能介绍

  • 其实本质上面和极速秒传的原理是差不多的,只不过一个是上传,另一个是下载。而极速上传是将本地文件的 MD5 值和服务器上面的进行比对,如果服务器存在这个 MD5 值的文件,就将这份文件映射一份到这个用户的网盘上面,从而达到了极速秒传的效果。而极速下载也是同理,根据后台给的文件 MD5 值和本地文件进行对比,如果存在这个文件并且 MD5 值一致,证明这个文件和服务器上面的文件是一致的,那么就直接跳过下载,直接回调下载成功监听。

  • 极速秒传和极速下载两者相同的共同点就是,利用缓存来达到极速的效果,只不过一者通过的是服务器的缓存,另一者使用的是本地的缓存,这两者都有一个共同的特点,就是减少服务器的压力,节省用户的等待时间。

代码定位功能介绍

  • 框架会在日志打印中输出在网络请求的代码位置,这样开发者可以直接通过点击 Log 来定位是在哪个类哪行代码,这样可以极大提升我们排查问题的效率,特别是在请求一多且业务复杂的情况下,我相信没有一个人会拒绝这样的功能。

延迟发起请求功能介绍

  • 通常我们都有这样一种需求,就是我们希望发起网络请求,但是希望它不是及时,而是经过一定的延迟才去请求,EasyHttp 针对这种场景进行了考虑并封装,我们可以这样写来做到这种效果:
EasyHttp.post(this)
        .api(new XxxApi())
        .delay(3000)
        .request(new HttpCallbackProxy<HttpData<XxxBean>>(this) {

            @Override
            public void onHttpSuccess(HttpData<XxxBean> result) {

            }
        });
  • 延迟请求功能不仅支持同步和异步请求,还支持下载请求延迟。

作者的其他开源项目

微信公众号:Android轮子哥

Android 技术 Q 群:10047167

如果您觉得我的开源库帮你节省了大量的开发时间,请扫描下方的二维码随意打赏,要是能打赏个 10.24 🐵就太👍了。您的支持将鼓励我继续创作:octocat:点击查看捐赠列表

特别感谢

License

Copyright 2019 Huang JinQun

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

easyhttp's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

easyhttp's Issues

一些看法

你说Retrofit的一些缺点,我是认同的。但是你说的维护性其实是不认同的。
一,Retrofit不支持动态参数修改是个败笔,但是一个接口一个类的时代已经过去了,真的不建议再弄回来。与其造轮子,不如写个类似Retrofit的库,开放出动态修改参数这样的功能即可。
二,下载的功能着实有些鸡肋,没有贬低的意思,网络请求就好好做网络请求就好了。

提 issue 需知

Github沟通效率实在是很低下,大家伙有问题或者Bug直接到Q群78797078私信给我反馈就OK,我会尽快答复你的问题

Kotlin 使用EasyHttp 会自动添加 Companion 参数

使用的库版本:
api 'com.hjq:http:8.6'
api 'com.squareup.okhttp3:okhttp:4.8.0'
api 'com.google.code.gson:gson:2.8.6'

请求的对象封装:

class ApiGetAuthCode : BaseApi() {
    @HttpRename("email")
    private var email: ReqData? = null

    class ReqData{
        @HttpRename("userName")
        lateinit var userName:String
    }

    override fun getApi(): String {
        return "url" //这是一个合法的url
    }

    class Result{
    }
    companion object{
        fun param(email:String):ApiGetAuthCode{
            val api = ApiGetAuthCode()
            val data = ReqData()
            data.userName = email
            api.email = data
            return api
        }
    }
}

请求的日志:

D/EasyHttp: RequestMethod = POST
D/EasyHttp: ----------------------------------------
D/EasyHttp:  
    {
    	"Companion": {
    		
    	},
    	"email": {
    		"userName": "[email protected]"
    	}
    }
    ----------------------------------------

其中Companion字段是我们并不需要的字段,有没有某种方式可以隐藏掉这种字段呢?还是说我必须得使用Java代码来进行相对应的封装?
另外,我目前为止还没有看到返回的日志,我想这个库应该是支持的吧?
期待能够尽早回应,谢谢

父类和子类存在相同属性时解析报错

implementation 'com.hjq:http:9.2'

错误:

class com.xxx.bean.XxxBean declares multiple JSON fields named createTime
    com.hjq.http.exception.HttpException: class com.xxx.bean.XxxBean declares multiple JSON fields named createTime
        at com.cloudwave.lvzaimo.http.model.RequestHandler.requestFail(RequestHandler.java:197)
        at com.hjq.http.callback.NormalCallback.onFailure(NormalCallback.java:61)
        at com.hjq.http.callback.BaseCallback.onResponse(BaseCallback.java:56)
        at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)

为什么父类和子类存在相同属性呢,原本打算是通用属性放到父类,但因为用了greendao的库,greendao在保存对象的时候不会去读取父类的属性生成列因此只能在某个子类里加上跟父类相同的属性。但这有导致用'com.hjq:http:9.2'时报错。

动态链接,在链接上数据是动态传的用EasyHttp如何实现

作者大佬您好! 请问一下动态参数在get链接里面,不是直接在后面添加参数,不是多域名,是动态链接 就是在链接上数据是动态传的,彩云的天气api `这个用EasyHttp如何实现 代码如下

/ /今天的天气 v2/ 后的是彩云的AppId
@GET("v2/xxxxx/GK/ {longitude}, {latitude}/realtime.json")
Observable<HomeWeatherTodayEntity> saveHomeWeatherTodayRx (@Header ("Cache-Control") String cacheControl,
                                                          (@Path("longitude") String longitude),
                                                          (@Path("latitude") String latitude );

混淆问题

能否详细说说怎么进行混淆,按照DEMO上的混淆规则打包后无法正常使用框架,但是程序没有闪退

一个功能问题,一个设计问题

  1. 请求加密问题
    比如我们的APP请求都是对内容加密的,目前的方案是在intercept做加密,在RequestHandler的success和fail里面做解密,但总感觉不太合适,能否提供一个方法,这个方法是在success和fail的前面调用的,我们可以在这里做统一的解密

  2. 拦截器设计问题
    登录问题,目前只看到在返回的时候判断返回的code里面的值判断是否是未登录状态。但实际上更好的办法是在客户端请求的时候就应该先判断本地token是否存在或者是否过期,如果不存在或者过期直接回调不用请求服务端。这个token判断是否过期用户可以放到intercept里面自己判断,另外IRequestApi应该提供或者用户自己定义一个注解,标识此接口是否需要登录才能调用,这些都可以在拦截器里实现。但目前拦截器的实现只是添加参数,拦截器的本质没有体现出来,就是不符合的请求要能在客户端拦截掉不需要请求服务端了。所以建议拦截器的方法里支持返回boolean,用户根据业务判断后如果需要继续请求就返回true,如果不需要继续请求而是直接回调结束那就返回false。

以上就是个人的使用体会,作者可以考虑一下。

如何配置多个域名

不是正式测试服的配置,我除了正式的域名外还需要调用其他的域名,有动态配置的方法吗

建议:是否可以添加可以选择性不上传某个字段。即便是设置了@HttpRename

项目中会有这样的一种需求。某些字段在某些情况下会参与请求,某些情况下会参与请求。
如:

@HttpRename("param1")
String param1;

@HttpRename("param2)
String param2;

在场景A中需要仅请求 param1 参数
在场景B中需要仅请求 param2 参数
在场景C中需要两个参数同时请求。
如果按照现在的注解方案,需要写三份Java代码,针对于一个请求url。

请问有对于这种使用状况的一种改善方案吗?

获取列表数据如何解析

比如获取github api,获取个人仓库列表。我这边老是解析失败,raw response是成功返回了。
https://api.github.com/users/{用户名}/repos

发起请求:
// 获取验证码
EasyHttp.get(this)
.api(new ListInfoApi()
.setUserName("用户名"))
.request(new HttpCallback<HttpListData>(this) {
@OverRide
public void onStart(Call call) {
super.onStart(call);
}

                @Override
                public void onSucceed(HttpListData<ReposInfo> data) {
                    Log.d("xxx","data:"+data.toString());

                }

                @Override
                public void onFail(Exception e) {
                    super.onFail(e);
                    Log.d("xxx","onFail:e:"+e.getMessage());
                }
            });
}

jiava bean:
public final class HttpListData extends HttpData<HttpListData.ListBean> {
public static class ListBean {

    /** 当前页码 */
    private int pageIndex;
    /** 页大小 */
    private int pageSize;
    /** 总数量 */
    private int totalNumber;
    /** 数据 */
    private List<T> 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<T> getItems() {
        return items;
    }
}

}

关于post请求不能用gson模式问题?

您好,我发现这个框架post请求只能用表单形式么,如果使用json形式就返回错误,我看了您的demo,启用gson模式后 @OverRide public BodyType getType() { // 参数以 Json 格式提交(默认是表单) return BodyType.JSON; },就会报错,但是我们公司现在api必须要用gson形式的,请指教

返回数据拦截判断?

请问可以对返回的数据进行拦截判断吗,比如返回的JSON数据中Code字段判断到指定的值,就当错误处理,产生onFail回调。

isBeanType对Null的判断问题

    /**
     * 判断对象是否为 Bean 类
     */
    public static boolean isBeanType(Object object) {
        // Number:Long、Integer、Short、Double、Float、Byte
        // CharSequence:String、StringBuilder、StringBuilder
        return !(object instanceof Number || object instanceof CharSequence ||
                object instanceof Boolean || object instanceof File ||
                object instanceof InputStream || object instanceof RequestBody ||
                object instanceof Character || object instanceof JSONObject ||
                object instanceof JSONArray);
    }

这里如果传过来的参数是空也认为是对象,但实际上有时候是封装类型,只不过值为空而已。
所以当参数结构如下时:

public class XXApi implements IRequestApi, Serializable {
    private String name;
    private Map<String, Object> params;

如果往params里面添加一个值为null的属性时提交请求会报错。
异常代码:

case JSON:
                        if (value instanceof List) {
                            // 如果这是一个 List 参数
                            params.put(key, EasyUtils.listToJsonArray(((List<?>) value)));
                        } else if (value instanceof Map) {
                            // 如果这是一个 Map 参数
                            params.put(key, EasyUtils.mapToJsonObject(((Map<?, ?>) value)));
                        } else if (EasyUtils.isBeanType(value)) {
                            // 如果这是一个 Bean 参数
                            params.put(key, EasyUtils.mapToJsonObject(EasyUtils.beanToHashMap(value)));
                        } else {
                            // 如果这是一个普通的参数
                            params.put(key, value);
                        }
                        break;
                    default:

由于isBeanType的判断错误导致调用了beanToHashMap,而此时的value是null,所以就异常了。

Socket 快速连接

可以增加一个实现 WebSocket 请求连接的封装工具类吗,用于快速实现 WebSocket 请求连接。选择性导入包的。Socket太麻烦了

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.