前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >大前端开发中的路由管理之三:Android篇

大前端开发中的路由管理之三:Android篇

作者头像
QQ音乐技术团队
发布2021-11-10 19:40:22
3.1K0
发布2021-11-10 19:40:22
举报

        我们通常认为Android开发中的路由管理主要分为两部分,Android原生页面栈和混合开发页面栈。在native原生页面中,使用最多的是四大组件之一的Activity和依托于其的Fragment。在混合开发页面中,通常又分为Activity-H5(WebView),Activity-Weex/React-Native,和Activity-Flutter这几种跨平台的页面交互方式。

1、原生之Activity的页面跳转与管理

1.1 从Activity启动模式入手

        在Android开发中,在默认的情况下(Standard 标准启动模式),如果我们多次启动同一个Activity,系统会创建多个实例并把它们一一放入任务栈中。当我们点击返回键进行页面切换时,会将这些Activity实例从任务栈中逐个移除,遵循先进后出的原则。出于多次启动同一个Activity,系统创建多个实例放入任务栈中会耗费内存资源的考虑,Android为Actiivty提供了启动模式,不同的模式会影响Activity返回时的页面跳转行为。

        我们知道Activity的启动模式有4种,具体如下:

        Standard 标准启动模式。每启动Activity都会创建一个新的实例置于任务栈栈顶。如图当页面返回时,Activity B出栈销毁,会进入当前Activity A任务栈新的栈顶Activity。

        Single Top 栈顶复用模式。当需要新建的Activity处于栈顶,则重用该Activity实例,否则新建该Activity并将其置于栈顶。该模式不会对任务栈中存在的Activity实例造成顺序上的影响,当页面返回时,会按照先进后出的顺序跳转进新的栈顶Activity。     

        Single Task 栈内复用模式。当需要新建的Activity想要的任务栈(通过TaskAffinity指定)不存在,则先创建该任务栈,新建该Activity实例并将其置于栈顶;若该任务栈存在,判断该Activity是否存在于栈中,若存在,则将其之上的Activity实例全部出栈,使其置于栈顶,重用该Activity实例;否则新建该Activity并将其置于栈顶。

        该模式可能会对任务栈中存在的Activity实例造成顺序上的影响,若将目标Activity之上的实例全部出栈,当页面返回时,会按照先进后出的顺序跳转进剩余的任务栈实例中。

Single Instance 单例复用模式。新建一个任务栈B并新建该Activity实例并置于栈顶。当页面返回时,会返回并使用打开该Activity之前的任务栈A,按照先进后出的顺序跳转进任务栈A的栈顶Activity。

        可以看到,不同的启动模式会影响Activity返回时的页面跳转行为,一些模式下会对任务栈及其内的Activity顺序产生改变,开发过程中需要根据不同场景选择不同模式,同时充分考虑其产生的对返回时页面跳转行为的影响。

        在Activity页面之间的跳转管理中,对于这些Activity的创建、回退、跳转、复用等,Android提供了完备的AMS(ActivityManagerService)管理机制。

        ActivityManagerService被用来管理Android四大组件,在对于Activity的管理中,主要体现在任务栈上。主要的任务栈管理模型如上图,可以从中看出ctivityRecord、TaskRecord和ActivityStack三者间的管理、包含关系。

        上图是三者的UML类图。根据图中的主要关系与类方法,我们更加容易理解其主要职责:ActivityRecord 是应用层Activity组件在AMS的代表,每个启动的Activity都有一个与之对应的ActivityRecord实例。TaskRecord 是任务栈(也叫做返回栈),遵循先进后出的栈原则,栈内用来记录APP跳转过程中的ActivityRecord集合。ActivityStack 是用于管理任务栈TaskRecord而维护的集合,一般情况下栈内管理着位于前台的TaskRecord和数个后台TaskRecord。

        可以看到,此三者贯穿AMS管理Activity的整个逻辑,任务栈管理模型为我们提供了灵活的Activity的创建、回退、跳转、复用等页面栈操作的实现。

1.2 Activity间的页面桥梁-Intent

        当我们需要进行不同Activity之间的跳转时,需要用到启动Activity的桥梁:显式Intent & 隐式Intent。下面以两个Activity之间的跳转为例:

