1. 程式人生 > >【朝花夕拾】Android效能篇之(六)Android程序管理機制

【朝花夕拾】Android效能篇之(六)Android程序管理機制

一、Android程序管理的特殊設計

       Linux系統對程序的管理方式是一旦程序活動停止,系統就會結束該程序。儘管Android基於Linux Kernel,但在程序管理上,卻採取了另外一種獨特的設計:當程序活動停止時,系統並不會立刻結束它,而是會盡可能地將該程序儲存在記憶體中,在以後的某個時間,一旦需要該程序,系統就會立即啟動該程序。只有當剩餘記憶體不夠用了,為了維持新開啟的程序或者比較重要的程序的正常執行,系統才會選擇性地殺掉一些不重要的記憶體,騰出記憶體空間來,所以Android系統永遠不會有記憶體不足的提示。

二、Android獨特記憶體管理設計的好處

      Android這種獨特的設計,也正是Android標榜的優勢之一,這有兩個好處:

  1、最大限度地提高記憶體的使用率。

       比如,你的記憶體是8G,如果每次使用完某個程序就殺掉,那麼被使用的記憶體基本上會始終保持在某個值比如4G以內,那麼記憶體的使用率就總是儲存在50%以內,剩餘的4G記憶體形同虛設,發揮用處的機會非常少。而Android的這種設計,就可以做到有多少記憶體就用多少記憶體,儘可能大地提高記憶體使用率。同樣比如有8G記憶體,使用完的程序仍保留在記憶體中,累積下來,被使用的記憶體就儘可能地會接近8G。

  2、提高再次啟動時的啟動速度

       被駐留在記憶體中不再活動的程序(後臺程序或空程序,後面會再講到),很多是經常需要使用的,當再次使用該程序的時候,系統立即啟動這個程序,而不需要再重新初始化。例如,我們常用的瀏覽器,當暫時不再使用時,按下back鍵,瀏覽器程序就變成了不再活動的程序。如果下次又要使用了,在最近使用應用列表中點選瀏覽器即可,瀏覽器介面仍然保持著退出前的介面。但如果退出時把該程序移除了,那麼再次使用時,就需要重新初始化,然後進入該應用,這往往會花費數秒的時間。

