掃二維碼與項目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
一、前言

創(chuàng)新互聯(lián)主要從事成都網(wǎng)站建設(shè)、網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)河曲,十余年網(wǎng)站建設(shè)經(jīng)驗,價格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):13518219792
現(xiàn)在越來越多的 App 以 Router 路由的形式,來實現(xiàn)模塊化。一般而言,這種 Router 的方案,從外部直接調(diào)起的方式,是由一個 ProxyActivity 做一個代理,然后再由它去跳轉(zhuǎn)到項目內(nèi)的其他目標 TargetActivity 。這樣的實現(xiàn),理論上,是可以從外部調(diào)起 App 內(nèi)所有的 Activity 的。
但是這樣就面臨一個問題,如果從外部調(diào)起了一個子頁的 Activity,舉例是一個影片詳情頁,如果想在用戶回退的時候,進入到應(yīng)用主頁,而非直接退出了。就需要特殊處理了。
本文就以如何優(yōu)化的回退到我們需要的 Activity 來做一個說明。
二、分析和拆解問題
對于前面舉例的情況來說,實際上我們只需要處理好以下問題即可。
對于這樣幾個問題,區(qū)分來源,最粗暴的方式,就是在 Intent 里增加一個類似 from 的字段,來標記是從那個頁面過來的,如果不是我們指定的話,在 finish() 或者 onBackPressed() 的時候,就打開 MainActivity 即可。
這是一個粗暴的方式,雖然它有用,可它不夠優(yōu)雅。
而在 Support v4 22.0.0 開始,針對這樣的情況,為我們增加了一個 NavUtils 的類,專門用于處理這種情況,接下來就來看看如何使用它。
三、NavUtils 如何使用
NavUtils 從名稱上就可以看出來,它是一個用于處理導航的輔助工具類。
先來看看它的方法,它主要的方法主要分三中:
為了方便使用 getParentActivityIntent() 提供了很多的重載,但是實際上目的都是一樣的,就是拿到我們指定的當前 Activity 的上一級 Activity(父 Activity )。
既然是 Support v4 包下的輔助類,它其實在內(nèi)部也是做好了很多兼容的處理。它針對不同的 Android Level 做了不同的實現(xiàn),可以看到 NavUtilsImplBase 和 NavUtilsImplJB 都是為了處理兼容性的問題,他們都實現(xiàn)了 NavUtilsImpl 接口,而且在靜態(tài)代碼塊內(nèi)處理好了兼容性問題。
好了,源碼就先聊到這里,先來看看如何使用。
如果想要使用 NavUtils 還需要在 AndroidManifest.xml 中,為 Activity 指定一個父的 Activity。
這里有個 Demo ,有兩個 Activity,分別是 MainActivity 和 ChildActivity 。我們需要對 ChildActivity 設(shè)定父 Activity。
為 Activity 設(shè)定父 Activity,是需要區(qū)分版本的,在 4.1 之后,是可以使用 android:parentActivityName 直接指定即可。而對于 4.0 及一下的版本,如果想要使用,可以配置 meta-data 標簽,name 必須是 android.support.PARENT_ACTIVITY 而 value 就是用來指定父 Activity 的。
先來看看官方推薦的使用示例。
它的流程非常的簡單,首先使用 NavUtils.getParentActivityIntent() 方法,獲得它的父 Activity,然后使用 NavUtils.shouldUpRecreateTask() 方法,確定當前 Activity 是否需要一個 Task ,如果需要,使用 TaskStackBuilder 來操作,如果不需要就直接調(diào)用 NavUtils.navigateUpTo() 方法來繼續(xù)接下去的邏輯。
可以看到這一套邏輯,非常的簡單,如果按照文檔的描述,使用起來應(yīng)該體驗挺不錯的,但是它有坑,后面講。
三、NavUtils 的源碼
先來看看 NavUtilsImplBase 這個 Api Level 16 以下的 Api 實現(xiàn),它因為沒有一些高版本的 Api,從源碼上能看出跟多細節(jié)。
簡單關(guān)注一下它的細節(jié),shouldUpRecreateTask() 方法,實際上是通過校驗 Action 是否等于ACTION_MAIN 來確定的,而 navigateUpTo() 只是為 upIntent 添加了FLAG_ACTIVITY_CLEAR_TOP 這個 flag ,然后啟動父 Activity 并且關(guān)閉自己。而 getParentActivityIntent() 的代碼,其實核心還是在 NavUtils.getParentActivityName(),最終可以看到,它和我們配置的一樣,是從 meta-data 中獲取的數(shù)據(jù)。
再來看看 NavTilsImplJB 這個 Api Level 16 上的實現(xiàn)。
可以看到,它其實很多邏輯都放在 NavUtilsJB 這個類中。
而它實際上很多方法都是直接調(diào)用的 Activity 中對應(yīng)的方法,有興趣可以去看看 Activity 的源碼中的實現(xiàn)。
四、填坑和最終實現(xiàn)
到這里,基本上就已經(jīng)了解了 NavUtils 的實現(xiàn)原理了,看樣子用起來應(yīng)該沒那么多問題,但是如果實際使用起來,你就會發(fā)現(xiàn)有坑了。
1、shouldUpRecreateTask() 永遠返回的是 false。
實際上,這并不是 shouldUpRecreateTask() 方法在實現(xiàn)上有什么 Bug,它實際上是給Notification 使用的,在 Notification 使用 PendingIntent 的時候,使用 TaskStackBuilder來構(gòu)建它,其構(gòu)造一個 Back Task ,在這里就可以使用 shouldUpRecreateTask() 方法來做判斷了。
但是大多數(shù)情況下,我們并不只是在 Notification 中使用它,并且有一些推送的消息,這個Notification 并非我們?nèi)?gòu)造的,而是由第三方 SDK 來構(gòu)建的,這就導致這種情況并不符合大多數(shù)場景。
下面是官方提供的一個 Demo。
有興趣可以移步到官方文檔查看:
https://developer.android.com/guide/topics/ui/notifiers/notifications.html#NotificationResponse
所以我們可以使用 Activity.isTaskRoot() 來做輔助判斷,它是 Activity 的方法,可以判斷當前 Activity 是否是在當前 Task 的根 Activity,這樣就說明再回退的話,實際上就會將當前 Task 完整的清空,表現(xiàn)就是退出去了。
2、navigateUpTo() 會重新啟動MainActivity
navigateUpTo() 方法從源碼上可以看出來,它實際上是強加了一個FLAG_ACTIVITY_CLEAR_TOP ,然后重新啟動它,這樣的話,在某些設(shè)備上,默認是有動畫處理的,因為這里是打開了一個新的頁面,而非 finish() 之后,自動回退到上一個頁面的操作。
那么解決方案也非常的簡單,在判斷當前 Activity 不需要使用 TaskStackBuilder 構(gòu)造一個 Task Stack ,就直接 finish() 掉當前的頁面,因為這樣的判斷說明當前 Activity 是在頁面內(nèi)正常打開的,所以直接 finish() 就可以退回到上一個頁面了。
最終改動之后的實現(xiàn)效果就變成了這樣,AndroidManifest.xml 中的配置不變。
【本文為專欄作者“張旸”的原創(chuàng)稿件,轉(zhuǎn)載請通過微信公眾號聯(lián)系作者獲取授權(quán)】
戳這里,看該作者更多好文

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流