代码语言:javascript
复制
// 显示:使用构造函数传入Class对象 Intent intent = new Intent(this, SecondActivity.class);  startActivity(intent);
// 隐式:通过Category、Action、Data设置 Intent intent = new Intent();  intent.addCategory(Intent.CATEGORY_DEFAULT);  intent.setAction("com.test.action");  startActivity(intent);  new Intent(Intent.ACTION_VIEW, Uri.parse(Url))

        可以看到,显式调用需要明确指定被启动对象的组件信息,包括包名和类名。一般是在同一个应用程序内部使用的。隐式调用通过Intent Filter来实现,Android系统会根据在隐式意图中设置的动作(action)、类别(category)、Data(URI和数据类型)找到合适的组件来处理这个意图。一般用于不同的应用程序之间。

        从启动对象来看,显式Intent通过明确启动对象的组件信息使得有固定的接收方,隐式Intent通过Intent Filter过滤匹配合适的启动对象;从使用场景上看,在同一项目下的页面跳转可以使用显式Intent,跨项目的页面跳转官方推荐使用隐式Intent;对于同一个Intent既有显式又有隐式调用,则以显式调用为主。

2、原生之Fragment的页面跳转与管理

2.1 Fragment与Activity间的页面跳转

        Fragment 的发明是为了灵活的布局和复用布局,比如在屏幕较大的 Pad 上,可以一个 Activity 左边呈现 A,右边呈现 B。下图是其生命周期:

        考虑到Fragment与Activity之间的页面跳转,无非在于以下四种:

代码语言:javascript
复制
 ①  activity1_fragment1 -> fragment2   ②  activity1_fragment1 -> activity2 ③  activity1_fragment1 -> activity2_fragment2 ④  activity1 -> activity2_fragment2

        可以看到包含单Activity多Fragment跳转、多Activity多Fragment跳转,以及Activity与Fragment相互跳转。由于Fragment 没有继承 View,是被添加到 Activity 的某个 ViewGroup 中,并且具有完整的生命周期,其生命周期受宿主 Activity 生命周期影响。可以简单地理解,Fragment 是具有类似于 Activity 生命周期和返回栈的视图容器。所以对于Fragment与Activity之间进行页面跳转时,只需要理清其生命周期的对应关系和依赖关系,处于复杂情况下仍然万变不离其宗。

2.2 Navigation路由框架

        Navigation是一个页面路由导航框架,简化了单Activity多Fragment之间的跳转,本质上是封装的一套跳转逻辑,我们在使用时只要将所有的需要跳转的Fragment全部放到布局里面后通过简单的调用即可。

        Navigation和Flutter的路由有一定的相似性,这里是将frament作为跳转点,在开发时,可以清晰地看到每个界面的跳转路径。同时,Navigation 组件提供管理所有返回堆栈的功能,堆栈的顶部为当前屏幕,堆栈中记录着访问的目的地顺序,堆栈的底部是应用的起始地,同时提供了相关更改返回栈的方法,使得我们可以灵活在不同Fragment之间实现页面跳转。

        其实现页面栈跳转的原理主要是:

3、混合开发的页面跳转与管理

        跨平台层作为前端与Native的中间混合层,主要目标是为Hybrid/Weex/Fultter/RN(或者其他跨平台方案)提供更好的服务能力或者互动能力(比如获取地理位置信息或者设置容器导航标题与按钮等等)。

        Web技术:主要依赖于WebView的技术,功能支持受限(如在需要频繁拖拽且显示动画的场景下流畅度下降),比如PhoneGap、Cordova、小程序。

        原生渲染:使用JavaScript做为编程语言,经过中间层转化为原生控件来渲染UI界面,比如React Native、Weex。

        自渲染技术:自行实现一套渲染框架,可经过调用skia等方式完成自渲染,而不依赖于原生控件,比如Flutter、Unity。

3.1 Activity-H5(webview)

        我们知道在Android原生控件与WebView的混合开发中,Activity通过在布局内置WebView控件来加载目标H5;WebView通过显式/隐式调用Intent实现跳转到native页面,WebView本身可以通过常见的工具类如WebSettings、WebViewClient、WebChromeClient实现配置、加载与请求处理。

需要关注的是,当由Activity跳转进入WebView,伴随着从Activity任务栈进入H5任务栈,如果我们希望接下来在H5内做页面前进或后退页面跳转,如按下返回键后不返回Activity任务栈,而是实现WebView任务栈的后退,则需要根据WebView提供的一些判断网页是否可以前进后退的api,拦截对于返回键的监听以实现。

