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

注意

从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.鸿蒙南向开发方向

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/486981.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前台处理:CO主数据之成本中心-<KS01>

一、背景&#xff1a; 前面讲解了成本要素和成本要素组&#xff0c;我们继续介绍成本控制与核算的主数据之成本中心&#xff0c;成本控制分主数据篇和业务篇&#xff1a; 主数据篇主要内容&#xff1a;成本要素、成本中心、订单、作业类型、工作中心&#xff1b; 业务篇主要…

Spring boot2.7整合jetcache方法缓存 设置定时刷新 解决多系统同时操作数据问题

上文 Spring boot2.7整合jetcache方法缓存 处理数据发生变化时同步更新缓存 删除缓存操作 解决了 缓存更新的问题 但是 现在有个问题 例如 我们 A系统 和 B系统 同时缓存了这一组数据 但是 A系统数据发生了更新 但是 B系统并不知道 其实 也没有特别好的办法同步通知 但可以控…

Git (版本控制,git安装和配置,git代码托管服务,git操作本地远程仓库,分支,idea整合git)【看这一片就够】

目录 一、版本控制介绍 1. 版本控制介绍 2. 版本控制工具 3. git简介 二、git安装与配置 1. 下载git 2. 安装git 2. 配置git 三、git代码托管服务 1. 常见的git代码托管服务 2. 注册码云帐号【这里介绍一种的用法&#xff0c;其它也是一样的操作】 3. 创建远程仓库 …

试试前端自动化测试(基础篇)

众所周知的原因&#xff0c;前端作为一种特殊的 GUI 软件&#xff0c;做自动化测试困难重重。在快速迭代&#xff0c;UI 变动大的业务中&#xff0c;自动化测试想要落地更是男上加男 &#x1f436;。 近期的学习过程中&#xff0c;翻阅了众多前端自动化测试相关的文章&#xf…

微信商家转账到零钱:实用指南,涵盖开通、使用与常见问题

商家转账到零钱是什么&#xff1f; 商家转账到零钱功能整合了企业付款到零钱和批量转账到零钱&#xff0c;支持批量对外转账&#xff0c;操作便捷。如果你的应用场景是单付款&#xff0c;体验感和企业付款到零钱基本没差别。 商家转账到零钱的使用场景有哪些&#xff1f; 这…

路由控制过滤策略出口 filter-policy export实验简述(直连路由)

配置过滤策略 filter-policy实验简述&#xff08;直连路由&#xff09; filter-policy export可以实现对特定流量的筛选和导出。 实验拓扑图&#xff1a; 实验基础配置&#xff1a; 销售部电脑&#xff1a;192.168.1.100/24/192.168.1.254 通过直连路由引入外部路由 财务部电…

Unity-UGUI系统

UGUI是什么 UGUI是Unity引擎内自带的UI系统官方称之为:Unity Ul 是目前Unity商业游戏开发中使用最广泛的UI系统开发解决方案 它是基于Unity游戏对象的UI系统&#xff0c;只能用来做游戏UI功能 不能用于开发Unity编辑器中内置的用户界面 六大基础组件 概述 Canvas EventS…

并发VS并行

参考文章 面试必考的&#xff1a;并发和并行有什么区别&#xff1f; 并发&#xff1a;一个人同时做多件事&#xff08;射击游戏队友抢装备&#xff09; 并行&#xff1a;多人同时处理同一件事&#xff08;射击游戏敌人同时射击对方&#xff09;

2024年最新阿里云服务器价格表(收费标准报价)

2024年阿里云服务器优惠价格表&#xff0c;一张表整理阿里云服务器最新报价&#xff0c;阿里云服务器网整理云服务器ECS和轻量应用服务器详细CPU内存、公网带宽和系统盘详细配置报价单&#xff0c;大家也可以直接移步到阿里云CLUB中心查看 aliyun.club 当前最新的云服务器优惠券…

存储级内存SCM:PCM对决ReRAM

