鸿蒙(HarmonyOS)版Retrofit网络请求框架

news/2024/7/21 11:23:11 标签: harmonyos, retrofit, 网络, 华为, 微服务, 鸿蒙开发, NAPI

注意

从3.0开始,官方已经废弃Java了。鸿蒙最终选择了高效简洁的JS/eTS语言为主要开发语言,即从3.0 Beta开始,鸿蒙将重心主要放在JS类Web式、eTS声明式两大类开发范式,兼容C/C++类。Java类API不再演进,但是会持续运营维护。我还是会维护该库,但推荐大家去学JS/eTS。

一、介绍

蒹葭(JianJia)是一款鸿蒙系统上的网络请求框架,其实就是将安卓的Retrofit移植到鸿蒙系统上,我将鸿蒙版的Retrofit命名为蒹葭(JianJia)。蒹葭不仅能实现Retrofit的功能,还会提供一些Retrofit没有的功能。Retrofit不支持动态替换域名,国内的应用一般都是有多个域名的,蒹葭支持动态替换域名。

二、添加依赖

2、1 在项目根目录下的build.gradle文件中添加mavenCentral()仓库,打开项目根目录下的build.gradle文件,在build.gradle文件的repositories闭包下面添加mavenCentral()

buildscript {
    repositories {
        // 添加maven中央仓库
        mavenCentral()
        maven {
            url 'https://mirrors.huaweicloud.com/repository/maven/'
        }
        maven {
            url 'https://developer.huawei.com/repo/'
        }
        maven {
            url 'http://maven.aliyun.com/nexus/content/repositories/central/'
        }
        jcenter()
    }
    dependencies {
        classpath 'com.huawei.ohos:hap:2.4.2.5'
        classpath 'com.huawei.ohos:decctest:1.0.0.6'
    }
}

allprojects {
    repositories {
        // 添加maven中央仓库
        mavenCentral()
        maven {
            url 'https://mirrors.huaweicloud.com/repository/maven/'
        }
        maven {
            url 'https://developer.huawei.com/repo/'
        }
        maven {
            url 'http://maven.aliyun.com/nexus/content/repositories/central/'
        }
        jcenter()
    }
}

2、2 打开entry目录下的build.gradle文件中,在build.gradle文件中的dependencies闭包下添加下面的依赖。

// 蒹葭的核心代码
implementation 'io.gitee.zhongte:jianjia:1.0.3'
// 数据转换器,数据转换器使用gson来帮我们解析json,不需要我们手动解析json
implementation 'io.gitee.zhongte:converter-gson:1.0.2'
implementation "com.google.code.gson:gson:2.8.2"
// 日志拦截器,通过日志拦截器可以看到请求头、请求体、响应头、响应体
implementation 'com.squareup.okhttp3:logging-interceptor:3.7.0'
// 如果服务端返回的json有特殊字符,比如中文的双引号,gson在解析的时候会对特殊字符进行转义
// 这时就需要将转义后的字符串进行反转义,commons-lang可以对特殊字符进行转义和反转义
implementation 'commons-lang:commons-lang:2.6'

2、3 在配置文件中添加如下的权限

ohos.permission.INTERNET

三、用法

创建接口,在方法里面使用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());
    }
});

在上面的示例代码中,接口里面定义的方法的方法的返回值是Call对象,泛型是ResponseBody。 在这种情况下,服务端返回给端上的数据就会在ResponseBody里面,端上需要手动解析json, 将json解析成一个实体类。其实,我们没必要手动解析json,可以让gson帮我们解析json。 蒹葭支持添加数据转换器,在创建对象的时候添加数据转换器,也就是把gson添加进来。 在onResponse方法里面就可以直接得到实体类对象了,gson帮我们把json解析成了一个实体对象。 如下代码,在onResponse方法里面调用response.body()body方法直接返回了一个banner对象。 gson帮我们把服务端返回的json解析成banner对象,这样我们就可以直接使用banner对象,不需要我们手动解析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()) {
                // json已经被解析成banner对象了
                Banner banner = response.body();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    @Override
    public void onFailure(Call<Banner> call, Throwable t) {
        LogUtils.info("yunfei", t.getMessage());
    }
});