代码语言:javascript
复制
// WebView提供apiWebview.canGoBack //判断是否可以后退Webview.goBack //后退网页Webview.canGoForward //判断是否可以前进Webview.goForward //前进网页
// 拦截返回键,实现WebView返回的页面跳转public boolean onKeyDown(int keyCode, KeyEvent event) {  if ((keyCode == KEYCODE_BACK) && mWebView.canGoBack) {    mWebView.goBack;    return true;  }  return super.onKeyDown(keyCode, event);}

3.2 Activity-Weex、React-Native

        Weex和React-Native经过中间层转化为原生控件来渲染UI界面(通过一套规则,映射到原生控件)。Activity-Weex之间的页面跳转和Activity-React Native原理上是类似的。

        我们知道Android的页面跳转是通过Intent、RN是通过路由,而两者直接的页面互相跳转是需要原生借助JS暴露接口给RN来实现。在Android原生页面与RN之间的页面管理中,主要分为三类:

        ① 以Intent实现的原生跳转到RN,此时页面栈交由Activity任务栈管理;

        ② 以路由Navigation实现的RN跳转到RN,此时页面栈交由路由导航中的堆栈管理;

        ③以及RN跳转到原生,主要包含三步:定义Module类,继承ReactContextBaseJavaModule、定义Package类,实现接口ReactPackage、定义Application类,继承android的Application,并实现ReactApplication接口,其实是在原生端创建Module类通过桥接的方式导出到JS端供JS代码调用原生端代码来实现的。

        由此可知,对于更加复杂的如RN-RN-原生-RN-原生-原生页面间跳转等情况,都可拆分为由任务栈管理、由Navigation路由管理、以及由桥接方式实现路由管理。

3.3 Activity-Flutter

        简单地来说,Flutter是使用跨平台的图形渲染引擎在view上画控件,Activity-Flutter之间的页面跳转和Activity-React Native原理大体上是类似的。

        我们知道Android的页面跳转是通过Intent、Flutter是通过Widget进行路由管理,在Android原生页面与Flutter之间的页面管理如图所示。更多的关于Flutter Widget、Channel的内容可以在后续系列文章的该部分进行查看。

        由此可知,对于更加复杂的如Flutter-Flutter-原生-Flutter-原生-原生页面间跳转等情况,同样可拆分为由任务栈管理、由Widget路由管理、以及由Channel方式实现路由管理。

4、小结

        通过上述对于Android开发中的路由管理的介绍,可以看出Android原生页面栈和混合开发页面栈的相关实现在实际应用中极具灵活性。

        在原生页面中,通过理解AMS,重点关注Activity的启动模式、Fragment的Navigation路由框架以及两者之间涉及到的页面栈跳转方式;在混合开发页面中,从native方-跨平台方-双方交互这三个角度简化路由管理,分别梳理了native-H5(WebView)、native-Weex/React-Native、native-Flutter这几种常见的跨平台的页面交互方式,使得在更加复杂的页面管理下仍可万变不离其宗。


        至此,我们了解到了Android端是如何去实现路由管理的,那么,就请期待我们下一篇文章《大前端开发中的路由管理之四:iOS篇》吧,下篇文章将为大家揭秘iOS端是如何去做路由管理的。

参考资料

[1] Activity启动模式与页面管理 https://www.jianshu.com/p/32938446e4e0

[2] Fragment页面管理

https://www.jianshu.com/p/fef256de6364

[3] 从AMS理解Android任务栈

https://www.jianshu.com/p/fdd80e9a0bdc

[4] Navigation路由框架

https://www.jianshu.com/p/95796d895cfc

[5] 混合开发发展史

https://juejin.cn/post/6856414014948900877

[6] Flutter原生混合开发

https://juejin.cn/post/6844903929625444359#heading-12

QQ音乐招聘 Android / iOS 客户端开发,点击左下方“查看原文”投递简历~

也可将简历发送至邮箱:tmezp@tencent.com


文末为大家推荐一个技术号《腾讯音乐天琴实验室》,TME天琴实验室致力于对业内前沿科技如AI等方向进行相关研发,持续推出新技术提升TME旗下QQ音乐等平台的音乐视听体验,对音视频相关AI研发感兴趣的同仁们一起交流学习起来吧!!!

↓   ↓   ↓

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-11-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 腾讯音乐技术团队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.1 从Activity启动模式入手
  • 1.2 Activity间的页面桥梁-Intent
  • 2.1 Fragment与Activity间的页面跳转
  • 2.2 Navigation路由框架
  • 3.1 Activity-H5(webview)
  • 3.2 Activity-Weex、React-Native
  • 3.3 Activity-Flutter
  • 参考资料
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档