在22年7月份有一件震惊存储圈的事情&#xff0c;那就是Intel说要放弃Optane产品线&#xff0c;包括PMEM和SSD两个方向都要放弃。存储圈看到听到这个消息也是一脸的茫然。 在Optane产品发布之前&#xff0c;大家针对DRAM和SSD之间的性能gap一直在苦苦找寻合适的产品。SCM存储级内…

Tempo Talents | 创新专业建设方案,赋能高校4+N大数据学科人才培养

数字经济成为国家战略&#xff0c;是新一轮的经济发展引擎&#xff0c;数字人才、复合型人才成为发展的关键和核心要素。各级政府、区域开始以区域产业为导向&#xff0c;培育、聚集产业所需的数智化人才。 高校作为人才培养的重要基地&#xff0c;也发挥着不可或缺的作用。他…

【Java程序设计】【C00373】基于(JavaWeb)Springboot的社区疫情返乡管控系统(有论文)

TOC 博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;博客中有上百套程序可供参考&#xff0c;欢迎共同交流学习。 项目简介 项目获取 &#x1f345;文末点击卡片…

网络七层模型之传输层:理解网络通信的架构(四)

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

排序大乱炖

目录 一&#xff1a;插入排序 1.1直接插入排序 1.2希尔排序 二&#xff1a;选择排序 2.1选择排序 2.2堆排序 三&#xff1a;交换排序 3.1冒泡排序 3.2快速排序 3.2.1Hoare版本 3.2.2双指针法 3.2.3非递归 一&#xff1a;插入排序 1.1直接插入排序 直接插入排序…

ESCTF赛题WP

ESCTF_reverse题解 逆吧腻吧babypybabypolyreeasy_rere1你是个好孩子完结撒花 Q_W_Q 逆吧腻吧 下载副本后无壳&#xff0c;直接拖入ida分析分析函数逻辑&#xff1a;ida打开如下&#xff1a;提取出全局变量res的数据后&#xff0c;编写异或脚本进行解密&#xff1a; a[0xBF, …

技术与业务:项目成功的黄金关键

目录 前言1 明确业务需求2 技术选择与业务匹配3 解决方案设计与业务一致4 开发与实施5 持续监控与优化6 反馈循环与持续改进结语 前言 在当今数字化时代&#xff0c;技术与业务之间的紧密联系对于项目的成功至关重要。无论是开发新产品、提供服务还是改进现有流程&#xff0c;…

Avalonia(11.0.2)+.NET6 打包设置发布包的版本号

Avalonia11.0.2+.NET6 打包设置发布包的版本号 系统版本如何打包设置打包的版本号本文是对上一篇打包文章的补充,后台好多人私信我说打包的版本号如何设置,今天出个补充说明 Avalonia(11.0.2)+.NET6 打包运行到银河麒麟V10桌面系统 系统版本 如何打包 Avalonia(11.0.2)+.NET…

基于nodejs+vue宿舍管理系统python-flask-django-php

随着信息时代的来临&#xff0c;过去的传统管理方式缺点逐渐暴露&#xff0c;对过去的传统管理方式的缺点进行分析&#xff0c;采取计算机方式构建宿舍管理系统。本文通过课题背景、课题目的及意义相关技术&#xff0c;提出了一种楼宇信息、宿舍信息、宿舍安排、缺勤信息等于一…

职场口才提升之道

职场口才提升之道 在职场中&#xff0c;口才的重要性不言而喻。无论是与同事沟通协作&#xff0c;还是向上级汇报工作&#xff0c;亦或是与客户洽谈业务&#xff0c;都需要具备良好的口才能力。一个出色的职场人&#xff0c;除了拥有扎实的专业技能外&#xff0c;还应具备出色…

赚钱的四条途径:信息、认知、执行力与竞争力的差异

一、引言 在追求财富的道路上&#xff0c;每个人都希望能够找到一条通往成功的捷径。事实上&#xff0c;赚钱的途径并非神秘莫测&#xff0c;而是由一系列关键因素所构成。其中&#xff0c;信息相差、认知相差、执行力相差以及竞争力相差&#xff0c;是赚钱的四条重要途径。这…