文档管理中心

【开发者说】HarmonyOS如何实现一套网络请求框架

本文作者:裴云飞,爱奇艺应用开发工程师

本期我们为大家带来的是开发者裴云飞投稿的“HarmonyOS网络请求框架实现”,这个网络请求框架被命名为“蒹葭(JianJia)”。其原理是将Retrofit移植到HarmonyOS上,同时还实现一些Retrofit所不具备的功能。

比如:Retrofit不支持动态替换域名。众所周知,国内的应用一般都是有多个域名的,蒹葭的一大优势就是支持动态替换域名。如想了解更多内容,可以直接复制链接看源码哦:https://gitee.com/zhongte/JianJia

接下来我们将挑选几个关键点做解释,希望能给你的HarmonyOS开发之旅带来一些启发~

在敲代码之前,需要大家下载安装“Huawei DevEco Studio”,如有疑问,可参考官网指南。

●  Huawei DevEco Studio安装指南:

https://developer.harmonyos.com/cn/docs/documentation/doc-guides/software_install-0000001053582415

为了保证顺利使用,我们先来看一下使用前的一些配置,这里我们主要介绍两个方面配置,一个是针对代码混淆的处理,一个是添加依赖。

一、使用前的配置

1. 代码混淆处理

如果项目开启了代码混淆,请在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.**{*;}

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)在build.gradle文件的dependencies闭包下添加依赖

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

收起
自动换行
深色代码主题
复制
// 蒹葭的核心代码
implementation 'io.gitee.zhongte:jianjia:1.0.1'
// 数据转换器,数据转换器使用gson来帮我们解析json,不需要我们手动解析json
implementation 'io.gitee.zhongte:converter-gson:1.0.1'
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'

3)在配置文件中添加以下权限

收起
自动换行
深色代码主题
复制
ohos.permission.INTERNET

二、用法

蒹葭与Retrofit用法相同,并且蒹葭提供一系列的注解。“注解”可以看作是对一个类/方法的一个扩展的模版,每个类/方法按照注解类中的规则,来为类/方法注解不同的参数,在用到的地方可以得到不同的类/方法中注解的各种参数与值。在进行网络请求的时候,就需要用到这些注解,下文将介绍蒹葭中常用和需要注意的注解。

1. GET注解

创建接口,在方法里面使用GET注解,GET注解用于标识这是一个GET请求,方法的返回值是Call对象,泛型是ResponseBody,其实泛型也可以是具体的实体对象,我们将在下文“16.添加数据转换器“中详说。

蒹葭如何完成网络请求?使用构造者模式创建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());
    }
});

2. BaseUrl注解

国内的应用一般都是有多个域名的,BaseUrl注解可以对某个接口设置单独的域名。

收起
自动换行
深色代码主题
复制
public interface Wan {
    @BaseUrl("https://api.apiopen.top")
    @GET("getJoke")
    Call<ResponseBody> getJoke(@QueryMap Map<String, String> param);
}

3. Path注解

Path注解在路径中替换指定的参数值,定义下面的方法。可以看到我们定义了一个getArticle方法,方法接收一个page参数,并且我们的@GET注解中使用{page}声明了访问路径,这里你可以把{page}当做占位符,而实际运行中会通过@Path("page")所标注的参数进行替换。

收起
自动换行
深色代码主题
复制
public interface Wan {
    @GET("article/list/{page}/json")
    Call<ResponseBody> getArticle(@Path("page") int page);
}

4. Query注解

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

5. QueryMap注解

QueryMap注解以map的形式添加查询参数,被QueryMap注解修饰的参数类型必须是Map对象。

收起
自动换行
深色代码主题
复制
public interface Wan {
    @GET("wxarticle/list/405/1/json")
    Call<ResponseBody> search(@QueryMap Map<String, String> param);
}

6. SkipCallbackExecutor注解

在HarmonyOS中,默认将服务端的响应回调到主线程,如果在方法上使用SkipCallbackExecutor注解,就不会将服务端的结果回调到主线程。