三、Android程序的五個等級

        Android系統將盡量長時間地保持應用程序,但為了新建程序或執行更重要的程序,最終需要移除舊程序來回收記憶體。為了確定保留或終止哪些程序,系統會根據程序中正在執行的元件以及這些元件的狀態,將每個程序放入“重要性層次結構”中。必要時,系統會首先消除重要性最低的程序,然後是重要性略遜的程序,以此類推,以回收系統資源。該“重要性層級結構”將程序分為了五個等級:

  1、前臺程序(foreground)

       前臺程序是指那些有元件正和使用者進行互動的應用程式的程序,也稱為Active程序。這些都是Android嘗試通過回收其他應用程式來使其保持相應的程序。這些程序的數量非常少,只有等到最後關頭才會終止這些程序,是使用者最不希望終止的程序。例如:而當你執行瀏覽器這類應用時,它們的介面就會顯示在前臺,它們就屬於前臺程序,當你按home鍵回到主介面,他們就變成了後臺程式。

       如果一個程序滿足以下任一條件,即視為前臺程序:

     (1)託管處於活動狀態的Activity,也就是說,它們位於前臺並對使用者事件進行響應,此時的情形為響應了Activity中的onResume()生命週期方法,但沒有響應onPause()。

     (2)託管正在執行onReceive()方法處理事件程式的BroadcastReceiver。

     (3)託管正在執行onStart()、onCreate()或onDestroy()事件處理程式的Service。

     (4)託管正在執行且被標記為在前臺執行的Service,即呼叫了該Service的startForeground()方法。

     (5)託管某個Service,且該Service正繫結在使用者正在互動的Activity的Service,即該Activity正處於活動狀態。

  2、可見程序(visible)

        沒有任何前臺元件、但仍然會影響使用者在螢幕上所見內容的程序。如果一個程序滿足以下任一條件,即視為可見程序:

    (1)託管不在前臺、但仍對使用者可見的Activity(已呼叫其onPause()方法)。例如:如果前臺Acitivty啟動了一個對話方塊,或者啟動了一個非全屏,亦或是一個透明的Activity,允許在其後顯示上一個Activity,則可能會發生這種情況,這類Activity不在前臺執行,也不能對使用者事件作出反應。

    (2)託管繫結到可見Activity的Service。(官網上說是繫結到可見或前臺Activity,但筆者有一點疑問,這個和“前臺程序”中第(5)點相矛盾嗎,繫結到前臺Activity,那就是前臺程序了)

        可見程序被視為是極其重要的程序,這類程序的數量也很少,只有在資源極度匱乏的環境下,為保證前臺程序繼續執行時才會終止。

  3、服務程序(Service)

        正在執行已使用startService()方法啟動的Serice且不屬於上述兩個更高類別程序的程序。儘管服務程序與使用者所見內容沒有直接關聯,但是它們通常在執行一些使用者關心的操作。因此,除非記憶體不足以維持所有前臺程序和可見程序同時執行,否則系統會讓服務程序保持執行狀態。

       有些資料上面也稱這種程序為次要服務(Secondary Service),而屬於上述兩個更高類別的程序則被稱為主要服務,主要服務往往屬於系統程序,如撥號程序等,不可能被程序管理輕易終止。這裡我們以Android開發者官網的稱呼為標準,稱為服務程序。

  4、後臺程序(hidden)

       包含目前對使用者不可見的Activity,即該Activity呼叫了onStop()方法。這些程序對使用者體驗沒有直接影響,系統可能隨時終止它們,以回收記憶體供上述三個更高級別的程序使用。通常會有很多後臺程序在執行,它們會儲存在LRU(Least Recently Used,最近最少使用)列表中,以確保包含使用者最近檢視的Activity的程序最後一個被終止。如果某個Activity正確實現了生命週期方法,並儲存了其當前狀態,則終止其程序不會對使用者體驗產生明顯影響,因為當用戶導航回該Activity時,Activity會恢復其所有可見狀態。

       這裡讀者可以做個試驗,先開啟微信,進入到朋友圈介面, 然後點選手機螢幕下方的導航欄中的Home按鍵進入到後臺,再點選最近使用應用列表顯示按鈕(不同的手機位置不一樣,有的在Home鍵左邊,有的則在Home鍵右邊),在顯示的最近使用應用的列表中清理掉微信應用,最後再點選桌面的微信圖示啟動微信,會發現顯示的介面仍然是朋友圈介面。

       後臺程序,我們可以簡單理解為,應用(只考慮只有Activity元件的情況)啟動後按Home鍵後被切換到後臺的程序。如瀏覽器、閱讀器等,當程式顯示在螢幕上時,它們所執行的程序即為前臺程序(foreground),一旦按home鍵(注意不是back鍵)返回到桌面,程式就停留在後臺,成為後臺程序。

  5、空程序(empty)

       不含任何活動應用元件的程序。保留這種程序的唯一目的是用作快取,以縮短下次再其中執行元件所需要的啟動時間。一般來說,當應用按back按鍵退出後應用後,就變成了一個空程序。比如BTE,在程式退出後,依然會在程序中駐留一個空程序,這個程序裡沒有任何資料在執行,作用往往是提高該程式下次的啟動速度或者記錄程式的一些歷史資訊。當系統記憶體不夠用時,無疑,該程序是應該最先終止的。在最近使用應用列表中,可以看到按back鍵退出的應用。

       根據程序中當前活動元件的重要程度,Android會將程序評定為它可能達到的最高級別。通俗地說,就是如果一個程序同時擁有多個對應上述不同等級程序的元件時,會以最高的那個等級作為該程序的等級。例如,如果某程序託管著服務和可見Activity,則會將此程序評定為可見程序,而不是服務程序。

       此外,一個程序的級別可能會因為其他程序對它的依賴而有所提高,即服務於另一程序的程序其級別永遠不會低於其所服務的程序。例如,如果程序A中的內容提供程式為程序B中的客戶端提供服務,或者如果程序A中的服務繫結到程序B中的元件,則程序A始終被視為至少與程序B同樣重要。

       由於執行服務的程序其級別高於託管後臺Activity的程序,因此啟動長時間執行操作的Activity最好為該操作啟動Service,而不是簡單地建立工作執行緒,當操作有可能比Activity更加持久時更應該如此。例如,正在將圖片上傳到網站的Activity應該啟動服務來執行上傳,這樣一來,即使使用者退出Activity,仍可在後臺繼續執行上傳操作。使用服務可以保證,無論Activity發生什麼情況,該操作至少具備“服務程序”優先順序。如果某個Activity開啟了執行緒執行耗時操作,當Activity退出時,該Activity的例項將不會釋放記憶體資源,直到執行緒執行完,這樣容易導致記憶體洩漏。同理,廣播接收器也應該使用服務,而不是簡單地將耗時冗長的操作放入執行緒中。

