豆瓣的混合开发框架 -- Rexxar

原文链接:http://lincode.github.io/Rexxar-OpenSource,版权归作者所有!

混合开发

混合开发的直白解释是 Native 和 Web 技术都要用。但形式上,应用仍然和浏览器无关,用户还是需要在 App Store 和 Android Market 下载应用。只是在开发时,开发者以 Native 代码为主体,在合适的地方部分使用 Web 技术。比如在 iOS 中的 UIViewController 内放置一个 UIWebview(一个浏览器引擎,只拥有渲染 HTML,CSS 和执行 JavaScript 的核心功能)。这样,部分用户界面就可以在 UIWebView 中使用 Web 技术实现。

促使我们在移动开发中使用 Web 技术主要动力在于,相比于 Native 技术,Web 技术具有诸多优势:

  • 高效率的界面开发:HTML,CSS,JavaScript 的组合被证明在用户界面开发方面具有很高的效率。

  • 跨平台:统一的浏览器内核标准,使得 Web 技术具有跨平台特性。iOS 和 Android 可以使用一套代码。

  • 热更新:可越过发布渠道自主更新应用。

这些优势都和开发效率有关。Web 技术具有这些优势的原因是,Web 技术是一个开放标准。基于开放的标准已经发展出来庞大生态,而且这个生态从 PC 时代发展至今已积累多年,开发者可以利用生态中产出的各种成果,从而省去很多重复工作。

在大型移动应用的开发中,项目代码庞杂,通常还需要 iOS,Android,移动 Web 和 桌面 Web 全平台支持。这种情况下,更高的开发效率就成了开发者不得不考虑的议题。这也是为何虽然移动端的 Web 技术在使用范围和性能上有诸多劣势,仍然有很多开发者付出努力,探索如何在移动开发中使用 Web 技术。

我们也是基于以上各种考虑,决定在豆瓣的移动开发中实践一些混合开发技术。

Rexxar 的背景

豆瓣在 2014 年推出一个主应用:豆瓣 App。这个主应用慢慢成长,逐渐覆盖了豆瓣在 Web 上的大部分功能。随着项目的扩大,产品线的扩展,豆瓣App 成为了一个需要同时提供 iOS,Android 和移动 Web 页面的多平台支持的服务。工程技术团队为了更从容地应对这种状况,开始投入较大的精力提高团队的开发效率。混合开发是其中主要的措施之一。

由于项目已经发展到一定程度,我们并不希望推倒以往的开发方式,也没有一切从头来的野心和勇气。只是希望在不影响 App 的性能前提下,在合适的地方使用 Web 技术部分地提高开发效率。而豆瓣App 中又确实存在部分页面是重度展示,并轻度交互的。这些页面恰恰适合使用 Web 技术来实现。

经过团队的一些努力,项目中部分页面已经使用 Web 技术实现,并取得了不错的效果。工程师使用 Web 技术可以以更快的速度完成产品需求,并且将一份代码部署到两个平台。开发效率得到了实质性提高。即使不提热更新,减少 Android 项目方法数这种附带好处,我们也已喜欢上这项技术,决定在豆瓣移动开发中推动混合开发技术的使用。

现在,我们将这个过程的主要产出:Rexxar 这个项目开源。一方面,是为了给大家提供一些借鉴的方向;另一方面,是为了提高项目本身的质量。我们知道还存在不少问题。所以,会悉心接受大家的意见和建议。

Rexxar 的介绍

Rexxar 是一个针对移动端的混合开发框架。现在支持 Android 和 iOS 平台。并有一个 Web 基础库。

团队中喜欢玩魔兽的同学将该项目命名为 Rexxar(《魔兽世界》中人物,出生于卡利姆多大陆的菲拉斯,同时具有雷骨兽人和南部菲拉斯野生食人魔两种血统)。