收起
自动换行
深色代码主题
复制
public interface Wan {
    @SkipCallbackExecutor
    @GET("wxarticle/list/405/1/json")
    Call<ResponseBody> search(@QueryMap Map<String, String> param);
}

7. FormUrlEncoded注解和Field注解

“FormUrlEncoded注解”用于发送一个表单请求,使用该注解必须在方法的参数中添加Field注解,被Field注解修饰的参数类型可以是数组、集合、字符串等。

收起
自动换行
深色代码主题
复制
public interface Wan {
    @POST("user/login")
    @FormUrlEncoded
    Call<ResponseBody> login(@Field("username") String username, @Field("password") String password);
}

8. FormUrlEncoded注解和FieldMap注解

有时候表单的参数会比较多,如果使用Field注解,方法的参数就会比较多,此时就可以使用FieldMap注解,FieldMap注解以map的形式发送一个表单请求。如果被FieldMap注解修饰的参数不是Map类型,就会报异常。如果Map的键值对为空,也会报异常。

收起
自动换行
深色代码主题
复制
public interface Wan {
    @POST("user/login")
    @FormUrlEncoded
    Call<ResponseBody> login(@FieldMap Map<String, String> map);
}

9. Body注解

服务端会要求客户端把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);
}

10. Url注解

Url注解用于添加接口的完整地址。在Retrofit里面,如果接口的域名与创建retrofit对象指定的域名不相同,就会使用Url注解来解决问题。在蒹葭里面不但可以使用Url注解来解决问题,还提供了BaseUrl来解决该问题。

收起
自动换行
深色代码主题
复制
public interface Wan {
    @GET()    Call<ResponseBody> getArticle(@Url String url);
}

11. Header注解

Header注解是作用于参数上的注解,用于添加请求头。

收起
自动换行
深色代码主题
复制
public interface Wan {
    @GET()   Call<ResponseBody> foo(@Header("Accept-Language") String lang);
}

12. Headers注解

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

13. HeaderMap注解

HeaderMap注解是作用于参数上的注解,以Map的形式添加请求头,Map中每一项的键和值都不能为空,否则会报异常。

收起
自动换行
深色代码主题
复制
public interface Wan {
    @GET("/search")
    Call<ResponseBody> list(@HeaderMap Map<String, String> headers);
}

14. Multipart注解和Part注解

Multipart注解和Part注解用于上传单个文件。

在配置文件添加下面两个权限,这两个权限需要动态申请。

收起
自动换行
深色代码主题
复制
ohos.permission.READ_MEDIA
ohos.permission.WRITE_MEDIA

定义一个upload方法,方法参数使用Part注解修饰,参数类型必须是MultipartBody.Part。

收起
自动换行
深色代码主题
复制
public interface Wan {
    /**
    * 上传文件,需要使用Multipart注解和Part注解
    *
    * @param photo 本地文件的路径
    * @return
    */
    @Multipart
    @POST()
    Call<ResponseBody> upload(@Part MultipartBody.Part photo);
}
// 文件路径
File file = new File(getExternalCacheDir(), "icon.png");
// 创建请求体对象
RequestBody photoBody = RequestBody.create(MediaType.parse("image/png"), file);
MultipartBody.Part photo = MultipartBody.Part.createFormData("photos""icon.png", photoBody);
   
JianJia jianJia = new JianJia.Builder()
    .baseUrl("https://www.wanandroid.com")
    .build();
Wan wan = jianJia.create(Wan.class);
// 上传文件
wan.upload(photo).enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        // 上传成功
    }
    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        LogUtils.info("yunfei", t.getMessage());
    }
});

15. PartMap注解和Multipart注解

PartMap注解和Multipart注解用于上传多个文件在配置文件添加下面两个权限,这两个权限需要动态申请:

收起
自动换行
深色代码主题
复制
ohos.permission.READ_MEDIA
ohos.permission.WRITE_MEDIA

PartMap注解修饰的Map,Map的第一个泛型必须是String

收起
自动换行
深色代码主题
复制
public interface Wan {
    /**
     * 使用PartMap注解上传多个文件
     *
     * @param params 第一个泛型必须是String
     * @return
     */
     @Multipart
     @POST()
     Call<ResponseBody> upload(@PartMap Map<String, RequestBody> params);
}

