使用 Router 实现的模块化,如何优雅的回到主页面

版权声明:html

本帐号发布文章均来自公众号,承香墨影(cxmyDev),版权归承香墨影全部。android

每周会统一更新到掘金,若是喜欢,可关注公众号获取最新文章。ide

未经容许,不得转载。模块化

1、前言

如今愈来愈多的 App 以 Router 路由的形式,来实现模块化。通常而言,这种 Router 的方案,从外部直接调起的方式,是由一个 ProxyActivity 作一个代理,而后再由它去跳转到项目内的其余目标 TargetActivity 。这样的实现,理论上,是能够从外部调起 App 内全部的 Activity 的。工具

可是这样就面临一个问题,若是从外部调起了一个子页的 Activity,举例是一个影片详情页,若是想在用户回退的时候,进入到应用主页,而非直接退出了。就须要特殊处理了。优化

本文就以如何优化的回退到咱们须要的 Activity 来作一个说明。动画

2、分析和拆解问题

对于前面举例的状况来讲,实际上咱们只须要处理好如下问题便可。ui

  1. 如何区分当前 Activity 是从应用内打开,仍是从应用外直接打开,就是打开 Activity 的来源。
  2. 在区分出来 Activity 打开的来源以后,如何优雅的退回到咱们须要的 Activity。
  3. 要处理一些特殊状况。

对于这样两个问题,区分来源,最粗暴的方式,就是在 Intent 里增长一个相似 from 的字段,来标记是从那个页面过来的,若是不是咱们指定的话,在 finish() 或者 onBackPressed() 的时候,就打开 MainActivity 便可。3d

这是一个粗暴的方式,虽然它有用,可它不够优雅。代理

而在 Support v4 22.0.0 开始,针对这样的状况,为咱们增长了一个 NavUtils 的类,专门用于处理这种状况,接下来就来看看如何使用它。

3、NavUtils 如何使用

NavUtils 从名称上就能够看出来,它是一个用于处理导航的辅助工具类。

/nav-class.png
/nav-class.png

先来看看它的方法,它主要的方法主要分三中:

  • getParentActivityIntent():得到一个用户回退到父Activity 的Intent。
  • shouldUpRecreateTask():是否须要从新构建一个 Task。
  • navigateUpTo():回退到 Intent 指定的父 Activity。

为了方便使用 getParentActivityIntent() 提供了不少的重载,可是实际上目的都是同样的,就是拿到咱们指定的当前 Activity 的上一级 Activity(父 Activity )。

既然是 Support v4 包下的辅助类,它其实在内部也是作好了不少兼容的处理。它针对不一样的 Android Level 作了不一样的实现,能够看到 NavUtilsImplBaseNavUtilsImplJB 都是为了处理兼容性的问题,他们都实现了 NavUtilsImpl 接口,并且在静态代码块内处理好了兼容性问题。

/nav-jianrong.png
/nav-jianrong.png

好了,源码就先聊到这里,先来看看如何使用。

若是想要使用 NavUtils 还须要在 AndroidManifest.xml 中,为 Activity 指定一个父的 Activity。

这里有个 Demo ,有两个 Activity,分别是 MainActivity 和 ChildActivity 。咱们须要对 ChildActivity 设定父 Activity。

/nav-manifest.png
/nav-manifest.png

为 Activity 设定父 Activity,是须要区分版本的,在 4.1 以后,是能够直接使用 android:parentActivityName 直接指定便可。而对于 4.0 及一下的版本,若是想要使用,能够配置 meta-data 标签,name 必须是 android.support.PARENT_ACTIVITYvalue 就是用来指定父 Activity 的。

先来看看官方推荐的使用示例。

/nav-doccode.png
/nav-doccode.png

它的流程很是的简单,首先使用 NavUtils.getParentActivityIntent() 方法,得到它的父 Activity,而后使用 NavUtils.shouldUpRecreateTask() 方法,肯定当前 Activity 是否须要一个 Task ,若是须要,使用 TaskStackBuilder 来操做,若是不须要就直接调用 NavUtils.navigateUpTo() 方法来继续接下去的逻辑。

能够看到这一套逻辑,很是的简单,若是按照文档的描述,使用起来应该体验挺不错的,可是它有坑,后面讲。

3、NavUtils 的源码

先来看看 NavUtilsImplBase 这个 Api Level 16 如下的 Api 实现,它由于没有一些高版本的 Api,从源码上能看出跟多细节。

/nav-nojbmethod.png
/nav-nojbmethod.png

简单关注一下它的细节,shouldUpRecreateTask() 方法,其实是经过校验 Action 是否等于 ACTION_MAIN 来肯定的,而 navigateUpTo() 只是为 upIntent 添加了 FLAG_ACTIVITY_CLEAR_TOP 这个 flag ,而后启动父 Activity 而且关闭本身。而 getParentActivityIntent() 的代码,其实核心仍是在 NavUtils.getParentActivityName() ,最终能够看到,它和咱们配置的同样,是从 meta-data 中获取的数据。

/nav-gatemate.png
/nav-gatemate.png

再来看看 NavTilsImplJB 这个 Api Level 16 上的实现。

/nav-jbmethod.png
/nav-jbmethod.png

能够看到,它其实不少逻辑都放在 NavUtilsJB 这个类中。

/nav-utilsjb.png
/nav-utilsjb.png

而它实际上不少方法都是直接调用的 Activity 中对应的方法,有兴趣能够去看看 Activity 的源码中的实现。

4、填坑和最终实现

到这里,基本上就已经了解了 NavUtils 的实现原理了,看样子用起来应该没那么多问题,可是若是实际使用起来,你就会发现有坑了。

一、shouldUpRecreateTask() 永远返回的是 false。

实际上,这并非 shouldUpRecreateTask() 方法在实现上有什么 Bug,它其实是给 Notification 使用的,在 Notification 使用 PendingIntent 的时候,使用 TaskStackBuilder 来构建它,其构造一个 Back Task ,在这里就可使用 shouldUpRecreateTask() 方法来作判断了。

可是大多数状况下,咱们并不仅是在 Notification 中使用它,而且有一些推送的消息,这个 Notification 并不是咱们去构造的,而是由第三方 SDK 来构建的,这就致使这种状况并不符合大多数场景。

下面是官方提供的一个 Demo。

/nav-notifydemo.png
/nav-notifydemo.png

有兴趣能够移步到官方文档查看:

developer.android.com/guide/topic…

因此咱们可使用 Activity.isTaskRoot() 来作辅助判断,它是 Activity 的方法,能够判断当前 Activity 是不是在当前 Task 的根 Activity,这样就说明再回退的话,实际上就会将当前 Task 完整的清空,表现就是退出去了。

二、navigateUpTo() 会从新启动MainActivity

navigateUpTo() 方法从源码上能够看出来,它其实是强加了一个 FLAG_ACTIVITY_CLEAR_TOP ,而后从新启动它,这样的话,在某些设备上,默认是有动画处理的,由于这里是打开了一个新的页面,而非 finish() 以后,自动回退到上一个页面的操做。

那么解决方案也很是的简单,在判断当前 Activity 不须要使用 TaskStackBuilder 构造一个 Task Stack ,就直接 finish() 掉当前的页面,由于这样的判断说明当前 Activity 是在页面内正常打开的,因此直接 finish() 就能够退回到上一个页面了。

最终改动以后的实现效果就变成了这样,AndroidManifest.xml 中的配置不变。

/nav-demo.png
/nav-demo.png

公众号二维码.jpg
公众号二维码.jpg
相关文章
相关标签/搜索