为了方便getParentActivityIntent()的使用,提供了很多重载,但其实目的都是一样的,就是获取我们指定的当前活动的上层活动(父活动)。由于是Supportv4包下的一个辅助类,其实内部做了很多兼容处理。它针对不同的Android级别有不同的实现。可以看出NavUtilsImplBase和NavUtilsImplJB都是为了处理兼容性问题而设计的。它们都实现了NavUtilsImpl接口并处理静态代码块中的兼容性问题。好了,这里先说说源码,我们来看看怎么使用吧。如果要使用NavUtils,还需要在AndroidManifest.xml中为Activity指定一个父Activity。这是一个包含两个活动的演示,MainActivity和ChildActivity。我们需要将父Activity设置为ChildActivity。为Activity设置父Activity,需要区分版本。4.1之后可以直接使用android:parentActivityName指定。对于4.0及以下版本,如果要使用,可以配置meta-data标签,名字必须是android.support.PARENT_ACTIVITY,value用来指定父Activity。我们先来看看官方推荐的使用示例。它的流程很简单,首先使用NavUtils.getParentActivityIntent()方法获取其父Activity,然后使用NavUtils.shouldUpRecreateTask()方法判断当前Activity是否需要一个Task,如果需要则使用TaskStackBuilder进行操作,如果不需要直接调用NavUtils.navigateUpTo()方法就可以继续接下来的逻辑了。可以看到这组逻辑非常简单。如果按照文档中的描述进行操作,应该会有很好的使用体验,但是它也有陷阱,后面会讲到。3、NavUtils的源码首先我们来看下ApiLevel16以下的Api实现NavUtilsImplBase,因为没有高版本的Apis,更多的细节可以从源码中看到。简单看一下它的细节,shouldUpRecreateTask()方法其实是通过检查Action是否等于ACTION_MAIN来判断的,而navigateUpTo()只是给upIntent加上了flagFLAG_ACTIVITY_CLEAR_TOP,然后启动父Activity,然后关闭自己。至于getParentActivityIntent()的代码,其实核心就是NavUtils.getParentActivityName()。最后可以看到和我们的配置一样,都是从meta-data中获取的数据。我们来看看NavTilsImplJB在ApiLevel16上的实现,可以看到,它的很多逻辑其实都放在了NavUtilsJB类中。其实它的很多方法都是直接调用的Activity中对应的方法。有兴趣的可以看看Activity源码中的实现。4.填洞和最终实现至此,你已经基本了解了NavUtils的实现原理。看起来用起来应该不会有那么多问题,但是真正用起来就会发现有坑。1.shouldUpRecreateTask()总是返回false。事实上,这并不是shouldUpRecreateTask()方法实现中的错误。它实际上用于通知。当Notification使用PendingIntent时,使用TaskStackBuilder构建它,它构造了一个BackTask,这里可以使用shouldUpRecreateTask()方法进行判断。但是大多数情况下,我们并不只是在Notification中使用,还有一些推送消息。这个Notification不是我们自己构造的,而是由第三方SDK构造的,导致这种情况大部分场景不一致。下面是官方提供的demo。有兴趣的可以移步官方文档查看:https://developer.android.com/guide/topics/ui/notifiers/notifications.html#NotificationResponse所以我们可以使用Activity.isTaskRoot()来做辅助判断,是Activity方法,可以判断当前Activity是否是当前Task的根Activity,也就是说如果回滚,当前Task实际上会被彻底清空,也就是说你已经退出了。2.navigateUpTo()会重启MainActivity。navigateUpTo()方法可以从源码中看到。它实际上强加了一个FLAG_ACTIVITY_CLEAR_TOP然后重新启动它。这种情况下,在某些设备上,默认是有动画处理的,因为这里有一个打开新页面后自动返回上一页的操作,而不是finish()。那么解决方法也很简单。在判断当前Activity不需要使用TaskStackBuilder构造TaskStack时,直接finish()当前页面即可,因为这种判断表明当前Activity在页面中正常打开,所以直接finish()即可回到前面的页面。最终更改后的实现效果变成这样,AndroidManifest.xml中的配置保持不变。【本文为专栏作家“张扬”原创稿件,转载请微信♂联系作者获得授权】点此查看作者更多好文