各平台代码仓库地址如下:

  • Rexxar Web:https://github.com/douban/rexxar-web

  • Rexxar iOS:https://github.com/douban/rexxar-ios

  • Rexxar Android:https://github.com/douban/rexxar-android

Rexxar 主要由以下三部分组成:

  • Rexxar Route,我们使用 URL 来标识每一个页面。在 App 中通过指明 URL 跳转到此页面。所以,需要一个路由表。通过路由表可以根据 URL 找到一个 Rexxar Web 的对应资源来正确展示相应页面;

  • Rexxar Web,前端代码库,由 HTML、CSS、JavaScript、Image 等组成,用来提供在移动客户端使用的用户页面;

  • Rexxar Container,一个前端代码的运行容器。它其实是一个内嵌的浏览器(WebView),我们为内嵌浏览器提供了一些必要的原生端支持,包括 API 的 OAuth 授权、图片缓存、Native UI 组件的调用等;现在有 Android 和 iOS 两个版本的实现。

在项目实践中,Rexxar Web 和 Rexxar Route 由一个项目实现,并部署于同一个 Web 项目中。

Rexxar Route

Rexxar Route 比较简单,只需要表达一个路由表即可。我们使用了一个 json 文件来表达路由表。给出一个路由表的例子:

 
 
  1.    {

  2.        count: 4,

  3.        items: [{

  4.            remote_file: "https://img1.doubanio.com/dae/rexxar/files/orders/orders-70dbdbcb1c.html",

  5.            uri: "douban://douban.com/orders[/]?.*"

  6.        }, {

  7.            remote_file: "https://img1.doubanio.com/dae/rexxar/files/related_doulists/related_doulists-1d7d99e1fb.html",

  8.            uri: "douban://douban.com/(tag|tv|movie|book|music)/(\w+)/related_doulists[/]?.*"

  9.        }   ],

  10.        deploy_time: "Fri, 04 Mar 2016 11:12:29 GMT"

  11.    }

我们发布的每个版本的 App 安装包都会包含最新版本的 routes.json 文件。在 App 启动时,都会尝试下载最新版本的 routes.json。在遇到无法解析的 URL 时,也会去下载新版 routes.json。

Rexxar Web

Rexxar Web 是 Rexxar 前端实现。Rexxar Container 的实现和 Rexxar Web 的实现是分离的。Rexxar Container 对 Rexxar Web 使用何种技术实现并不关心。所以,你可以选择自己的前端技术和 Rexxar Container 进行组合。比如,我们在业务层选择了 React 作为前端开发框架。

Rexxar Web 包括了三部分内容:

工具

一套开发 Rexxar Web 所需的打包,调试,发布工具。

公共的前端组件
  • 通用的错误处理、Loading等效果

  • 页面点击反馈效果

  • List 的支持

对 Rexxar Container 实现的 Widget 的调用
  • ActionBar 的 title 定制

  • ActionBar 的 button 定制

  • Dialog

  • 下拉刷新

  • Toast

有了这些组件,我们日常产品开发的难度就降低了。普通移动开发工程师经过一段时间的学习,也可以像前端工程师一样,以 Rexxar 为工具为 App 做一些产品开发了。这部分可以视为一个纯粹的前端项目。

Rexxar Container

