Android面試點(diǎn)總結(jié)

為了面試隔节,為了高工資舞竿,廢話不多說(shuō),不定期更新。

1. Activity正常和異常情況下的生命周期分析星掰。

Activity生命周期是一個(gè)老生常談的問(wèn)題,但是有些刁鉆的問(wèn)題也會(huì)一時(shí)半會(huì)的影響大家的思路嫩舟,我們還是耐著性子來(lái)分析下吧氢烘。

I.正常生命周期

  1. 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的顯示饭于。

  1. onStart

在Activity即將對(duì)用戶可見(jiàn)之前調(diào)用(該接口與onResume對(duì)應(yīng)蜀踏,onResume代表已經(jīng)可見(jiàn)了)维蒙,可以理解為已經(jīng)在后臺(tái)準(zhǔn)備就緒了。

  1. onResume

在Activity即將開(kāi)始與用戶進(jìn)行交互前調(diào)用果覆,此時(shí)Activity處于Activity堆棧的頂層颅痊,并具有用戶輸入焦點(diǎn)。

  1. 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。

  1. onStop

在Activity對(duì)用戶完全不可見(jiàn)時(shí)調(diào)用昭卓,可以認(rèn)為處于后臺(tái)愤钾。

  1. onDestory

在Activity被銷(xiāo)毀前調(diào)用,這是Activity將收到的最后的調(diào)用候醒。
一般會(huì)在此銷(xiāo)毀啟動(dòng)的線程能颁,或者處理一些可能導(dǎo)致內(nèi)存泄露的問(wèn)題。

  1. 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.異常生命周期

  1. 資源配置發(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)嘴办,這是一種典型的委托思想瞬场,上層委托下層,父容器委托子容器去處理一些事情涧郊。

  1. 資源或者內(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)模式的指定存在兩種方式:

  1. launchMode指定:無(wú)法指定FLAG_ACTIVITY_CLEAR_TOP.
  2. 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)程毙疗迹活

參考鏈接:Android 進(jìn)程泵蹑ⅲ活招式大全

進(jìn)程毕勖辏活包括兩個(gè)方面:

  1. 提升進(jìn)程優(yōu)先級(jí),降低進(jìn)程被殺死的概率悯许。
  2. 進(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)程的優(yōu)先級(jí),進(jìn)程分為以下五類(lèi):

  1. 前臺(tái)進(jìn)程
  2. 可見(jiàn)進(jìn)程
  3. 服務(wù)進(jìn)程
  4. 后臺(tái)進(jìn)程
  5. 空進(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)程被殺死的情況