File photoFile = new File(getExternalCacheDir(), "photo.png");
RequestBody photoBody = RequestBody.create(MediaType.parse("image/png"), photoFile);

File avatarFile = new File(getExternalCacheDir(), "avatar.png");
RequestBody avatarBody = RequestBody.create(MediaType.parse("image/png"), avatarFile);
Map<String, RequestBody> photos = new HashMap<>();
photos.put("photo", photoBody);photos.put("avatar", avatarBody);

JianJia jianJia = new JianJia.Builder()
        .baseUrl("https://www.wanandroid.com")
        .build();
        
Wan wan = jianJia.create(Wan.class);
// 上传文件
wan.upload(photos).enqueue(new Callback<ResponseBody>() {
    @Override 
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        // 上传成功
    }
    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        LogUtils.info("yunfei", t.getMessage());
    }
 });

16. 添加数据转换器

之前我们在接口里面定义方法(GET注解中)的时候,方法的返回值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(" 
        .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());
    }
});

三、Demo演示

我们将以一个简单的Demo来演示我们如何使用蒹葭,效果如下:

Video Player is loading.
Current Time 0:00
Duration 0:14
Loaded: 0%
Stream Type LIVE
Remaining Time 0:14
 
1x
    • Chapters
    • descriptions off, selected
    • subtitles off, selected

      ●  示例中的网页地址:https://www.wanandroid.com/blog/show/2

      视频内显示的是一个网页上的文章列表。客户端通过使用蒹葭网络库访问该网站提供的接口,以此来获取网页的文章列表,当请求成功后,文章列表将显示在模拟器页面上。

      以下为Demo代码关键步骤的详解:

      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();
      }

      2. 创建“jianjia”对象,整个项目只需一个jianjia对象即可。

      如何确保只有一个 jianjia对象?当代码运行起来后,首先会创建AbilityPackage对象,调用AbilityPackage的onInitialize方法,AbilityPackage执行完成后才会启动Ability。AbilityPackage就是一个全局的单例,所以在 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(" 
                      .addConverterFactory(GsonConverterFactory.create())
                      .build();
              mWan = mJianJia.create(Wan.class);
          }
      }

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

      收起
      自动换行
      深色代码主题
      复制
      // 获取全局的蒹葭对象
      BaseApplication.getInstance().getJianJia();
      // 获取全局的接口实例对象
      BaseApplication.getInstance().getWan();

      3. 在MainAbilitySlice里面添加ListContainer,关于ListContainer的用法,请查看 官方文档 ,此处不再赘述。

      ●  官方文档

      https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ui-java-component-listcontainer-0000001060007847

      接着调用“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 = (ListContainerfindComponentById(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();
          }
      }

      四、关于转义字符问题的解决

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

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

      收起
      自动换行
      深色代码主题
      复制
      // commons-lang可以对特殊字符进行转义和反转义
      implementation 'commons-lang:commons-lang:2.6'

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

      收起
      自动换行
      深色代码主题
      复制
      // json里面有一些特殊符号,特殊符号会被gson转义,
      // StringEscapeUtils可以对转义的字符串进行反转义
      String title = StringEscapeUtils.unescapeHtml(data.title);
      componentHolder.title.setText(title);

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

      温馨提示:如果开发者具备以下知识的基础,看源码的时会更加轻松哦。

      • 熟悉okhttp的常见用法

      • 熟悉面向接口编程、反射、泛型、注解

      • 熟悉构造者模式、适配器模式、工厂模式、策略模式、静态代理、动态代理、责任链模式等设计模式

      Demo刚运行的时候页面可能会出现白屏的情况,是因为此时正在请求网络。可以增加一个进度条来提升用户体验,大家可以自行添加~

      目前只获取了第一页的文章列表,有兴趣的开发者可以自行实现分页加载,让内容更加丰富,由于篇幅有限,此处只展示简单的示例。

      总的来说,蒹葭的原理跟Retrofit是一样的,如有兴趣,可点击链接查看源码:https://gitee.com/zhongte/JianJia

      学无止境,希望本期分享的“蒹葭(jianjia)网络请求框架”能给你新的启发。同时也欢迎更多开发者与我们共享开发成果、技术解读与经验心得,说不定下一期的主角就是你哦!

       

       HarmonyOS%E5%BC%80%E5%8F%91%E5%B0%8F%E5%8A%A9%E6%89%8B%E5%BE%AE%E4%BF%A1%E4%BA%8C%E7%BB%B4%E7%A0%812.jpgcke_408.gif

      扫码添加开发者小助手微信

      获取更多HarmonyOS开发资源和开发者活动资讯

      点赞
      13
      收藏
      12
      回复
      29
      分享
      举报
      浏览6381 编辑于2022-02-07 02:42未知归属地
      全部评论
      最多点赞
      最新发布
      最早发布
      怎么添加拦截器
      23楼回复于2022-07-04 11:16 未知归属地
      已完成HarmonyOS开发者授权
      21楼回复于2021-05-22 16:39 未知归属地
      已完成HarmonyOS开发者授权


      20楼回复于2021-05-21 18:28 未知归属地
      已完成HarmonyOS开发者授权
      19楼回复于2021-05-21 12:41 未知归属地
      普及的非常好,感谢楼主
      18楼回复于2021-05-21 12:37 未知归属地
      你们填完信息和实名认证后就让你绑定银行卡,然后就会告诉你,没法给你升级鸿蒙了!尊敬的开发者,您好!
      很遗憾地通知您,由于资源有限,您的OTA测试申请没有通过。
      17楼回复于2021-05-21 10:09 未知归属地

      Retrofit不支持动态替换域名???????????????????????????????

      1
      16楼回复于2021-05-21 08:16 未知归属地
      • 裴云飞

        retrofit要求在创建retrofit对象的时候,就要指定域名,后续所有的接口都使用这个域名。但在实际开发中,一个应用不仅仅只有一个域名,会有多个域名。也就是说,某个接口的域名跟你在创建retrofit对象时指定的域名不相同,这个时候怎么办?Url注解虽然可以解决问题,但Url注解是让你传入Url地址,并不是让你传入域名,Url注解并不能修改域名。当然了,也有一些方式来动态修改某个接口的域名,但方式有点复杂。蒹葭为了解决这个问题,提供了一个BaseUrl注解,如果某个接口的域名跟你在创建蒹葭对象时指定的域名不相同,那就可以使用BaseUrl注解,BaseUrl注解可以为特定的接口指定特定的域名。如下代码,getJoke接口的域名是https://www.example.com,getJoke接口的域名跟你在创建蒹葭对象时指定的域名不相同,此时就可以使用BaseUrl注解,将getJoke接口的域名指定为https://www.example.com。

        public interface Wan {
        
            @BaseUrl("https://www.example.com")
            @GET("getJoke")
            Call<ResponseBody> getJoke(@QueryMap Map<String, String> param);
        }


        2021-05-21 15:18 未知归属地
      已完成HarmonyOS开发者授权,跪求HarmonyOS 2.0开发者Beta公测资格,每贴必赞


      15楼回复于2021-05-21 03:31 未知归属地

      这个解析完的json数据什么样子的?这个是怎么把解析的数据添加到组件里边的??

      1
      14楼回复于2021-05-20 17:17 未知归属地
      • 寻常

        是这个里边的api啊

        2021-05-20 17:34 未知归属地

      图片框架有迁移到鸿蒙的吗

      1
      13楼回复于2021-05-20 10:04 未知归属地
      共3页
      写回答
      新增插入模板功能
      一键使用模板,快速填写内容,轻松发帖~
      知道了
      • 为了保障您的信息安全,请勿上传您的敏感个人信息(如您的密码等信息)和您的敏感资产信息(如关键源代码、签名私钥、调试安装包、业务日志等信息),且您需自行承担由此产生的信息泄露等安全风险。
      • 如您发布的内容为转载内容,请注明内容来源。

      我要发帖子

      了解社区公约,与您携手共创和谐专业的开发者社区。