四、對一些疑惑的解釋

  1、這麼多駐留在記憶體的程序,不會耗電嗎?

       大多數用慣了Windows作業系統的童鞋,看到Android系統儘可能保留不在活動的程序的設計,可能第一反應就是質疑,難道這樣不會增加耗電量嗎?其實,但一個程式按home鍵變成後臺程序或者按back鍵退出變成空程序後,其實已經被暫停了,只保留了執行狀態,不會消耗cpu,一個程式會耗電,是因為它需要呼叫cpu來運算,現在不消耗cpu了,當然就不會耗電了。當然,開了service的應用就另當別論了,比如QQ音樂播放器,當按home鍵或back鍵後,音樂仍然播放,是因為它開啟了服務,而且是一個前臺服務,在後面我們會繼續講到,此時它是一個前臺程序,而不是後臺程序或空程序。

  2、為什麼一個不太app,執行時會佔用很大的記憶體呢?

        我們經常會碰到這樣一種現象,一個只有20M的App,執行起來的時候,卻會耗掉100M以上的記憶體。一方面是,程式執行時為物件分配記憶體,另一方面,是Android虛擬機器的原因。Android中的應用啟動的時候,系統都會給它開啟一個獨立的虛擬機器,這樣做的好處是可以避免虛擬機器崩潰導致整個系統崩潰(此處可以參考筆者以前的文章:【朝花夕拾】Android效能優化篇之(五)Android虛擬機器),代價就是耗用更多的記憶體。

  3、為什麼記憶體少的時候,執行大型程式會卡頓呢?

        當剩餘記憶體不多時,開啟大型程式,系統會觸發自身的程序排程策略,去移除一些等級比較低的程序來回收記憶體,以供大型程式執行。而這個程序排程策略在決定哪些程序需要被移除的過程,是一個十分消耗資源的操作,特別是一個程式頻繁像系統申記憶體的時候,這樣就導致了系統的卡頓。

 4、應用開得太多了,手機變慢,是因為記憶體被佔用太多嗎?

        其實手機變慢的根本原因是cpu被耗用太多,而不是記憶體佔用太多,因為真正執行程式所要完成的任務的最終執行者是CPU,而不是記憶體(RAM)。在記憶體足夠的情況下,如果系統中佔用cpu的程序太多,那無疑cpu總有忙不過來的時候,那肯定就會變慢了。這就好比,在一條道路上駕車,道路就像記憶體,車的引擎就像cpu,如果車的引擎的動力不夠,或者承載的貨物太多,車都跑不快,即便是道路上一路暢通無阻,也無濟於事。所以,記憶體佔用多少並不重要,只要道路提供給車輛前行的空間是足夠的,手機變慢的責任,就和記憶體無關了。這個比喻用來解釋第三點也很恰當,道路提供的車輛前進的空間無法滿足車輛所必需的空間時,就需要交通機制花時間來調節交通,給這輛車提供足夠的空間,而在此期間,這輛車只能乖乖候著。

  5、Android手機越用越慢,是什麼原因呢?

       Android手機常常是越用越慢,即使是恢復出廠設定,也無法改變這個現象。手機越用越慢,主要由如下幾個原因:(1)虛擬機器機制問題。這一點在上一個問題中也提到了,在Android4.4以前的系統,使用的是Dalvik虛擬機器,它的設計機制有缺陷,就是越用越慢;在Android4.4系統中有切換按鈕,可以在Art虛擬機器和Dalvik虛擬機器之間切換;在Android4.4以後的系統就徹底拋棄了Dalvik而全面使用Art(讀者可以參考:【朝花夕拾】Android效能優化篇之(五)Android虛擬機器)。(2)開啟了太多的服務,導致耗用太多的CPU。隨著手機開機使用時間的增長,應用使用越來越多,很多應用看似退出了,而其實後臺可能開了不少的服務,而他們可能還沒有關閉。這些服務正在執行一些操作,會消耗CPU,而CPU才是手機變慢的根本原因。 而且Android app比較開放的,有很多不良應用充斥其中,可能對服務處理不當,濫用服務等,增加系統中的服務。(3)系統頻繁呼叫自身的程序排程演算法。這一點在前面已經說明了,這裡不再贅述。(4)手機硬體的自然老化。這一點請閱讀:手機越用越慢?魯大師老化實驗室告訴你真相 。

