為了面試隔节,為了高工資舞竿,廢話不多說(shuō),不定期更新。
1. Activity正常和異常情況下的生命周期分析星掰。
Activity生命周期是一個(gè)老生常談的問(wèn)題,但是有些刁鉆的問(wèn)題也會(huì)一時(shí)半會(huì)的影響大家的思路嫩舟,我們還是耐著性子來(lái)分析下吧氢烘。
I.正常生命周期
- onCreate
Avtivity啟動(dòng)時(shí)調(diào)用的第一個(gè)方法,表示Activity正在被創(chuàng)建家厌。應(yīng)該在此初始化Activity的必需組件,最重要的是必需在該方法中調(diào)用setContentView方法來(lái)設(shè)置Activity的布局播玖。
不應(yīng)在此接口中執(zhí)行耗時(shí)操作,否則會(huì)影響Activity的顯示饭于。
- onStart
在Activity即將對(duì)用戶可見(jiàn)之前調(diào)用(該接口與onResume對(duì)應(yīng)蜀踏,onResume代表已經(jīng)可見(jiàn)了)维蒙,可以理解為已經(jīng)在后臺(tái)準(zhǔn)備就緒了。
- onResume
在Activity即將開(kāi)始與用戶進(jìn)行交互前調(diào)用果覆,此時(shí)Activity處于Activity堆棧的頂層颅痊,并具有用戶輸入焦點(diǎn)。
- onPause
當(dāng)系統(tǒng)開(kāi)始顯示另外一個(gè)Activity(Dialog不算局待,Dialog形式的Activity算)時(shí)調(diào)用斑响,通常用于確認(rèn)對(duì)持久性數(shù)據(jù)的保存更改,停止動(dòng)畫(huà)及其他可能消耗CPU的內(nèi)容钳榨。它應(yīng)該快速執(zhí)行所需操作舰罚,因?yàn)樗祷睾螅乱粋€(gè)Activity才能繼續(xù)執(zhí)行薛耻。
此時(shí)Activity對(duì)于用戶來(lái)講是部分可見(jiàn)的沸停,例如彈出一個(gè)Dialog形式的Activity。
- onStop
在Activity對(duì)用戶完全不可見(jiàn)時(shí)調(diào)用昭卓,可以認(rèn)為處于后臺(tái)愤钾。
- onDestory
在Activity被銷(xiāo)毀前調(diào)用,這是Activity將收到的最后的調(diào)用候醒。
一般會(huì)在此銷(xiāo)毀啟動(dòng)的線程能颁,或者處理一些可能導(dǎo)致內(nèi)存泄露的問(wèn)題。
- onRestart
系統(tǒng)由不可見(jiàn)變?yōu)榭梢?jiàn)時(shí)調(diào)用倒淫,調(diào)用順序:onRestart-onstart-onResume.
Note: 除非程序在onCreate()方法里面就調(diào)用了finish()方法伙菊,系統(tǒng)通常是在執(zhí)行了onPause()與onStop() 之后再調(diào)用onDestroy() 。在某些情況下敌土,例如我們的activity只是做了一個(gè)臨時(shí)的邏輯跳轉(zhuǎn)的功能镜硕,它只是用來(lái)決定跳轉(zhuǎn)到哪一個(gè)activity,這樣的話返干,需要在onCreate里面調(diào)用finish方法兴枯,這樣系統(tǒng)會(huì)直接調(diào)用onDestory,跳過(guò)生命周期中的其他方法矩欠。
II.異常生命周期
- 資源配置發(fā)生變更導(dǎo)致的Activity殺死重建
當(dāng)系統(tǒng)配置發(fā)生變化后财剖,例如屏幕方向切換、系統(tǒng)語(yǔ)言切換癌淮,生命周期方法調(diào)用如下
onPause---onSaveInstanceState---onStop---onDestory---onCreate---onRestoreInstanceState--onStart--onResume
在Activity異常終止時(shí)躺坟,系統(tǒng)會(huì)調(diào)用onSaveInstanceState來(lái)保存一些狀態(tài)信息(在onStop方法之前調(diào)用,但是和onPause方法并無(wú)明確的調(diào)用順序)乳蓄。
重建Activity時(shí)咪橙,onSaveInstanceState保存的信息Bundle會(huì)以參數(shù)的形式傳入onCreate和onRestoreInstanceState(在onStart方法之后調(diào)用)方法中,所以我們可以在這兩個(gè)方法中進(jìn)行一些狀態(tài)恢復(fù)。
Note:onCreate方法的Bundle參數(shù)可能為空美侦,所以我們一般配對(duì)使用onSaveInstanceState和onRestoreInstanceState产舞。
保存數(shù)據(jù)的思想:
Activity被意外終止時(shí),Activity會(huì)調(diào)用onSavedInstanceState去保存數(shù)據(jù)音榜,然后Activity會(huì)委托Window去保存數(shù)據(jù)庞瘸,接著Window再委托它上面的頂級(jí)容器去保存數(shù)據(jù),頂層容器是一個(gè)ViewGroup赠叼,一般來(lái)說(shuō)它很可能是一個(gè)DecorView.最后頂層容器再去一一通知它的子元素來(lái)保存數(shù)據(jù)擦囊,這樣整個(gè)數(shù)據(jù)保存過(guò)程就完成了,可以發(fā)現(xiàn)嘴办,這是一種典型的委托思想瞬场,上層委托下層,父容器委托子容器去處理一些事情涧郊。
- 資源或者內(nèi)存不足導(dǎo)致被殺死
對(duì)于Activity我們分為三種:
a.前臺(tái)Activity(高)
b.可見(jiàn)非前臺(tái)Activity(中)
c.后臺(tái)Activity(低)
當(dāng)系統(tǒng)發(fā)現(xiàn)內(nèi)存不足時(shí)贯被,會(huì)按照上面的優(yōu)先級(jí)選擇殺死Activity所在的進(jìn)程,并在后續(xù)(需要再顯示的時(shí)候)進(jìn)行恢復(fù)妆艘。
Note:系統(tǒng)只恢復(fù)那些被開(kāi)發(fā)者指定過(guò)id的控件彤灶,如果沒(méi)有為控件指定id,則系統(tǒng)就無(wú)法恢復(fù)了。
2. Android啟動(dòng)模式分析
2.1 Application&Task&Process
Application
通俗的講批旺,Application就是一組組件的集合幌陕,每一個(gè)應(yīng)用都是一個(gè)Application。
Task
Task是在程序運(yùn)行時(shí)汽煮,只針對(duì)Activity的概念搏熄,Task是一組相互關(guān)聯(lián)的Activity的集合,它存在framework層的一個(gè)概念暇赤,控制界面的跳轉(zhuǎn)和返回心例。
Process
進(jìn)程是操作系統(tǒng)內(nèi)核的一個(gè)概念,表示接受內(nèi)核調(diào)度的執(zhí)行單位鞋囊。
在默認(rèn)情況下止后,一個(gè)應(yīng)用程序的所有組件運(yùn)行在同一個(gè)進(jìn)程中。
2.2 啟動(dòng)模式
Activity存在四種啟動(dòng)模式:standard,singleTop,singleTask,singleInstance,使用時(shí)需要在AndroidManifest文件中配置失暴。
standard:標(biāo)準(zhǔn)
標(biāo)準(zhǔn)模式坯门,也是系統(tǒng)默認(rèn)的Activity啟動(dòng)模式。
Activity可以多次實(shí)例化逗扒,而且每個(gè)實(shí)例可以屬于不同的任務(wù),并且一個(gè)任務(wù)可以擁有多個(gè)實(shí)例欠橘。
誰(shuí)啟動(dòng)的Activity矩肩,Activity就和它處于同一個(gè)任務(wù)棧中。
在使用ApplicationContext或者Service中的Context啟動(dòng)Activity時(shí),需要添加FLAG_ACTIVITY_NEW_TASK的標(biāo)志才能正常啟動(dòng)Activity黍檩;因?yàn)榉茿ctivity的Context沒(méi)有所謂的任務(wù)棧叉袍。
singleTop:棧頂復(fù)用
棧頂復(fù)用模式。
如果當(dāng)前任務(wù)棧頂已經(jīng)存在Activity的一個(gè)實(shí)例刽酱,系統(tǒng)會(huì)調(diào)用該實(shí)例的onNewIntent方法向其傳遞Intent喳逛,而不會(huì)創(chuàng)建Activity的新實(shí)例。(該Activity的onCreate棵里、onStart方法不會(huì)被系統(tǒng)調(diào)用润文,因?yàn)樗](méi)有發(fā)生改變)
Activity可以多次實(shí)例化,而每個(gè)實(shí)例均可屬于不同的任務(wù)殿怜,并且一個(gè)任務(wù)可以擁有多個(gè)實(shí)例(但前提是位于返回棧頂部的 Activity并不是Activity的現(xiàn)有實(shí)例)典蝌。
例如,假設(shè)任務(wù)返回棧包含Activity A 以及 Activity B头谜、C 和位于頂部的 D(堆棧是 A-B-C-D骏掀;D 位于頂部)。
收到針對(duì) D 類(lèi) Activity 的 Intent柱告。
如果 D 具有默認(rèn)的 "standard" 啟動(dòng)模式截驮,則會(huì)創(chuàng)建該類(lèi)的新實(shí)例,且堆棧會(huì)變成 A-B-C-D-D际度。
但是葵袭,如果 D 的啟動(dòng)模式是 "singleTop",則 D 的現(xiàn)有實(shí)例會(huì)通過(guò) onNewIntent() 接收 Intent甲脏,因?yàn)樗挥诙褩5捻敳靠舭荆欢褩H詾?A-B-C-D。
但是块请,如果收到針對(duì) B 類(lèi) Activity 的 Intent娜氏,則會(huì)向堆棧添加B新實(shí)例,即便其啟動(dòng)模式為 "singleTop" 也是如此墩新。
singleTask:棧內(nèi)復(fù)用
棧內(nèi)復(fù)用模式贸弥。
啟動(dòng)一個(gè)singleTask的Activity實(shí)例時(shí),如果任務(wù)棧中已經(jīng)存在這樣一個(gè)實(shí)例海渊,就會(huì)將這個(gè)實(shí)例調(diào)度到任務(wù)的棧頂绵疲,并清除它當(dāng)前所在任務(wù)中位于它上面的所有的activity,不會(huì)重新實(shí)例化該Activity臣疑;如果任務(wù)棧中不存在該Activity實(shí)例盔憨,則創(chuàng)建實(shí)例后入棧。
和SingleTop一樣讯沈,系統(tǒng)會(huì)回調(diào)它的oneNewInent方法郁岩。
singleInstance:?jiǎn)卫?/h3>
單例模式。
總是在新的任務(wù)中開(kāi)啟,并且這個(gè)新的任務(wù)中有且只有這一個(gè)實(shí)例问慎,也就是說(shuō)被該實(shí)例啟動(dòng)的其他activity會(huì)自動(dòng)運(yùn)行于另一個(gè)任務(wù)中萍摊。當(dāng)再次啟動(dòng)該activity的實(shí)例時(shí),會(huì)重用已存在的任務(wù)和實(shí)例如叼。并且會(huì)調(diào)用這個(gè)實(shí)例的onNewIntent()方法冰木,將Intent實(shí)例傳遞到該實(shí)例中。
同一時(shí)刻在系統(tǒng)中只會(huì)存在一個(gè)這樣的Activity實(shí)例笼恰。
taskAffinity
taskAffinity表示Activity所處的任務(wù)棧踊沸,默認(rèn)情況下一個(gè)應(yīng)用中所有的Activity具有相同的taskAffinity(處于同一個(gè)任務(wù)中),默認(rèn)的taskAffinity為應(yīng)用的包名挖腰。
可以指定taskAffinity為空字符串雕沿,代表Activity不屬于任何任務(wù)。
taskActivity主要結(jié)合singleTask啟動(dòng)模式或者allowTaskReparenting屬性配對(duì)使用猴仑,在其他情況下無(wú)意義审轮。
面試的時(shí)候,給面試官舉例說(shuō)明辽俗,更加簡(jiǎn)潔明了疾渣,省的表述不清。
啟動(dòng)模式指定
啟動(dòng)模式的指定存在兩種方式:
- launchMode指定:無(wú)法指定FLAG_ACTIVITY_CLEAR_TOP.
- Intent Flag指定(優(yōu)先級(jí)高):無(wú)法指定singleInstance.
FLAG
FLAG_ACTIVITY_SINGLE_TOP
相當(dāng)于singleTop
FLAG_ACTIVITY_NEW_TASK
沒(méi)有對(duì)應(yīng)的啟動(dòng)模式崖飘,它的部分特性與singleTask相同榴捡。
默認(rèn)的跳轉(zhuǎn)類(lèi)型,會(huì)重新創(chuàng)建一個(gè)新的Activity,比方說(shuō)Task1中有A,B,C三個(gè)Activity,此時(shí)在C中啟動(dòng)D的話朱浴,如果在Manifest.xml文件中給D添加了Affinity的值和Task中的不一樣的話吊圾,則會(huì)在新標(biāo)記的Affinity所存在的Task中壓入這個(gè)Activity。如果是默認(rèn)的或者指定的Affinity和Task一樣的話翰蠢,就和標(biāo)準(zhǔn)模式一樣了啟動(dòng)一個(gè)新的Activity.
FLAG_ACTIVITY_CLEAR_TOP
當(dāng)Intent對(duì)象包含這個(gè)標(biāo)記時(shí)项乒,如果在棧中發(fā)現(xiàn)存在Activity實(shí)例,則清空這個(gè)實(shí)例之上的Activity梁沧,使其處于棧頂檀何。
在使用默認(rèn)的“standard”啟動(dòng)模式下,如果沒(méi)有在Intent使用到FLAG_ACTIVITY_SINGLE_TOP標(biāo)記廷支,那么它將關(guān)閉后重建频鉴,如果使用了這個(gè)FLAG_ACTIVITY_SINGLE_TOP標(biāo)記,則會(huì)使用已存在的實(shí)例恋拍;對(duì)于其他啟動(dòng)模式垛孔,無(wú)需再使用FLAG_ACTIVITY_SINGLE_TOP,它都將使用已存在的實(shí)例施敢,Intent會(huì)被傳遞到這個(gè)實(shí)例的onNewIntent()中似炎。
3. 進(jìn)程毙疗迹活
進(jìn)程毕勖辏活包括兩個(gè)方面:
- 提升進(jìn)程優(yōu)先級(jí),降低進(jìn)程被殺死的概率悯许。
- 進(jìn)程被殺死后仆嗦,進(jìn)行拉活。
進(jìn)程優(yōu)先級(jí)
Android系統(tǒng)將盡量長(zhǎng)時(shí)間地保持應(yīng)用進(jìn)程先壕,但為了新建進(jìn)程或運(yùn)行更重要的進(jìn)程瘩扼,最終需要清除舊進(jìn)程來(lái)回收內(nèi)存。 為了確定保留或終止哪些進(jìn)程垃僚,系統(tǒng)會(huì)根據(jù)進(jìn)程中正在運(yùn)行的組件以及這些組件的狀態(tài)集绰,將每個(gè)進(jìn)程放入“重要性層次結(jié)構(gòu)”中。 必要時(shí)谆棺,系統(tǒng)會(huì)首先消除重要性最低的進(jìn)程栽燕,然后是清除重要性稍低一級(jí)的進(jìn)程,依此類(lèi)推改淑,以回收系統(tǒng)資源碍岔。
按照進(jìn)程的優(yōu)先級(jí),進(jìn)程分為以下五類(lèi):
- 前臺(tái)進(jìn)程
- 可見(jiàn)進(jìn)程
- 服務(wù)進(jìn)程
- 后臺(tái)進(jìn)程
- 空進(jìn)程
前臺(tái)進(jìn)程
用戶操作所必需的進(jìn)程朵夏,如果一個(gè)進(jìn)程滿足以下任何一個(gè)條件蔼啦,即為前臺(tái)進(jìn)程:
- 托管用戶正在交互的 Activity(已調(diào)用 Activity 的 onResume() 方法)
- 托管某個(gè)Service,后者綁定到用戶正在交互的Activity
- 托管正在“前臺(tái)”運(yùn)行的Service(服務(wù)已調(diào)用 startForeground())
- 托管正執(zhí)行一個(gè)生命周期回調(diào)的Service(onCreate()捏肢、onStart() 或 onDestroy())
- 托管正執(zhí)行其onReceive()方法的 BroadcastReceiver
通常,在任意給定時(shí)間前臺(tái)進(jìn)程都為數(shù)不多饥侵。
只有在內(nèi)存不足以支持它們同時(shí)繼續(xù)運(yùn)行這一萬(wàn)不得已的情況下鸵赫,系統(tǒng)才會(huì)終止它們。
此時(shí)爆捞,設(shè)備往往已達(dá)到內(nèi)存分頁(yè)狀態(tài)奉瘤,因此需要終止一些前臺(tái)進(jìn)程來(lái)確保用戶界面正常響應(yīng)。
可見(jiàn)進(jìn)程
沒(méi)有任何前臺(tái)組件煮甥、但仍會(huì)影響用戶在屏幕上所見(jiàn)內(nèi)容的進(jìn)程盗温。
如果一個(gè)進(jìn)程滿足以下任一條件,即視為可見(jiàn)進(jìn)程:
- 托管不在前臺(tái)成肘、但仍對(duì)用戶可見(jiàn)的 Activity(已調(diào)用其 onPause() 方法)卖局。
例如,如果前臺(tái) Activity 啟動(dòng)了一個(gè)對(duì)話框双霍,允許在其后顯示上一 Activity砚偶,則有可能會(huì)發(fā)生這種情況批销。 - 托管綁定到可見(jiàn)(或前臺(tái))Activity 的 Service。
可見(jiàn)進(jìn)程被視為是極其重要的進(jìn)程染坯,除非為了維持所有前臺(tái)進(jìn)程同時(shí)運(yùn)行而必須終止均芽,否則系統(tǒng)不會(huì)終止這些進(jìn)程。
服務(wù)進(jìn)程
盡管服務(wù)進(jìn)程與用戶所見(jiàn)內(nèi)容沒(méi)有直接關(guān)聯(lián)单鹿,但是它們通常在執(zhí)行一些用戶關(guān)心的操作(例如掀宋,在后臺(tái)播放音樂(lè)或從網(wǎng)絡(luò)下載數(shù)據(jù))。因此仲锄,除非內(nèi)存不足以維持所有前臺(tái)進(jìn)程和可見(jiàn)進(jìn)程同時(shí)運(yùn)行劲妙,否則系統(tǒng)會(huì)讓服務(wù)進(jìn)程保持運(yùn)行狀態(tài)。
正在運(yùn)行 startService() 方法啟動(dòng)的服務(wù)儒喊,且不屬于上述兩個(gè)更高類(lèi)別進(jìn)程的進(jìn)程镣奋。
后臺(tái)進(jìn)程
包含目前對(duì)用戶不可見(jiàn)的 Activity 的進(jìn)程(已調(diào)用 Activity 的 onStop() 方法)。
這些進(jìn)程對(duì)用戶體驗(yàn)沒(méi)有直接影響怀愧,系統(tǒng)可能隨時(shí)終止它們侨颈,以回收內(nèi)存供前臺(tái)進(jìn)程、可見(jiàn)進(jìn)程或服務(wù)進(jìn)程使用掸驱。
通常會(huì)有很多后臺(tái)進(jìn)程在運(yùn)行肛搬,因此它們會(huì)保存在 LRU (最近最少使用)列表中,以確保包含用戶最近查看的 Activity 的進(jìn)程最后一個(gè)被終止毕贼。
如果某個(gè) Activity 正確實(shí)現(xiàn)了生命周期方法温赔,并保存了其當(dāng)前狀態(tài),則終止其進(jìn)程不會(huì)對(duì)用戶體驗(yàn)產(chǎn)生明顯影響鬼癣,因?yàn)楫?dāng)用戶導(dǎo)航回該 Activity 時(shí)陶贼,Activity 會(huì)恢復(fù)其所有可見(jiàn)狀態(tài)。
空進(jìn)程
不含任何活動(dòng)應(yīng)用組件的進(jìn)程待秃。保留這種進(jìn)程的的唯一目的是用作緩存拜秧,以縮短下次在其中運(yùn)行組件所需的啟動(dòng)時(shí)間。 為使總體系統(tǒng)資源在進(jìn)程緩存和底層內(nèi)核緩存之間保持平衡章郁,系統(tǒng)往往會(huì)終止這些進(jìn)程枉氮。
進(jìn)程被殺死的情況
綜上,可以得出減少進(jìn)程被殺死概率無(wú)非就是想辦法提高進(jìn)程優(yōu)先級(jí)暖庄,減少進(jìn)程在內(nèi)存不足等情況下被殺死的概率聊替。
提升進(jìn)程優(yōu)先級(jí)方案
1. 利用Activity提升
方案設(shè)計(jì)思想:監(jiān)控手機(jī)鎖屏解鎖事件,在屏幕鎖屏?xí)r啟動(dòng)1個(gè)像素的 Activity培廓,在用戶解鎖時(shí)將 Activity 銷(xiāo)毀掉惹悄。注意該 Activity 需設(shè)計(jì)成用戶無(wú)感知。
通過(guò)該方案肩钠,可以使進(jìn)程的優(yōu)先級(jí)在屏幕鎖屏?xí)r間由4提升為最高優(yōu)先級(jí)1泣港。
方案適用范圍:
適用場(chǎng)景:本方案主要解決第三方應(yīng)用及系統(tǒng)管理工具在檢測(cè)到鎖屏事件后一段時(shí)間(一般為5分鐘以內(nèi))內(nèi)會(huì)殺死后臺(tái)進(jìn)程暂殖,已達(dá)到省電的目的問(wèn)題。
適用版本:適用于所有的 Android 版本当纱。
方案具體實(shí)現(xiàn):
首先定義 Activity呛每,并設(shè)置 Activity 的大小為1像素。
其次惫东,從 AndroidManifest 中通過(guò)如下屬性莉给,排除 Activity 在 RecentTask 中的顯示。
excludeFromRecents="true"
exported="false"
finishOnTaskLaunch="false"
最后廉沮,設(shè)置Activity為透明主題。
2. 利用Notification提升
方案設(shè)計(jì)思想:Android 中 Service 的優(yōu)先級(jí)為4徐矩,通過(guò) setForeground 接口可以將后臺(tái) Service 設(shè)置為前臺(tái) Service滞时,使進(jìn)程的優(yōu)先級(jí)由4提升為2,從而使進(jìn)程的優(yōu)先級(jí)僅僅低于用戶當(dāng)前正在交互的進(jìn)程滤灯,與可見(jiàn)進(jìn)程優(yōu)先級(jí)一致坪稽,使進(jìn)程被殺死的概率大大降低。
方案實(shí)現(xiàn)挑戰(zhàn):從 Android2.3 開(kāi)始調(diào)用 setForeground 將后臺(tái) Service 設(shè)置為前臺(tái) Service 時(shí)鳞骤,必須在系統(tǒng)的通知欄發(fā)送一條通知窒百,也就是前臺(tái) Service 與一條可見(jiàn)的通知時(shí)綁定在一起的少欺。
對(duì)于不需要常駐通知欄的應(yīng)用來(lái)說(shuō)片排,該方案雖好盯捌,但卻是用戶感知的郁副,無(wú)法直接使用鞠绰。
方案挑戰(zhàn)應(yīng)對(duì)措施:通過(guò)實(shí)現(xiàn)一個(gè)內(nèi)部 Service铺峭,在 LiveService 和其內(nèi)部 Service 中同時(shí)發(fā)送具有相同 ID 的 Notification纱兑,然后將內(nèi)部 Service 結(jié)束掉因俐。隨著內(nèi)部 Service 的結(jié)束榴嗅,Notification 將會(huì)消失妄呕,但系統(tǒng)優(yōu)先級(jí)依然保持為2。
方案適用范圍:適用于目前已知所有版本嗽测。
拉起進(jìn)程
1. 利用系統(tǒng)廣播拉活
方案設(shè)計(jì)思想:在發(fā)生特定系統(tǒng)事件時(shí)绪励,系統(tǒng)會(huì)發(fā)出響應(yīng)的廣播,通過(guò)在 AndroidManifest 中“靜態(tài)”注冊(cè)對(duì)應(yīng)的廣播監(jiān)聽(tīng)器唠粥,即可在發(fā)生響應(yīng)事件時(shí)拉活疏魏。
方案適用范圍:適用于全部 Android 平臺(tái)。但存在如下幾個(gè)缺點(diǎn):
- 廣播接收器被管理軟件厅贪、系統(tǒng)軟件通過(guò)“自啟管理”等功能禁用的場(chǎng)景無(wú)法接收到廣播蠢护,從而無(wú)法自啟。
- 系統(tǒng)廣播事件不可控养涮,只能保證發(fā)生事件時(shí)拉活進(jìn)程葵硕,但無(wú)法保證進(jìn)程掛掉后立即拉活眉抬。
因此,該方案主要作為備用手段懈凹。
2. 利用第三方廣播拉活
方案設(shè)計(jì)思想:該方案總的設(shè)計(jì)思想與接收系統(tǒng)廣播類(lèi)似蜀变,不同的是該方案為接收第三方 Top 應(yīng)用廣播。
通過(guò)反編譯第三方 Top 應(yīng)用介评,如:手機(jī)QQ库北、微信、支付寶们陆、UC瀏覽器等寒瓦,以及友盟、信鴿坪仇、個(gè)推等 SDK杂腰,找出它們外發(fā)的廣播,在應(yīng)用中進(jìn)行監(jiān)聽(tīng)椅文,這樣當(dāng)這些應(yīng)用發(fā)出廣播時(shí)喂很,就會(huì)將我們的應(yīng)用拉活。
方案適用范圍:該方案的有效程度除與系統(tǒng)廣播一樣的因素外皆刺,主要受如下因素限制:
- 反編譯分析過(guò)的第三方應(yīng)用的多少
- 第三方應(yīng)用的廣播屬于應(yīng)用私有少辣,當(dāng)前版本中有效的廣播,在后續(xù)版本隨時(shí)就可能被移除或被改為不外發(fā)羡蛾。
這些因素都影響了拉活的效果漓帅。
3. 利用系統(tǒng)Service機(jī)制拉活
方案設(shè)計(jì)思想:將 Service 設(shè)置為 START_STICKY,利用系統(tǒng)機(jī)制在 Service 掛掉后自動(dòng)拉活林说。
方案適用范圍:如下兩種情況無(wú)法拉活
Service 第一次被異常殺死后會(huì)在5秒內(nèi)重啟煎殷,第二次被殺死會(huì)在10秒內(nèi)重啟,第三次會(huì)在20秒內(nèi)重啟腿箩,一旦在短時(shí)間內(nèi) Service 被殺死達(dá)到5次豪直,則系統(tǒng)不再拉起。
進(jìn)程被取得 Root 權(quán)限的管理工具或系統(tǒng)工具通過(guò) forestop 停止掉珠移,無(wú)法重啟弓乙。
4 利用Native進(jìn)程拉活
方案設(shè)計(jì)思想:
主要思想:利用 Linux 中的 fork 機(jī)制創(chuàng)建 Native 進(jìn)程,在 Native 進(jìn)程中監(jiān)控主進(jìn)程的存活钧惧,當(dāng)主進(jìn)程掛掉后暇韧,在 Native 進(jìn)程中立即對(duì)主進(jìn)程進(jìn)行拉活。
主要原理:在 Android 中所有進(jìn)程和系統(tǒng)組件的生命周期受 ActivityManagerService 的統(tǒng)一管理浓瞪。而且懈玻,通過(guò) Linux 的 fork 機(jī)制創(chuàng)建的進(jìn)程為純 Linux 進(jìn)程,其生命周期不受 Android 的管理乾颁。
感知進(jìn)程死亡
要在 Native 進(jìn)程中感知主進(jìn)程是否存活有兩種實(shí)現(xiàn)方式:
在 Native 進(jìn)程中通過(guò)死循環(huán)或定時(shí)器涂乌,輪訓(xùn)判斷主進(jìn)程是否存活艺栈,當(dāng)主進(jìn)程不存活時(shí)進(jìn)行拉活。該方案的很大缺點(diǎn)是不停的輪詢執(zhí)行判斷邏輯湾盒,非常耗電湿右。
在主進(jìn)程中創(chuàng)建一個(gè)監(jiān)控文件,并且在主進(jìn)程中持有文件鎖罚勾。在拉活進(jìn)程啟動(dòng)后申請(qǐng)文件鎖將會(huì)被堵塞毅人,一旦可以成功獲取到鎖,說(shuō)明主進(jìn)程掛掉尖殃,即可進(jìn)行拉活丈莺。由于 Android 中的應(yīng)用都運(yùn)行于虛擬機(jī)之上,Java 層的文件鎖與 Linux 層的文件鎖是不同的分衫,要實(shí)現(xiàn)該功能需要封裝 Linux 層的文件鎖供上層調(diào)用场刑。
拉活主進(jìn)程
通過(guò) Native 進(jìn)程拉活主進(jìn)程的部分代碼如下,即通過(guò) am 命令進(jìn)行拉活蚪战。通過(guò)指定“--include-stopped-packages”參數(shù)來(lái)拉活主進(jìn)程處于 forestop 狀態(tài)的情況。
如何保證 Native 進(jìn)程的唯一
從可擴(kuò)展性和進(jìn)程唯一等多方面考慮铐懊,將 Native 進(jìn)程設(shè)計(jì)成 C/S 結(jié)構(gòu)模式邀桑,主進(jìn)程與 Native 進(jìn)程通過(guò) Localsocket 進(jìn)行通信。在Native進(jìn)程中利用 Localsocket 保證 Native 進(jìn)程的唯一性科乎,不至于出現(xiàn)創(chuàng)建多個(gè) Native 進(jìn)程以及 Native 進(jìn)程變成僵尸進(jìn)程等問(wèn)題壁畸。
方案適用范圍:該方案主要適用于 Android5.0 以下版本手機(jī)。
該方案不受 forcestop 影響茅茂,被強(qiáng)制停止的應(yīng)用依然可以被拉活捏萍,在 Android5.0 以下版本拉活效果非常好。
對(duì)于 Android5.0 以上手機(jī)空闲,系統(tǒng)雖然會(huì)將native進(jìn)程內(nèi)的所有進(jìn)程都?xì)⑺懒铊荆@里其實(shí)就是系統(tǒng)“依次”殺死進(jìn)程時(shí)間與拉活邏輯執(zhí)行時(shí)間賽跑的問(wèn)題,如果可以跑的比系統(tǒng)邏輯快碴倾,依然可以有效拉起逗噩。記得網(wǎng)上有人做過(guò)實(shí)驗(yàn),該結(jié)論是成立的跌榔,在某些 Android 5.0 以上機(jī)型有效异雁。
5. 利用 JobScheduler 機(jī)制拉活
方案設(shè)計(jì)思想:Android5.0 以后系統(tǒng)對(duì) Native 進(jìn)程等加強(qiáng)了管理,Native 拉活方式失效僧须。系統(tǒng)在 Android5.0 以上版本提供了 JobScheduler 接口纲刀,系統(tǒng)會(huì)定時(shí)調(diào)用該進(jìn)程以使應(yīng)用進(jìn)行一些邏輯操作。
在本項(xiàng)目中担平,我對(duì) JobScheduler 進(jìn)行了進(jìn)一步封裝示绊,兼容 Android5.0 以下版本锭部。
方案適用范圍:該方案主要適用于 Android5.0 以上版本手機(jī)。
該方案在 Android5.0 以上版本中不受 forcestop 影響耻台,被強(qiáng)制停止的應(yīng)用依然可以被拉活空免,在 Android5.0 以上版本拉活效果非常好。
僅在小米手機(jī)可能會(huì)出現(xiàn)有時(shí)無(wú)法拉活的問(wèn)題盆耽。
6. 利用賬號(hào)同步機(jī)制拉活
方案設(shè)計(jì)思想:Android 系統(tǒng)的賬號(hào)同步機(jī)制會(huì)定期同步賬號(hào)進(jìn)行蹋砚,該方案目的在于利用同步機(jī)制進(jìn)行進(jìn)程的拉活。
方案適用范圍:該方案適用于所有的 Android 版本摄杂,包括被 forestop 掉的進(jìn)程也可以進(jìn)行拉活坝咐。
最新 Android 版本(Android N)中系統(tǒng)好像對(duì)賬戶同步這里做了變動(dòng),該方法不再有效析恢。
其他
其他還有一些技術(shù)之外的措施墨坚,比如說(shuō)應(yīng)用內(nèi) Push 通道的選擇:
- 國(guó)外版應(yīng)用:接入 Google 的 GCM。
- 國(guó)內(nèi)版應(yīng)用:根據(jù)終端不同映挂,在小米手機(jī)(包括 MIUI)接入小米推送泽篮、華為手機(jī)接入華為推送;其他手機(jī)可以考慮接入騰訊信鴿或極光推送與小米推送做 A/B Test柑船。
4. IntentService與Service的區(qū)別帽撑?IntentService的實(shí)現(xiàn)原理。
本人已經(jīng)在另外一篇文章分析過(guò)鞍时,麻煩移步亏拉。[IntentService-你可能需要知道這些](http://www.reibang.com/p/6b9358cbfc26)
5. 優(yōu)雅的展示Bitmap大圖
郭霖大神的文章,移步逆巍。[優(yōu)雅的展示BitMap大圖](http://blog.csdn.net/guolin_blog/article/details/9316683)
6. Retrofit使用的注解是哪種注解及塘?以及,注解的底層實(shí)現(xiàn)是怎樣的锐极。
Retrofit采用的是運(yùn)行時(shí)注解笙僚,下面上實(shí)錘(@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),為運(yùn)行時(shí)注解無(wú)疑溪烤。
關(guān)于注解味咳,本人寫(xiě)過(guò)一篇文章分析,麻煩移步檬嘀。注解-你可能需要知道這些
7. Thread和HandlerThread
簡(jiǎn)單的總結(jié)下槽驶,兩者的區(qū)別:
- HandlerThread是Thread的子類(lèi)。
- HandlerThread內(nèi)部持有一個(gè)Looper鸳兽,可以使用MessageQueue重復(fù)使用當(dāng)前線程掂铐,節(jié)省系統(tǒng)開(kāi)銷(xiāo)。
- HandlerThread一般結(jié)合Handler使用,按順序處理任務(wù)全陨。
HandlerThread的源碼很簡(jiǎn)單爆班,我們來(lái)簡(jiǎn)單分析下。
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。
重點(diǎn)來(lái)看下run方法:
@Override
public void run() {
mTid = Process.myTid();
// 為當(dāng)前線程設(shè)置Looper
Looper.prepare();
synchronized (this) {
// getLooper()返回的就是mLooper
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
// 開(kāi)始處理消息隊(duì)列
Looper.loop();
mTid = -1;
}
Handler+Thread的方式雨涛,在開(kāi)發(fā)中我們經(jīng)常使用到枢舶,我們來(lái)對(duì)比下HandlerThread+Handler和Handler+Thread的實(shí)現(xiàn)方式。
- Handler+Thread
private MyHandler mHandler;
public void buildHandler() {
new Thread(new Runnable() {
@Override
public void run() {
// 為當(dāng)前線程設(shè)置Looper
Looper.prepare();
// 使用當(dāng)前線程的Looper構(gòu)造Handler
mHandler = new MyHandler(Looper.myLooper());
// 開(kāi)始處理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() {
// 構(gòu)造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);
}
}
結(jié)合HandlerThread的源碼和兩種方式的對(duì)比替久,驗(yàn)證了我們?cè)陂_(kāi)頭的總結(jié)凉泄。
- HandlerThread是Thread的子類(lèi)。
2.HandlerThread內(nèi)部持有一個(gè)Looper蚯根,可以使用MessageQueue重復(fù)使用當(dāng)前線程后众,節(jié)省系統(tǒng)開(kāi)銷(xiāo)。 - HandlerThread一般結(jié)合Handler使用颅拦,按順序處理任務(wù)蒂誉。
8. Java是值傳遞還是引用傳遞
Java中方法參數(shù)傳遞方式是按值傳遞。
如果參數(shù)是基本類(lèi)型距帅,傳遞的是基本類(lèi)型的字面量值的拷貝拗盒。
如果參數(shù)是引用類(lèi)型,傳遞的是該參量所引用的對(duì)象在堆中地址值的拷貝锥债。
9. final和static的區(qū)別
static
static變量
按照是否靜態(tài)的對(duì)類(lèi)成員變量進(jìn)行分類(lèi)可分兩種:
- 一種是被static修飾的變量,叫靜態(tài)變量或類(lèi)變量痊臭。
- 另一種是沒(méi)有被static修飾的變量哮肚,叫實(shí)例變量。
兩者的區(qū)別是:
靜態(tài)變量在內(nèi)存中只有一個(gè)拷貝(節(jié)省內(nèi)存)广匙,JVM只為靜態(tài)變量分配一次內(nèi)存允趟,在加載類(lèi)的過(guò)程中完成靜態(tài)變量的內(nèi)存分配,可用類(lèi)名直接訪問(wèn)(方便)鸦致,當(dāng)然也可以通過(guò)對(duì)象來(lái)訪問(wèn)(但是這是不推薦的)潮剪。
實(shí)例變量屬于某個(gè)確定的實(shí)例,每當(dāng)創(chuàng)建一個(gè)實(shí)例分唾,就會(huì)為實(shí)例變量分配一次內(nèi)存抗碰,實(shí)例變量可以在內(nèi)存中有多個(gè)拷貝,互不影響(靈活)绽乔。
static代碼塊
static代碼塊是類(lèi)加載時(shí)弧蝇,初始化自動(dòng)執(zhí)行的。如果static代碼塊有多個(gè),JVM將按照它們?cè)陬?lèi)中出現(xiàn)的先后順序依次執(zhí)行它們看疗,每個(gè)代碼塊只會(huì)被執(zhí)行一次沙峻。
static方法
static方法是被static修飾的方法,可以通過(guò)類(lèi)名直接調(diào)用两芳,因此static方法不能使用this和super關(guān)鍵字(實(shí)例)摔寨,不能訪問(wèn)類(lèi)實(shí)例變量和方法,只能訪問(wèn)靜態(tài)變量和靜態(tài)方法怖辆;因?yàn)槠鋵儆陬?lèi)而獨(dú)立于任何實(shí)例是复,所以必須為非抽象。
因?yàn)榉椒▽儆陬?lèi)疗隶,所以子類(lèi)和父類(lèi)可以存在同名的方法佑笋,但是不涉及重載(不能被繼承)。
final
final變量
聲明 final 字段有助于編譯器作出更好的優(yōu)化決定斑鼻,因?yàn)槿绻幾g器知道字段的值不會(huì)更改蒋纬,那么它能安全地在寄存器中高速緩存該值。
final 字段還通過(guò)讓編譯器強(qiáng)制該字段為只讀來(lái)提供額外的安全級(jí)別坚弱。
其初始化可以在兩個(gè)地方:
- 一是其定義處蜀备,也就是說(shuō)在final變量定義時(shí)直接給其賦值
- 二是在構(gòu)造函數(shù)中。
這兩個(gè)地方只能選其一荒叶,要么在定義時(shí)給值碾阁,要么在構(gòu)造函數(shù)中給值,不能同時(shí)既在定義時(shí)給了值些楣,又在構(gòu)造函數(shù)中給另外的值脂凶。不能通過(guò)調(diào)用方法來(lái)賦值。
一旦被初始化便不可改變愁茁,這里不可改變的意思對(duì)基本類(lèi)型來(lái)說(shuō)是其值不可變蚕钦,而對(duì)于對(duì)象變量來(lái)說(shuō)其引用不可再變。
final方法
- 把方法鎖定鹅很,防止任何繼承類(lèi)修改它的意義和實(shí)現(xiàn)嘶居。
- 高效。編譯器在遇到調(diào)用final方法時(shí)候會(huì)轉(zhuǎn)入內(nèi)嵌inline機(jī)制促煮,大大提高執(zhí)行效率邮屁。
final類(lèi)
final類(lèi)不能被繼承,因此final類(lèi)的成員方法沒(méi)有機(jī)會(huì)被覆蓋菠齿,默認(rèn)都是final的佑吝。
在設(shè)計(jì)類(lèi)時(shí)候,如果這個(gè)類(lèi)不需要有子類(lèi)泞当,類(lèi)的實(shí)現(xiàn)細(xì)節(jié)不允許改變迹蛤,并且確信這個(gè)類(lèi)不會(huì)載被擴(kuò)展民珍,那么就設(shè)計(jì)為final類(lèi)
Note:同時(shí)被static和final修飾的變量,必須在聲明時(shí)立即賦值盗飒。
可以同時(shí)使用static和final來(lái)修飾方法嚷量,但是意義不大。因?yàn)楸籹tatic修飾的方法本身就是無(wú)
法被繼承的逆趣。
10. HashMap的實(shí)現(xiàn)原理
本人已經(jīng)分析了HashMap的實(shí)現(xiàn)原理蝶溶,請(qǐng)移步。HashMap-你可能需要知道這些
11. HashMap和HashSet的區(qū)別
HashMap | HashSet |
---|---|
HashMap實(shí)現(xiàn)了Map接口 | HashSet實(shí)現(xiàn)了Set接口 |
HashMap存儲(chǔ)鍵值對(duì) | HashSet存儲(chǔ)對(duì)象 |
使用put方法添加值 | 使用add方法添加值 |
HashMap中使用鍵對(duì)象來(lái)計(jì)算hashcode值 | HashSet使用成員對(duì)象來(lái)計(jì)算hashcode值宣渗,對(duì)于兩個(gè)對(duì)象來(lái)說(shuō)hashcode可能相同抖所,所以equals()方法用來(lái)判斷對(duì)象的相等性,如果兩個(gè)對(duì)象不同的話痕囱,那么返回false |
HashMap比較快田轧,因?yàn)槭鞘褂梦ㄒ坏逆I來(lái)獲取對(duì)象 | HashSet較HashMap來(lái)說(shuō)比較慢 |
HashSet取值的時(shí)候部分情況會(huì)比HashMap快,因?yàn)镠ashSet不允許重復(fù)值鞍恢,hash到那個(gè)地方直接取值了傻粘,而HashMap有可能有下拉鏈表,這樣就要再遍歷鏈表了帮掉;算法兩者用的都是hash算法弦悉,而hashMap可能多一步鏈表遍歷,所以肯定是HashSet部分情況比hashMap快蟆炊。
HashSet不允許存在重復(fù)的值稽莉。(hashCode和equals來(lái)判斷)
12. 淺拷貝&深拷貝區(qū)別
淺拷貝:使用一個(gè)已知實(shí)例對(duì)新創(chuàng)建實(shí)例的成員變量逐個(gè)賦值,這個(gè)方式被稱為淺拷貝涩搓。
深拷貝:當(dāng)一個(gè)類(lèi)的拷貝構(gòu)造方法污秆,不僅要復(fù)制對(duì)象的所有非引用成員變量值,還要為引用類(lèi)型的成員變量創(chuàng)建新的實(shí)例昧甘,并且初始化為形式參數(shù)實(shí)例值混狠,這個(gè)方式稱為深拷貝。
也就是說(shuō)淺拷貝只復(fù)制一個(gè)對(duì)象疾层,傳遞引用,不能復(fù)制實(shí)例贡避。而深拷貝對(duì)對(duì)象內(nèi)部的引用均復(fù)制痛黎,它是創(chuàng)建一個(gè)新的實(shí)例,并且復(fù)制實(shí)例刮吧。
深拷貝實(shí)現(xiàn)方式:
- 對(duì)其引用變量逐個(gè)深拷貝并復(fù)制給新對(duì)象湖饱。
- 實(shí)現(xiàn)序列化接口,并結(jié)合流進(jìn)行寫(xiě)入和讀出杀捻。(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)代理&動(dòng)態(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)
總結(jié)的話有三種:
- resource器赞!=null,root==null,attachToRott=false,返回解析到的resource的布局,布局參數(shù)無(wú)效墓拜。
- 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,還擔(dān)任了一部分Controller的職責(zé)
- 請(qǐng)求的業(yè)務(wù)代碼往往被丟到了Activity里面嘴拢,布局文件只能提供默認(rèn)的UI設(shè)置桩盲,所以開(kāi)發(fā)中視圖層的變化也被丟到了Activity里面。
- 再加上Activity本身承擔(dān)著控制層的責(zé)任席吴。所以Activity達(dá)成了MVC集合的成就,實(shí)現(xiàn)了代碼的耦合赌结,最終我們的Activity就變得越來(lái)越難看,從幾百行變成了幾千行孝冒。維護(hù)的成本也越來(lái)越高柬姚。
MVP:
MVP與MVC最大的不同,其實(shí)是Activity職責(zé)的變化庄涡,由原來(lái)的C (控制層) 變成了 V(視圖層)量承,不再管控制層的問(wèn)題,只管如何去顯示穴店。
控制層的角色就由我們的新人 Presenter來(lái)?yè)?dān)當(dāng)撕捍,這種架構(gòu)就解決了Activity過(guò)度耦合控制層和視圖層的問(wèn)題。
16. Activity有沒(méi)有事件分發(fā)機(jī)制泣洞?
有忧风,必須有。
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
Activity是事件分發(fā)的起點(diǎn)球凰,只有Activity不攔截處理事件狮腿,事件才會(huì)分發(fā)至Window--DecorView--View腿宰。
17. 引用的分類(lèi)和區(qū)別
引用主要分為四種類(lèi)型:
強(qiáng)引用
Object object = new Object(),object就是一個(gè)強(qiáng)引用了缘厢。
當(dāng)內(nèi)存空間不足吃度,Java虛擬機(jī)寧愿拋出OutOfMemoryError錯(cuò)誤,使程序異常終止昧绣,也不會(huì)靠隨意回收具有強(qiáng)引用的對(duì)象來(lái)解決內(nèi)存不足問(wèn)題规肴。軟引用
只有內(nèi)存不夠時(shí)才回收,常用于緩存;當(dāng)內(nèi)存達(dá)到一個(gè)閥值夜畴,GC就會(huì)去回收它拖刃;弱引用
弱引用的對(duì)象擁有更短暫的生命周期。
在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過(guò)程中贪绘,一旦發(fā)現(xiàn)了只具有弱引用的對(duì)象兑牡,不管當(dāng)前內(nèi)存空間足夠與否,都會(huì)回收它的內(nèi)存税灌。虛引用
"虛引用"顧名思義均函,就是形同虛設(shè),與其他幾種引用都不同菱涤,虛引用并不會(huì)決定對(duì)象的生命周期苞也。如果一個(gè)對(duì)象僅持有虛引用,那么它就和沒(méi)有任何引用一樣粘秆,在任何時(shí)候都可能被垃圾回收如迟。
18. Synchronized
作用位置:
- synchronized 方法
- synchronized static 方法
- 代碼塊
synchronized方法:
- synchronized方法控制對(duì)類(lèi)成員變量的訪問(wèn).
- 每個(gè)類(lèi)實(shí)例對(duì)應(yīng)一把鎖,每個(gè)synchronized方法都必須獲得調(diào)用該方法的類(lèi)實(shí)例的鎖方能執(zhí)行攻走,否則所屬線程阻塞殷勘,方法一旦執(zhí)行,就獨(dú)占該鎖昔搂,直到從該方法返回時(shí)才將鎖釋放玲销,此后被阻塞的線程方能獲得該鎖,重新進(jìn)入可執(zhí)行狀態(tài).
- 同一時(shí)刻對(duì)于每一個(gè)類(lèi)實(shí)例摘符,其所有聲明為 synchronized 的成員方法中至多只有一個(gè)處于可執(zhí)行狀態(tài)(因?yàn)橹炼嘀挥幸粋€(gè)能夠獲得該類(lèi)實(shí)例對(duì)應(yīng)的鎖)贤斜,從而有效避免了類(lèi)成員變量的訪問(wèn)沖突(只要所有可能訪問(wèn)類(lèi)成員變量的方法均被聲明為 synchronized.
synchronized static 方法:
某個(gè)類(lèi)的范圍,synchronized static aStaticMethod{}防止多個(gè)線程同時(shí)訪問(wèn)這個(gè)類(lèi)中的synchronized static 方法逛裤。它可以對(duì)類(lèi)的所有對(duì)象實(shí)例起作用蠢古。
Class Foo {
// 同步的static 函數(shù)
public synchronized static void methodAAA() {
//….
}
public void methodBBB() {
synchronized(Foo.class) // class literal(類(lèi)名稱字面常量)
}
}
代碼中的methodBBB()方法是把class literal作為鎖的情況,它和同步的static函數(shù)產(chǎn)生的效果是一樣的别凹,取得的鎖很特別,是當(dāng)前調(diào)用這個(gè)方法的對(duì)象所屬的類(lèi)(Class洽糟,而不再是由這個(gè)Class產(chǎn)生的某個(gè)具體對(duì)象了)炉菲。
synchronized代碼塊:
- 一個(gè)時(shí)間內(nèi)只能有一個(gè)線程得到執(zhí)行堕战。另一個(gè)線程必須等待當(dāng)前線程執(zhí)行完這個(gè)代碼塊以后才能執(zhí)行該代碼塊。
- 當(dāng)一個(gè)線程訪問(wèn)object的一個(gè)synchronized(this)同步代碼塊時(shí)拍霜,另一個(gè)線程仍然可以訪問(wèn)該object中的非synchronized(this)同步代碼塊嘱丢。
- 一個(gè)線程訪問(wèn)object的一個(gè)synchronized(this)同步代碼塊時(shí),其他線程對(duì)object中所有其它synchronized(this)同步代碼塊的訪問(wèn)將被阻塞祠饺。
- 也就是說(shuō)越驻,當(dāng)一個(gè)線程訪問(wèn)object的一個(gè)synchronized(this)同步代碼塊時(shí),它就獲得了這個(gè)object的對(duì)象鎖道偷。結(jié)果缀旁,其它線程對(duì)該object對(duì)象所有同步代碼部分的訪問(wèn)都被暫時(shí)阻塞。
19. 解耦的本質(zhì)
設(shè)計(jì)模式中的幾大原則:
- SRP:單一職責(zé)原則勺鸦,一個(gè)類(lèi)應(yīng)該僅有一個(gè)引起它變化的原因并巍。
- OCP:開(kāi)閉原則,對(duì)于擴(kuò)展開(kāi)發(fā)换途,對(duì)于修改封閉懊渡。
- 里氏替換原則,所有使用基類(lèi)的地方必須能透明的使用其子類(lèi)對(duì)象军拟。
- DIP:依賴倒置原則剃执。指代了一種特定的解耦形式,使得高層次的模塊不依賴于低層次模塊的實(shí)現(xiàn)細(xì)節(jié)懈息,依賴模塊被顛倒了肾档。
a. 高層模塊不應(yīng)該依賴于低層次模塊,兩者都應(yīng)該依賴于抽象漓拾。
b. 抽象不應(yīng)該依賴于細(xì)節(jié)阁最,細(xì)節(jié)應(yīng)該依賴于抽象。
總結(jié):模塊間的依賴通過(guò)抽象發(fā)生骇两,實(shí)現(xiàn)類(lèi)不應(yīng)該發(fā)生直接的依賴關(guān)系速种,依賴關(guān)系是通過(guò)接口或者抽象實(shí)現(xiàn)。 - ISP:接口隔離原則低千,類(lèi)之間的依賴關(guān)系應(yīng)該建立在最小的接口之上配阵。
- LOD:迪米特原則,一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象有最少的了解示血。
那么很明確了棋傍,解耦的本質(zhì)就是DIP原則。
20. Android運(yùn)行時(shí)權(quán)限
權(quán)限分類(lèi)
危險(xiǎn)權(quán)限
phone
讀寫(xiě)外部存儲(chǔ)
camera
如果你申請(qǐng)某個(gè)危險(xiǎn)的權(quán)限难审,假設(shè)你的app早已被用戶授權(quán)了同一組的某個(gè)危險(xiǎn)權(quán)限瘫拣,那么系統(tǒng)會(huì)立即授權(quán),而不需要用戶去點(diǎn)擊授權(quán)告喊。
彈出的權(quán)限請(qǐng)求dialog描述的是一組權(quán)限麸拄,無(wú)法定制
正常權(quán)限
獲取網(wǎng)絡(luò)狀態(tài)
如何檢查和申請(qǐng)
檢查
checkSlefPermission
deny
grant
請(qǐng)求
requestPermissions
異步方法
處理的回調(diào)方法:onRequestPermissionsResult
解釋
shouldShowRequestPermissionRationale
只有在第一次用戶已經(jīng)拒絕情況下派昧,該方法才會(huì)返回true
開(kāi)源框架
MPermissions
21. Activity-Window-View
- 每個(gè)Activity在創(chuàng)建的時(shí)候,都會(huì)產(chǎn)生一個(gè)Window對(duì)象拢切,這個(gè)Window對(duì)象實(shí)際為PhoneWindow蒂萎。
- PhoneWindow對(duì)象包含一個(gè)根View:DecorView,Activity要顯示的內(nèi)容就包含在DecorView中淮椰。
setContentView的顯示流程
Activity setContentView->PhoneWindow setContentView ->mLayoutInflater.inflate(layoutResID, mContentParent) -> onResume時(shí)將DecorView添加至WindowManager ->WindowManagerImpl.addView--ViewRootImpl.setView ->1. ViewRootImpl.requestLayout -> 2. 向WMS發(fā)起顯示W(wǎng)indow的請(qǐng)求
22. Activity與Fragment通信方式
- Handler
- 接口
- EventBus
- 廣播
23. Fragment的坑
getActivity為空
在Fragment基類(lèi)里設(shè)置一個(gè)Activity mActivity的全局變量五慈,在onAttach(Activity activity)里賦值,使用mActivity代替getActivity()主穗,保證Fragment即使在onDetach后泻拦,仍持有Activity的引用(有引起內(nèi)存泄露的風(fēng)險(xiǎn),但是異步任務(wù)沒(méi)停止的情況下黔牵,本身就可能已內(nèi)存泄漏聪轿,相比Crash,這種做法“安全”些)
Can not perform this action after onSaveInstanceState
在重新回到該Activity的時(shí)候(onResumeFragments()或onPostResume())猾浦,再執(zhí)行該事務(wù)陆错!
Fragment重疊異常
在類(lèi)onCreate()的方法加載Fragment,并且沒(méi)有判斷saveInstanceState==null或if(findFragmentByTag(mFragmentTag) == null)金赦,導(dǎo)致重復(fù)加載了同一個(gè)Fragment導(dǎo)致重疊音瓷。
24. 在向Fragment傳遞參數(shù)時(shí),為什么不采用構(gòu)造方法中傳遞夹抗?
- 如果通過(guò)構(gòu)造參數(shù)進(jìn)行傳遞绳慎,那么在Fragment銷(xiāo)毀重建時(shí)參數(shù)就無(wú)法保持了。
- 一般通過(guò)setArguments傳遞參數(shù)漠烧,從而在Fragment銷(xiāo)毀重建時(shí)保持?jǐn)?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);
// 恢復(fù)保存的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) {
// 恢復(fù)視圖狀態(tài)
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
mWindow.restoreHierarchyState(windowState);
}
}
}
從上面兩個(gè)接口我們可以看到已脓,在Activity銷(xiāo)毀重建時(shí)會(huì)保存恢復(fù)Fragment的狀態(tài)珊楼,但是我們還是沒(mé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;
// 構(gòu)造待保存Fragment的狀態(tài)信息(變量)
// FragmentState會(huì)記錄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;
}
恢復(fù)信息:
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);
}
// 恢復(fù)Fragment
// 通過(guò)FragmentState來(lái)生成新的Fragment厕宗,之前保存到惡狀態(tài)信息得以恢復(fù)
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?
===提供了插拔式體驗(yàn),高度解耦
===內(nèi)部實(shí)現(xiàn)了ViewHolder機(jī)制堕担,因此使用上更方便
- 你想要控制其顯示的方式已慢,請(qǐng)通過(guò)布局管理器LayoutManager
- 你想要控制Item間的間隔(可繪制),請(qǐng)通過(guò)ItemDecoration
- 你想要控制Item增刪的動(dòng)畫(huà)霹购,請(qǐng)通過(guò)ItemAnimator
- 你想要控制點(diǎn)擊佑惠、長(zhǎng)按事件,請(qǐng)自己寫(xiě)(擦,這點(diǎn)尼瑪膜楷。)
Adapter的幾個(gè)主要方法:
- public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
- public abstract void onBindViewHolder(VH holder, int position);