四、示例代码效果

在源码的entry目录下提供了示例代码,代码运行结果请查看上图。 上图显示的一个网页上的内容,端上使用蒹葭网络库访问该网站提供的接口来 获取首页的文章列表,当请求成功后,将文章列表显示在页面上。 目前只获取了第一页的文章列表,有兴趣的同学可以自行实现分页加载。 demo刚运行的时候页面白屏,那是因为此时正在请求网络,正常情况下,应当加个进度条, 只不过示例中没有进度条。在运行示例代码时,如果在安装的时候出现INSTALL_PARSE_FAILED_USESDK_ERROR, 请把config.json文件中的"releaseType": "Beta1"删除。

五、示例代码讲解

5、1 在示例代码中的com.poetry.jianjia.net包下面创建了如下的接口,把所有的请求放在一个接口里面即可, 没必要创建多个接口类。

/**
 * @author 裴云飞
 * @date 2021/1/23
 */
public interface Wan {

    @GET("article/list/{page}/json")
    Call<ResponseBody> getArticle(@Path("page") int page);

    @GET("article/list/{page}/json")
    Call<Article> getHomeArticle(@Path("page") int page);

    @GET()
    Call<ResponseBody> getArticle(@Url String url);

    @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);

    @GET("wxarticle/list/405/1/json")
    Call<ResponseBody> search(@QueryMap Map<String, String> param);

    @GET("article/list/0/json")
    Call<ResponseBody> getArticle(@QueryMap Map<String, String> param);

    @BaseUrl("https://api.apiopen.top")
    @GET("getJoke")
    Call<ResponseBody> getJoke(@QueryMap Map<String, String> param);

    @POST("user/login")
    @FormUrlEncoded
    Call<ResponseBody> login(@Field("username") String username, @Field("password") String password);

    @POST("user/login")
    @FormUrlEncoded
    Call<ResponseBody> login(@FieldMap Map<String, String> map);

    @GET("banner/json")
    Call<Banner> getBanner();
}

5、2 创建jianjia对象,整个项目只需一个jianjia对象即可,如何确保只有一个 jianjia对象?当代码运行起来后,首先会创建AbilityPackage对象, 调用AbilityPackageonInitialize方法,AbilityPackage执行完成后 才会启动AbilityAbilityPackage就是一个全局的单例,所以在 AbilityPackage里面创建的对象就是一个单例对象。只需在AbilityPackage 里面创建jianjia对象,就能确保整个项目只有一个jianjia对象。 AbilityPackage这个类不需要手动创建,在创建的项目的时候, 编译器会自动创建一个继承于AbilityPackage的类。

public class BaseApplication extends AbilityPackage {

    private static BaseApplication instance;

    private JianJia mJianJia;
    private Wan mWan;

    public static BaseApplication getInstance() {
        return instance;
    }

    /**
     * 获取全局的蒹葭对象
     *
     * @return 全局的蒹葭对象
     */
    public JianJia getJianJia() {
        return mJianJia;
    }

    /**
     * 获取全局的接口实例对象
     *
     * @return 全局的接口实例对象
     */
    public Wan getWan() {
        return mWan;
    }

    @Override
    public void onInitialize() {
        super.onInitialize();
        instance = this;
        // 创建全局的蒹葭对象
        mJianJia = new JianJia.Builder()
                .baseUrl("https://www.wanandroid.com")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        mWan = mJianJia.create(Wan.class);
    }
}

如上代码,BaseApplication继承AbilityPackage,在onInitialize 方法创建全局的蒹葭对象。同时,整个项目只创建了一个接口类,所以可以在创建完蒹葭对象后 直接调用蒹葭的create方法来创建接口的实例对象。其它地方只需要通过下面的方式即可获取蒹葭对象和接口实例对象。

// 获取全局的蒹葭对象
BaseApplication.getInstance().getJianJia();
// 获取全局的接口实例对象
BaseApplication.getInstance().getWan();