五、程序管理軟體的使用

       Windows作業系統使用者往往總想著保留更多的記憶體,在使用Android手機的時候,喜歡經常清理後臺程序或空程序,而且清理完後,心裡有一種特別爽的感覺,就像給家裡做了一次大掃除一樣,筆者最初使用Android手機的時候也是這樣的心態-_-!基於這樣的心態,一些程序清理軟體,很受普通使用者的青睞。其實這樣做卻正好抹殺了Android系統所標榜的優勢,如前文所講到的。

       那麼程序管理軟體有無必要呢?當然有的,只是需要注意使用場合。當需要執行大型程式的時候,可以手動關閉掉一些程序,騰出足夠的空間供大型程式使用,這樣就可以有效避免系統呼叫程序排程策略而引起的卡頓,這一點,第四大點第3小節中有說明。而且由於開發者的原因,可能是程式寫得太爛,或程式容易出錯,或做不該做的動作,或是惡意程式,對於這類程式程序,手動移除也是有好處的。

       但如果是執行一些小程式,就完全沒有必要去預先殺程序了,完全可以交給系統自己管理。讀者可能會疑惑,因為小程式啟動的時候,也有可能會因為記憶體不足而導致需要移除部分程序的情況。筆者認為,即便是記憶體不足,小程式執行引起的呼叫程序排程策略測的次數非常少,要移除的程序也非常少,產生的影響不大。同時,我們也要意識到另外一點就是,無論是手動殺死程序還是自動殺程序,都需要cpu去執行這些任務,所以也會拖慢手機和消耗電量。所以從這一點看,頻繁殺程序,也是一個不好的習慣。

六、Home鍵、Back鍵和多工鍵

       Home鍵、Back鍵和多工鍵,在手機螢幕的下方,這三個按鍵一般稱為導航欄,中間的按鈕為Home鍵,多工鍵和Back鍵分別在其左右,一般根據手機品牌不同,左右位置也有所差異。

       在執行App的時候,如果按一下Home鍵或者Back鍵,都可以退到桌面,那麼這兩者有什麼區別呢?

  • Home鍵。按Home鍵的時候,App如果沒有Service開啟,會從一個前臺程序轉變為一個後臺程序;如果有前臺service執行,就仍然是前臺程序,比如QQ音樂播放器等;如果是隻有普通service執行,那麼就轉變為服務程序(參照前文中講的Android程序的5個級別)。
  • Back鍵。按Back鍵的時候,App如果沒有Service開啟,會從一個前臺程序轉變為一個空程序;對於有Service執行的情況,和按Home鍵一樣。

        後臺程序和空程序,都是駐留在後臺,處於暫停狀態,也都是除了佔用一部分記憶體外,不佔用其他如cpu等資源的,那麼問題來了,為什麼要設計後臺程序和空程序這兩種空程序呢?它們的區別到底在哪裡呢?我們在前文講Android程序的5個等級的時候講到過,當剩餘記憶體不足的時候,系統會按照等級順序,優先移除不太重要程序,以收回記憶體供更重要的程序執行。那麼,它們的區別就是,在剩餘記憶體不足時,會優先移除空程序,再不足,才會移除空程序。所以,如果確實要退出某個應用一段時間內不大使用了,如果這款應用有退出按鈕,就用應用自帶的退出功能;如果沒有,則最好按系統的Back鍵,這樣可以變成空程序,當系統要回收記憶體時,就會優先被回收,從而釋放的所佔的資源。如果只是暫時退出去做點別的,過一會還要切換回來,或者對這款應用使用比較頻繁,那就使用Home鍵,因為相比於按Back鍵,這樣可以儘可能保住後臺程序,方便下次使用的時候快速啟動。

       當然,按Home鍵或Back鍵,對使用者來說,其實感覺不到差異,使用起來沒什麼兩樣,但是,對於Android開發者來說,卻有必要作為常識來了解其中的道理和差異。無論是按Home鍵還是按Back鍵,在按多工鍵的時候,都可以看到這些程序,如下圖所示。最下面的按鍵為清理按鍵,點選後可以清除掉這些程序,回收記憶體了,當然,前面也講了很多遍了,不建議這樣做。

       

