新聞中心
一、前言

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


咨詢
建站咨詢
