使用 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
相關文章
相關標籤/搜索