七、程序移除順序的依據——閾(yu,第四聲)值

        前面講到,系統呼叫程序排程演算法後,會根據程序的等級來決定優先回收那類程序。那麼系統是根據什麼來判斷需要移除這些程序的呢?答案是閾值。

  1、檢視閾值

        我們可以採用如下方法檢視手機中各個等級程序的閾值(需要root許可權),如第二排資料所示(其單位為頁):

        

       以第一個資料44032為例,計算方法為:

       1page=4KB=4*1024B=4096B

       44032page* 4048B/page =  180355072B

       180355072B/1024/1024 = 172M

       即第一個等級的程序的閾值為172M。依次類推,閾值依次為:172M,190M,208M,226M,316M,415M。

       有必要說明一下,在Android開發者官方文件中,是將Android應用程序分為了5個等級,但很多資料卻是分的6個等級,在後臺程序和空程序之間還有一個“內容提供節點(content provider)程序”。內容提供節點,沒有實體程式,僅提供內容供別的程式去用 ,比如日曆供應節點,郵件供應節點等,在終止程序時,這類程序有比較高的優先權。手機中應該是採用的6個等級的方式,如上六個資料,正好對應著六個等級的程序,等級越高,閾值越低,即前臺程序閾值為172M,空程序為415M。當系統的剩餘記憶體只剩餘不到415M的時候,系統首先會回收空程序,依次類推,只有剩餘記憶體不到172M了,才會去回收前臺程序,這樣就起到了優化保護重要程序的作用。

  2、修改閾值。

       可以採用命令:echo "44032,48640,53248,57856,80896,106241" > /sys/module/lowmemorykiller/parameters/minfree來修改閾值,如下所示:

       

       重啟後,會恢復為原來的值。至於如何永久性修改該閾值,這裡不深入探討,有興趣的童鞋可以自行研究,一般來說,就按照系統給定的預設值使用就可以了,沒特殊用途的話,沒必要修改。

       對於這一節閾值的內容,暫時先講到這裡,如果要更深入,可以自行多研究研究。筆者也沒有看到比較好的更深入的文章,所以也不好推薦,如果讀者看到比較好的,可以推薦給筆者,感激不盡。

八、開發者選項中的記憶體管理功能

        Android手機都帶有開發者選項,隱藏了很多功能,顧名思義,這些功能主要用於輔助開發者除錯程式用的。其中有一些就是關於記憶體管理功能的,筆者這裡介紹一下其中兩款,如下圖紅框部分所示:

       

  • 不保留活動。使用者離開以後即銷燬每個活動(Activity),這樣做使得後臺程序都被銷燬了。筆者試驗過幾款app,比如微信,瀏覽器,開啟/關閉“不保留活動”前後,按Home鍵後,再開啟應用,有明顯的差別。當然,也試用了簡訊,DD打車,就沒看出起了什麼作用。讀者若是感興趣可以深入研究研究,到時候在指導指導筆者!
  • 後臺程序限制。

       

筆者目前只知道兩個功能,

https://developer.android.com/guide/components/processes-and-threads#Processes

https://www.cnblogs.com/nathan909/p/5372981.html

https://www.cnblogs.com/tiger-wang-ms/p/6491429.html

https://blog.csdn.net/ls5718/article/details/51858976