5、3 在MainAbilitySlice里面添加ListContainer,关于ListContainer的用法,请查看  官方文档 ,这里不过多介绍了。接着调用getHomeArticle方法请求服务器,getHomeArticle方法会获取在AbilityPackage里面创建在接口实例对象来执行网络请求, 请求成功后调用setHomeArticle方法来刷新页面。

public class MainAbilitySlice extends AbilitySlice {

    private ListContainer mListContainer;
    private HomeArticleProvider mHomeArticleProvider;
    List<Article.Data.Datas> mDatas;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);
        mListContainer = (ListContainer) findComponentById(ResourceTable.Id_list);
        mDatas = new ArrayList<>();
        mHomeArticleProvider = new HomeArticleProvider(this, mDatas);
        mListContainer.setItemProvider(mHomeArticleProvider);
        // 从服务端获取数据
        getHomeArticle();
    }

    /**
     * 从服务端获取数据
     */
    public void getHomeArticle() {
        BaseApplication.getInstance().getWan().getHomeArticle(0).enqueue(new Callback<Article>() {
            @Override
            public void onResponse(Call<Article> call, Response<Article> response) {
                if (response.isSuccessful()) {
                    // 请求成功
                    setHomeArticle(response.body());
                }
            }

            @Override
            public void onFailure(Call<Article> call, Throwable t) {
                // 请求失败
                LogUtils.info("yunfei", t.getMessage());
            }
        });
    }

    @Override
    public void onActive() {
        super.onActive();
    }

    @Override
    public void onForeground(Intent intent) {
        super.onForeground(intent);
    }

    public void setHomeArticle(Article article) {
        if (article == null || article.data == null || article.data.datas == null) {
            return;
        }
        mDatas.addAll(article.data.datas);
        // 刷新列表
        mHomeArticleProvider.notifyDataChanged();
    }

}

5、4 解决转义字符问题。如果服务端返回的json有特殊字符,比如中文的双引号。 gson在解析的时候会对特殊字符进行转义,这时就需要将转义后的字符串进行反转义。如下图所示

如何将转义后的字符串进行反转义?commons-lang这个库可以将转义后的字符串进行反转义 在build.gradle文件添加下的依赖

// commons-lang可以对特殊字符进行转义和反转义
implementation 'commons-lang:commons-lang:2.6'

调用StringEscapeUtilsunescapeHtml方法,如果字符串中没有转义字符,unescapeHtml方法 会直接返回原字符串,否则会对字符串进行反转义。具体的代码可查看示例代码中的HomeArticleProvider类。

// json里面有一些特殊符号,特殊符号会被gson转义,
// StringEscapeUtils可以对转义的字符串进行反转义
String title = StringEscapeUtils.unescapeHtml(data.title);
componentHolder.title.setText(title);

反转义之后,特殊字符正常显示

六、获取请求和响应日志

6、1 可以通过charles、fiddler等抓包工具来查看到完整的请求和响应信息。关于抓包,可以自行搜索相关文章。
6、2 使用拦截器打印日志,添加下面的依赖

implementation 'com.squareup.okhttp3:logging-interceptor:3.7.0'

给OkHttp添加拦截器。

// 创建日志拦截器
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
// 为OKHTTP添加日志拦截器
OkHttpClient okHttpClient = new OkHttpClient.Builder()
    .addInterceptor(logging)
    .build();
// 创建全局的蒹葭对象
mJianJia = new JianJia.Builder()
    // 使用自定义的okHttpClient对象
    .callFactory(okHttpClient)
    .baseUrl("https://www.wanandroid.com")
    .addConverterFactory(GsonConverterFactory.create())
    .build();
mWan = mJianJia.create(Wan.class);

经过上面的配置,就可以打印日志了

七、混淆

如果项目开启了混淆,请在proguard-rules.pro添加如下的代码。关于混淆,可以查看 鸿蒙代码配置混淆

