自从鸿蒙手机版发布以来,我就一直在作移植的工做,将安卓代码移植到鸿蒙系统上。Retrofit是安卓系统上一款优秀的网络请求框架,鸿蒙系统并无相似的网络请求框架。因而,我决定实现一套鸿蒙版的Retrofit。java
蒹葭(JianJia)是一款鸿蒙系统上的网络请求框架,其实就是将安卓的Retrofit移植到鸿蒙系统上,我将鸿蒙版的Retrofit命名为蒹葭(JianJia)。蒹葭不只能实现Retrofit的功能,还会提供一些Retrofit没有的功能。Retrofit不支持动态替换域名,国内的应用通常都是有多个域名的,蒹葭支持动态替换域名。Retrofit并不可以直接添加拦截器,只能经过okhttp来添加拦截器,蒹葭会支持添加拦截器。android
Demo演示视频戳此git
注:文档附件在最下面编程
https://gitee.com/zhongte/JianJiajson
要想读懂源码,须要具有如下技能。设计模式
熟悉okhttp的常见用法 api
熟悉面向接口编程、反射、泛型、注解数组
熟悉构造者模式、适配器模式、工厂模式、策略模式、静态代理、动态代理、责任链模式等设计模式网络
蒹葭提供了一系列的注解,在进行网络请求的时候,就须要用到这些注解。框架
建立接口,在方法里面使用GET注解,GET注解用于标识这是一个GET请求,方法的返回值是Call对象,泛型是ResponseBody,其实泛型也能够是具体的实体对象,这个后面再说。蒹葭如何完成网络请求?使用构造者模式建立jianjia对象,baseUrl就是域名,在建立jianjia对象的时候就必须指定域名。调用create方法来生成接口的实例,调用wan.getBanner().enqueue来执行网络请求,请求成功就会回调onResponse方法,请求失败就会回调onFailure方法
public interface Wan { @GET("banner/json") Call<ResponseBody> getBanner(); } JianJia jianJia = new JianJia.Builder() .baseUrl("https://www.wanandroid.com") .build(); Wan wan = jianJia.create(Wan.class); wan.getBanner().enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { try { String json = response.body().string(); } catch (IOException e) { e.printStackTrace(); } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { LogUtils.info("yunfei", t.getMessage()); } });
国内的应用通常都是有多个域名的,BaseUrl注解能够对某个接口设置单独的域名。
public interface Wan { @BaseUrl("https://api.apiopen.top") @GET("getJoke") Call<ResponseBody> getJoke(@QueryMap Map<String, String> param); }
Path注解在路径中替换指定的参数值,定义下面的方法。能够看到咱们定义了一个getArticle方法,方法接收一个page参数,而且咱们的@GET注解中使用{page}声明了访问路径,这里你能够把{page}当作占位符,而实际运行中会经过@Path("page")所标注的参数进行替换。
public interface Wan { @GET("article/list/{page}/json") Call<ResponseBody> getArticle(@Path("page") int page); }
Query注解用于给get请求添加请求参数,被Query注解修饰的参数类型能够是数组、集合、字符串等
public interface Wan { @GET("wxarticle/list/405/1/json") Call<ResponseBody> search(@Query("k") String k); @GET("wxarticle/list/405/1/json") Call<ResponseBody> search(@Query("k") String... k); @GET("wxarticle/list/405/1/json") Call<ResponseBody> search(@Query("k") List<String> k); }
QueryMap注解以map的形式添加查询参数,被QueryMap注解修饰的参数类型必须是Map对象
public interface Wan { @GET("wxarticle/list/405/1/json") Call<ResponseBody> search(@QueryMap Map<String, String> param); }
在鸿蒙系统下,默认会将服务端的响应回调到主线程,若是在方法上使用SkipCallbackExecutor注解,那就不会将服务端的结果回调到主线程
public interface Wan { @SkipCallbackExecutor @GET("wxarticle/list/405/1/json") Call<ResponseBody> search(@QueryMap Map<String, String> param); }
FormUrlEncoded注解用于发送一个表单请求,使用该注解必须在方法的参数添加Field注解,被Field注解修饰的参数类型能够是数组、集合、字符串等
public interface Wan { @POST("user/login") @FormUrlEncoded Call<ResponseBody> login(@Field("username") String username, @Field("password") String password); }
有时候表单的参数会比较多,若是使用Field注解,方法的参数就会比较多,此时就可使用FieldMap注解,FieldMap注解以map的形式发送一个表单请求。若是被FieldMap注解修饰的参数不是Map类型,就会抛异常。若是Map的键值对为空,也会抛异常。
public interface Wan { @POST("user/login") @FormUrlEncoded Call<ResponseBody> login(@FieldMap Map<String, String> map); }
服务端会要求端上把json字符串做为请求体发给服务端。此时就可使用Body注解定义的参数能够直接传入一个实体类,内部会把该实体序列化并将序列化后的结果直接做为请求体发送出去。
若是被Body注解修饰的参数的类型是RequestBody对象,那调用者能够不添加数据转换器,内部会使用默认的数据转换器
若是被Body注解修饰的参数的类型不是RequestBody对象,是一个具体的实体类,那调用者须要自定义一个类,而且继承Converter.Factory
public interface Wan { /** * 被Body注解修饰的参数的类型是RequestBody对象,那调用者能够不添加数据转换器,内部会使用默认的数据转换器 * * @param body * @return */ @POST("user/register") Call<ResponseBody> register(@Body RequestBody body); /** * 被Body注解修饰的参数的类型不是RequestBody对象,是一个具体的实体类,那调用者须要自定义一个类,而且继承Converter.Factory * * @param user * @return */ @POST("user/register") Call<ResponseBody> register(@Body User user); }
Url注解用于添加接口的完整地址。在Retrofit里面,若是接口的域名与建立retrofit对象指定的域名不相同,那就会使用Url注解来解决问题。在蒹葭里面一样可使用Url注解来解决问题,但蒹葭还提供了BaseUrl来解决该问题。
public interface Wan { @GET() Call<ResponseBody> getArticle(@Url String url); }
Headers注解是做用于方法上的注解,用于添加一个或多个请求头。
public interface Wan { @Headers("Cache-Control: max-age=640000") @GET("/") Call<ResponseBody> getArticle(@Url String url); @Headers({ "X-Foo: Bar", "X-Ping: Pong" }) @GET("/") Call<ResponseBody> getArticle(@Url String url); }
Header注解是做用于参数上的注解,用于添加请求头
public interface Wan { @GET() Call<ResponseBody> foo(@Header("Accept-Language") String lang); }
HeaderMap注解是做用于参数上的注解,以map的形式添加请求头,map中每一项的键和值都不能为空,不然会抛异常
public interface Wan { @GET("/search") Call<ResponseBody> list(@HeaderMap Map<String, String> headers); }
以前咱们在接口里面定义方法的时候,方法的返回值时Call对象,泛型是ResponseBody。在这种状况下,服务端返回给端上的数据就会在ResponseBody里面,端上须要手动解析json,将json解析成一个实体类。
其实,咱们不必手动解析json,可让gson帮咱们解析json。蒹葭支持添加数据转换器,在建立对象的时候添加数据转换器,也就是把gson添加进来。在onResponse方法里面就能够直接获得实体类对象了,gson帮咱们把json解析成了一个实体类。
public interface Wan { @GET("banner/json") Call<Banner> getBanner(); } JianJia jianJia = new JianJia.Builder() .baseUrl("https://www.wanandroid.com") .addConverterFactory(GsonConverterFactory.create()) .build(); Wan wan = jianJia.create(Wan.class); wan.getBanner().enqueue(new Callback<Banner>() { @Override public void onResponse(Call<Banner> call, Response<Banner> response) { try { if (response.isSuccessful()) { Banner banner = response.body(); } } catch (IOException e) { e.printStackTrace(); } } @Override public void onFailure(Call<Banner> call, Throwable t) { LogUtils.info("yunfei", t.getMessage()); } });
六、1 上传文件也须要使用注解,目前蒹葭尚未上传文件的注解,上传文件的注解正在开发当中。
六、2 蒹葭目前还不支持直接添加拦截器,后续会把拦截器的功能加上。
六、3 网络请求框架的二次封装,网络请求框架的二次封装应当隔离掉第三方网络框架,若是未来有更好的网络框架,或者公司要求使用公司自研的网络框架,那就能够很容易的替换掉以前的网络框架,而不须要大改特改。
六、4 因为疫情缘由,我估计得在北京过年了,没法回老家过年。因此能够趁放假的时候,给你们录视频。手把手的叫你们实现本身的网络框架,但愿可以帮助你们理解蒹葭的实现原理。固然了,蒹葭的实现原理跟Retrofit的原理是如出一辙的。自己蒹葭就是从Retrofit移植过来的,只不过蒹葭能够运行在鸿蒙系统上。
原文连接:https://developer.huawei.com/consumer/cn/forum/topic/0204470884054220049?fid=0101303901040230869
原做者:义薄云天小关羽