安卓開發大神級人物 JakeWharton 前不久在接受採訪時提出一個頗具爭議而又沒有給出緣由的建議:一個 App 只須要一個 Activity ,你可使用 Fragments,只是別用 Fragments 回退棧。android
針對這一言論,有關 JakeWharton 建議的背後緣由的一個提問迅速在 Reddit 國外網站的安卓開發頻道引起熱評。程序員
衆多安卓開發人員紛紛提出本身的看法。其中獲贊最高的一條,甚至獲得 JakeWharton 本人的親自贊評。面試
咱們來看看這條回答都提到了哪些內容,對 Activity 和 Fragment 之間的愛恨情仇有何獨到的看法,憑什麼能獲得 JakeWharton 本尊的青睞有加。微信
由於 Activity 是一個程序入口。你能夠將其視爲 app 的一個 main
函數。站在用戶的立場上,一般你進入 app 的方式可能包括如下幾種:app
launcher 桌面程序(main
函數入口);dom
來自參數化 main
函數入口的通知欄,而且導航到 app 的指定位置;函數
若是你作的是一個相機應用,那麼須要處理圖片請求的 intents;佈局
若是你作的是一個社交產品,那麼須要處理 share
請求的 intents;動畫
差很少相似這些場景。網站
可是,若是你真的不用分享和來自應用的 intents 的話,而且惟一的程序入口就是 launcher 桌面,別爲每個頁面建立一個新的入口。這樣作其實沒有意義。爲何沒有意義?由於這種場景下,進程死掉後 launcher 可以啓動任何你應用中的 Activity 頁面。
Fragments 是處理生命週期事件的視圖控制器,而且很是不錯。然而,Fragments 回退棧簡直垃圾;回退棧變化監聽器老是不正常地被調用( 1 次 transaction 三次調用?),而且不告訴你調用什麼,而在恢復事務時也不知道哪些 fragments 是可用的。
你能夠給事務添加 tag 標籤,而後從棧中彈出操做,可是僅僅是一個 main -> Events -> Details(id=123)
的操做流程就至關繁瑣了。
一樣的,一旦你將一個 Fragment 放進回退棧中,我我的不知道它的生命週期開始作什麼。我曾經遇到過一個後臺中的 fragment 被調用四次 onCreateView()
方法,我甚至不知道究竟怎麼了。而沒有位於回退棧中的 Fragments 是能夠被預見的。它們的動畫支持有點古怪,但至少它們還能使用。
因此若是你想知道哪些 Fragments 是你可以操做的而且哪些 views 是你正在展現的而且可以在你本身的導航狀態控制之中,那麼你應該本身處理導航操做。把「應用邏輯」抽象化到一個 presenter(亦楓注:MVP 模式)中聽起來來很棒,可是你是否是脫離了應用視圖層裏面的真實狀況?
可是單一 activity 的優點是什麼?
更簡單的生命週期處理(例如,當 app 進入後臺時,你只須要處理 onStop
方法),更少錯誤空間,和更多控制。一樣的,你能夠移動視圖層外面的導航狀態到 domain 層,或者至少到 presenter 中。不須要太多 view.navigateToDetail(songId)
之類的東西,你只須要在你的 presenter 或者 ViewModel 或者不管哪些時髦的用法中使用 backstack.goTo(SongKey.create(songId))
就行。藉助一個合適的庫,當你到了 onResume
時它會自動將這些導航調用加入隊列,而且不會導致 fragment 事務發生崩潰,很是得好。
儘管 Google 給出的案例也在用 commitAllowingStateLoss()
,我有使用 commitNow()
的動畫愛好。在我看來,單個 activity 可以看得見的好處就是,頁面間共享 views 的能力,取代經過使用 <include
標籤在 18 個佈局文件重複視圖。其餘固然是更簡單的導航操做。
以上即是深得 JakeWharton 大神心意的一條回答。話雖如此,可是系統 Fragment 存在的未解之謎或者說出乎你意料的坑實在太多。若是必定要在多 activity 部分 fragments 和單 activity 多 fragments 之間選擇的話,我想不僅是我,不少人仍是堅決果斷地選擇前者。
更多討論內容,參見:
備註:關於使用 Fragments 的那些道道,我以前也寫過相關總結性的文章來,感興趣地不妨去個人博客搜索看看。
關於我:亦楓,博客地址:yifeng.studio/,新浪微博:IT亦楓
微信掃描二維碼,歡迎關注個人我的公衆號:安卓筆記俠
不只分享個人原創技術文章,還有程序員的職場遐想
彩蛋:公衆號回覆關鍵字「面試資料」,獲取 BAT 面試大牛爲你準備的全套面試資料!
![]()