綜上,可以得出減少進(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):

  1. 廣播接收器被管理軟件厅贪、系統(tǒng)軟件通過(guò)“自啟管理”等功能禁用的場(chǎng)景無(wú)法接收到廣播蠢护,從而無(wú)法自啟。
  2. 系統(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)廣播一樣的因素外皆刺,主要受如下因素限制:

  1. 反編譯分析過(guò)的第三方應(yīng)用的多少
  2. 第三方應(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ú)法拉活

  1. Service 第一次被異常殺死后會(huì)在5秒內(nèi)重啟煎殷,第二次被殺死會(huì)在10秒內(nèi)重啟,第三次會(huì)在20秒內(nèi)重啟腿箩,一旦在短時(shí)間內(nèi) Service 被殺死達(dá)到5次豪直,則系統(tǒng)不再拉起。

  2. 進(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)方式:

  1. 在 Native 進(jìn)程中通過(guò)死循環(huán)或定時(shí)器涂乌,輪訓(xùn)判斷主進(jìn)程是否存活艺栈,當(dāng)主進(jìn)程不存活時(shí)進(jìn)行拉活。該方案的很大缺點(diǎn)是不停的輪詢執(zhí)行判斷邏輯湾盒,非常耗電湿右。

  2. 在主進(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ū)別:

  1. HandlerThread是Thread的子類(lèi)。
  2. HandlerThread內(nèi)部持有一個(gè)Looper鸳兽,可以使用MessageQueue重復(fù)使用當(dāng)前線程掂铐,節(jié)省系統(tǒng)開(kāi)銷(xiāo)。
  3. 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)方式。

  1. 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);
        }
    }


  1. 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é)凉泄。

  1. HandlerThread是Thread的子類(lèi)。
    2.HandlerThread內(nèi)部持有一個(gè)Looper蚯根,可以使用MessageQueue重復(fù)使用當(dāng)前線程后众,節(jié)省系統(tǒng)開(kāi)銷(xiāo)。
  2. 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)可分兩種:

  1. 一種是被static修飾的變量,叫靜態(tài)變量或類(lèi)變量痊臭。
  2. 另一種是沒(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è)地方:

  1. 一是其定義處蜀备,也就是說(shuō)在final變量定義時(shí)直接給其賦值
  2. 二是在構(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方法

  1. 把方法鎖定鹅很,防止任何繼承類(lèi)修改它的意義和實(shí)現(xiàn)嘶居。
  2. 高效。編譯器在遇到調(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)方式:

  1. 對(duì)其引用變量逐個(gè)深拷貝并復(fù)制給新對(duì)象湖饱。
  2. 實(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的獲取:

  1. LayoutInflater.from(context)
  2. LayoutInflater LayoutInflater =
    (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

第一種方式是封裝了第二種方式仅仆。

inflate方法:

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) 

總結(jié)的話有三種:

  1. resource器赞!=null,root==null,attachToRott=false,返回解析到的resource的布局,布局參數(shù)無(wú)效墓拜。
  2. resource港柜!=null,root咳榜!=null,attachToRott=false夏醉,返回解析到的resource的布局,布局參數(shù)有效涌韩。
  3. resource畔柔!=null,root臣樱!=null,attachToRott=true靶擦,返回root,resource被添加到root中擎淤,布局參數(shù)有效奢啥。

15.MVC與MVP的區(qū)別

MVC:

  1. Activity不僅要顯示UI,還擔(dān)任了一部分Controller的職責(zé)
  2. 請(qǐng)求的業(yè)務(wù)代碼往往被丟到了Activity里面嘴拢,布局文件只能提供默認(rèn)的UI設(shè)置桩盲,所以開(kāi)發(fā)中視圖層的變化也被丟到了Activity里面。
  3. 再加上Activity本身承擔(dān)著控制層的責(zé)任席吴。所以Activity達(dá)成了MVC集合的成就,實(shí)現(xiàn)了代碼的耦合赌结,最終我們的Activity就變得越來(lái)越難看,從幾百行變成了幾千行孝冒。維護(hù)的成本也越來(lái)越高柬姚。

MVP:

  1. MVP與MVC最大的不同,其實(shí)是Activity職責(zé)的變化庄涡,由原來(lái)的C (控制層) 變成了 V(視圖層)量承,不再管控制層的問(wèn)題,只管如何去顯示穴店。

  2. 控制層的角色就由我們的新人 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)型:

  1. 強(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)題规肴。

  2. 軟引用
    只有內(nèi)存不夠時(shí)才回收,常用于緩存;當(dāng)內(nèi)存達(dá)到一個(gè)閥值夜畴,GC就會(huì)去回收它拖刃;

  3. 弱引用
    弱引用的對(duì)象擁有更短暫的生命周期。
    在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過(guò)程中贪绘,一旦發(fā)現(xiàn)了只具有弱引用的對(duì)象兑牡,不管當(dāng)前內(nèi)存空間足夠與否,都會(huì)回收它的內(nèi)存税灌。

  4. 虛引用
    "虛引用"顧名思義均函,就是形同虛設(shè),與其他幾種引用都不同菱涤,虛引用并不會(huì)決定對(duì)象的生命周期苞也。如果一個(gè)對(duì)象僅持有虛引用,那么它就和沒(méi)有任何引用一樣粘秆,在任何時(shí)候都可能被垃圾回收如迟。


18. Synchronized

作用位置:

  1. synchronized 方法
  2. synchronized static 方法
  3. 代碼塊

synchronized方法:

  1. synchronized方法控制對(duì)類(lèi)成員變量的訪問(wèn).
  2. 每個(gè)類(lèi)實(shí)例對(duì)應(yīng)一把鎖,每個(gè)synchronized方法都必須獲得調(diào)用該方法的類(lèi)實(shí)例的鎖方能執(zhí)行攻走,否則所屬線程阻塞殷勘,方法一旦執(zhí)行,就獨(dú)占該鎖昔搂,直到從該方法返回時(shí)才將鎖釋放玲销,此后被阻塞的線程方能獲得該鎖,重新進(jìn)入可執(zhí)行狀態(tài).
  3. 同一時(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代碼塊:

  1. 一個(gè)時(shí)間內(nèi)只能有一個(gè)線程得到執(zhí)行堕战。另一個(gè)線程必須等待當(dāng)前線程執(zhí)行完這個(gè)代碼塊以后才能執(zhí)行該代碼塊。
  2. 當(dāng)一個(gè)線程訪問(wèn)object的一個(gè)synchronized(this)同步代碼塊時(shí)拍霜,另一個(gè)線程仍然可以訪問(wèn)該object中的非synchronized(this)同步代碼塊嘱丢。
  3. 一個(gè)線程訪問(wèn)object的一個(gè)synchronized(this)同步代碼塊時(shí),其他線程對(duì)object中所有其它synchronized(this)同步代碼塊的訪問(wèn)將被阻塞祠饺。
  4. 也就是說(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ì)模式中的幾大原則:

  1. SRP:單一職責(zé)原則勺鸦,一個(gè)類(lèi)應(yīng)該僅有一個(gè)引起它變化的原因并巍。
  2. OCP:開(kāi)閉原則,對(duì)于擴(kuò)展開(kāi)發(fā)换途,對(duì)于修改封閉懊渡。
  3. 里氏替換原則,所有使用基類(lèi)的地方必須能透明的使用其子類(lèi)對(duì)象军拟。
  4. 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)。
  5. ISP:接口隔離原則低千,類(lèi)之間的依賴關(guān)系應(yīng)該建立在最小的接口之上配阵。
  6. 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

  1. 每個(gè)Activity在創(chuàng)建的時(shí)候,都會(huì)產(chǎn)生一個(gè)Window對(duì)象拢切,這個(gè)Window對(duì)象實(shí)際為PhoneWindow蒂萎。
  2. 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通信方式

  1. Handler
  2. 接口
  3. EventBus
  4. 廣播

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)造方法中傳遞夹抗?

  1. 如果通過(guò)構(gòu)造參數(shù)進(jìn)行傳遞绳慎,那么在Fragment銷(xiāo)毀重建時(shí)參數(shù)就無(wú)法保持了。
  2. 一般通過(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

  1. 為什么要使用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);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乍丈,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子把将,更是在濱河造成了極大的恐慌,老刑警劉巖忆矛,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡餐抢,警方通過(guò)查閱死者的電腦和手機(jī)偿衰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)漫拭,“玉大人亚兄,你說(shuō)我怎么就攤上這事〔勺ぃ” “怎么了审胚?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)礼旅。 經(jīng)常有香客問(wèn)我膳叨,道長(zhǎng),這世上最難降的妖魔是什么痘系? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任菲嘴,我火速辦了婚禮,結(jié)果婚禮上汰翠,老公的妹妹穿的比我還像新娘龄坪。我一直安慰自己,他們只是感情好复唤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布健田。 她就那樣靜靜地躺著,像睡著了一般苟穆。 火紅的嫁衣襯著肌膚如雪抄课。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,155評(píng)論 1 299
  • 那天雳旅,我揣著相機(jī)與錄音跟磨,去河邊找鬼。 笑死攒盈,一個(gè)胖子當(dāng)著我的面吹牛抵拘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播型豁,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼僵蛛,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼尚蝌!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起充尉,我...
    開(kāi)封第一講書(shū)人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤飘言,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后驼侠,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體姿鸿,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年倒源,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了苛预。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡笋熬,死狀恐怖昔馋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情妥粟,我是刑警寧澤播急,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布飞崖,位于F島的核電站康聂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏约巷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一旱捧、第九天 我趴在偏房一處隱蔽的房頂上張望独郎。 院中可真熱鬧,春花似錦枚赡、人聲如沸氓癌。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)贪婉。三九已至,卻和暖如春卢肃,著一層夾襖步出監(jiān)牢的瞬間疲迂,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工莫湘, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留尤蒿,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓逊脯,卻偏偏與公主長(zhǎng)得像优质,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353