首页 文章 精选 留言 我的

精选列表

搜索[SSH客户端],共10000篇文章
优秀的个人博客,低调大师

每日一博 | 有道词典 Android 客户端包体积优化之路

摘要 有道词典已经发展成一个综合性的学习平台,小巧快速的初心仍然指引着我们不断进行启动速度及包体积优化。本文主要介绍了包体积包含的内容以及优化方法,分析了技术实现的具体细节,在接下来的工作中,我们会对启动速度、安装包体积以及内存占用等多方面进行持续优化,欢迎大家关注! youdao 一、背景 ydtech 有道词典从移动互联网之初就凭借小巧快速、功能强大的印象让用户爱上翻译查词,爱上学习。随着业务不断地迭代以及功能不断完善,有道词典不再是单纯的查词软件,而是变成了用户的综合学习平台。我们探索过社区、问答、直播、信息流等业务,目前也承载着音频、视频、课程、背单词、写作批改等等的功能。词典已经发展成为一个综合性的学习平台,小巧快速的初心仍然指引着我们不断进行启动速度以及包体积优化。 经过了不断的性能优化,目前我们的冷启动时间已经能维持在业界标准水平3s以内。我们近一个季度主要的性能优化工作集中在安装包体积优化上面。经过一系列的努力,我们包体积减少了23.7%,安装包体积从177MB减少到135MB,整体少了42MB。 以下详细介绍我们的分析以及实现细节。 youdao 二、分析 ydtech 介绍下包体积包含的内容以及优化方法概述。 一般的APK安装包包含了以下一些目录和资源:(排序) META-INF/ 签名文件 assets/ 程序使用的辅助资源文件 res/ 没有编译进入resources.arsc 资源文件,一般是图片 lib/ 依赖的不同native平台的库文件 resource.arsc 编译之后的文案、色值、大小、主题等资源索引 classes.dex 编译后的代码 AndroidMenifest.xml 应用的名称、版本、访问权限和引用的库文件信息 可以看出占比较大的部分主要是分别是assets/、lib/、res/、classes.dex以及resources.arsc,大概对应的就是资源、库文件、代码以及资源索引。我们主要的优化思路如下(其中蓝色框部分为目前已经处理部分): youdao 三、技术实现细节 ydtech 3.1 图片压缩 在APK打包的过程中,aapt 工具会默认对图片进行无损压缩,不过默认的压缩并不能达到一个很好的压缩效果,经过了对比webp以及tinypng的压缩效果,我们最终选择了使用tinypng对图片进行压缩。并且我们编写了编译工具,对图片进行自动化压缩。 有损webp > tinypng > 无损webp 比如这张启动图,原大小724KB,压到75%左右的质量只有23.7KB。效果上有一点点差异,但可以接受。那么我们是否可以把全部png图压成有损webp呢?答案是否定的,可以看看下面的例子: 压缩前: 压缩后: 可以看到,相同的压缩质量下(75%),这个图就变得十分模糊,哪怕选择到了99%的压缩质量,渐变区域依然会出现一些没有自然过渡的条纹。 对于上述的情况,用tinypng方案更好 原图:643KB, tinypng: 152KB, webp:339KB 综上,对于有损webp,无法找到一个固定的压缩质量来适配所有场景。有损webp有些时候甚至比tinypng还大,但显示质量更差。 我们最初使用的抖音的McImage插件对图片进行处理,不过这个方案存在一些明显的问题: 1.方案采用有损webp,有损webp无法定一个通用的压缩质量适应所有场景。 2.每次打包都要对所有图进行压缩,严重影响迭代效率。打包机要40分钟,且经常OOM。 3.没有对assets目录的图片进行处理。 针对以上问题,我们自己开发了一套使用tinypng的自动化图片压缩工具,做出以下调整: 1.对于大图png,用手工压成有损webp。收益大,且风险可控。 2.对于非大图,开发了一个image-optimization插件进行压缩。该插件方案为: · png转tinypng。虽然是有损的,但从抽样来看,肉眼完全看不到明显变化。 · 对assets进行处理。assets内有前端png图,转tinypng不转webp的好处是不需要单独改html、js等文件,且对低版本系统兼容性更友好。flutter相关项目的flutter_assets图片比较大且没注意压缩。插件统一处理可以不需要打开flutter工程单独优化、重新打包。 · 对于已压缩的图片,做缓存处理,不需要重新压缩,打包的时候动态替换。压缩缓存跟随词典工程提交到gitlab统一管理。 以下是我们图片自动化压缩插件处理的流程图: 这里压缩图是否可用判断,主要是大小判断,如果压出来比原图大,那么将舍弃。比如crunchPng压缩就存在这种情况。 附加1: 因为已经用了tinypng统一压缩,那么google官方自带的crunchPng建议关闭,否则打包速度变慢,而且优化好的图片也可能又变大,加入这行即可: buildTypes.all{ isCrunchPngs = false} 附加2: 无损webp和tinypng对比 如图所示,全量换tinypng比全量换webp(包含assets)少7.7MB。如果考虑到assets内的14.7MB其实是不能简单换webp的,差距会更大。 附加3: tinypng已经是最好的方案吗? 参考另一个ImageOptim工具,它结合OptiPNG, PNGCrush, AdvanceComp, PNGOUT, Jpegoptim + Jpegtran, 和 Gifsicle 等几个工具提供最好的优化效果,而且是几乎无损的。对于小部分图片ImageOptim压出来小,看起来没有差别。不过压缩速度非常慢。 所以,如果做到极致的话,可以进行多种压缩方案,选最佳的图作为替换。且我们的image-optimization插件从一开始设计的时候就预留了这种可扩展性。 附加4: AndResGuard优化对比 试了一下效果不明显,且出现部分资源丢失而崩溃的情况。效果不明显的原因,猜测是目前R8对资源名也有混淆压缩(以前proguard没有),所以AndResGuard现在的作用比较微弱。至于7zip的压缩没有开,理论上会导致启动速度变慢,觉得得不偿失(另外会导致Google Pay的Patch优化算法失效)。 3.2 resources.arsc优化 · 语言包优化 打开resources.arsc的string,我们可以看到如下表格,会发现大量空的地方(如上图)。这些空白的地方,其实是用FF FF FF…字符进行占位的,占用了很多空间(如下图)。由于有道词典没有进行国际化翻译(有一个国际化版本叫U-Dictionary,欢迎支持),因此删掉不必要的语言版本有助于减少体积。 android{defaultConfig{resConfigs"zh"}} · 如上所示,增加一行,保留中文即可。收获比想象中大,直接减少了3MB。 · dimens优化查看了最近几个版本的arsc体积,发现有一个版本增加了5MB。 · 在这个版本我们做了平板适配功能,由于我们采用的是SmallestWith限定符适配方案(可以先了解下这个屏幕适配方案),因此产生大量的尺寸资源。 一共是有3000多个资源,每一个资源有“values-sw300dp”到"values-sw1200dp"共90个版本,这块存在较大的优化空间。 sqb_px_xx”这一项是用于字体适配的,但词典用到最大的字体是“sqb_px_144”,所以优化了生成规则,减少了这一类资源。 优化后,资源数量由3012变成1662,减少了近一半。直接减少了2.5MB。 3.3 业务代码的消除 由于Proguard以及lint等工具是从代码引用的角度进行分析和代码裁剪,如果一些废弃的代码不先进行删除会影响后续工作的效果。对于一些已经废弃没有入口的业务,不进行处理的话那么代码、资源会只增不减。业务删减应该是所有包体积流程的第一步,否则后面的去掉无用资源、图片压缩、混淆等等效果都要打一个折扣。如果时间有限的话,那么删最近的需求会比删远古时代的需求收益会大点,原因是越靠近现在的项目,图片资源、字体资源,以及用到so库都会比较大(尤其是音视频)。 这部分工作主要是对业务功能的整理以及沟通部分陈旧业务是否可以进行删除,除此之外就是需要细致的引用分析将废弃业务相关代码剥离出来进行删除。 一个良好的项目架构对于日后业务代码的剥离有很大好处。目前新开发的功能我们采用的是分层分模块的组织架构,功能模块之间不存在相互依赖,因此以后对于业务的抽离或者删除会更加方便。 3.4 无用资源删除 对于无用资源删除我们主要使用了两个方法,一个是通过 lint 工具找到应用中可能没有使用的资源并逐一进行判断确认没有使用后进行删除,第二个是在build.gradle文件中加入shrinkResources在编译阶段使用R8工具进行删除。 buildTypes {release {// Zipalign优化zipAlignEnabled true// 移除无用的resource文件shrinkResources true// 移除没用的代码minifyEnabled true}} 使用 lint 工具需要注意对以下一些场景进行再次判断确认 1. 对于反射性引用资源,可能会被识别成无用资源,比如push用到的通知栏icon 2. DataBinding用到的layout资源会被识别成无用资源 3.4 压缩混淆 使用R8工具在编译阶段对代码进行压缩混淆,从而达到压缩安装包体积的效果。主要分为以下4个步骤: 1. 压缩(shrink) 移除未使用的类、方法、字段等; 2. 优化(optimize) 优化字节码、简化代码等操作; 3. 混淆(obfuscate) 使用简短的、无意义的名称重命名类名、方法名、字段等; 4. 预校验(preverify) 为class添加预校验信息。 我们在两年前就引入了Proguard,不过考虑到混淆带来的问题使用了-dontobfuscate配置取消混淆。我们发现之前的规则中从依赖库中继承了 -dontoptimize 的配置导致优化也没有生效。这次优化中,我们全面解决了混淆带来的众多问题,全面开启了优化以及混淆。 由于我们之前已经开启过了压缩,因此需要使用到的类已经在proguard中进行了保留。开启混淆后还需要处理以下一些问题: · getIdentifier 通过名称获取资源问题。如果是普通模式,则会自动不去掉相关资源: · 检查Resources.getValue 相关逻辑 · 检查AssetManager.open相关逻辑 · 反射,全局搜一下反射包,修改相关位置 java.lang.reflect · 处理Retrofit报错问题(https://github.com/square/retrofit/issues/3588),目前使用升级Gradle插件版本进行解决 Caused by:java.lang.IllegalArgumentException:Method returntype must notincludea type variable orwildcard:ho8<su3<?>>for method CheckInApi.popupConfigat retrofit2.Utils.methodError(SourceFile:5)at retrofit2.Utils.methodError(SourceFile:1)at retrofit2.ServiceMethod.parseAnnotations(SourceFile:7)at retrofit2.Retrofit.loadServiceMethod(SourceFile:4)at retrofit2.Retrofit$1.invoke(SourceFile:6)at java.lang.reflect.Proxy.invoke(Proxy.java:1006)at $Proxy23.popupConfig(Unknown Source)at com.youdao.dict.checkin.CheckInPopupManager.requestPopupConfig(SourceFile:3)at java.lang.reflect.Method.invoke(Native Method) Proguard的规则会很大程度上影响R8对代码压缩和混淆带来的效果,因此对压缩规则的回顾以及整理可以帮助进一步的体积压缩。 3.6 字体优化 字体优化这部分是在之前的版本已经实现过的,取得的效果也挺明显,这里补充说明一下。 · 字体裁剪 一般的字体库大小会有十几二十兆。但实际上用到的字符只有很少一部分,因此针对实际的使用场景对字体库进行适当的裁剪,收益非常大。 常用字列表:https://github.com/DavidSheh/CommonChineseCharacter 字体压缩工具:https://github.com/forJrking/FontZip · 字体合并 一般来说,我们开发都会模块化,不同的团队采用在开发不同功能的时候,有可能用到相同的字体。如果稍不注意就会复制成两份、三份,文件大大增加。词典这边的方案是把共有的字体下沉到底层core基础库,供各个模块引用。 youdao 四、展望 ydtech 经过了上述的工作,目前词典的安装包体积优化了23.7%,整体减少了42MB。在接下来的Q2,我们将准备做两方面的事情。 4.1 包体积监控 在包体积优化的过程中,我们在含辛茹苦地砍掉一点体积之后,转过头来发现别的同学又随随便便扔进去几MB的大图。因此,如何坚守胜利的果实,让包体积保持最佳状态成了重中之重。 打包任务增加了是否检查包大小限制(默认都要检查) 的选项;merge request之后,词典的打包任务会触发自动构建; 打包任务完成之后,如果需要检查包大小,那就开始触发apkcheck步骤;具体如下: 1. 打包任务完成之后增加脚本操作,把本次构建的数据(如apk文件地址,mapping文件地址,R文本地址等)写入临时文件; 2. 打包任务构建后操作增加 Trigger parameterized build on other projects,触发apk 大小检查任务; 3. 开始检查流程,检查流程根据参数对apk进行检查任务,并且把任务结果生成html; 4.2 动态分发 · 整体业务分发 可以使用插件化以及动态加载等技术,不过这些可能不是最难的,最难的是如何把一些祖传的、低频的、而又相互依赖的代码抽离出来,形成独立模块去做分发、动态加载。 · 业务子功能分发(预计可优化39.6MB) 1. 数据库(单词锁屏8MB) 单词锁屏可以保留几百kb数据在本地让用户备用,同时再下载完整的词库。 2. OCR引擎数据(22.5MB) 用户应该可以按需下载训练模型,而不是直接内置;当没有训练模型的时候,可以直接网络请求。 3. 字体(9.1MB) 除了查词等高频业务,低频业务的字体可以动态分发,有则显示,无则使用系统的即可。但emoji的兼容库比较特殊,主要用在首页信息流的帖子、UGC发帖等。如果没有兼容库,用户在遇到特别的emoji中可能会显示“豆腐块”,这个时候如果emoji字体库还没下载完成,需要进行替换兜底处理。另外哪怕用户系统已经有这个emoji内置字体,也有可能显示效果各个手机不太一样,需要跟UI确认一下是要替换掉,还是暂时这样显示。 保持住词典小巧快速、功能强大的初心是我们不停进行性能优化的动力,在接下来的工作中,我们会对启动速度、安装包体积以及内存占用等多方面进行持续优化和改进,欢迎大家继续关注和支持! youdao 五、参考 ydtech 1.Reduce your app size 2.Shrink, obfuscate, and optimize your app 3.抖音图片压缩插件McImage 4.腾讯包体积监控ApkChecker 往期推荐 本文分享自微信公众号 - 有道技术团队(youdaotech)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

Forest v1.5.9 版本发布,轻量级 HTTP 客户端框架

v1.5.9 版本发布了,此次更新主要解决了URL Encoder在某些情况下不正确的问题 为此自己重新实现了URL解析和URL编码,抛弃了原有的Java自带的URI对象解析和URLEncoder类 实现了URL语义化的字符串模板 自动区分模板参数属于URL的哪个部分 /** * 新版本能够识别 {a} 和 {b} 是一个URL的Query参数 * 会按照查询参数的要求进行URL编码:会将'&'符号转义 * 而 {path} 会被识别URL路径的一部分 * 会按照URL路径的要求来进行URL编码:会保留'&'符号,不做转义 */ @Get("/data/{path}?a={a}&b={b}") String getData(@Var("path") String path, @Var("a") String a, @Var("b") String b); {}与${}的区别 {}代表一个Query参数 {}模板参数,在?a={a}的情况下,会被认为是一个Query参数,即便变量可能包含"1&x=10&y=20"这样多个参数的字符串,也会被转义成一个Query参数 @Get("http://localhost/data?a={a}&b={b}") String getData(@Var("a") String a, @Var("b") String b) // 最终产生的URL是 // http://localhost/data?a=1%26x%3D10%26y%3D20&b=hello // 也就是只会有 a 和 b 两个Query参数 myClient.getData("1&x=10&y=20", "hello"); ${}可以包含多个Query参数 而${}模板参数,可以认为是一种字符串替换,替换完再对URL参数进行解析,所以一个模板参数引用的变量中可能包含多个参数,也会被解析成多个参数 @Get("http://localhost/data?a=${a}&b=${b}") String getData(@Var("a") String a, @Var("b") String b) // 最终产生的URL是 // http://localhost/data?a=1&x=10&y=20&b=hello // 也就是只会有 a、x、y、b 四个Query参数 myClient.getData("1&x=10&y=20", "hello"); 推荐使用{}作为模板参数 基于这两种模板参数各自的特性,都各有各的用处,但一般情况下,推荐使用{} 因为它更结构化、更语义化,也更容易让人理解,不容易出错,尤其是在URL参数中传递另一个URL地址时的作用更为突出 比如,要传一个带参数的子URL:https://search.gitee.com/?type=repository&q=forest 接到父URL后为http://localhost/data?call={url} 如果是用${url}就会出问题 @Get("/data?call=${url}") String getData(@Var("url") String url); // 最后产生的URL是 // http://localhost/data?call=https://search.gitee.com/?type=repository&q=forest 咋看起来没错,但最后那部分&q=forest会被认为是父URL的Query参数,但其实应该是子URL的 如果用{url}就没关系,即使后来再有其它参数也毫无问题 @Get("/data?call={url}&x={x}") String getData(@Var("url") String url, @Var("x") String x); // 最后产生的URL是 // http://localhost/data?call=https://search.gitee.com/?type=repository%26q=forest&x=xxx 可以看到,子URL中Query参数的连接符&被转义了,这样就解决了子URL参数和父URL参数(如后面的x)之间产生的歧义 FIX的BUG fix: URI路径的URLEncoder编码结果不正确 (#I4DUFG:URI路径的URLEncoder编码结果不正确) 项目介绍 Forest是一个高层的、极简的轻量级HTTP调用API框架。 相比于直接使用Httpclient您不再用写一大堆重复的代码了,而是像调用本地方法一样去发送HTTP请求。 文档和示例 项目主页 中文文档 JavaDoc Demo工程 发送JSON数据 /** * 将对象参数解析为JSON字符串,并放在请求的Body进行传输 */ @Post("/register") String registerUser(@JSONBody MyUser user); /** * 将Map类型参数解析为JSON字符串,并放在请求的Body进行传输 */ @Post("/test/json") String postJsonMap(@JSONBody Map mapObj); /** * 直接传入一个JSON字符串,并放在请求的Body进行传输 */ @Post("/test/json") String postJsonText(@JSONBody String jsonText); 发送XML数据 /** * 将一个通过JAXB注解修饰过的类型对象解析为XML字符串 * 并放在请求的Body进行传输 */ @Post("/message") String sendXmlMessage(@XMLBody MyMessage message); /** * 直接传入一个XML字符串,并放在请求的Body进行传输 */ @Post("/test/xml") String postXmlBodyString(@XMLBody String xml); 发送Protobuf数据 /** * ProtobufProto.MyMessage 为 Protobuf 生成的数据类 * 将 Protobuf 生成的数据对象转换为 Protobuf 格式的字节流 * 并放在请求的Body进行传输 * * 注: 需要引入 google protobuf 依赖 */ @Post(url = "/message", contentType = "application/octet-stream") String sendProtobufMessage(@ProtobufBody ProtobufProto.MyMessage message); 详细文档请看:http://forest.dtflyx.com

优秀的个人博客,低调大师

Forest v1.5.8 版本发布,轻量级 HTTP 客户端框架

v1.5.8 版本发布,该版本主要解决以下BUG: fix: 使用@DownloadFile下载文件时,在某些环境下会发送阻塞 (#I4DLBI:使用@DownloadFile下载文件,在某些环境下会发送阻塞) fix: response对google protobuf包依赖 (#I4DKQW:forest-spring-boot-starter 1.5.6缺少protobuf-java的jar包) 项目介绍: Forest是一个高层的、极简的轻量级HTTP调用API框架。 相比于直接使用Httpclient您不再用写一大堆重复的代码了,而是像调用本地方法一样去发送HTTP请求。 文档和示例: 项目主页 中文文档 JavaDoc Demo工程 Forest有哪些特性? 以Httpclient和OkHttp为后端框架 通过调用本地方法的方式去发送Http请求, 实现了业务逻辑与Http协议之间的解耦 因为针对第三方接口,所以不需要依赖Spring Cloud和任何注册中心 支持所有请求方法:GET, HEAD, OPTIONS, TRACE, POST, DELETE, PUT, PATCH 支持文件上传和下载 支持灵活的模板表达式 支持拦截器处理请求的各个生命周期 支持自定义注解 支持OAuth2验证 支持过滤器来过滤传入的数据 基于注解、配置化的方式定义Http请求 支持Spring和Springboot集成 JSON格式数据序列化和反序列化 XML格式数据序列化和反序列化 Protobuf格式数据序列化和反序列化 JSON、XML或其他类型转换器可以随意扩展和替换 支持JSON转换框架: Fastjson, Jackson, Gson 支持JAXB形式的XML转换 可以通过OnSuccess和OnError接口参数实现请求结果的回调 配置简单,一般只需要@Request一个注解就能完成绝大多数请求的定义 支持异步请求调用 发送JSON数据 /** * 将对象参数解析为JSON字符串,并放在请求的Body进行传输 */ @Post("/register") String registerUser(@JSONBody MyUser user); /** * 将Map类型参数解析为JSON字符串,并放在请求的Body进行传输 */ @Post("/test/json") String postJsonMap(@JSONBody Map mapObj); /** * 直接传入一个JSON字符串,并放在请求的Body进行传输 */ @Post("/test/json") String postJsonText(@JSONBody String jsonText); 发送XML数据 /** * 将一个通过JAXB注解修饰过的类型对象解析为XML字符串 * 并放在请求的Body进行传输 */ @Post("/message") String sendXmlMessage(@XMLBody MyMessage message); /** * 直接传入一个XML字符串,并放在请求的Body进行传输 */ @Post("/test/xml") String postXmlBodyString(@XMLBody String xml); 发送Protobuf数据 /** * ProtobufProto.MyMessage 为 Protobuf 生成的数据类 * 将 Protobuf 生成的数据对象转换为 Protobuf 格式的字节流 * 并放在请求的Body进行传输 * * 注: 需要引入 google protobuf 依赖 */ @Post(url = "/message", contentType = "application/octet-stream") String sendProtobufMessage(@ProtobufBody ProtobufProto.MyMessage message); 文件上传 /** * 用@DataFile注解修饰要上传的参数对象 * OnProgress参数为监听上传进度的回调函数 */ @Post("/upload") Map upload(@DataFile("file") String filePath, OnProgress onProgress); 可以用一个方法加Lambda同时解决文件上传和上传的进度监听 Map result = myClient.upload("D:\\TestUpload\\xxx.jpg", progress -> { System.out.println("progress: " + Math.round(progress.getRate() * 100) + "%"); // 已上传百分比 if (progress.isDone()) { // 是否上传完成 System.out.println("-------- Upload Completed! --------"); } }); 基本签名验证 @Post("/hello/user?username=${username}") @BasicAuth(username = "${username}", password = "bar") String send(@DataVariable("username") String username);

优秀的个人博客,低调大师

Forest v1.5.6 版本发布,轻量级 HTTP 客户端框架

Forest v1.5.6 版本发布了,此次更新主要修复一些BUG 项目介绍: Forest 是一个高层的、极简的轻量级 HTTP 调用 API 框架。 相比于直接使用 Httpclient ,您不再用写一大堆重复的代码了,而是像调用本地方法一样去发送 HTTP 请求。 文档和示例: 项目主页 仓库地址 中文文档 JavaDoc Demo 工程 Forest 有哪些特性? 以 Httpclient 和 OkHttp 为后端框架 通过调用本地方法的方式去发送 Http 请求, 实现了业务逻辑与 Http 协议之间的解耦 因为针对第三方接口,所以不需要依赖 Spring Cloud 和任何注册中心 支持所有请求方法:GET 、HEAD 、OPTIONS 、TRACE 、POST 、DELETE 、PUT 、PATCH 支持文件上传和下载 支持灵活的模板表达式 支持拦截器处理请求的各个生命周期 支持自定义注解 支持 OAuth2 验证 支持过滤器来过滤传入的数据 基于注解、配置化的方式定义 Http 请求 支持 Spring 和 Springboot 集成 JSON 字符串到 Java 对象的自动化解析 XML 文本到 Java 对象的自动化解析 JSON、XML 或其他类型转换器可以随意扩展和替换 支持 JSON 转换框架: Fastjson 、Jackson 、Gson 支持 JAXB 形式的 XML 转换 可以通过 OnSuccess 和 OnError 接口参数实现请求结果的回调 配置简单,一般只需要 @Request 一个注解就能完成绝大多数请求的定义 支持异步请求调用 支持 Protobuf @Post( url = "/proto", contentType = ContentType.APPLICATION_OCTET_STREAM) ProtobufProto.Data sendProtobufData(@ProtobufBody ProtobufProto.Data data); 后端切换的快捷注解 // 切换到 okhttp3 @OkHttp3 @Post("/data1") String sendData1(@Body MyUser user); // 切换到 httpclient @HttpClient @Post("/data2") String sendData2(@Body MyUser user); 新增特性: feat: 将 timeout 细化为 connectTimeout 和 readTimeout (#I4DH21:将timeout细化为connectTimeout和readTimeout) FIX 的 BUG: fix: Google Protobuf 包依赖错误 (#I4DDZY:Google Protobuf包依赖错误) fix: 配置 forest.connect-timeout 不生效 (#I45298:Spring Boot工程配置forest.connect-timeout不生效)

优秀的个人博客,低调大师

Forest v1.5.5 版本发布,轻量级 HTTP 客户端框架

Forest v1.5.5 版本发布了,此版本主要支持了Protobuf,以及修改了若干Bug 项目介绍: Forest是一个高层的、极简的轻量级HTTP调用API框架。 相比于直接使用Httpclient您不再用写一大堆重复的代码了,而是像调用本地方法一样去发送HTTP请求。 文档和示例: 项目主页 仓库地址 中文文档 JavaDoc Demo工程 Forest有哪些特性? 以Httpclient和OkHttp为后端框架 通过调用本地方法的方式去发送Http请求, 实现了业务逻辑与Http协议之间的解耦 因为针对第三方接口,所以不需要依赖Spring Cloud和任何注册中心 支持所有请求方法:GET, HEAD, OPTIONS, TRACE, POST, DELETE, PUT, PATCH 支持文件上传和下载 支持灵活的模板表达式 支持拦截器处理请求的各个生命周期 支持自定义注解 支持OAuth2验证 支持过滤器来过滤传入的数据 基于注解、配置化的方式定义Http请求 支持Spring和Springboot集成 JSON字符串到Java对象的自动化解析 XML文本到Java对象的自动化解析 JSON、XML或其他类型转换器可以随意扩展和替换 支持JSON转换框架: Fastjson, Jackson, Gson 支持JAXB形式的XML转换 可以通过OnSuccess和OnError接口参数实现请求结果的回调 配置简单,一般只需要@Request一个注解就能完成绝大多数请求的定义 支持异步请求调用 支持Protobuf @Post( url = "/proto", contentType = ContentType.APPLICATION_OCTET_STREAM) ProtobufProto.Data sendProtobufData(@ProtobufBody ProtobufProto.Data data); 后端切换的快捷注解 // 切换到 okhttp3 @OkHttp3 @Post("/data1") String sendData1(@Body MyUser user); // 切换到 httpclient @HttpClient @Post("/data2") String sendData2(@Body MyUser user); 新特性: feat: 支持每个请求都可自定义序列化转换器 (#I4CLV8:支持每个请求都可自定义序列化转换器) feat: 二进制类型请求体 (#I4D4GY:二进制类型请求体) feat: Protobuf请求体 (#I4D4JT:Protobuf请求体) feat: 转换器对应包依赖 需要时获取 (#I29XE0:处理Protobuf的序列化和反序列化的转换器) Fix的Bug: fix: getAttribute方法在多线程情况下取值会串 (#I4D5KS:getAttribute在多线程情况下返回值会串) fix: 在onRetry方法中,调用 addQuery 或 addBody 能API无效 (#I4CZS5:在onRetry方法中,调用 addQuery 或 addBody 能API无效) 代码改动: add:@okhttp注解 add: @HttpClient 注解 add: ForestRequest.type(ForestRequestType type) 方法 add: ForestLogHandler.logContent(String content) 方法 特别鸣谢: @yakax

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册