我们使用混合开发技术提高开发效率的一个前提是,不损伤 App 的使用体验。基于这个前提,在 Native 和 Web 如何分工方面我们做了一些尝试。首先,为了保证使用体验,我们把 App 里页面切换留给了 Native。这样,每个页面(Controller 或者 Activity)都是一个 Container。Container 内嵌一个浏览器内核。页面内的功能和逻辑在 Native 和 Web 之间如何分工呢?我们尝试过有几种策略:

  • 纯浏览器方案:也就是 Native 除了扔给内嵌浏览器一个 URL 地址之外,就不做任何事情了,剩余的事情都由 Web 完成。这和用 Safari 或 Chrome 等普通浏览器打开一个网页并没有太多区别。只是我们固定了访问的地址。

  • 前端模板渲染容器方案:这种方案大部分事情由 Native 完成,Web 部分只是负责页面元素的呈现,不参与页面界面之外的其他部分。我们在客户端存储了一个 HTML 作为 UI 模板。Native 代码负责获取数据,向 HTML 文件模板中填入动态数据,得到一个可以在内嵌浏览器渲染的 HTML 文件。这个过程有点类似于 Web 框架里模板渲染库(例如,Jinja2)的作用。

  • Rexxar Container 方案:Rexxar 采用的方案介于上述两种方案之间。Rexxar Container 同样提供了一个运行前端代码的容器。它也是一个内嵌的浏览器(WebView)。只是,我们并不是只扔给内嵌浏览器一个 URL 地址就放手不管了,还对对内嵌的浏览器做了很多开发,为其包装了很多附加功能。

Rexxar Container 方案中,Container 需要实现以下功能:

  • Rexxar Route 路由表的更新,已经在客户端的保存;

  • 为 Rexxar Web 前端代码发出的 API 请求提供包装。带上必要的 OAuth 参数;

  • 缓存 Rexxar Web 前端代码所需要的静态文件,包括 HTML、CSS、JavaScript、Image(图片素材)等;

  • 缓存 Rexxar Web 中所需要加载的资源文件,例如图片等;

  • 通过协议为 Rexxar Web 提供一些原生支持的功能:包括 Native UI 组件调用,获取 Native 的计算结果。

这种实现方案,是基于保证使用体验的前提下,尽量让 Web 技术多做一些事情的考虑。

Rexxar Container 和 Rexxar Web 之间的交互

混合开发实践中,一般都会涉及到 Native 和 Web 如何通信的问题。这是因为我们把一件事情交给两种技术完成,那么它们之间便会存在有一些通信和协调。很多混合开发方案会使用 JSBridge(Android: JsBridge,iOS:WebViewJavascriptBridge) 来实现 Native 和 Web 的相互调用。

但在 Rexxar 中,我没有使用类似 JSBridge 这样的方案。而是通过从 Rexxar Web 发出 HTTP 请求的方式,由 Rexxar Container 截获的方式进行通信。Native 和 Web 之间协议是由 URL 定义的。Rexxar Web 访问某个特定的 URL, Rexxar Container 截获这些 URL 请求,调用 Native 代码完成相应的功能。

例如,Rexxar 中 UI 相关的功能的协议如下:

  • 请求 douban://rexxar.douban.com/widget/nav_title,可以定义 Navigation Bar Title。

  • 请求 douban://rexxar.douban.com/widget/nav_menu,可以定义 Navigation Bar Button。

  • 请求 douban://rexxar.douban.com/widget/toast,可以出现一个消息通知 toast。

Rexxar Web 具体前端实现是在 DOM 中加入一个 iframe 来加载此 URL,以来完成对 Rexxar Container 的通知。

将 Native 和 Web 的通信以协议的形式规范起来,是因为我们希望 Native 和 Web 之间的通信是可定义的,可控的。有这种期望的原因是,我们以 Rexxar 完成的页面,不仅仅在 App 内使用,还会在移动 Web 页面上使用。我们的移动站点,特别是分享到外部(如微信,微博)的页面希望复用 Rexxar 在 App 内的工作成果。如果,任由开发者自由地定义接口随意的依赖于原生实现的功能,那么我们就无法顺利地迁移到移动 Web 上去。标准浏览器并不支持 JSBridge 的大部分功能。但可以看到我们已经实现的协议,大部分在移动 Web 是被可以自动被忽略(比如,nav_title, nav_menu),或者我们也可以较容易地以移动 Web 支持的形式再实现一次(比如,toast)。这样,Rexxar 中的前端业务代码无需太多改动,即可迁移到移动 Web 和桌面 Web 端。

Rexxar Container 的技术实现