-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable
-dontwarn javax.annotation.**
-keepattributes Signature, InnerClasses, EnclosingMethod, Exceptions
# 蒹葭
-dontwarn poetry.jianjia.**
-keep class poetry.jianjia.** { *; }
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations
-keepclassmembers,allowshrinking,allowobfuscation interface * {
    @poetry.jianjia.http.* <methods>;
}

# OkHttp3
-dontwarn okhttp3.logging.**
-keep class okhttp3.internal.**{*;}
-dontwarn okio.**

# gson
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.stream.** { *; }
-keepattributes *Annotation*
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}
# 在我的示例代码中,com.poetry.jianjia.bean这个包下面的类实现了Serialized接口,
# 实现了Serialized接口的类不能被混淆,请把com.poetry.jianjia.bean这个包名替换成你自己的包名
-keep class com.poetry.jianjia.bean.**{*;}

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.
鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向


http://www.niftyadmin.cn/n/5450389.html

相关文章

【分布式】——分布式事务

分布式事务 ⭐⭐⭐⭐⭐⭐ Github主页&#x1f449;https://github.com/A-BigTree 笔记链接&#x1f449;https://github.com/A-BigTree/tree-learning-notes ⭐⭐⭐⭐⭐⭐ Spring专栏&#x1f449;https://blog.csdn.net/weixin_53580595/category_12279588.html SpringMVC专…

数据库审计和安全

互联网、云计算、物联网等新技术的应用&#xff0c;数据安全面临前所未有的挑战!我国信息安全已从终端安全、网络安全&#xff0c;发展到数据安全建设阶段。数据安全的核心是对“数据”全方位的安全防护&#xff0c;其产品及解决方案直接涉及国家和企业的核心机密 核心数据库存…

通过MobaXterm工具远程连接可视化服务器桌面并操控

目录 一、MobaXterm工具二、MobaXterm工具可视化服务器目录三、MobaXterm工具可视化服务器桌面 一、MobaXterm工具 MobaXterm是一款功能强大的远程连接工具&#xff0c;可以用于连接到各种类型的服务器&#xff0c;包括Linux、Windows和MacOS。它支持多种协议&#xff0c;包括…

矩阵最大路径与

题目描述 东九日在学习dp的时候&#xff0c;解决了经典的矩阵最大路径和问题&#xff1b; 他向队友小夨阐述他的感悟&#xff0c;dp要做的就是感受解空间&#xff1b; 为了防止东九日赛上犯病&#xff0c;小夨决定出一道改编版检查东九日的实力。 以上为题目背景&#xff1b…

使用LLaVA模型实现以文搜图和以图搜图

本文将会详细介绍如何使用多模态模型——LLaVA模型来实现以文搜图和以图搜图的功能。本文仅为示例Demo&#xff0c;并不能代表实际的以文搜图和以图搜图的技术实现方案。 1、实现原理 使用多模态模型获取图片的标题和详细描述以文搜图功能&#xff1a;使用ES实现查询匹配&…

15、Spring Cloud Alibaba Sentinel实现熔断与限流

注&#xff1a;本篇文章主要参考周阳老师讲解的cloud进行整理的&#xff01; 1、Sentinel 1.1、官网 https://sentinelguard.io/zh-cn/ 等价对标 Spring Cloud Circuit Breaker 1.2、是什么 https://github.com/alibaba/Sentinel/wiki 1.3、去哪下 https://github.com/alibab…

分类模型评估:混淆矩阵与ROC曲线

1.混淆矩阵2.ROC曲线 & AUC指标 理解混淆矩阵和ROC曲线之前&#xff0c;先区分几个概念。对于分类问题&#xff0c;不论是多分类还是二分类&#xff0c;对于某个关注类来说&#xff0c;都可以看成是二分类问题&#xff0c;当前的这个关注类为正类&#xff0c;所有其他非关注…

Vue3 + Vite + TS + Element-Plus + Pinia项目(5)对axios进行封装

1、在src文件夹下新建config文件夹后&#xff0c;新建baseURL.ts文件&#xff0c;用来配置http主链接 2、在src文件夹下新建http文件夹后&#xff0c;新建request.ts文件&#xff0c;内容如下 import axios from "axios" import { ElMessage } from element-plus im…