為了面試,為了高工資,廢話不多說,不定期更新。
1. Activity正常和異常情況下的生命周期分析。
Activity生命周期是一個老生常談的問題,但是有些刁鉆的問題也會一時半會的影響大家的思路,我們還是耐著性子來分析下吧。
I.正常生命周期
- onCreate
Avtivity啟動時調用的第一個方法,表示Activity正在被創(chuàng)建。應該在此初始化Activity的必需組件,最重要的是必需在該方法中調用setContentView方法來設置Activity的布局。
不應在此接口中執(zhí)行耗時操作,否則會影響Activity的顯示。
- onStart
在Activity即將對用戶可見之前調用(該接口與onResume對應,onResume代表已經(jīng)可見了),可以理解為已經(jīng)在后臺準備就緒了。
- onResume
在Activity即將開始與用戶進行交互前調用,此時Activity處于Activity堆棧的頂層,并具有用戶輸入焦點。
- onPause
當系統(tǒng)開始顯示另外一個Activity(Dialog不算,Dialog形式的Activity算)時調用,通常用于確認對持久性數(shù)據(jù)的保存更改,停止動畫及其他可能消耗CPU的內容。它應該快速執(zhí)行所需操作,因為它返回后,下一個Activity才能繼續(xù)執(zhí)行。
此時Activity對于用戶來講是部分可見的,例如彈出一個Dialog形式的Activity。
- onStop
在Activity對用戶完全不可見時調用,可以認為處于后臺。
- onDestory
在Activity被銷毀前調用,這是Activity將收到的最后的調用。
一般會在此銷毀啟動的線程,或者處理一些可能導致內存泄露的問題。
- onRestart
系統(tǒng)由不可見變?yōu)榭梢姇r調用,調用順序:onRestart-onstart-onResume.
Note: 除非程序在onCreate()方法里面就調用了finish()方法,系統(tǒng)通常是在執(zhí)行了onPause()與onStop() 之后再調用onDestroy() 。在某些情況下,例如我們的activity只是做了一個臨時的邏輯跳轉的功能,它只是用來決定跳轉到哪一個activity,這樣的話,需要在onCreate里面調用finish方法,這樣系統(tǒng)會直接調用onDestory,跳過生命周期中的其他方法。
II.異常生命周期
- 資源配置發(fā)生變更導致的Activity殺死重建
當系統(tǒng)配置發(fā)生變化后,例如屏幕方向切換、系統(tǒng)語言切換,生命周期方法調用如下
onPause---onSaveInstanceState---onStop---onDestory---onCreate---onRestoreInstanceState--onStart--onResume
在Activity異常終止時,系統(tǒng)會調用onSaveInstanceState來保存一些狀態(tài)信息(在onStop方法之前調用,但是和onPause方法并無明確的調用順序)。
重建Activity時,onSaveInstanceState保存的信息Bundle會以參數(shù)的形式傳入onCreate和onRestoreInstanceState(在onStart方法之后調用)方法中,所以我們可以在這兩個方法中進行一些狀態(tài)恢復。
Note:onCreate方法的Bundle參數(shù)可能為空,所以我們一般配對使用onSaveInstanceState和onRestoreInstanceState。
保存數(shù)據(jù)的思想:
Activity被意外終止時,Activity會調用onSavedInstanceState去保存數(shù)據(jù),然后Activity會委托Window去保存數(shù)據(jù),接著Window再委托它上面的頂級容器去保存數(shù)據(jù),頂層容器是一個ViewGroup,一般來說它很可能是一個DecorView.最后頂層容器再去一一通知它的子元素來保存數(shù)據(jù),這樣整個數(shù)據(jù)保存過程就完成了,可以發(fā)現(xiàn),這是一種典型的委托思想,上層委托下層,父容器委托子容器去處理一些事情。
- 資源或者內存不足導致被殺死
對于Activity我們分為三種:
a.前臺Activity(高)
b.可見非前臺Activity(中)
c.后臺Activity(低)
當系統(tǒng)發(fā)現(xiàn)內存不足時,會按照上面的優(yōu)先級選擇殺死Activity所在的進程,并在后續(xù)(需要再顯示的時候)進行恢復。
Note:系統(tǒng)只恢復那些被開發(fā)者指定過id的控件,如果沒有為控件指定id,則系統(tǒng)就無法恢復了。
2. Android啟動模式分析
2.1 Application&Task&Process
Application
通俗的講,Application就是一組組件的集合,每一個應用都是一個Application。
Task
Task是在程序運行時,只針對Activity的概念,Task是一組相互關聯(lián)的Activity的集合,它存在framework層的一個概念,控制界面的跳轉和返回。
Process
進程是操作系統(tǒng)內核的一個概念,表示接受內核調度的執(zhí)行單位。
在默認情況下,一個應用程序的所有組件運行在同一個進程中。
2.2 啟動模式
Activity存在四種啟動模式:standard,singleTop,singleTask,singleInstance,使用時需要在AndroidManifest文件中配置。
standard:標準
標準模式,也是系統(tǒng)默認的Activity啟動模式。
Activity可以多次實例化,而且每個實例可以屬于不同的任務,并且一個任務可以擁有多個實例。
誰啟動的Activity,Activity就和它處于同一個任務棧中。
在使用ApplicationContext或者Service中的Context啟動Activity時,需要添加FLAG_ACTIVITY_NEW_TASK的標志才能正常啟動Activity;因為非Activity的Context沒有所謂的任務棧。
singleTop:棧頂復用
棧頂復用模式。
如果當前任務棧頂已經(jīng)存在Activity的一個實例,系統(tǒng)會調用該實例的onNewIntent方法向其傳遞Intent,而不會創(chuàng)建Activity的新實例。(該Activity的onCreate、onStart方法不會被系統(tǒng)調用,因為它并沒有發(fā)生改變)
Activity可以多次實例化,而每個實例均可屬于不同的任務,并且一個任務可以擁有多個實例(但前提是位于返回棧頂部的 Activity并不是Activity的現(xiàn)有實例)。
例如,假設任務返回棧包含Activity A 以及 Activity B、C 和位于頂部的 D(堆棧是 A-B-C-D;D 位于頂部)。
收到針對 D 類 Activity 的 Intent。
如果 D 具有默認的 "standard" 啟動模式,則會創(chuàng)建該類的新實例,且堆棧會變成 A-B-C-D-D。
但是,如果 D 的啟動模式是 "singleTop",則 D 的現(xiàn)有實例會通過 onNewIntent() 接收 Intent,因為它位于堆棧的頂部;而堆棧仍為 A-B-C-D。
但是,如果收到針對 B 類 Activity 的 Intent,則會向堆棧添加B新實例,即便其啟動模式為 "singleTop" 也是如此。
singleTask:棧內復用
棧內復用模式。
啟動一個singleTask的Activity實例時,如果任務棧中已經(jīng)存在這樣一個實例,就會將這個實例調度到任務的棧頂,并清除它當前所在任務中位于它上面的所有的activity,不會重新實例化該Activity;如果任務棧中不存在該Activity實例,則創(chuàng)建實例后入棧。
和SingleTop一樣,系統(tǒng)會回調它的oneNewInent方法。
singleInstance:單例
單例模式。
總是在新的任務中開啟,并且這個新的任務中有且只有這一個實例,也就是說被該實例啟動的其他activity會自動運行于另一個任務中。當再次啟動該activity的實例時,會重用已存在的任務和實例。并且會調用這個實例的onNewIntent()方法,將Intent實例傳遞到該實例中。
同一時刻在系統(tǒng)中只會存在一個這樣的Activity實例。
taskAffinity
taskAffinity表示Activity所處的任務棧,默認情況下一個應用中所有的Activity具有相同的taskAffinity(處于同一個任務中),默認的taskAffinity為應用的包名。
可以指定taskAffinity為空字符串,代表Activity不屬于任何任務。
taskActivity主要結合singleTask啟動模式或者allowTaskReparenting屬性配對使用,在其他情況下無意義。
面試的時候,給面試官舉例說明,更加簡潔明了,省的表述不清。
啟動模式指定
啟動模式的指定存在兩種方式:
- launchMode指定:無法指定FLAG_ACTIVITY_CLEAR_TOP.
- Intent Flag指定(優(yōu)先級高):無法指定singleInstance.
FLAG
FLAG_ACTIVITY_SINGLE_TOP
相當于singleTop
FLAG_ACTIVITY_NEW_TASK
沒有對應的啟動模式,它的部分特性與singleTask相同。
默認的跳轉類型,會重新創(chuàng)建一個新的Activity,比方說Task1中有A,B,C三個Activity,此時在C中啟動D的話,如果在Manifest.xml文件中給D添加了Affinity的值和Task中的不一樣的話,則會在新標記的Affinity所存在的Task中壓入這個Activity。如果是默認的或者指定的Affinity和Task一樣的話,就和標準模式一樣了啟動一個新的Activity.
FLAG_ACTIVITY_CLEAR_TOP
當Intent對象包含這個標記時,如果在棧中發(fā)現(xiàn)存在Activity實例,則清空這個實例之上的Activity,使其處于棧頂。
在使用默認的“standard”啟動模式下,如果沒有在Intent使用到FLAG_ACTIVITY_SINGLE_TOP標記,那么它將關閉后重建,如果使用了這個FLAG_ACTIVITY_SINGLE_TOP標記,則會使用已存在的實例;對于其他啟動模式,無需再使用FLAG_ACTIVITY_SINGLE_TOP,它都將使用已存在的實例,Intent會被傳遞到這個實例的onNewIntent()中。
3. 進程保活
參考鏈接:Android 進程保活招式大全
進程保活包括兩個方面:
- 提升進程優(yōu)先級,降低進程被殺死的概率。
- 進程被殺死后,進行拉活。
進程優(yōu)先級
Android系統(tǒng)將盡量長時間地保持應用進程,但為了新建進程或運行更重要的進程,最終需要清除舊進程來回收內存。 為了確定保留或終止哪些進程,系統(tǒng)會根據(jù)進程中正在運行的組件以及這些組件的狀態(tài),將每個進程放入“重要性層次結構”中。 必要時,系統(tǒng)會首先消除重要性最低的進程,然后是清除重要性稍低一級的進程,依此類推,以回收系統(tǒng)資源。
按照進程的優(yōu)先級,進程分為以下五類:
- 前臺進程
- 可見進程
- 服務進程
- 后臺進程
- 空進程
前臺進程
用戶操作所必需的進程,如果一個進程滿足以下任何一個條件,即為前臺進程:
- 托管用戶正在交互的 Activity(已調用 Activity 的 onResume() 方法)
- 托管某個Service,后者綁定到用戶正在交互的Activity
- 托管正在“前臺”運行的Service(服務已調用 startForeground())
- 托管正執(zhí)行一個生命周期回調的Service(onCreate()、onStart() 或 onDestroy())
- 托管正執(zhí)行其onReceive()方法的 BroadcastReceiver
通常,在任意給定時間前臺進程都為數(shù)不多。
只有在內存不足以支持它們同時繼續(xù)運行這一萬不得已的情況下,系統(tǒng)才會終止它們。
此時,設備往往已達到內存分頁狀態(tài),因此需要終止一些前臺進程來確保用戶界面正常響應。
可見進程
沒有任何前臺組件、但仍會影響用戶在屏幕上所見內容的進程。
如果一個進程滿足以下任一條件,即視為可見進程:
- 托管不在前臺、但仍對用戶可見的 Activity(已調用其 onPause() 方法)。
例如,如果前臺 Activity 啟動了一個對話框,允許在其后顯示上一 Activity,則有可能會發(fā)生這種情況。 - 托管綁定到可見(或前臺)Activity 的 Service。
可見進程被視為是極其重要的進程,除非為了維持所有前臺進程同時運行而必須終止,否則系統(tǒng)不會終止這些進程。
服務進程
盡管服務進程與用戶所見內容沒有直接關聯(lián),但是它們通常在執(zhí)行一些用戶關心的操作(例如,在后臺播放音樂或從網(wǎng)絡下載數(shù)據(jù))。因此,除非內存不足以維持所有前臺進程和可見進程同時運行,否則系統(tǒng)會讓服務進程保持運行狀態(tài)。
正在運行 startService() 方法啟動的服務,且不屬于上述兩個更高類別進程的進程。
后臺進程
包含目前對用戶不可見的 Activity 的進程(已調用 Activity 的 onStop() 方法)。
這些進程對用戶體驗沒有直接影響,系統(tǒng)可能隨時終止它們,以回收內存供前臺進程、可見進程或服務進程使用。
通常會有很多后臺進程在運行,因此它們會保存在 LRU (最近最少使用)列表中,以確保包含用戶最近查看的 Activity 的進程最后一個被終止。
如果某個 Activity 正確實現(xiàn)了生命周期方法,并保存了其當前狀態(tài),則終止其進程不會對用戶體驗產(chǎn)生明顯影響,因為當用戶導航回該 Activity 時,Activity 會恢復其所有可見狀態(tài)。
空進程
不含任何活動應用組件的進程。保留這種進程的的唯一目的是用作緩存,以縮短下次在其中運行組件所需的啟動時間。 為使總體系統(tǒng)資源在進程緩存和底層內核緩存之間保持平衡,系統(tǒng)往往會終止這些進程。
進程被殺死的情況
綜上,可以得出減少進程被殺死概率無非就是想辦法提高進程優(yōu)先級,減少進程在內存不足等情況下被殺死的概率。
提升進程優(yōu)先級方案
1. 利用Activity提升
方案設計思想:監(jiān)控手機鎖屏解鎖事件,在屏幕鎖屏時啟動1個像素的 Activity,在用戶解鎖時將 Activity 銷毀掉。注意該 Activity 需設計成用戶無感知。
通過該方案,可以使進程的優(yōu)先級在屏幕鎖屏時間由4提升為最高優(yōu)先級1。
方案適用范圍:
適用場景:本方案主要解決第三方應用及系統(tǒng)管理工具在檢測到鎖屏事件后一段時間(一般為5分鐘以內)內會殺死后臺進程,已達到省電的目的問題。
適用版本:適用于所有的 Android 版本。
方案具體實現(xiàn):
首先定義 Activity,并設置 Activity 的大小為1像素。
其次,從 AndroidManifest 中通過如下屬性,排除 Activity 在 RecentTask 中的顯示。
excludeFromRecents="true"
exported="false"
finishOnTaskLaunch="false"
最后,設置Activity為透明主題。
2. 利用Notification提升
方案設計思想:Android 中 Service 的優(yōu)先級為4,通過 setForeground 接口可以將后臺 Service 設置為前臺 Service,使進程的優(yōu)先級由4提升為2,從而使進程的優(yōu)先級僅僅低于用戶當前正在交互的進程,與可見進程優(yōu)先級一致,使進程被殺死的概率大大降低。
方案實現(xiàn)挑戰(zhàn):從 Android2.3 開始調用 setForeground 將后臺 Service 設置為前臺 Service 時,必須在系統(tǒng)的通知欄發(fā)送一條通知,也就是前臺 Service 與一條可見的通知時綁定在一起的。
對于不需要常駐通知欄的應用來說,該方案雖好,但卻是用戶感知的,無法直接使用。
方案挑戰(zhàn)應對措施:通過實現(xiàn)一個內部 Service,在 LiveService 和其內部 Service 中同時發(fā)送具有相同 ID 的 Notification,然后將內部 Service 結束掉。隨著內部 Service 的結束,Notification 將會消失,但系統(tǒng)優(yōu)先級依然保持為2。
方案適用范圍:適用于目前已知所有版本。
拉起進程
1. 利用系統(tǒng)廣播拉活
方案設計思想:在發(fā)生特定系統(tǒng)事件時,系統(tǒng)會發(fā)出響應的廣播,通過在 AndroidManifest 中“靜態(tài)”注冊對應的廣播監(jiān)聽器,即可在發(fā)生響應事件時拉活。
方案適用范圍:適用于全部 Android 平臺。但存在如下幾個缺點:
- 廣播接收器被管理軟件、系統(tǒng)軟件通過“自啟管理”等功能禁用的場景無法接收到廣播,從而無法自啟。
- 系統(tǒng)廣播事件不可控,只能保證發(fā)生事件時拉活進程,但無法保證進程掛掉后立即拉活。
因此,該方案主要作為備用手段。
2. 利用第三方廣播拉活
方案設計思想:該方案總的設計思想與接收系統(tǒng)廣播類似,不同的是該方案為接收第三方 Top 應用廣播。
通過反編譯第三方 Top 應用,如:手機QQ、微信、支付寶、UC瀏覽器等,以及友盟、信鴿、個推等 SDK,找出它們外發(fā)的廣播,在應用中進行監(jiān)聽,這樣當這些應用發(fā)出廣播時,就會將我們的應用拉活。
方案適用范圍:該方案的有效程度除與系統(tǒng)廣播一樣的因素外,主要受如下因素限制:
- 反編譯分析過的第三方應用的多少
- 第三方應用的廣播屬于應用私有,當前版本中有效的廣播,在后續(xù)版本隨時就可能被移除或被改為不外發(fā)。
這些因素都影響了拉活的效果。
3. 利用系統(tǒng)Service機制拉活
方案設計思想:將 Service 設置為 START_STICKY,利用系統(tǒng)機制在 Service 掛掉后自動拉活。
方案適用范圍:如下兩種情況無法拉活
Service 第一次被異常殺死后會在5秒內重啟,第二次被殺死會在10秒內重啟,第三次會在20秒內重啟,一旦在短時間內 Service 被殺死達到5次,則系統(tǒng)不再拉起。
進程被取得 Root 權限的管理工具或系統(tǒng)工具通過 forestop 停止掉,無法重啟。
4 利用Native進程拉活
方案設計思想:
主要思想:利用 Linux 中的 fork 機制創(chuàng)建 Native 進程,在 Native 進程中監(jiān)控主進程的存活,當主進程掛掉后,在 Native 進程中立即對主進程進行拉活。
主要原理:在 Android 中所有進程和系統(tǒng)組件的生命周期受 ActivityManagerService 的統(tǒng)一管理。而且,通過 Linux 的 fork 機制創(chuàng)建的進程為純 Linux 進程,其生命周期不受 Android 的管理。
感知進程死亡
要在 Native 進程中感知主進程是否存活有兩種實現(xiàn)方式:
在 Native 進程中通過死循環(huán)或定時器,輪訓判斷主進程是否存活,當主進程不存活時進行拉活。該方案的很大缺點是不停的輪詢執(zhí)行判斷邏輯,非常耗電。
在主進程中創(chuàng)建一個監(jiān)控文件,并且在主進程中持有文件鎖。在拉活進程啟動后申請文件鎖將會被堵塞,一旦可以成功獲取到鎖,說明主進程掛掉,即可進行拉活。由于 Android 中的應用都運行于虛擬機之上,Java 層的文件鎖與 Linux 層的文件鎖是不同的,要實現(xiàn)該功能需要封裝 Linux 層的文件鎖供上層調用。
拉活主進程
通過 Native 進程拉活主進程的部分代碼如下,即通過 am 命令進行拉活。通過指定“--include-stopped-packages”參數(shù)來拉活主進程處于 forestop 狀態(tài)的情況。
如何保證 Native 進程的唯一
從可擴展性和進程唯一等多方面考慮,將 Native 進程設計成 C/S 結構模式,主進程與 Native 進程通過 Localsocket 進行通信。在Native進程中利用 Localsocket 保證 Native 進程的唯一性,不至于出現(xiàn)創(chuàng)建多個 Native 進程以及 Native 進程變成僵尸進程等問題。
方案適用范圍:該方案主要適用于 Android5.0 以下版本手機。
該方案不受 forcestop 影響,被強制停止的應用依然可以被拉活,在 Android5.0 以下版本拉活效果非常好。
對于 Android5.0 以上手機,系統(tǒng)雖然會將native進程內的所有進程都殺死,這里其實就是系統(tǒng)“依次”殺死進程時間與拉活邏輯執(zhí)行時間賽跑的問題,如果可以跑的比系統(tǒng)邏輯快,依然可以有效拉起。記得網(wǎng)上有人做過實驗,該結論是成立的,在某些 Android 5.0 以上機型有效。
5. 利用 JobScheduler 機制拉活
方案設計思想:Android5.0 以后系統(tǒng)對 Native 進程等加強了管理,Native 拉活方式失效。系統(tǒng)在 Android5.0 以上版本提供了 JobScheduler 接口,系統(tǒng)會定時調用該進程以使應用進行一些邏輯操作。
在本項目中,我對 JobScheduler 進行了進一步封裝,兼容 Android5.0 以下版本。
方案適用范圍:該方案主要適用于 Android5.0 以上版本手機。
該方案在 Android5.0 以上版本中不受 forcestop 影響,被強制停止的應用依然可以被拉活,在 Android5.0 以上版本拉活效果非常好。
僅在小米手機可能會出現(xiàn)有時無法拉活的問題。
6. 利用賬號同步機制拉活
方案設計思想:Android 系統(tǒng)的賬號同步機制會定期同步賬號進行,該方案目的在于利用同步機制進行進程的拉活。
方案適用范圍:該方案適用于所有的 Android 版本,包括被 forestop 掉的進程也可以進行拉活。
最新 Android 版本(Android N)中系統(tǒng)好像對賬戶同步這里做了變動,該方法不再有效。
其他
其他還有一些技術之外的措施,比如說應用內 Push 通道的選擇:
- 國外版應用:接入 Google 的 GCM。
- 國內版應用:根據(jù)終端不同,在小米手機(包括 MIUI)接入小米推送、華為手機接入華為推送;其他手機可以考慮接入騰訊信鴿或極光推送與小米推送做 A/B Test。
4. IntentService與Service的區(qū)別?IntentService的實現(xiàn)原理。
本人已經(jīng)在另外一篇文章分析過,麻煩移步。[IntentService-你可能需要知道這些](http://www.lxweimin.com/p/6b9358cbfc26)
5. 優(yōu)雅的展示Bitmap大圖
郭霖大神的文章,移步。[優(yōu)雅的展示BitMap大圖](http://blog.csdn.net/guolin_blog/article/details/9316683)
6. Retrofit使用的注解是哪種注解?以及,注解的底層實現(xiàn)是怎樣的。
Retrofit采用的是運行時注解,下面上實錘(@GET):
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface GET {
/**
* A relative or absolute path, or full URL of the endpoint. This value is optional if the first
* parameter of the method is annotated with {@link Url @Url}.
* <p>
* See {@linkplain retrofit2.Retrofit.Builder#baseUrl(HttpUrl) base URL} for details of how
* this is resolved against a base URL to create the full endpoint URL.
*/
String value() default "";
}
我們可以看到@GET的@Retention(RUNTIME),為運行時注解無疑。
關于注解,本人寫過一篇文章分析,麻煩移步。注解-你可能需要知道這些
7. Thread和HandlerThread
簡單的總結下,兩者的區(qū)別:
- HandlerThread是Thread的子類。
- HandlerThread內部持有一個Looper,可以使用MessageQueue重復使用當前線程,節(jié)省系統(tǒng)開銷。
- HandlerThread一般結合Handler使用,按順序處理任務。
HandlerThread的源碼很簡單,我們來簡單分析下。
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason is isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
/**
* Quits the handler thread's looper.
* <p>
* Causes the handler thread's looper to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*
* @see #quitSafely
*/
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
/**
* Quits the handler thread's looper safely.
* <p>
* Causes the handler thread's looper to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* Pending delayed messages with due times in the future will not be delivered.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p>
* If the thread has not been started or has finished (that is if
* {@link #getLooper} returns null), then false is returned.
* Otherwise the looper is asked to quit and true is returned.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
/**
* Returns the identifier of this thread. See Process.myTid().
*/
public int getThreadId() {
return mTid;
}
}
從源碼中,我們可以看到,HandlerThread繼承于Thread。
重點來看下run方法:
@Override
public void run() {
mTid = Process.myTid();
// 為當前線程設置Looper
Looper.prepare();
synchronized (this) {
// getLooper()返回的就是mLooper
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
// 開始處理消息隊列
Looper.loop();
mTid = -1;
}
Handler+Thread的方式,在開發(fā)中我們經(jīng)常使用到,我們來對比下HandlerThread+Handler和Handler+Thread的實現(xiàn)方式。
- Handler+Thread
private MyHandler mHandler;
public void buildHandler() {
new Thread(new Runnable() {
@Override
public void run() {
// 為當前線程設置Looper
Looper.prepare();
// 使用當前線程的Looper構造Handler
mHandler = new MyHandler(Looper.myLooper());
// 開始處理MessageQueue
Looper.loop();
}
}).start();
}
class MyHandler extends Handler {
MyHandler(Looper looper){
super(looper);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
- HandlerThread+Handler
private MyHandler mHandler;
public void buildHandler() {
// 構造HandlerThread
HandlerThread handlerThread = new HandlerThread("WorkThread");
handlerThread.start();
// 直接使用HandlerThread的looper創(chuàng)建Handler
mHandler = new MyHandler(handlerThread.getLooper());
}
class MyHandler extends Handler {
MyHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
結合HandlerThread的源碼和兩種方式的對比,驗證了我們在開頭的總結。
- HandlerThread是Thread的子類。
2.HandlerThread內部持有一個Looper,可以使用MessageQueue重復使用當前線程,節(jié)省系統(tǒng)開銷。 - HandlerThread一般結合Handler使用,按順序處理任務。
8. Java是值傳遞還是引用傳遞
Java中方法參數(shù)傳遞方式是按值傳遞。
如果參數(shù)是基本類型,傳遞的是基本類型的字面量值的拷貝。
如果參數(shù)是引用類型,傳遞的是該參量所引用的對象在堆中地址值的拷貝。
9. final和static的區(qū)別
static
static變量
按照是否靜態(tài)的對類成員變量進行分類可分兩種:
- 一種是被static修飾的變量,叫靜態(tài)變量或類變量。
- 另一種是沒有被static修飾的變量,叫實例變量。
兩者的區(qū)別是:
靜態(tài)變量在內存中只有一個拷貝(節(jié)省內存),JVM只為靜態(tài)變量分配一次內存,在加載類的過程中完成靜態(tài)變量的內存分配,可用類名直接訪問(方便),當然也可以通過對象來訪問(但是這是不推薦的)。
實例變量屬于某個確定的實例,每當創(chuàng)建一個實例,就會為實例變量分配一次內存,實例變量可以在內存中有多個拷貝,互不影響(靈活)。
static代碼塊
static代碼塊是類加載時,初始化自動執(zhí)行的。如果static代碼塊有多個,JVM將按照它們在類中出現(xiàn)的先后順序依次執(zhí)行它們,每個代碼塊只會被執(zhí)行一次。
static方法
static方法是被static修飾的方法,可以通過類名直接調用,因此static方法不能使用this和super關鍵字(實例),不能訪問類實例變量和方法,只能訪問靜態(tài)變量和靜態(tài)方法;因為其屬于類而獨立于任何實例,所以必須為非抽象。
因為方法屬于類,所以子類和父類可以存在同名的方法,但是不涉及重載(不能被繼承)。
final
final變量
聲明 final 字段有助于編譯器作出更好的優(yōu)化決定,因為如果編譯器知道字段的值不會更改,那么它能安全地在寄存器中高速緩存該值。
final 字段還通過讓編譯器強制該字段為只讀來提供額外的安全級別。
其初始化可以在兩個地方:
- 一是其定義處,也就是說在final變量定義時直接給其賦值
- 二是在構造函數(shù)中。
這兩個地方只能選其一,要么在定義時給值,要么在構造函數(shù)中給值,不能同時既在定義時給了值,又在構造函數(shù)中給另外的值。不能通過調用方法來賦值。
一旦被初始化便不可改變,這里不可改變的意思對基本類型來說是其值不可變,而對于對象變量來說其引用不可再變。
final方法
- 把方法鎖定,防止任何繼承類修改它的意義和實現(xiàn)。
- 高效。編譯器在遇到調用final方法時候會轉入內嵌inline機制,大大提高執(zhí)行效率。
final類
final類不能被繼承,因此final類的成員方法沒有機會被覆蓋,默認都是final的。
在設計類時候,如果這個類不需要有子類,類的實現(xiàn)細節(jié)不允許改變,并且確信這個類不會載被擴展,那么就設計為final類
Note:同時被static和final修飾的變量,必須在聲明時立即賦值。
可以同時使用static和final來修飾方法,但是意義不大。因為被static修飾的方法本身就是無
法被繼承的。
10. HashMap的實現(xiàn)原理
本人已經(jīng)分析了HashMap的實現(xiàn)原理,請移步。HashMap-你可能需要知道這些
11. HashMap和HashSet的區(qū)別
HashMap | HashSet |
---|---|
HashMap實現(xiàn)了Map接口 | HashSet實現(xiàn)了Set接口 |
HashMap存儲鍵值對 | HashSet存儲對象 |
使用put方法添加值 | 使用add方法添加值 |
HashMap中使用鍵對象來計算hashcode值 | HashSet使用成員對象來計算hashcode值,對于兩個對象來說hashcode可能相同,所以equals()方法用來判斷對象的相等性,如果兩個對象不同的話,那么返回false |
HashMap比較快,因為是使用唯一的鍵來獲取對象 | HashSet較HashMap來說比較慢 |
HashSet取值的時候部分情況會比HashMap快,因為HashSet不允許重復值,hash到那個地方直接取值了,而HashMap有可能有下拉鏈表,這樣就要再遍歷鏈表了;算法兩者用的都是hash算法,而hashMap可能多一步鏈表遍歷,所以肯定是HashSet部分情況比hashMap快。
HashSet不允許存在重復的值。(hashCode和equals來判斷)
12. 淺拷貝&深拷貝區(qū)別
淺拷貝:使用一個已知實例對新創(chuàng)建實例的成員變量逐個賦值,這個方式被稱為淺拷貝。
深拷貝:當一個類的拷貝構造方法,不僅要復制對象的所有非引用成員變量值,還要為引用類型的成員變量創(chuàng)建新的實例,并且初始化為形式參數(shù)實例值,這個方式稱為深拷貝。
也就是說淺拷貝只復制一個對象,傳遞引用,不能復制實例。而深拷貝對對象內部的引用均復制,它是創(chuàng)建一個新的實例,并且復制實例。
深拷貝實現(xiàn)方式:
- 對其引用變量逐個深拷貝并復制給新對象。
- 實現(xiàn)序列化接口,并結合流進行寫入和讀出。(ByteArrayOutputStream&ObjectInputStream)。
public Object deepClone() throws Exception{
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
13. 靜態(tài)代理&動態(tài)代理
移步本人的文章,注解-你可能需要知道這些.
14. LayoutInflater.inflate有幾種使用方式
LayoutInflater的獲取:
- LayoutInflater.from(context)
- LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
第一種方式是封裝了第二種方式。
inflate方法:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
總結的話有三種:
- resource!=null,root==null,attachToRott=false,返回解析到的resource的布局,布局參數(shù)無效。
- resource!=null,root!=null,attachToRott=false,返回解析到的resource的布局,布局參數(shù)有效。
- resource!=null,root!=null,attachToRott=true,返回root,resource被添加到root中,布局參數(shù)有效。
15.MVC與MVP的區(qū)別
MVC:
- Activity不僅要顯示UI,還擔任了一部分Controller的職責
- 請求的業(yè)務代碼往往被丟到了Activity里面,布局文件只能提供默認的UI設置,所以開發(fā)中視圖層的變化也被丟到了Activity里面。
- 再加上Activity本身承擔著控制層的責任。所以Activity達成了MVC集合的成就,實現(xiàn)了代碼的耦合,最終我們的Activity就變得越來越難看,從幾百行變成了幾千行。維護的成本也越來越高。
MVP:
MVP與MVC最大的不同,其實是Activity職責的變化,由原來的C (控制層) 變成了 V(視圖層),不再管控制層的問題,只管如何去顯示。
控制層的角色就由我們的新人 Presenter來擔當,這種架構就解決了Activity過度耦合控制層和視圖層的問題。
16. Activity有沒有事件分發(fā)機制?
有,必須有。
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
Activity是事件分發(fā)的起點,只有Activity不攔截處理事件,事件才會分發(fā)至Window--DecorView--View。
17. 引用的分類和區(qū)別
引用主要分為四種類型:
強引用
Object object = new Object(),object就是一個強引用了。
當內存空間不足,Java虛擬機寧愿拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象來解決內存不足問題。軟引用
只有內存不夠時才回收,常用于緩存;當內存達到一個閥值,GC就會去回收它;弱引用
弱引用的對象擁有更短暫的生命周期。
在垃圾回收器線程掃描它所管轄的內存區(qū)域的過程中,一旦發(fā)現(xiàn)了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。虛引用
"虛引用"顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用并不會決定對象的生命周期。如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時候都可能被垃圾回收。
18. Synchronized
作用位置:
- synchronized 方法
- synchronized static 方法
- 代碼塊
synchronized方法:
- synchronized方法控制對類成員變量的訪問.
- 每個類實例對應一把鎖,每個synchronized方法都必須獲得調用該方法的類實例的鎖方能執(zhí)行,否則所屬線程阻塞,方法一旦執(zhí)行,就獨占該鎖,直到從該方法返回時才將鎖釋放,此后被阻塞的線程方能獲得該鎖,重新進入可執(zhí)行狀態(tài).
- 同一時刻對于每一個類實例,其所有聲明為 synchronized 的成員方法中至多只有一個處于可執(zhí)行狀態(tài)(因為至多只有一個能夠獲得該類實例對應的鎖),從而有效避免了類成員變量的訪問沖突(只要所有可能訪問類成員變量的方法均被聲明為 synchronized.
synchronized static 方法:
某個類的范圍,synchronized static aStaticMethod{}防止多個線程同時訪問這個類中的synchronized static 方法。它可以對類的所有對象實例起作用。
Class Foo {
// 同步的static 函數(shù)
public synchronized static void methodAAA() {
//….
}
public void methodBBB() {
synchronized(Foo.class) // class literal(類名稱字面常量)
}
}
代碼中的methodBBB()方法是把class literal作為鎖的情況,它和同步的static函數(shù)產(chǎn)生的效果是一樣的,取得的鎖很特別,是當前調用這個方法的對象所屬的類(Class,而不再是由這個Class產(chǎn)生的某個具體對象了)。
synchronized代碼塊:
- 一個時間內只能有一個線程得到執(zhí)行。另一個線程必須等待當前線程執(zhí)行完這個代碼塊以后才能執(zhí)行該代碼塊。
- 當一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊。
- 一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞。
- 也就是說,當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就獲得了這個object的對象鎖。結果,其它線程對該object對象所有同步代碼部分的訪問都被暫時阻塞。
19. 解耦的本質
設計模式中的幾大原則:
- SRP:單一職責原則,一個類應該僅有一個引起它變化的原因。
- OCP:開閉原則,對于擴展開發(fā),對于修改封閉。
- 里氏替換原則,所有使用基類的地方必須能透明的使用其子類對象。
- DIP:依賴倒置原則。指代了一種特定的解耦形式,使得高層次的模塊不依賴于低層次模塊的實現(xiàn)細節(jié),依賴模塊被顛倒了。
a. 高層模塊不應該依賴于低層次模塊,兩者都應該依賴于抽象。
b. 抽象不應該依賴于細節(jié),細節(jié)應該依賴于抽象。
總結:模塊間的依賴通過抽象發(fā)生,實現(xiàn)類不應該發(fā)生直接的依賴關系,依賴關系是通過接口或者抽象實現(xiàn)。 - ISP:接口隔離原則,類之間的依賴關系應該建立在最小的接口之上。
- LOD:迪米特原則,一個對象應該對其他對象有最少的了解。
那么很明確了,解耦的本質就是DIP原則。
20. Android運行時權限
權限分類
危險權限
phone
讀寫外部存儲
camera
如果你申請某個危險的權限,假設你的app早已被用戶授權了同一組的某個危險權限,那么系統(tǒng)會立即授權,而不需要用戶去點擊授權。
彈出的權限請求dialog描述的是一組權限,無法定制
正常權限
獲取網(wǎng)絡狀態(tài)
如何檢查和申請
檢查
checkSlefPermission
deny
grant
請求
requestPermissions
異步方法
處理的回調方法:onRequestPermissionsResult
解釋
shouldShowRequestPermissionRationale
只有在第一次用戶已經(jīng)拒絕情況下,該方法才會返回true
開源框架
MPermissions
21. Activity-Window-View
- 每個Activity在創(chuàng)建的時候,都會產(chǎn)生一個Window對象,這個Window對象實際為PhoneWindow。
- PhoneWindow對象包含一個根View:DecorView,Activity要顯示的內容就包含在DecorView中。
setContentView的顯示流程
Activity setContentView->PhoneWindow setContentView ->mLayoutInflater.inflate(layoutResID, mContentParent) -> onResume時將DecorView添加至WindowManager ->WindowManagerImpl.addView--ViewRootImpl.setView ->1. ViewRootImpl.requestLayout -> 2. 向WMS發(fā)起顯示W(wǎng)indow的請求
22. Activity與Fragment通信方式
- Handler
- 接口
- EventBus
- 廣播
23. Fragment的坑
getActivity為空
在Fragment基類里設置一個Activity mActivity的全局變量,在onAttach(Activity activity)里賦值,使用mActivity代替getActivity(),保證Fragment即使在onDetach后,仍持有Activity的引用(有引起內存泄露的風險,但是異步任務沒停止的情況下,本身就可能已內存泄漏,相比Crash,這種做法“安全”些)
Can not perform this action after onSaveInstanceState
在重新回到該Activity的時候(onResumeFragments()或onPostResume()),再執(zhí)行該事務!
Fragment重疊異常
在類onCreate()的方法加載Fragment,并且沒有判斷saveInstanceState==null或if(findFragmentByTag(mFragmentTag) == null),導致重復加載了同一個Fragment導致重疊。
24. 在向Fragment傳遞參數(shù)時,為什么不采用構造方法中傳遞?
- 如果通過構造參數(shù)進行傳遞,那么在Fragment銷毀重建時參數(shù)就無法保持了。
- 一般通過setArguments傳遞參數(shù),從而在Fragment銷毀重建時保持數(shù)據(jù)。
那么參數(shù)是如何保持的呢?
Activity--onSaveInstanceState
protected void onSaveInstanceState(Bundle outState) {
// 保存Activity的視圖狀態(tài)
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
// 獲取了所有Fragment的狀態(tài)并保存到了outState中
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
getApplication().dispatchActivitySaveInstanceState(this, outState);
}
Activity--onCreate
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);
if (mLastNonConfigurationInstances != null) {
mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders);
}
if (mActivityInfo.parentActivityName != null) {
if (mActionBar == null) {
mEnableDefaultActionBarUp = true;
} else {
mActionBar.setDefaultDisplayHomeAsUpEnabled(true);
}
}
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
// 恢復保存的Fragment狀態(tài)
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);
}
mFragments.dispatchCreate();
getApplication().dispatchActivityCreated(this, savedInstanceState);
if (mVoiceInteractor != null) {
mVoiceInteractor.attachActivity(this);
}
mCalled = true;
}
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mWindow != null) {
// 恢復視圖狀態(tài)
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
mWindow.restoreHierarchyState(windowState);
}
}
}
從上面兩個接口我們可以看到,在Activity銷毀重建時會保存恢復Fragment的狀態(tài),但是我們還是沒有看到setArguments的參數(shù)保持啊,接著往下看。
public FragmentState(Fragment frag) {
mClassName = frag.getClass().getName();
mIndex = frag.mIndex;
mFromLayout = frag.mFromLayout;
mFragmentId = frag.mFragmentId;
mContainerId = frag.mContainerId;
mTag = frag.mTag;
mRetainInstance = frag.mRetainInstance;
mDetached = frag.mDetached;
mArguments = frag.mArguments;
mHidden = frag.mHidden;
}
Parcelable p = mFragments.saveAllState();
保存Fragment信息:
public Parcelable saveAllState() {
return mHost.mFragmentManager.saveAllState();
}
// FragmentManager->saveAllState
Parcelable saveAllState() {
// Make sure all pending operations have now been executed to get
// our state update-to-date.
execPendingActions();
mStateSaved = true;
if (mActive == null || mActive.size() <= 0) {
return null;
}
// First collect all active fragments.
int N = mActive.size();
FragmentState[] active = new FragmentState[N];
boolean haveFragments = false;
for (int i=0; i<N; i++) {
Fragment f = mActive.get(i);
if (f != null) {
if (f.mIndex < 0) {
throwException(new IllegalStateException(
"Failure saving state: active " + f
+ " has cleared index: " + f.mIndex));
}
haveFragments = true;
// 構造待保存Fragment的狀態(tài)信息(變量)
// FragmentState會記錄Fragment的mArguments
FragmentState fs = new FragmentState(f);
active[i] = fs;
if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
// 保存fragment的一些視圖狀態(tài)信息
fs.mSavedFragmentState = saveFragmentBasicState(f);
if (f.mTarget != null) {
if (f.mTarget.mIndex < 0) {
throwException(new IllegalStateException(
"Failure saving state: " + f
+ " has target not in fragment manager: " + f.mTarget));
}
if (fs.mSavedFragmentState == null) {
fs.mSavedFragmentState = new Bundle();
}
putFragment(fs.mSavedFragmentState,
FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
if (f.mTargetRequestCode != 0) {
fs.mSavedFragmentState.putInt(
FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
f.mTargetRequestCode);
}
}
} else {
fs.mSavedFragmentState = f.mSavedFragmentState;
}
if (DEBUG) Log.v(TAG, "Saved state of " + f + ": "
+ fs.mSavedFragmentState);
}
}
if (!haveFragments) {
if (DEBUG) Log.v(TAG, "saveAllState: no fragments!");
return null;
}
int[] added = null;
BackStackState[] backStack = null;
// Build list of currently added fragments.
if (mAdded != null) {
N = mAdded.size();
if (N > 0) {
added = new int[N];
for (int i=0; i<N; i++) {
added[i] = mAdded.get(i).mIndex;
if (added[i] < 0) {
throwException(new IllegalStateException(
"Failure saving state: active " + mAdded.get(i)
+ " has cleared index: " + added[i]));
}
if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i
+ ": " + mAdded.get(i));
}
}
}
// Now save back stack.
if (mBackStack != null) {
N = mBackStack.size();
if (N > 0) {
backStack = new BackStackState[N];
for (int i=0; i<N; i++) {
backStack[i] = new BackStackState(this, mBackStack.get(i));
if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
+ ": " + mBackStack.get(i));
}
}
}
FragmentManagerState fms = new FragmentManagerState();
fms.mActive = active;
fms.mAdded = added;
fms.mBackStack = backStack;
return fms;
}
恢復信息:
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);
void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
// 檢查保存的狀態(tài)信息
if (state == null) return;
FragmentManagerState fms = (FragmentManagerState)state;
if (fms.mActive == null) return;
List<FragmentManagerNonConfig> childNonConfigs = null;
// First re-attach any non-config instances we are retaining back
// to their saved state, so we don't try to instantiate them again.
if (nonConfig != null) {
List<Fragment> nonConfigFragments = nonConfig.getFragments();
childNonConfigs = nonConfig.getChildNonConfigs();
final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0;
for (int i = 0; i < count; i++) {
Fragment f = nonConfigFragments.get(i);
if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f);
FragmentState fs = fms.mActive[f.mIndex];
fs.mInstance = f;
f.mSavedViewState = null;
f.mBackStackNesting = 0;
f.mInLayout = false;
f.mAdded = false;
f.mTarget = null;
if (fs.mSavedFragmentState != null) {
fs.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
FragmentManagerImpl.VIEW_STATE_TAG);
f.mSavedFragmentState = fs.mSavedFragmentState;
}
}
}
// Build the full list of active fragments, instantiating them from
// their saved state.
mActive = new ArrayList<>(fms.mActive.length);
if (mAvailIndices != null) {
mAvailIndices.clear();
}
for (int i=0; i<fms.mActive.length; i++) {
FragmentState fs = fms.mActive[i];
if (fs != null) {
FragmentManagerNonConfig childNonConfig = null;
if (childNonConfigs != null && i < childNonConfigs.size()) {
childNonConfig = childNonConfigs.get(i);
}
// 恢復Fragment
// 通過FragmentState來生成新的Fragment,之前保存到惡狀態(tài)信息得以恢復
Fragment f = fs.instantiate(mHost, mParent, childNonConfig);
if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
mActive.add(f);
fs.mInstance = null;
} else {
mActive.add(null);
if (mAvailIndices == null) {
mAvailIndices = new ArrayList<>();
}
if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i);
mAvailIndices.add(i);
}
}
// Update the target of all retained fragments.
if (nonConfig != null) {
List<Fragment> nonConfigFragments = nonConfig.getFragments();
final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0;
for (int i = 0; i < count; i++) {
Fragment f = nonConfigFragments.get(i);
if (f.mTargetIndex >= 0) {
if (f.mTargetIndex < mActive.size()) {
f.mTarget = mActive.get(f.mTargetIndex);
} else {
Log.w(TAG, "Re-attaching retained fragment " + f
+ " target no longer exists: " + f.mTargetIndex);
f.mTarget = null;
}
}
}
}
// Build the list of currently added fragments.
if (fms.mAdded != null) {
mAdded = new ArrayList<Fragment>(fms.mAdded.length);
for (int i=0; i<fms.mAdded.length; i++) {
Fragment f = mActive.get(fms.mAdded[i]);
if (f == null) {
throwException(new IllegalStateException(
"No instantiated fragment for index #" + fms.mAdded[i]));
}
f.mAdded = true;
if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ": " + f);
if (mAdded.contains(f)) {
throw new IllegalStateException("Already added!");
}
mAdded.add(f);
}
} else {
mAdded = null;
}
// Build the back stack.
if (fms.mBackStack != null) {
mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length);
for (int i=0; i<fms.mBackStack.length; i++) {
BackStackRecord bse = fms.mBackStack[i].instantiate(this);
if (DEBUG) {
Log.v(TAG, "restoreAllState: back stack #" + i
+ " (index " + bse.mIndex + "): " + bse);
LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
PrintWriter pw = new FastPrintWriter(logw, false, 1024);
bse.dump(" ", pw, false);
pw.flush();
}
mBackStack.add(bse);
if (bse.mIndex >= 0) {
setBackStackIndex(bse.mIndex, bse);
}
}
} else {
mBackStack = null;
}
}
FragmentState.instantiate
public Fragment instantiate(FragmentHostCallback host, Fragment parent,
FragmentManagerNonConfig childNonConfig) {
if (mInstance == null) {
final Context context = host.getContext();
if (mArguments != null) {
mArguments.setClassLoader(context.getClassLoader());
}
mInstance = Fragment.instantiate(context, mClassName, mArguments);
if (mSavedFragmentState != null) {
mSavedFragmentState.setClassLoader(context.getClassLoader());
mInstance.mSavedFragmentState = mSavedFragmentState;
}
mInstance.setIndex(mIndex, parent);
mInstance.mFromLayout = mFromLayout;
mInstance.mRestored = true;
mInstance.mFragmentId = mFragmentId;
mInstance.mContainerId = mContainerId;
mInstance.mTag = mTag;
mInstance.mRetainInstance = mRetainInstance;
mInstance.mDetached = mDetached;
mInstance.mHidden = mHidden;
mInstance.mFragmentManager = host.mFragmentManager;
if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
"Instantiated fragment " + mInstance);
}
mInstance.mChildNonConfig = childNonConfig;
return mInstance;
}
25. RecyclerView
- 為什么要使用RecyclerView?
===提供了插拔式體驗,高度解耦
===內部實現(xiàn)了ViewHolder機制,因此使用上更方便
- 你想要控制其顯示的方式,請通過布局管理器LayoutManager
- 你想要控制Item間的間隔(可繪制),請通過ItemDecoration
- 你想要控制Item增刪的動畫,請通過ItemAnimator
- 你想要控制點擊、長按事件,請自己寫(擦,這點尼瑪。)
Adapter的幾個主要方法:
- public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
- public abstract void onBindViewHolder(VH holder, int position);