Rexxar Container 主要的工作是截获 Rexxar Web 的数据请求和原生功能请求。Rexxar Container 截获请求之后,做相应的反应。这种 Native 和 Web 的交互被抽象成三种接口:

  • Decorator:修改数据请求。例如,数据请求加上 OAuth 认证信息。

  • Widget: 调用某些 Native UI 组件。例如,调起一个 Toast。

  • ContainerAPI:给 Web 一个 Native 的计算结果。例如,给出当前位置信息。

这三种接口都是由 Rexxar Web 发起某种形式的 URL 调用的。Rexxar Web 的业务代码在 App 的 Rexxar Container 内工作方式,和在普通浏览器里差别不大。我们只是在 Web 技术的基础上做了一些拓展,保留了大部分 Web 原有的编写和运行方式。代码都是标准 Web 式的,没有为原生移动开发做太多定制。因此,移植到 Web 平台,在各种浏览器中,代码无需做太多修改就可以正确运行。以 URL 作为协议,也为 Web 和 Native 划定了清晰的边界和数据传递方式。

我们为 iOS 和 Android 各开发了一个 Rexxar Container。iOS 和 Android 平台截获请求的方式由于平台差异,并不完全相同。但本质上都是在 Web 和 Native 之间实现了一个 Proxy。Web 发出的请求会被 Proxy 预先处理。要么是修改后再发出去,要么是由 Rexxar Container 自己处理。

具体的实现可以参看两个平台的项目代码。

Rexxar 页面执行过程

例如,客户端接到一个页面请求,要打开一个 URL:douban://douban.com/movie/1292052。Rexxar 的工作流如下:

  1. 根据 URL 查询本机缓存的路由表 routes.json,看是否能够找到对应的资源记录(一般是一个 HTML 文件)。如果找到不到,请求 Rexxar Route 服务,获得最新的全量路由表 routes.json,更新本地缓存,找到对应的资源记录;

  2. 根据路由表指示的 HTML 文件的路径,看本地是否找到对应的文件。如果找不到,请求 Rexxar Web 资源服务器,更新本地缓存;

  3. 在 Rexxar Container 里展示该 HTML 文件;如有需要,会在 Container 中请求图片资源,图片资源也有缓存,Rexxar Container 会先检查本地缓存。如不存在,会请求 CDN 的图片或者图片服务器;

  4. Rexxar Web 前端代码在 Container 里继续执行,发出 API 请求。Rexxar Container 代理这些请求,为 API 请求添加 OAuth 验证;

  5. Rexxar Web 前端代码继续执行,根据 API 返回的结果,展示响应的页面,可能会请求 CDN 的图片或者图片服务器等;

  6. Rexxar Web 前端代码继续执行,如果需要修改 NavigationBar 等原生界面,可能通过定义好的协议请求 URL: douban://rexxar.douban.com。Rexxar Container 拦截请求,按定义好的协议作出反应。例如,修改 NavigationBar 上的按钮。

Rexxar 的问题

性能

混合开发的问题在于现阶段,Web 的性能没法和 Native 相比。这种状况可能会长期存在。因为,前端代码运行于内嵌浏览器之上,和直接调用原生系统相比,理论上总会存在性能上的差距。我们现在基本是以规避的方式面对性能问题:即性能问题会明显影响到用户体验时,我们就不使用 Rexxar 来做,而是使用传统 Native 老老实实写两份代码,一份 iOS,一份 Android。当然,这就限缩了 Rexxar 的使用范围。

在 Rexxar iOS 中,我们做了使用 WKWebView 替代 UIWebView 的尝试。但是现在看起来这会是一个长远目标。WKWebView 在速度和内存消耗上都优于 UIWebView。但 WKWebView 并不完善。对于 Rexxar iOS 而言,最重要的缺陷是不支持使用 NSURLProtocol 截获 WKWebView 中发出的网络请求。所以在现有的 Rexxar 的实现中,并没有使用 WKWebView。但是,我们会持续努力,以寻找切换至 WKWebView 的可能性。

错误报告

我们在应用中使用 Rexxar 之后,在收集到的 Crash Report 中,JavaScript 的相关错误,和浏览器相关的错误开始增加。而对这类错误,由于移动应用的使用环境更为复杂,错误报告经过了 JavaScript 引擎,原生系统两层之后,给出的错误信息并不够明确。我们在这方面的经验也并不多,导致我们还没有很好的办法降低这类错误。这对提高 App 的稳定性带来了问题。

总结

Rexxar 这个混合开发框架在豆瓣移动开发中使用,确实在一定程度上提高了我们的开发效率。以前一个页面需要 iOS 和 Android 两位工程师各开发一遍,现在只需要一位工程师写一次前端代码,甚至还可以应用到移动 Web 上去。虽然 Rexxar 仍然存在一些问题,和使用上的限制。但是在有限的使用中,我们仍然收获不少。所以,在未来我们应该会持续推动 Rexxar 在豆瓣移动开发中的使用。

希望这个 Rexxar 这个开源项目对大家能起到一点启示效果。并得到大家的反馈和建议,帮助我们提高。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
注意:本项目使用android studio开发,eclipse可能无法使用。 豆芽的名字取自“Douban, Yet Another”的缩写和中文词语“豆芽”的拼音正好相同的巧合。 取名之后,我才得知豆瓣的 Windows Phone 客户端的名字也叫做“豆芽”。所以相对于豆瓣官方应用“一个叫‘豆瓣’的App”,也正好将这个应用称为“另一个叫豆芽的应用”了。 为什么要有豆芽? 直接访问豆瓣的所有人里,最普遍而一致的用法是围绕电影、电视、书、唱片、活动(我们叫做”条目”的东西)的评分评论、发现和讨论。我们会把和网站同步的评分评论作为一个起点和基础,在手机上重新构建围绕个人兴趣的发现和讨论。 一个叫“豆瓣”的App——豆瓣日志 豆瓣从来不是一个单一的网站,而对于豆瓣的用法自然不尽相同。使用豆瓣是为了获取信息,但信息的获取是基于条目和算法,还是基于友邻和人,这个问题在豆瓣的多次改版中大概一直悬而未决。 这次,一个叫“豆瓣”的应用选择的是基于条目的推荐。但我个人作为一个重度豆瓣用户,重视的却恰好是基于人的推荐,喜欢的是友邻间的鸡犬相闻,以及闲逛时从条目评论、广播、日记中发现新友邻的惊喜。正如我在某次“还我旧版”运动中听到的声音,“不管怎么改版,只要友邻们还在就好”,改版是豆瓣不断良好发展的必经之路,但这句话中对友邻的珍重又令我感受到了豆瓣最宝贵的特质。 豆瓣作为一个工具的价值可以通过条目很好体现,但豆瓣作为一个独一无二的社区,只能通过它独有的、克制的、以人为本的方式才能维系。作为一个普通但也深爱豆瓣的用户,我希望豆瓣在这个方向上也不要失落,因为一个只有工具属性的网站对我而言将再也没有这样的归属感。 我在这一点上与豆瓣应用有了不同的追求,并且恰好有一些这方面的能力,又恰好豆瓣提供了开放的 API,于是就想要将这个想法实现出来了。 选择开始豆芽这个项目,还有一个原因是我希望在豆瓣继续看到平台原生的设计。 豆瓣广播在几年前就已经是国内少有的几个 Android Design 的应用,这一点一直令我钦佩和喜爱。在豆瓣应用最开始的版本中,也曾有过 Material Design 的尝试,但随着和 iOS 风格设计的杂糅,逐渐显得不合时宜,以至于最终选择了完全的 iOS 风格。我对此一直感到有些遗憾,况且 Material Design 也是一款更加优秀的设计语言。所以,我希望实现未能见到的另一种可能性。 部分特性 Material Design 首页友邻广播 启动速度优化 界面动画 支持屏幕旋转 平板多列视图 支持使用 Custom Tabs 打开网页 支持切换长/短链接显示 关于名字 指导原则 豆芽的最终目标是为豆瓣中基于友邻的信息获取方式提供在移动端的便利。为了优雅地实现这个目标,豆芽将主要遵循以下的原则: 遵循 Material Design 规范,且指导思想优先于细节规定。 像素完美,但更注重以人为本。 实现精确,代码可以自我辩护。 行为合格,支持屏幕旋转和平板布局。 功能崇尚简约,不打扰用户。 行为默认值合理,且用户可调节。 积极表现豆瓣特性,如广播、友邻、豆邮等。 通过细节设计,提倡用心、考虑到他人的内容。 规则可以被打破,但前提是理解规则。 功能架构 豆芽的架构将与当前网站的设计十分类似。 你可能会问,难道豆芽只是要做一个豆瓣网站的移动端界面么?并非如此。豆芽的最终目标是为基于友邻的信息获取方式提供便利,所以架构设计也是为此服务。而架构与当前网页端设计基本相同,则是因为现在网页端正是一个符合这个目标的设计,并且与移动端的导航也可以很好地契合。 让我们详细地规划一下豆芽吧。 导航采用抽屉一级导航 选项卡二级导航的方式。工具栏上将显示全局的动作。 工具栏 提醒:所有类别的提醒,可以查看历史提醒 豆邮:用户间的邮件往来,希望鼓励郑重而非聊天。 搜索:立即访问想要的内容。 用户:点击后显示个人页面,相当于“我的豆瓣”。 首页 友邻广播:友邻互动、友邻推荐、系统定制的推荐。 九点:友邻的日记、博客文章等,有深度的内容。 一刻:全站范围的热门内容推荐。 同城:基于地理位置的内容。 线上活动:基于共同兴趣的内容。 读书 分类浏览、首页推荐:入口,以及最有可能发现新内容的地方。 我读:管理自己的读书标记、创造内容。 动态:查看友邻的阅读动态,互动、获得推荐。 豆瓣猜:基于算法的推荐。 电影 类似读书。 音乐 类似读书。 设置:提供应用设置等。 在子页面设计中,豆芽将尽量鼓励长内容和用心的互动。因为我相信只有豆瓣值得这样尝试。 实现状况 我在最初的二十天内冲刺实现了应用的网络层、账户系统等基础架构,和查看友邻广播需要的大部分功能,大约 8000 行代码。 在接下来的八十天中,由于课业、其他事情和速度瓶颈,实现过程有所减慢。但是,应用的细节功能和界面交互都正在不断地被实现和优化。代码量达到了 14000 行,同时为此应用而写作的多个开源库的数千行代码并没有被计入。 此后项目经历了大型的重构,以适应代码复用和支持屏幕旋转的需求。在此之后,我得以实现了一个较为美观的个人资料页,并且对应用的许多细节进行了完善。 目前实现了原“豆瓣广播”应用的大部分功能。剩下的工作也正在继续进行中。 实现架构 数据层面 应用除了对少数内容进行缓存,其他内容均直接从网络获取。 基于 Android 账户系统提供用户账户和身份认证。 使用 Volley 及部分自定义增强处理网络请求。 使用 Gson 自动填充数据模型。 使用 Glide 加载图片。 使用 DiskLRUCache 及自定义增强对首页数据进行缓存。 使用 EventBus 同步不同页面间对象状态。 界面层面 使用 Support Library 中的 AppCompat、Design、CardView、RecyclerView 进行 Material Design 实现,在必要时引入/自己写作第三方库以实现部分界面元素和效果。 使用框架的 Shared Element Transition 实现在 Android 5.0 以上的界面过渡动画。 界面实现一般分为 Activity、Fragment、Adapter 三个模块,分别负责作为容器,发起请求、展示数据和用户交互,以及数据/交互绑定。 实现难点 网络请求 Volley 本身是一个不算十分完备的库,对于请求参数、重试、认证等方面都需要开发者自己实现。在豆芽中,应用对 Volley 进行了包装,增加了以上功能,并且尽力做到了通用,为之后 API 层建立提供了很多方便。 磁盘缓存 DiskLRUCache 是一个只实现了同步读取写入的库,因此豆芽对其进行了包装,提供了异步读写的 API,正确实现,提高了应用的响应速度。 状态同步 由于各个界面独自获取数据,数据本身与常规的 ContentProvider 机制中不同,是去中心化的,即可能遇到状态不同步的问题。 具体地说,即有可能用户在广播详情界面中点赞后,回到主界面列表视图,发现并未更新状态。 而豆芽解决方案则是使用 EventBus,在请求完成后通知所有界面刷新同一数据。 界面动画 Android 5.0 以上提供了 SharedElementTransition,然而默认情况下共享的界面元素在动画时却会被放置在其他界面元素之上,导致其突然越过 AppBar 或 StatusBar 的情况。 通过大量的文档阅读、源代码分析和调试,经过大约一周的时间,最终实现了较为理想的效果。 屏幕旋转 Android 在屏幕旋转时,会销毁视图和 Activity 并重建,此时如何保存视图状态和已加载的数据、正在进行的网络请求即是问题。 Android 对部分视图状态提供了自动保存恢复,而豆芽对于其他需要保存的状态则通过自定义的 onSaveViewState() 和 onRestoreViewState()。 对于数据,豆芽通过自定义的一个无界面的 RetainDataFragment 进行数据保留,并且接口十分简单易用。 同时,由于网络请求的异步特性,豆芽通过自定义的一个 RequestFragment 实现了网络请求在 Activity 重建期间的保留,并且能够在 Activity 重建完成后将请求前的状态和请求结果回调至新的 Activity。 平板适配 Android 本身的资源系统提供了对不同配置的很好支持,通过建立不同的资源文件,即可在手机和平板上使用不同的界面设定。 此外,由于采用了 RecyclerView,通过在运行时判断当前设备配置,可以动态给界面设置为 1、2、3 列视图,充分利用屏幕空间。 启动速度 Android 默认在冷启动应用进程至能够调用 Activity.onCreate() 前会加载应用主题中的背景作为预览,而默认背景是白色,与应用在上部拥有绿色 AppBar 的效果不相匹配。 为了生成适应于不同屏幕大小、系统版本的图片,我使用 bash 编写了一系列脚本,并实现了一个通用的模板化 SVG 格式,详情见 MaterialColdStart 和 AndroidSVGScripts。 经过自定义窗口背景和其他优化,应用在手机上已经可以达到立即启动的视觉效果。 派生开源库 为此项目诞生的五个开源库: MaterialColdStart,800 Stars MaterialProgressBar,500 Stars CustomTabsHelper,200 Stars MaterialEditText SystemUiHelper 第三方库 PhotoView Glide Gson ButterKnife DiskLruCache ThreeTenABP Volley EventBus CustomTabsHelper MaterialEditText MaterialProgressBar SystemUiHelper MaterialColdStart 构建 APK 文件可以在本项目的 Releases 中找到。 至于手动构建本项目的基本步骤: 创建 signing.properties: storeFile=YOUR_STORE_FILE storePassword= keyAlias= keyPassword= 执行 ./gradlew build。 使用 安装应用后,请安装 豆芽 API Key 设置向导 以设置 API Key。 暂时没有内置的更新渠道,请关注本项目的 Release。 请不要安装从不可靠的来源获取的 APK,以免泄漏您的用户名和密码。 本项目git地址:https://github.com/DreaminginCodeZH/Douya

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值