Android 匯總:基礎(chǔ)知識

大綱

Android 知識匯總

Java 基礎(chǔ)

1. ==是趴、equals 和 hashCode 的區(qū)別

== 用于基礎(chǔ)數(shù)據(jù)類型的判斷時滞详,比較的是值,用于引用類型的判斷時拂共,比較的是對象在內(nèi)存中的存放地址牺弄。這是因?yàn)閷ο笫欠旁诙阎械模瑮V写娣诺氖菍ο蟮牡刂芬撕?== 是對棧中的值進(jìn)行比較的势告。

如果要比較堆中對象的內(nèi)容是否相同,就要重寫 equals抚恒。Object 中的 equals 方法同 ==咱台,通過覆蓋該方法來實(shí)現(xiàn)具體功能。

集合查找對象時俭驮,需要使用 equals 遍歷比較回溺,數(shù)據(jù)較多時效率很低,為了提高查找對象的效率混萝,誕生了 hashCode遗遵。對象的 hashCode 有如下特性:

對象相同,hashCode 一定相同譬圣;
hashCode 相同瓮恭,對象不一定相同雄坪。
依據(jù)此特性厘熟,在存對象時屯蹦,可以直接根據(jù) hashCode 值映射出對象應(yīng)該存儲的地址,如果該地址沒對象绳姨,則直接存儲登澜,如果已經(jīng)有 hashCode 值相同的對象存在,則調(diào)用 equals 函數(shù)比較飘庄,相同則丟棄脑蠕,不同則生成鏈表存儲起來。

這樣存儲跪削,相當(dāng)于把 hashCode 值相同的對象放在了一個桶里谴仙,每次查找對象時,直接從目標(biāo)桶里挨個查找想要的對象碾盐,大大提高了查找效率晃跺。

因此,在重寫對象的 equals 函數(shù)時毫玖,必須重寫其 hashCode 函數(shù)掀虎,如果不重寫,在 HashSet 等散列集合存儲時付枫,會因存放桶的不同烹玉,而存儲了 equals 相同的多個對象,這并不是我們想要的結(jié)果阐滩。

在重寫 hashCode 時二打,遵循倆個原則:

倆個 equals 相同的對象,hashCode 返回值一定相同(定位到正確的鏈表掂榔,保證正確去重)址儒;
倆個 equals 不同的對象,hashCode 可能相同衅疙,但是要盡可能保證桶的均勻莲趣,這樣才能達(dá)到最大查找效率。

2. 基礎(chǔ)數(shù)據(jù)類型各占多少字節(jié)

基礎(chǔ)類型 字節(jié)數(shù)
byte 1
boolean 1
char 2
short 2
int 4
float 4
long 8
double 8

3. 對多態(tài)的理解

多態(tài)指允許不同類的對象對同一消息作出響應(yīng)饱溢。即同一消息可以根據(jù)發(fā)送對象的不同而采用多種不同的行為方式喧伞。

實(shí)現(xiàn)多態(tài)的技術(shù)稱為動態(tài)綁定,是指在執(zhí)行期間判斷所引用對象的實(shí)際類型绩郎,根據(jù)實(shí)際類型調(diào)用相應(yīng)方法潘鲫。

Java 的倆種多態(tài)是指:運(yùn)行時多態(tài)和編譯時多態(tài)。多態(tài)是面向?qū)ο蟮暮诵奶卣髦焕哒龋惖亩鄳B(tài)性提供類中成員設(shè)計的靈活性和方法執(zhí)行的多樣性溉仑。

4. String、StringBuffer 與 StringBuilder 的區(qū)別

String 的值是不可變的状植,每次對 String 的操作都會生成新的 String 對象浊竟,效率低且浪費(fèi)內(nèi)存空間怨喘。當(dāng)對字符串進(jìn)行修改時,可以使用 StringBuffer 和 StringBuilder振定。StringBuffer 多線程安全必怜,而 StringBuilder 適用于單線程且效率更高。

引申:String 為什么要設(shè)計成不可變的后频?

  1. Java 中的字符串常量池梳庆,保證了當(dāng)創(chuàng)建一個 String 對象時,假如值已存在于常量池中卑惜,則不會創(chuàng)建新的對象膏执,而是引用已經(jīng)存在的。這是一種常見的優(yōu)化方案露久“幕基于此风范,假如字符串對象允許改變,則改變一個對象會影響到另一個獨(dú)立對象。
  2. 安全性上講仑性,String 不可變更有利于方法參數(shù)傳遞裙戏。
  3. 字符串不變性保證了 hash 碼的唯一性孵构,可以放心進(jìn)行緩存而不必每次都去計算新的哈希碼恍飘。

引申參考鏈接和例子

5. 父類的靜態(tài)方法能否被子類重寫

不能

重寫指根據(jù)運(yùn)行時對象的類型來決定調(diào)用哪個方法(動態(tài)綁定),而靜態(tài)變量和靜態(tài)方法是在編譯時與類綁定的梯啤。

6. 線程和進(jìn)程的區(qū)別

  1. 進(jìn)程是資源分配的最小單位竖伯,線程是程序執(zhí)行的最小單位(資源調(diào)度的最小單位)。
  2. 進(jìn)程有自己的獨(dú)立地址空間因宇,每啟動一個進(jìn)程七婴,系統(tǒng)就會為它分配地址空間,建立數(shù)據(jù)表來維護(hù)代碼段察滑、堆棧段和數(shù)據(jù)段,這種操作非常昂貴贺辰。
    而線程是共享進(jìn)程中的數(shù)據(jù)的户盯,使用相同的地址空間,因此 CPU 切換一個線程的花費(fèi)遠(yuǎn)比進(jìn)程要小很多饲化,同時創(chuàng)建一個線程的開銷也比進(jìn)程要小很多莽鸭。
  3. 線程之間的通信更方便,同一進(jìn)程下的線程共享全局變量吃靠、靜態(tài)變量等數(shù)據(jù)硫眨,而進(jìn)程之間的通信需要以通信的方式(IPC)進(jìn)行。
  4. 多進(jìn)程程序更健壯巢块,多線程程序只要有一個線程死掉礁阁,整個進(jìn)程也死掉了巧号,而一個進(jìn)程死掉并不會對另外一個進(jìn)程造成影響,因?yàn)檫M(jìn)程有自己獨(dú)立的地址空間氮兵。

引申:線程間和進(jìn)程間同步的方式裂逐。

線程間同步:

  1. 同步代碼塊歹鱼;
  2. 同步方法泣栈;
  3. 互斥鎖 ReetrantLock;

進(jìn)程間同步:

  1. FileLock

同步時會阻塞當(dāng)前線程弥姻,直到喚醒為止南片。異步則可以繼續(xù)干其它事情,其它線程完成任務(wù)時通知即可庭敦。要分清楚倆者的概念疼进。

引申:線程間和進(jìn)程間通信的方式。

線程間通信:

  1. Handler
  2. Activity.runOnUiThread
  3. View.post
  4. AsyncTask

進(jìn)程間通信:

  1. 共享內(nèi)存
  2. 管道
  3. UDS
  4. RPC

Android 進(jìn)程間通信:

  1. Intent
  2. 文件共享
  3. Message
  4. AIDL
  5. ContentProvider
  6. Broadcast
  7. Socket
  8. Binder

完整RPC通信過程如下:
a. 客戶端調(diào)用 Stub 接口秧廉;
b. Stub 根據(jù)操作系統(tǒng)的要求進(jìn)行打包伞广,并執(zhí)行相應(yīng)的系統(tǒng)調(diào)用;
c. 服務(wù)器端 Stub 解包并調(diào)用與數(shù)據(jù)包匹配的進(jìn)程疼电;
d. 進(jìn)程執(zhí)行操作嚼锄;
e. 服務(wù)器以上述步驟的逆向過程將結(jié)果返回給客戶端。

引申參考鏈接

7. final蔽豺、finally区丑、finalize 的區(qū)別

final:

java 中的關(guān)鍵字、修飾符修陡。被 final 修飾的:

  1. 類不能再派生子類沧侥,不能作為父類被繼承。因此魄鸦,一個類不能同時被聲明為抽象類和 final 類宴杀。
  2. 變量必須在聲明時給定初值,而在以后的引用中只能讀取拾因,不可修改婴氮。
  3. 方法只能使用,不能重載盾致。

finally:

Java 的一種異常處理機(jī)制主经,是對 Java 異常處理模型的補(bǔ)充。finally 結(jié)構(gòu)使代碼總會執(zhí)行庭惜,而不管無異常發(fā)生罩驻。使用 finally 可以維護(hù)對象的內(nèi)部狀態(tài),并可以清理非內(nèi)存資源护赊。特別是在關(guān)閉數(shù)據(jù)庫連接這方面惠遏,如果程序員把數(shù)據(jù)庫連接的 close() 方法放到 finally 中砾跃,就會大大降低程序出錯的幾率。

finalize:

Java 使用 finalize 方法在垃圾收集器將對象從內(nèi)存中清除出去前节吮,做必要的清理工作抽高。這個方法是由垃圾收集器在確定這個對象沒被引用時對這個對象調(diào)用的。它是在 Object 類中定義的透绩,因此所有類都繼承了它翘骂。子類覆蓋 finalize() 方法以整理系統(tǒng)資源或者執(zhí)行其他清理工作。

8. Serializable 和 Parcelable 的區(qū)別

使用序列化一般有以下三種用途:

  1. 本地文件保存對象帚豪;
  2. 網(wǎng)絡(luò)傳遞對象碳竟;
  3. 進(jìn)程間傳遞對象。

Serializable 是 Java 的序列化技術(shù)狸臣。
使用簡單莹桅,對序列化的 class 增加繼承,并添加一個序列化 id 即可烛亦。手動設(shè)置序列化 id 的好處是诈泼,當(dāng)前 class 的成員變量發(fā)生改變時(比如增刪),會影響自動生成的 id煤禽,從而導(dǎo)致反序列化失敗铐达,通常情況下手動或自動設(shè)置均可。
Serializable 的原理是通過本地讀寫文件實(shí)現(xiàn)呜师,會產(chǎn)生大量的臨時變量,占用內(nèi)存高且可能引發(fā) GC知牌。
當(dāng)一個父類實(shí)現(xiàn)序列化扁藕,子類自動實(shí)現(xiàn)序列化,不需要再顯式實(shí)現(xiàn) Serializable 接口。

Parcelable 是 Android 特有的序列化 API,雖然使用較為繁瑣板熊,但是效率高容劳、占用內(nèi)存少留量。
原理是在內(nèi)存中建立一塊共享數(shù)據(jù)塊浩峡,序列化和反序列化均是操作這塊的數(shù)據(jù)纸淮。
當(dāng)內(nèi)存使用場景為主時(如通過共享內(nèi)存實(shí)現(xiàn) IPC 通信),推薦使用 Parcelable塘辅,因?yàn)樾阅芨呱胩琛5?Parcelable 不適合持久化濒持,原因是不安全(增刪變量會導(dǎo)致無法升級)酒奶,且有版本兼容問題,至于效率問題則有爭議。

Parcelable

9. 內(nèi)部類的設(shè)計意圖及種類

參考鏈接

10. 鎖機(jī)制相關(guān)知識:synchronized臼朗、volatile 和 Lock

Java 鎖機(jī)制詳解(一)synchronized
Java 鎖機(jī)制詳解(二)volatile
Java 鎖機(jī)制詳解(三)Lock

Lock 手寫實(shí)現(xiàn)倆個線程交替、多個線程順序執(zhí)行

進(jìn)入 synchronized 代碼塊前如果拿不到鎖耗绿,則進(jìn)入鎖池等待下次競爭堕绩;
進(jìn)入 synchronized 代碼塊后如果調(diào)用對象的 wait浅浮,則線程釋放鎖本股,并進(jìn)入等待池。直到 notify 喚醒承边,從等待池進(jìn)入鎖池城瞎,重新獲得競爭鎖的權(quán)利。

11. 常見編碼和字節(jié)占用數(shù)

ASCII編碼字節(jié)數(shù):
英文字符:1

Unicode編碼字節(jié)數(shù):
英文字符:4
中文簡體:4
中文繁體:4

UTF-8編碼字節(jié)數(shù):
英文字符:1
中文簡體:3
中文繁體:3

12. 深拷貝和淺拷貝的區(qū)別

  1. 淺拷貝:將原對象或原數(shù)組的引用直接賦給新對象,新數(shù)組,新對象/數(shù)組只是原對象的一個引用
  2. 深拷貝:創(chuàng)建一個新的對象和數(shù)組追城,將原對象的各項(xiàng)屬性的“值”(數(shù)組的所有元素)拷貝過來,是“值”而不是“引用”

13. 靜態(tài)代理和動態(tài)代理的區(qū)別馋吗,什么場景使用

靜態(tài)代理類:由程序員創(chuàng)建或由特定工具自動生成源代碼,再對其編譯蒋困。在程序運(yùn)行前龄糊,代理類的 .class 文件就已經(jīng)存在了退敦。
動態(tài)代理類:在程序運(yùn)行時战虏,運(yùn)用反射機(jī)制動態(tài)創(chuàng)建而成中符。

使用場景上,靜態(tài)代理通常只代理一個類汛蝙,并且事先知道要代理什么,比較適合專一化場景谱煤;而動態(tài)代理是代理一個接口下的多個實(shí)現(xiàn)類徽级,并且事先并不知道要代理的東西,只有運(yùn)行時才知道,更加泛用。

14. 如何將一個 Java 對象序列化到文件里

  1. 對象需要實(shí)現(xiàn) Serializable 接口;
  2. 通過 ObjectOutputStream.writeObject() 寫入喊衫,
    ObjectInputStream.readObject() 讀取世剖。

15. 談?wù)剬ψ⒔獾睦斫?/h4>

Java 注解詳解

以下是總結(jié):

注解本質(zhì)是繼承了 Annotation 的接口埠胖,是一種特殊的標(biāo)注谋竖,本身沒有意義驱敲,依賴解析器來實(shí)現(xiàn)它的價值娩梨。

注解利用幾個元注解來分別決定它的作用范圍纽什、生命周期让蕾、是否允許繼承等萌抵。其中生命周期可分為 Source(編譯期注解可見,Class 不可見)萝快、Class(Class 注解可見冰更,類加載后丟棄)和 Runntime(永久保存,運(yùn)行時可反射)官册。

注解在不同階段有不同的價值击敌,這里舉一些例子牲证。Source 階段缝其,通過 APT 掃描拴驮,可以獲取注解信息。諸如通過 @Override 注解來檢測父類是否有同名方法绪氛,EventBus3 也在該時期通過掃描 @Subscribe 來生成 java 類以保存訂閱信息唆鸡,另外還有 ARouter 生成路由、ButterKnife 生成綁定 id 等枣察。Class 階段實(shí)例不多争占,但是可以應(yīng)用于字節(jié)碼插樁燃逻。Runntime 階段,可以利用反射獲取注解信息臂痕,如 EventBus2 事件總線關(guān)系就是這么生成的伯襟,EventBus3也會在運(yùn)行時未找到指定訂閱時,嘗試通過反射去獲取刻蟹。

除 Java 提供的注解外逗旁,Android 還提供了自己的注解庫 support-annotations,包括如 @Null舆瘪、@StringRes、@StringDef 等注解红伦。

16. 談?wù)剬σ蕾囎⑷氲睦斫?/h4>

依賴注入英古,即由容器動態(tài)的將某個依賴關(guān)系注入到組件之中。

依賴注入的目的并非為軟件系統(tǒng)帶來更多功能昙读,而是為了提升組件重用的頻率召调,并為系統(tǒng)搭建一個靈活、可擴(kuò)展的平臺蛮浑。通過依賴注入機(jī)制唠叛,我們只需要通過簡單的配置,而無需任何代碼就可指定目標(biāo)需要的資源沮稚,完成自身的業(yè)務(wù)邏輯艺沼,而不需要關(guān)心具體的資源來自何處,由誰實(shí)現(xiàn)蕴掏。

17. 談?wù)剬Ψ盒偷睦斫?/h4>

泛型是 Java 1.5 的新特性障般,泛型的本質(zhì)是參數(shù)化類型,也就是說所操作的數(shù)據(jù)類型被指定為一個參數(shù)盛杰。這種參數(shù)類型可以用在類挽荡、接口和方法的創(chuàng)建中,分別稱為泛型類即供、泛型接口定拟、泛型方法。

在 Java SE 1.5 之前逗嫡,沒有泛型的情況的下青自,通過對類型 Object 的引用來實(shí)現(xiàn)參數(shù)的“任意化”,“任意化”帶來的缺點(diǎn)是要做顯式的強(qiáng)制類型轉(zhuǎn)換祸穷,而這種轉(zhuǎn)換是要求開發(fā)者對實(shí)際參數(shù)類型可以預(yù)知的情況下進(jìn)行的性穿。對于強(qiáng)制類型轉(zhuǎn)換錯誤的情況,編譯器可能不提示錯誤雷滚,在運(yùn)行的時候才出現(xiàn)異常需曾,這是一個安全隱患。

泛型的好處有三個:分別是安全性,泛型提供了編譯期類型檢測的機(jī)制呆万;然后是擴(kuò)展性商源,相比較 Object,泛型使數(shù)據(jù)的類別可以像參數(shù)一樣由外傳遞進(jìn)來谋减;最后是可讀性牡彻,不必等到運(yùn)行時才去強(qiáng)制轉(zhuǎn)換,定義或?qū)嵗A段出爹,就能看到要操作的數(shù)據(jù)類型庄吼。

Java 的泛型是偽泛型,在生成的 Java 字節(jié)碼中是不包含泛型中的類型信息的严就,虛擬機(jī)層面是不存在所謂『泛型』的概念的总寻。在編譯期間,所有的泛型信息都會被擦除掉梢为。這個過程就稱為類型擦除渐行。

在泛型類被類型擦除的時候,之前泛型類中的類型參數(shù)部分如果沒有指定上限铸董,如 <T> 則會被轉(zhuǎn)譯成普通的 Object 類型祟印,如果指定了上限如 <T extends String> 則類型參數(shù)就被替換成類型上限。

類型擦除雖然有局限性粟害,但也可以利用類型擦除來繞過編譯器不允許的操作限制蕴忆。如利用反射往 List 中添加其它類型的參數(shù)。

詳情參考鏈接

18. ArrayList 和 LinkedList 的區(qū)別我磁,以及應(yīng)用場景

  1. ArrayList 基于數(shù)組孽文,LinkedList 基于雙鏈表。
  2. 因?yàn)?ArrayList 基于數(shù)組夺艰,所以搜索和讀取數(shù)據(jù)很快芋哭,可以直接返回指定 index 位置的元素。但是插入郁副、刪除數(shù)據(jù)開銷很大减牺,需要移動數(shù)組插入位置后的所有元素。LinkList 則相反存谎。
  3. LinkedList 會占用更多的內(nèi)存拔疚,因?yàn)槊總€節(jié)點(diǎn)還存儲了前后節(jié)點(diǎn)的位置信息。

應(yīng)用場景:

  1. 隨機(jī)訪問較多既荚,使用 ArrayList稚失。插入刪除頻繁,使用 LinkedList恰聘。
  2. ArrayList 的插入句各、刪除也不一定比 LinkedList 慢吸占,如果在 List 末尾插入,LinkedList 需要一直查找到列表尾部凿宾,而 ArrayList 直接插入即可矾屯,這時 ArrayList 比 LinkedList 要快。所以要視情況而定初厚。

Java 虛擬機(jī)

Android 開發(fā)需要了解的虛擬機(jī)知識

包括:

  1. APK 的生成件蚕;
  2. APK 的安裝方式(JIT、AOT)产禾、Dalvik 和 ART 的異同等排作;
  3. Java 內(nèi)存結(jié)構(gòu);
  4. Java 內(nèi)存模型下愈;
  5. Java 引用類型纽绍;
  6. 垃圾識別策略;
  7. 垃圾回收算法势似;
  8. 類的加載過程(如何從 class 轉(zhuǎn)變?yōu)楸惶摂M機(jī)識別的類);

虛引用的功能及原理

虛引用必須與 ReferenceQueue 一起使用僧著,當(dāng) GC 準(zhǔn)備回收一個對象履因,如果發(fā)現(xiàn)它有虛引用,就會在回收之前盹愚,把這個 虛引用 加入到與之關(guān)聯(lián)的 ReferenceQueue 中栅迄。

僅通過 ReferenceQueue 就能拿到內(nèi)存泄漏的對象嗎?
不能皆怕,因?yàn)?GC 回收之后毅舆,ReferenceQueue 中的虛引用都是正常回收對象的虛引用愈腾,無法通過 ReferenceQueue 拿到內(nèi)存泄漏的對象憋活。
一般做法是,將虛引用和包含對象的弱引用相關(guān)聯(lián)(通過 map)虱黄,每次 ReferenceQueue 獲得新的虛引用時(queue.poll)悦即,就移除與之關(guān)聯(lián)的弱引用,這樣 GC 完畢之后橱乱,通過剩余的弱引用就能拿到內(nèi)存泄漏的對象辜梳。

Java 多線程

1. 開啟線程的幾種方式

繼承 Thread 類
實(shí)現(xiàn) Runnable 接口
通過 Callable 和 Future 創(chuàng)建對象

2. 線程的幾種狀態(tài)

Java 線程狀態(tài)在 Thread 中定義农渊,源碼中能看到有個枚舉 State乏沸,總共定義了六種狀態(tài):

NEW:新建狀態(tài)。線程對象已經(jīng)創(chuàng)建桐愉,但尚未啟動危纫;

RUNNABLE:就緒狀態(tài)宗挥,可運(yùn)行狀態(tài)乌庶。調(diào)用了線程的 start 方法,已經(jīng)在 java 虛擬機(jī)中執(zhí)行属韧,等待獲取操作系統(tǒng)資源如 CPU安拟,操作系統(tǒng)調(diào)度運(yùn)行;

BLOCKED:阻塞狀態(tài)宵喂。線程等待鎖的狀態(tài)糠赦,等待獲取鎖進(jìn)入同步塊/方法或調(diào)用 wait 后重新進(jìn)入需要競爭鎖;

WAITING:等待狀態(tài)锅棕。等待另一個線程以執(zhí)行特定的操作拙泽。調(diào)用以下方法進(jìn)入等待狀態(tài)。 Object.wait裸燎、Thread.join顾瞻、LockSupport.park;

TIMED_WAITING:線程等待一段時間德绿。調(diào)用方法 Thread.sleep荷荤、Object.wait、Thread.join移稳、LockSupport.parkNanos蕴纳、LockSupport.parkUntil;

TERMINATED:進(jìn)程結(jié)束狀態(tài)个粱。

處于 NEW古毛、TERMINATED 這兩個狀態(tài)的線程不會出現(xiàn)在堆棧信息里面。

3. 如何控制某個方法允許并發(fā)訪問線程的個數(shù)

利用 Semaphore 類都许。

參考鏈接
參考示例

4. wait稻薇、sleep 的區(qū)別

  1. wait 來自 Object 類,sleep 則是 Thread 的方法胶征;
  2. 雖然都會讓出 cpu 資源塞椎,但是 sleep 不會釋放鎖,而 wait 方法釋放了鎖弧烤,使得其他線程可以使用同步代碼塊或者同步方法忱屑;
  3. wait、notify 和 notifyAll 只能在同步方法或者同步代碼塊里面使用暇昂,而 sleep 可以在任何地方使用莺戒。

5. 什么可以導(dǎo)致線程阻塞/為什么會出現(xiàn)線程阻塞

一個線程需要依賴并等待另一個線程的執(zhí)行結(jié)果,這時就會產(chǎn)生阻塞急波。

  1. 線程睡眠从铲。Thread.sleep;
  2. 線程等待澄暮。Object.wait名段;
  3. 線程禮讓阱扬。Thread.yield;放棄執(zhí)行機(jī)會伸辟,重新進(jìn)入鎖池麻惶。
  4. 線程自閉。Thread.join信夫;在當(dāng)前線程中調(diào)用另一個線程的 join() 方法窃蹋,則當(dāng)前線程轉(zhuǎn)入阻塞狀態(tài),直到另一個進(jìn)程運(yùn)行結(jié)束静稻,當(dāng)前線程再由阻塞轉(zhuǎn)為就緒狀態(tài)警没。
  5. suspend 和 resume。因可能導(dǎo)致死鎖而廢棄振湾。

6. 線程如何關(guān)閉

  1. 運(yùn)行完畢后自動終止杀迹;
  2. Thread.stop,但是該方法是非常不安全的押搪。
  3. Thread.interrupt树酪,interrupt 依賴中斷線程對中斷狀態(tài)的響應(yīng)。

為什么棄用 stop大州?

  1. 調(diào)用 stop 方法會立刻停止 run() 方法中剩余的全部工作嗅回,包括在 catch 或 finally 語句中的,因此可能會導(dǎo)致一些清理性的工作的得不到完成摧茴,如文件,數(shù)據(jù)庫等的關(guān)閉埂陆。
  2. 調(diào)用 stop 方法會立即釋放該線程所持有的所有的鎖苛白,導(dǎo)致數(shù)據(jù)得不到同步,出現(xiàn)數(shù)據(jù)不一致的問題焚虱。

示例參考鏈接

7. Java 中實(shí)現(xiàn)同步的幾種方法(如何實(shí)現(xiàn)線程同步)

  1. 使用 synchronized 關(guān)鍵字购裙。可以用來修飾同步方法或同步代碼塊鹃栽。
  2. 使用 wait + notify躏率。
  3. 使用可重入鎖 ReentrantLock。ReentrantLock 是可重入民鼓、互斥薇芝、實(shí)現(xiàn)了 Lock 接口的鎖,它和 synchronized 具有相同的基本行為和語義丰嘉,并擴(kuò)展了其它能力夯到。
  4. 使用 ThreadLocal。使用 ThreadLocal 管理變量饮亏,則每一個使用該變量的線程都獲得該變量的副本耍贾,副本之間相互獨(dú)立阅爽,這樣每一個線程都可以隨意修改自己的變量副本,而不會對其他線程產(chǎn)生影響杂伟。
  5. 使用上層封裝好的 concurrent 包频蛔。比如阻塞隊列三圆。

8. ThreadLocal 原理,實(shí)現(xiàn)及如何保證 Local 屬性

ThreadLocal 源碼設(shè)計分析

每個線程都創(chuàng)建有一個 map围苫,其中 key 為 ThreadLocal强法,value 為要保存的變量副本库倘。這樣:
ThreadLocal.set 實(shí)際為 Thread.currentThread.map.put(this, value)
ThreadLocal.get 實(shí)際為 Thread.currentThread.map.get(this)
所以當(dāng)調(diào)用 get/set 方法時蚂且,實(shí)際是在操縱當(dāng)前線程的變量副本凡伊。

9. 什么是線程安全,如何保證線程安全(如何實(shí)現(xiàn)線程同步)

定義:在擁有共享數(shù)據(jù)的多條線程并行執(zhí)行的程序中饮寞,線程安全的代碼會通過同步機(jī)制保證各個線程都可以正常且正確的執(zhí)行咨油,不會出現(xiàn)數(shù)據(jù)污染等意外情況赚爵。

如何保證:

  1. synchronized 關(guān)鍵字;
  2. Lock 接口宴霸;
  3. volatile + CAS囱晴;(volatile 保證了可見性以及防止了指令重排,但是不具備原子性瓢谢,所以要配合 CAS)
  4. atomic 原子類畸写。

深入點(diǎn)說,線程安全可以體現(xiàn)在三個方面氓扛,分別是原子性枯芬、可見性和有序性(Java 內(nèi)存模型的三個特性)。針對這三點(diǎn)采郎,Java 提供了底層封裝的 API 來保證線程安全:

首先是 synchronized千所,它能保證原子性、可見性和有序性蒜埋,相對重量級淫痰。

需要注意的是,synchronized 雖然能保證原子性整份,但其實(shí)是每一個 synchronized 塊可以看成是一個原子操作待错,它保證每個時刻只有一個線程執(zhí)行同步代碼,非原子操作仍然可能發(fā)生指令重排烈评,如 new 操作并不是原子操作火俄,所以單例 double check 仍然需要使用 volatile 來防止指令重排。再比如自增不是原子操作讲冠,可能發(fā)生指令重排瓜客,但重排對結(jié)果沒有影響,因?yàn)?synchronized 保證了塊之間的有序執(zhí)行。

volatile 能保證可見性和有序性谱仪,并能真正防止指令重排玻熙。

需要注意的是,volatile 的有序性指的是指令與指令之間有序芽卿,它本身不具有原子性揭芍,如自增操作(可分解為 1.讀取原始值;2.加一卸例;3.寫入工作內(nèi)存)称杨,多線程下,雖然 volatile 保證了指令不重排筷转,但并不能保證自增過程中沒有其它線程插入執(zhí)行姑原,導(dǎo)致臟數(shù)據(jù)。

synchronized 有序性保證了塊與塊之間的有序
volatile 有序性保證了指令與指令之間的有序
根本原因是
synchronized 靠系統(tǒng)內(nèi)核互斥鎖實(shí)現(xiàn)
volatile 靠內(nèi)存屏障

其它保證線程安全的還有 Lock呜舒、 Atomic 類以及 concurrent 包锭汛。

synchronized 能防止指令重排序嗎-鏈接
cas 原理-鏈接

10. 如何保證 List 線程安全(線程間操作 List)

  1. Vector硝烂。效率低圃郊,已過時瘸恼。
  2. java.util.Collections.SynchronizedList捐腿。它能把所有 List 接口的實(shí)現(xiàn)類轉(zhuǎn)換成線程安全的 List,由于它所有方法都是帶同步對象鎖的邢滑,即使是讀操作淳蔼,所以性能也一般坷襟。
  3. java.util.concurrent.CopyOnWriteArrayList乡范。并發(fā)包里的并發(fā)集合類配名。即復(fù)制再寫入,就是在添加元素的時候晋辆,先把原 List 列表復(fù)制一份渠脉,再添加新的元素。添加元素時瓶佳,先加鎖芋膘,再進(jìn)行復(fù)制替換操作,最后再釋放鎖霸饲。獲取元素則沒有加鎖索赏,提升了讀取性能。

參考鏈接

11. Java 對象的生命周期

創(chuàng)建階段(Creation)贴彼、應(yīng)用階段(Using)、不可視階段(Invisible)埃儿、不可達(dá)階段(Unreachable)器仗、可收集階段(Collected)、終結(jié)階段(Finalized)與釋放階段(Free)。

12. 談?wù)?synchronized 中的類鎖精钮、方法鎖和可重入鎖

類鎖包括:

  1. synchronized 修飾靜態(tài)方法
  2. synchronized 鎖代碼塊威鹿,鎖為類

對象鎖包括:

  1. synchronized 修飾成員方法(即方法鎖,屬于對象鎖的一種)
  2. synchronized 鎖代碼塊轨香,鎖為對象

可重入鎖:

可重入鎖就是說某個線程已經(jīng)獲得某個鎖忽你,可以再次獲取鎖而不會出現(xiàn)死鎖。

可重入原理:

每一個鎖關(guān)聯(lián)一個線程持有者和計數(shù)器臂容,當(dāng)計數(shù)器為 0 時表示該鎖沒有被任何線程持有科雳,那么任何線程都可能獲得該鎖而調(diào)用相應(yīng)的方法;當(dāng)某一線程請求成功后脓杉,JVM 會記下鎖的持有線程糟秘,并且將計數(shù)器置為 1;此時其它線程請求該鎖球散,則必須等待尿赚;而該持有鎖的線程如果再次請求這個鎖,就可以再次拿到這個鎖蕉堰,同時計數(shù)器會遞增凌净;當(dāng)線程退出同步代碼塊時,計數(shù)器會遞減屋讶,如果計數(shù)器為 0冰寻,則釋放鎖。

13. synchronized 的原理

synchronized 底層是通過一個 monitor 的對象來完成丑婿,其實(shí) wait/notify 等方法也依賴于 monitor 對象性雄,這就是為什么只有在同步的塊或者方法中才能調(diào)用 wait/notify 等方法,否則會拋出 java.lang.IllegalMonitorStateException 異常的原因羹奉。

參考鏈接

14. synchronized 與 Lock 的區(qū)別

  1. synchronized 是 Java 的關(guān)鍵字秒旋,來自 JVM 層面,而 Lock 是一個類诀拭。
  2. synchronized 是可重入迁筛、不可中斷、非公平的鎖耕挨,而 Lock 可重入细卧、可中斷、可公平筒占。
  3. synchronized 無法判斷鎖狀態(tài)贪庙,Lock 可以。
  4. synchronized 發(fā)生異常會自動釋放鎖翰苫,因此不會死鎖止邮;Lock 發(fā)生異常这橙,不會主動釋放鎖,必須手動 unlock 來釋放鎖导披,可能引起死鎖屈扎。(所以最好將同步代碼塊用 try-catch 包起來,finally 中寫入 unlock撩匕,避免死鎖的發(fā)生)

至于性能鹰晨,1.6 版本優(yōu)化后 synchronized 要比 Lock 快很多,參考 synchronized 實(shí)現(xiàn)原理和鎖的優(yōu)化止毕。

15. 什么是死鎖模蜡,如何避免死鎖

多個線程因競爭多個資源而造成的一種相互等待的情況,若無外力作用滓技,這些進(jìn)程都將無法向前推進(jìn)哩牍。

避免死鎖的技術(shù):

  1. 加鎖順序。線程按照一定順序加鎖令漂;
  2. 加鎖時限膝昆。給嘗試獲取鎖加上一定的時限,超時則放棄獲取鎖叠必,并釋放自身占有的鎖荚孵,然后等待一段隨機(jī)的時間再重試;
  3. 死鎖檢測纬朝。主要針對不可能實(shí)現(xiàn)按序加鎖并且鎖超時也不可行的場景收叶。例如,線程 A 請求某鎖共苛,但是該鎖被線程 B 持有判没,這時就可以檢查下線程 B 是否在請求線程 A 當(dāng)前所持有的鎖,如果有則死鎖隅茎。檢測后有倆種方式解決死鎖問題:一種是釋放所有鎖澄峰,等待一段隨機(jī)時間后重試。另一種是設(shè)置優(yōu)先級辟犀,并讓一部分線程回退重試俏竞。

16. ReentrantLock 的內(nèi)部實(shí)現(xiàn)

ReentrantLock 的內(nèi)部實(shí)現(xiàn)

17. 死鎖的四個必要條件

  1. 互斥條件。即在一段時間內(nèi)某資源僅為一個進(jìn)程所占有堂竟。
  2. 不可剝奪條件魂毁。進(jìn)程所獲得的資源在未使用完畢之前,不能被其他進(jìn)程強(qiáng)行奪走出嘹。
  3. 請求與保持條件席楚。進(jìn)程已經(jīng)保持了至少一個資源,但又提出了新的資源請求税稼,而該資源已被其他進(jìn)程占有烦秩,此時請求進(jìn)程被阻塞刁赦,但對自己已獲得的資源保持不放。
  4. 循環(huán)等待條件闻镶。存在一種進(jìn)程資源的循環(huán)等待鏈,鏈中每一個進(jìn)程已獲得的資源同時被鏈中下一個進(jìn)程所請求丸升。

只要系統(tǒng)發(fā)生死鎖铆农,這些條件必然成立,而只要上述條件之一不滿足狡耻,就不會發(fā)生死鎖墩剖。

18. 線程池常見問題

1. 為什么使用線程池,線程池的優(yōu)勢或好處
  1. 節(jié)省頻繁創(chuàng)建線程的開銷夷狰;
  2. 避免線程棧溢出導(dǎo)致進(jìn)程崩潰岭皂;
2. 線程池類的繼承關(guān)系

線程池的真正實(shí)現(xiàn):ThreadPoolExecutor implements ExecutorService
創(chuàng)建固定類型線程池的類:Executors

3. ThreadPoolExecutor 有哪些參數(shù),作用分別是什么

核心線程數(shù)量:默認(rèn)情況下沼头,核心線程一直在線程池中存活爷绘,即使處于閑置狀態(tài)。
最大線程數(shù):達(dá)到該線程數(shù)进倍,后續(xù)新任務(wù)會被阻塞土至。
最大存活時間:非核心線程閑置時的超時時間,超過該時間非核心線程會被回收猾昆。
時間單位:最大存活時間的時間單位陶因。
BlockingQueue:任務(wù)隊列;
線程工廠:創(chuàng)建線程的線程工廠垂蜗;

4. 線程池的任務(wù)執(zhí)行規(guī)則
  1. 線程數(shù)量未達(dá)核心線程數(shù)量楷扬,則直接啟動一個核心線程執(zhí)行任務(wù);
  2. 線程池中的線程數(shù)量已經(jīng)達(dá)到或超過核心贴见,則進(jìn)入任務(wù)隊列等待烘苹;
  3. 如果無法插入到任務(wù)隊列,則隊列已滿蝇刀;此時如果線程數(shù)量未達(dá)到最大線程數(shù)螟加,則啟動一個非核心線程來執(zhí)行任務(wù);
  4. 如果隊列已滿且線程數(shù)量大于等于最大線程數(shù)吞琐,則拒絕任務(wù)捆探,并拋出 RejectedExecution 異常。
5. 有哪些固定類型的線程池

FixedThreadPool:只有核心線程站粟,用于更快響應(yīng)外界請求黍图。
CacheThreadPool:沒有核心線程,線程空閑時會被復(fù)用奴烙,閑置超過 60s 則會被回收助被。因?yàn)槿蝿?wù)會被立即執(zhí)行剖张,適合執(zhí)行大量耗時少的任務(wù)。
SingleThreadPool:只有一個核心線程,且任務(wù)隊列無上限矾端,主要用來順序執(zhí)行耗時任務(wù)陈辱。
ScheduledThreadPool:核心線程數(shù)量固定,非核心線程可以無限大顾犹,回收時間為 10s,用來執(zhí)行定時任務(wù)或周期性重復(fù)任務(wù)褒墨。

19. 談?wù)剬Χ嗑€程(并發(fā)編程)的理解

1. 為什么使用多線程(使用多線程的好處炫刷,使用多線程解決了哪些問題)
  1. 充分利用計算機(jī)處理器性能,提高程序運(yùn)行的效率郁妈;
  2. 任務(wù)處理異步化浑玛,不阻塞主任務(wù)。
2. 如何實(shí)現(xiàn)多線程
  1. 通過 new Thread噩咪;
  2. 通過線程池顾彰。
3. 多線程有哪些問題,如何解決多線程問題
  1. 線程安全問題剧腻。關(guān)于線程安全問題,參考【多線程-9-什么是線程安全书在,如何保證線程安全】
  2. 死鎖問題灰伟。關(guān)于死鎖問題,參考【多線程-15-什么是死鎖儒旬,如何避免死鎖】
  3. 資源消耗問題栏账。無限制的創(chuàng)建線程不僅導(dǎo)致資源消耗,還可能因線程棧溢出導(dǎo)致進(jìn)程崩潰栈源。

20. 談?wù)勀銓Χ嗑€程同步機(jī)制的理解

說白了就是:什么是線程同步(安全)挡爵?如何做到線程同步(安全)?

參考【多線程-9-什么是線程安全甚垦,如何保證線程安全】

21. 多線程斷點(diǎn)續(xù)傳原理

大致原理茶鹃,理解即可..

斷點(diǎn)續(xù)傳:

  1. 客戶端從臨時文件讀取斷點(diǎn)值并發(fā)送給服務(wù)端。
  2. 服務(wù)端與客戶端將文件指針移至斷點(diǎn)處(RandomAccessFile)
  3. 從斷點(diǎn)處傳輸文件艰亮。

多線程下載:

將源文件按長度為分為 M 塊文件闭翩,然后開辟 N 個線程,每個線程傳輸一部分迄埃,最后合并所有線程文件疗韵。

Http 相關(guān)

1. https 和 http 的區(qū)別

https 協(xié)議需要到 ca 申請證書,一般免費(fèi)證書很少侄非,需要交費(fèi)蕉汪。
http 是超文本傳輸協(xié)議流译,信息是明文傳輸,https 則是具有安全性的 ssl 加密傳輸協(xié)議者疤。
http 和 https 使用的是完全不同的連接方式用的端口也不一樣福澡,前者是 80,后者是 443驹马。

2. https 的請求過程

https 請求過程

3. 描述下網(wǎng)絡(luò)的幾層結(jié)構(gòu)

層級 作用 協(xié)議 關(guān)鍵字
實(shí)體層 把電腦通過物理方式連接起來竞漾,負(fù)責(zé)傳送 0 1 信號
鏈路層 對 0 1 信號分組,數(shù)據(jù)最長 1500 字節(jié) 以太網(wǎng)協(xié)議 MAC 地址(每塊網(wǎng)卡的唯一地址)窥翩,電腦到電腦
網(wǎng)絡(luò)層 引進(jìn)新的地址,區(qū)分不同計算機(jī)是否屬于同一子網(wǎng)絡(luò) IP 協(xié)議 IP 地址鳞仙,電腦到電腦
傳輸層 表示數(shù)據(jù)包供哪個進(jìn)程使用寇蚊。 UDP TCP 協(xié)議 端口到端口
應(yīng)用層 區(qū)分不同數(shù)據(jù)格式 HTTP FTP

其中 http 數(shù)據(jù)放在 TCP 數(shù)據(jù)包的數(shù)據(jù)部分,也就是圖中的【應(yīng)用層數(shù)據(jù)包】棍好。

數(shù)據(jù)包封裝

4. http 訪問網(wǎng)頁的完整流程

  1. 訪問 DNS 服務(wù)器仗岸,根據(jù)域名獲取 IP 地址;
  2. 根據(jù)子網(wǎng)掩碼判斷是否屬于同一子網(wǎng)絡(luò)借笙,不屬于則通過網(wǎng)關(guān)轉(zhuǎn)發(fā)扒怖;
  3. 先后封裝為 http 數(shù)據(jù)包、TCP 數(shù)據(jù)包业稼、IP 數(shù)據(jù)包盗痒、以太數(shù)據(jù)包,經(jīng)過網(wǎng)關(guān)轉(zhuǎn)發(fā)低散,到服務(wù)器俯邓。
  4. 服務(wù)器分別解析上述數(shù)據(jù)包,獲取 http 請求熔号,作出 http 響應(yīng)稽鞭,再按上述流程發(fā)回。

步驟 3 封裝圖:

數(shù)據(jù)包封裝過程

5. 描述下 http 的三次握手

  1. 客戶端向服務(wù)器發(fā)送 SYN (同步)包引镊;
  2. 服務(wù)器向客戶端發(fā)送 ACK (確認(rèn))包朦蕴,同時也向客戶端發(fā)送一條 SYN 包,倆者放到同一請求里弟头;
  3. 客戶端向服務(wù)器發(fā)送 ACK 包吩抓。

6. 網(wǎng)絡(luò)連接中使用到哪些設(shè)備

設(shè)備名稱 對應(yīng)層、協(xié)議 作用
集線器 實(shí)體層 連接設(shè)備
交換機(jī) 鏈路層 解決設(shè)備沖突亮瓷,實(shí)現(xiàn)任意倆臺設(shè)備的互聯(lián)琴拧,即 MAC 地址
路由器 網(wǎng)絡(luò)層 通過 IP 地址區(qū)分子網(wǎng)絡(luò),實(shí)現(xiàn)互聯(lián)網(wǎng)連接嘱支。

7. http2 和 http1.1 的區(qū)別

http2 和 http1.1 的區(qū)別

8. 描述下網(wǎng)絡(luò)通信協(xié)議

一般提問方式有:對網(wǎng)絡(luò)通信協(xié)議熟悉嗎沛膳?對 socket 和 http 了解嗎扔枫?能解釋下嗎?

網(wǎng)絡(luò)通信協(xié)議可以簡單分為五層锹安,分別是應(yīng)用層(http)短荐、傳輸層(TCP、UDP)叹哭、網(wǎng)絡(luò)層(IP)忍宋、鏈路層(MAC)、物理層风罩。

套接字(Socket)是支持 TCP/IP 協(xié)議的網(wǎng)絡(luò)通信的基本操作單元糠排。它是網(wǎng)絡(luò)通信過程中端點(diǎn)的抽象表示,包含進(jìn)行網(wǎng)絡(luò)通信必須的五種信息:連接使用的協(xié)議超升,本地主機(jī)的 IP 地址入宦,本地進(jìn)程的協(xié)議端口,遠(yuǎn)地主機(jī)的 IP 地址室琢,遠(yuǎn)地進(jìn)程的協(xié)議端口乾闰。

應(yīng)用層通過傳輸層進(jìn)行數(shù)據(jù)通信時,TCP 會遇到同時為多個應(yīng)用程序進(jìn)程提供并發(fā)服務(wù)的問題盈滴。多個 TCP 連接或多個應(yīng)用程序進(jìn)程可能需要通過同一個 TCP 協(xié)議端口傳輸數(shù)據(jù)涯肩。為了區(qū)別不同的應(yīng)用程序進(jìn)程和連接,許多計算機(jī)操作系統(tǒng)為應(yīng)用程序與 TCP/IP 協(xié)議交互提供了套接字(Socket)接口巢钓。應(yīng)用層可以和傳輸層通過 Socket 接口宽菜,區(qū)分來自不同應(yīng)用程序進(jìn)程或網(wǎng)絡(luò)連接的通信,實(shí)現(xiàn)數(shù)據(jù)傳輸?shù)牟l(fā)服務(wù)竿报。

建立 Socket 連接至少需要一對套接字铅乡,其中一個運(yùn)行于客戶端,稱為 ClientSocket 烈菌,另一個運(yùn)行于服務(wù)器端阵幸,稱為 ServerSocket。

套接字之間的連接過程分為三個步驟:服務(wù)器監(jiān)聽芽世,客戶端請求挚赊,連接確認(rèn)。

服務(wù)器監(jiān)聽:服務(wù)器端套接字并不定位具體的客戶端套接字济瓢,而是處于等待連接的狀態(tài)荠割,實(shí)時監(jiān)控網(wǎng)絡(luò)狀態(tài),等待客戶端的連接請求。

客戶端請求:指客戶端的套接字提出連接請求蔑鹦,要連接的目標(biāo)是服務(wù)器端的套接字夺克。為此,客戶端的套接字必須首先描述它要連接的服務(wù)器的套接字嚎朽,指出服務(wù)器端套接字的地址和端口號铺纽,然后就向服務(wù)器端套接字提出連接請求。

連接確認(rèn):當(dāng)服務(wù)器端套接字監(jiān)聽到或者說接收到客戶端套接字的連接請求時哟忍,就響應(yīng)客戶端套接字的請求狡门,建立一個新的線程,把服務(wù)器端套接字的描述發(fā)給客戶端锅很,一旦客戶端確認(rèn)了此描述其馏,雙方就正式建立連接。而服務(wù)器端套接字繼續(xù)處于監(jiān)聽狀態(tài)爆安,繼續(xù)接收其他客戶端套接字的連接請求尝偎。

http 是超文本傳輸協(xié)議,是應(yīng)用層協(xié)議鹏控,依賴于 TCP/IP 協(xié)議,是一種請求相應(yīng)式肤寝、無連接当辐、無狀態(tài)的協(xié)議,請求結(jié)束后鲤看,會自動釋放連接缘揪。

http1.0 中,客戶端的每次請求都要求建立一次單獨(dú)的連接义桂,在處理完本次請求后找筝,就自動釋放連接。

http1.1 中慷吊,則可以在一次連接中處理多個請求袖裕,并且多個請求可以重疊進(jìn)行,不需要等待一個請求結(jié)束后再發(fā)送下一個請求溉瓶。

http2.0 中急鳄,新增了多路復(fù)用,支持壓縮算法堰酿。

https 在 http 的基礎(chǔ)上新增 ssl 層疾宏,使用加密傳輸(會進(jìn)行加解密驗(yàn)證),端口號是 443触创。

9. 描述下 http 的消息結(jié)構(gòu)

請求

請求行:包括請求方法(Get坎藐、Post...)、URL(/hello.txt)哼绑、協(xié)議版本(HTTP/1.1)
請求頭:大量鍵值對,如 User-Agent:xxx座菠、Host:xxx 等
空行
請求數(shù)據(jù):post 請求數(shù)據(jù)在此

示例:

GET /hello.txt HTTP/1.1
User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
Host: www.example.com
Accept-Language: en, mi

響應(yīng)

狀態(tài)行:包括協(xié)議和狀態(tài)碼经备,如 HTTP/1.1 200 OK
消息報頭:大量鍵值對,如 Content-Type:text/html吟逝、Content-Length:122 等等
空行
響應(yīng)正文:響應(yīng)結(jié)果數(shù)據(jù)

示例:

HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
ETag: "34aa387-d-1568eb00"
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain

10. http 的請求類型有哪些

類型 功能
OPTIONS 返回服務(wù)器針對特定資源所支持的 http 請求方法。也可以利用向 Web 服務(wù)器發(fā)送'*'的請求來測試服務(wù)器的功能性赦肋。
HEAD 向服務(wù)器索要與 GET 請求相一致的響應(yīng)块攒,只不過響應(yīng)體將不會被返回。這一方法可以在不必傳輸整個響應(yīng)內(nèi)容的情況下佃乘,就可以獲取包含在響應(yīng)消息頭中的元信息囱井。
GET 向特定的資源發(fā)出請求。
POST 向指定資源提交數(shù)據(jù)進(jìn)行處理請求(例如提交表單或者上傳文件)程帕。數(shù)據(jù)被包含在請求體中住练。POST 請求可能會導(dǎo)致新的資源的創(chuàng)建或已有資源的修改。
PUT 向指定資源位置上傳其最新內(nèi)容愁拭。
DELETE 請求服務(wù)器刪除 Request-URI 所標(biāo)識的資源盏混。
TRACE 回顯服務(wù)器收到的請求惜论,主要用于測試或診斷混聊。
CONNECT http1.1 協(xié)議中預(yù)留給能夠?qū)⑦B接改為管道方式的代理服務(wù)器。

雖然 http 的請求方式有 8 種军浆,但是我們在實(shí)際應(yīng)用中常用的也就是 get 和 post挡闰,其他請求方式也都可以通過這兩種方式間接的來實(shí)現(xiàn)乒融。

Android 基礎(chǔ)

1. 四大組件及其簡單介紹

Activity、Service摄悯、BroadcastReceiver赞季、ContentProvider

Activity

活動,可以簡單的理解為頁面奢驯。

生命周期:onCreate申钩、onStart(可見不可交互)、onResume(可見可交互)叨橱、onPause(可見不可交互)、onStop(不可見)断盛、onDestroy罗洗、onRestart。

四種狀態(tài):Running(激活)钢猛、Paused(可見不可交互)伙菜、Stopped(不可見不可交互)、Killed(被殺掉或啟動以前)

Service

服務(wù)命迈,可以后臺運(yùn)行贩绕,不需要呈現(xiàn)頁面。Service 是運(yùn)行在主線程的壶愤,如果要耗時操作淑倾,可以在 Service 中創(chuàng)建一個子線程來執(zhí)行任務(wù),或者使用 IntentService征椒。

倆種啟動方式:

  1. start娇哆。生命周期為:onCreate → onStartCommand → onDestroy
  2. bind。生命周期為:onCreate → onBind → onUnbind → onDestroy

onStart 方法已被廢棄

使用 start 方式多次啟動時,onCreate 只執(zhí)行一次碍讨,onStartCommand 執(zhí)行多次治力,但僅存在一個 Service 實(shí)例。該 Service 將會一直在后臺運(yùn)行勃黍,而不管 Activity 是否還存在宵统,直到被調(diào)用 stopService,或自身的 stopSelf覆获。當(dāng)然如果系統(tǒng)資源不足马澈,android 系統(tǒng)也可能結(jié)束服務(wù)。

使用 bind 方式多次啟動時锻梳,onCreate箭券、onBind 均只執(zhí)行一次,onStartCommand 始終不會被調(diào)用疑枯。當(dāng)連接建立之后辩块,Service 將會一直運(yùn)行,除非調(diào)用 Context.unbindService 斷開連接或者之前調(diào)用 bindService 的 Context 不存在了荆永,系統(tǒng)將會自動停止 Service废亭,對應(yīng) onDestroy 將被調(diào)用。

既 start具钥,又 bind 的服務(wù)豆村,則該 Service 將會一直在后臺運(yùn)行。僅調(diào)用 unbindService 或 stopService 將不會停止 Service骂删,而必須同時調(diào)用來停止服務(wù)(測試結(jié)果是這樣掌动,網(wǎng)上有各種爭論..)。

IntentService 特性:

會創(chuàng)建獨(dú)立的工作線程來處理所有的 Intent 請求宁玫;
會創(chuàng)建獨(dú)立的工作線程來處理 onHandleIntent 方法實(shí)現(xiàn)的代碼粗恢,無需處理多線程問題;
所有請求處理完成后欧瘪,IntentService 會自動停止眷射;
為 Service 的 onBind 提供默認(rèn)實(shí)現(xiàn),返回 null佛掖;
為 Service 的 onStartCommand 提供默認(rèn)實(shí)現(xiàn)妖碉,將請求 Intent 添加到隊列中;

BroadcastReceiver

廣播接收器芥被,用于接收廣播欧宜。

倆種類型:

  1. 無序廣播。能被所有接收者接收拴魄,傳遞效率高鱼鸠。
  2. 有序廣播猛拴。傳遞有序,接收者可以將處理結(jié)果傳給下個接收者蚀狰,并且可以終止廣播的傳播愉昆。

倆種注冊方式:

  1. 靜態(tài)注冊。Androidmanifest 中注冊麻蹋,常駐跛溉,即使應(yīng)用程序關(guān)閉,程序也會被系統(tǒng)調(diào)用扮授。
  2. 動態(tài)注冊芳室。跟隨 context 的生命周期。如 activity 關(guān)閉時刹勃,其注冊的廣播也會被移除堪侯。

廣播接收順序:

  1. 有序廣播時,優(yōu)先級高的先接收荔仁。
  2. 無序廣播或同優(yōu)先級廣播接收器伍宦,動態(tài)高于靜態(tài)。
  3. 同優(yōu)先級的同類廣播接收器乏梁,先注冊的優(yōu)先于后注冊的次洼。

ContentProvider

內(nèi)容提供者。支持在多個應(yīng)用間存儲和讀取數(shù)據(jù)遇骑,相當(dāng)于數(shù)據(jù)庫卖毁。

它的作用就是將程序的內(nèi)部的數(shù)據(jù)和外部進(jìn)行共享,為數(shù)據(jù)提供外部訪問接口落萎,被訪問的數(shù)據(jù)主要以數(shù)據(jù)庫的形式存在亥啦,而且還可以選擇共享哪一部分的數(shù)據(jù)。這樣一來练链,對于程序當(dāng)中的隱私數(shù)據(jù)可以不共享翔脱,從而更加安全。ContentProvider 是 Android 中一種跨程序共享數(shù)據(jù)的重要組件兑宇。

2. Activity 在各種場景下的生命周期變化

1.正常情況

啟動:create → start → resume
返回鍵退出: pause → stop → destroy

2.Home 鍵

按 Home 鍵:pause → stop
再次啟動:restart → start → resume

3.覆蓋:
被半透明或非全屏 Activity 覆蓋:A.pause → B.create → B.start → B.resume
從覆蓋 Activity 回退:B.pause → A.resume → B.stop → B.destroy

4.跳轉(zhuǎn)
跳轉(zhuǎn)到新 Activity:A.pause → B.create → B.start → B.resume → A.stop
從新 Activity 回退:B.pause → A.restartA.startA.resume → B.stop → B.destroy

5.彈出 dialog
當(dāng) Activity 彈出 dialog 對話框的時候粱坤,Activity 不會調(diào)用 onPause();只有當(dāng) Activity 啟動了 dialog 風(fēng)格的 Activity 時才會調(diào)用梯嗽。

6.異常終止
onStop 前調(diào)用 onSaveInstance

3. Activity 的四種啟動模式

standard
默認(rèn)啟動模式崩掘,順序壓入棧

singleTop
如果新 activity 位于任務(wù)棧棧頂?shù)臅r候饵骨,activity 不會被重新創(chuàng)建竭翠,同時回調(diào) onNewIntent 方法。

實(shí)際生命周期函數(shù)為 a.pause → a.onNewIntent → a.resume

應(yīng)用場景:推送通知頁薇搁,避免啟動多個推送頁斋扰。

singleTask
如果 activity 實(shí)例在棧中存在,那么會將該 activity 彈至棧頂啃洋,其上 activity 全部彈出传货。生命周期同 singleTop。

應(yīng)用場景:退出到主界面宏娄。

singleInstance
如果實(shí)例不存在问裕,則創(chuàng)建實(shí)例并放置到獨(dú)立的棧中,里面有且僅有它自己一個實(shí)例孵坚,以后如果繼續(xù)啟動該 activity粮宛,則直接將棧移動到前臺。

singleInstance 由于其特殊性卖宠,下面根據(jù)具體場景說明 activity 的切換過程巍杈。

a 為 standard,b 為 singleInstance逗堵。

Q1:a 啟動 b秉氧,b 再啟動 a眷昆,此時棧信息是怎樣的蜒秤?
A1:初始 a 所在棧中有倆個 a 實(shí)例汁咏,另一個棧中有一個 b 實(shí)例。

Q2:在新啟動的 a 頁面點(diǎn)擊返回作媚,返回到初始 a 還是 b 頁面恩急?
A2:初始 a瞎领。因?yàn)樵谔D(zhuǎn)到新的 a 頁面時,已經(jīng)切換了棧。

Q3:在 Q2 的基礎(chǔ)上再次點(diǎn)擊返回等舔,返回到哪個頁面?
A3:返回到 b 頁面肋杖,因?yàn)榇藭r a 所在棧已經(jīng)空了趴腋,所以切換下一個棧。

4. Activity 之間的通信方式

  1. Intent吧兔。
  2. 類的靜態(tài)變量磷仰。
  3. Application。
  4. EventBus 事件總線境蔼。
  5. 借助 SharedPreference灶平、SQLite、File箍土、Service 等逢享。

5. App 的啟動流程,以及 Activity 的啟動過程

Android App 啟動流程源碼分析
源碼分析 Activity 可見性真實(shí)時機(jī)

Activity 啟動與 App 的啟動原理機(jī)制上大同小異吴藻,熟悉 App 的啟動過程即可瞒爬。

6. 橫豎屏切換的時候,Activity 的生命周期

1.未配置 configChanges:

onPause → onStop → onDestroy → onCreate → onStart → onResume

即重新調(diào)用各個生命周期沟堡,切橫屏執(zhí)行一次疮鲫,切豎屏執(zhí)行倆次。

2.配置 configChanges = "orientation"

重新調(diào)用各個生命周期弦叶,無論橫豎屏都只執(zhí)行一次俊犯。

3.配置 configChanges = "orientation|keyboardHidden|screenSize"

不會調(diào)用各個生命周期,只執(zhí)行 onConfigurationChanged 方法伤哺。

自從 Android 3.2(API 13)燕侠,在設(shè)置 Activity 的 android:configChanges="orientation|keyboardHidden" 后,還是會重新調(diào)用各個生命周期函數(shù)立莉。因?yàn)?screen size 也開始跟著設(shè)備的橫豎切換而改變绢彤。所以,在 AndroidManifest.xml 里設(shè)置的 MinSdkVersion 和 TargetSdkVersion 屬性大于等于 13 的情況下蜓耻,如果想阻止程序在運(yùn)行時重新加載 Activity茫舶,除了設(shè)置 "orientation", 你還必須設(shè)置 "ScreenSize"刹淌。

7. Activity 與 Fragment 之間生命周期比較

Fragment 的生命周期:

onAttach → onCreate → onCreateView → onActivityCreated → onStart → onResume → onPause → onStop → onDestroyView → onDestroy → onDetach

onAttach:Fragment 和 Activity 建立關(guān)聯(lián)時調(diào)用饶氏;
onCreateView:Fragment 創(chuàng)建視圖時調(diào)用讥耗;
onActivityCreated:相關(guān)聯(lián)的 Activity 的 onCreate 方法已返回時調(diào)用;
onDestroyView:Fragment 中的視圖被移除時調(diào)用疹启;
onDetach:Fragment 和 Activity 取消關(guān)聯(lián)時調(diào)用古程。

下面是當(dāng) Activity 包含一個 Fragment 的時候,Activity 和 Fragment 生命周期的變化喊崖,其中靜態(tài)加載和動態(tài)加載有所區(qū)別挣磨。

下面 f 指代 fragment,a 指代 activity荤懂,以下為親測數(shù)據(jù)茁裙。

靜態(tài)加載:
f.onAttach → f.onCreate → f.onCreateView → a.onCreate → f.onActivityCreated → f.onStart → a.onStart → a.onResume → f.onResume → f.onPause → a.onPause → f.onStop → a.stop → f.onDestroyView → f.destroy → f.detach → a.destroy

動態(tài)加載
a.onCreate → f.onAttach → f.onCreate → f.onCreateView → f.onActivityCreated → f.onStart → a.onStart → a.onResume → f.onResume → f.onPause → a.onPause → f.onStop → a.stop → f.onDestroyView → f.destroy → f.detach → a.destroy

有幾個需要注意的點(diǎn):

  1. 靜態(tài)注冊和動態(tài)注冊的區(qū)別在于,f.onAttach节仿、f.onCreate呜达、f.onCreateView 和 a.onCreate 的先后順序不同。
  2. 后續(xù)同類生命周期方法粟耻,均是 fragment 先于 activity 執(zhí)行查近,onResume 方法除外。
  3. 這里的順序是以方法執(zhí)行完畢為準(zhǔn)的挤忙,比如 a.super.onStart 中調(diào)用了 f.onStart霜威,可以說 a.onStart 先執(zhí)行(a.onStart 先開始執(zhí)行,以方法開始執(zhí)行為準(zhǔn))册烈,也可以說 a.onStart 后于 f.onStart 執(zhí)行(a.onStart 后執(zhí)行完畢戈泼,以方法執(zhí)行完畢為準(zhǔn))。因此網(wǎng)上可能有不同的順序赏僧,個人覺得沒必要太較真大猛,了解下圖的對應(yīng)關(guān)系即可,如果想知道詳細(xì)的順序淀零,可以參考再后一張圖挽绩。

簡化示意圖:

簡化對照關(guān)系

完整流程圖:

完整對照關(guān)系

8. Fragment 在各場景下的生命周期變化

1.Home 鍵或跳轉(zhuǎn)至其它 Activity

按下 Home 鍵或跳轉(zhuǎn)到其他 Activity:onPause → onStop
重新打開或回退:onStart → onResume

2.使用 replace 替換 fragment

b.onAttach → b.onCreate → a.onPausea.onStopa.onDestroyViewa.destroya.detach → b.onCreateView → b.onActivityCreated → b.onStart → b.onResume

3.使用 show/hide 切換 fragment

生命周期無調(diào)用

9. Fragment 之間以及和 Activity 的通信

1. Activity 將數(shù)據(jù)傳給 Fragment

  1. 使用 bundle
  2. 使用廣播
  3. EventBus

2. Fragment 將數(shù)據(jù)傳給 Activity

  1. 實(shí)現(xiàn)接口回調(diào)形式(onAttach 中將 Context 強(qiáng)轉(zhuǎn)為接口類型)
  2. 使用廣播
  3. EventBus

3. Fragment 之間通信

  1. 借助 Activity 傳 bunndle
  2. 定義接口
  3. EventBus

10. 本地廣播和全局廣播有什么差別?

1驾中、本地廣播:發(fā)送的廣播事件不被其他應(yīng)用程序獲取唉堪,也不能響應(yīng)其他應(yīng)用程序發(fā)送的廣播事件。本地廣播只能被動態(tài)注冊肩民,不能靜態(tài)注冊唠亚。動態(tài)注冊需要用到 LocalBroadcastManager。

2持痰、全局廣播:發(fā)送的廣播事件可被其他應(yīng)用程序獲取灶搜,也能響應(yīng)其他應(yīng)用程序發(fā)送的廣播事件。全局廣播既可以動態(tài)注冊,也可以靜態(tài)注冊割卖。

本地廣播實(shí)際還是通過 Handler 實(shí)現(xiàn)前酿,只不過利用了 HashMap 和 List 來維護(hù)廣播接收者、IntentFilter究珊、Intent 三者之間的關(guān)系。

11. ContentProvider纵苛、ContentResolver剿涮、ContentObserver 之間的關(guān)系

  1. ContentProvider:內(nèi)容提供者,對外共享數(shù)據(jù)攻人,可以通過 ContentProvider 把應(yīng)用中的數(shù)據(jù)共享給其他應(yīng)用訪問取试。
  2. ContentResolver:內(nèi)容解析者,其作用是按照一定規(guī)則訪問內(nèi)容提供者的數(shù)據(jù)腿椎。
  3. ContentObserver:內(nèi)容觀察者筝家,目的是觀察特定 URI 引起的數(shù)據(jù)庫的變化舀武,繼而做一些相應(yīng)的處理,類似于數(shù)據(jù)庫技術(shù)中的觸發(fā)器猿棉,當(dāng) ContentObserver 所觀察的 URI 發(fā)生變化時,便會觸發(fā)屑咳。

12. 廣播使用的方式和場景

方式:靜態(tài)注冊萨赁、動態(tài)注冊。詳見【Android 基礎(chǔ)-1-四大組件及其簡單介紹】

在 Android 系統(tǒng)中兆龙,為什么需要廣播機(jī)制呢杖爽?廣播機(jī)制,本質(zhì)上它就是一種組件間的通信方式紫皇,如果是兩個組件位于不同的進(jìn)程當(dāng)中慰安,那么可以用 Binder 機(jī)制來實(shí)現(xiàn),如果兩個組件是在同一個進(jìn)程中聪铺,那么它們之間可以用來通信的方式就更多了化焕,這樣看來,廣播機(jī)制似乎是多余的铃剔。然而锣杂,廣播機(jī)制卻是不可替代的,它和 Binder 機(jī)制不一樣的地方在于番宁,廣播的發(fā)送者和接收者事先是不需要知道對方的存在的元莫,這樣帶來的好處便是,系統(tǒng)的各個組件可以松耦合地組織在一起蝶押,這樣系統(tǒng)就具有高度的可擴(kuò)展性踱蠢,容易與其它系統(tǒng)進(jìn)行集成。
在軟件工程中,是非常強(qiáng)調(diào)模塊之間的高內(nèi)聚低耦合性的茎截,不然的話苇侵,隨著系統(tǒng)越來越龐大,就會面臨著越來越難維護(hù)的風(fēng)險企锌,最后導(dǎo)致整個項(xiàng)目的失敗榆浓。Android 應(yīng)用程序的組織方式,可以說是把這種高內(nèi)聚低耦合性的思想貫徹得非常透徹撕攒,在任何一個 Activity 中陡鹃,都可以使用一個簡單的 Intent,通過 startActivity 或者 startService抖坪,就可以把另外一個 Activity 或者 Service 啟動起來為它服務(wù)萍鲸,而且它根本上不依賴這個 Activity 或者 Service 的實(shí)現(xiàn),只需要知道它的字符串形式的名字即可擦俐,而廣播機(jī)制更絕脊阴,它連接收者的名字都不需要知道。
引自 Android 系統(tǒng)中的廣播機(jī)制簡要介紹

  1. 需要低耦合通信時使用蚯瞧;
  2. 由于性能消耗大嘿期,同類型的觀察者模式或者 EventBus 也能解決。故一般當(dāng)需要進(jìn)程間通信或者監(jiān)聽系統(tǒng)廣播事件時埋合,選擇 BroadcastReceiver秽五。比如監(jiān)聽網(wǎng)絡(luò)情況,有網(wǎng)重試饥悴。
  3. 全局監(jiān)聽場景坦喘。比如推送;

13. 廣播里可以直接啟動 Activity 嗎

onReceive 中 context 參數(shù)西设,如果是靜態(tài)注冊的廣播瓣铣,context 為 ReceiverRestrictedContext,所以在這里啟動一個 Activity贷揽,需要在 intent 中添加 Intent.FLAG_ACTIVITY_NEW_TASK棠笑;如果是動態(tài)注冊的廣播,context 為當(dāng)前注冊時所在的組件禽绪,比如 Activity 或者 Service(Service 里啟動 Activity 的話蓖救,也需要添加 FLAG_ACTIVITY_NEW_TASK)。

14. AlertDialog印屁、PopupWindow 的區(qū)別

AlertDialog 是 Dialog 的子類循捺,需要通過 builder 來創(chuàng)建對話框。通常用于提示用戶做一些額外的行為雄人,要求用戶做出行動才能繼續(xù)進(jìn)行从橘。

適用場景:輸入賬號密碼、請求權(quán)限、警告等恰力,總之是需要用戶明確知道一些信息叉谜,用戶做進(jìn)一步操作前,需要確定或者填入信息踩萎。

PopupWindow 是 android.widget 包里的類停局,因此是懸浮窗的一種,只不過這個懸浮窗是懸浮在當(dāng)前 activity 之上的香府。PopupWindow 可以用來顯示任意視圖董栽,是出現(xiàn)在當(dāng)前 activity 上的一個浮動容器。

適用場景:輸入的補(bǔ)全信息回还、下拉選擇的菜單裆泳,可以是一些提示的信息叹洲。

倆者對 activity 的生命周期都沒影響

15. getApplication 和 getApplicationContext 的區(qū)別

沒區(qū)別柠硕,來源歸根結(jié)底都是來自 LoadedApk.makeApplication() 創(chuàng)建/直接返回的 Application媚创,因此是同一個 Application着饥,只不過返回途徑不一樣而已规脸。

源碼分析:getApplication 和 getApplicationContext 的區(qū)別

16. Application 和 Activity 的 Context 對象的區(qū)別

  1. Application 的 Context 直接繼承自 ContextWrapper向抢,而 Activity 的 Context 直接繼承自 ContextThemeWrapper理朋。
  2. Application 的 Context 生命周期貫穿整個 App 存活期間跷乐,而使用 Activity 的 Context 則可能導(dǎo)致內(nèi)存泄漏扣墩。
  3. Application 相較 Activity骤视,不能 showDialog栈妆、startActivity(需要 Flag) 以及 LayoutInflate(直接使用默認(rèn)主題)胁编。
  4. 一個應(yīng)用只能有一個 Application Context。
區(qū)別

17. 描述下 Android 的幾種動畫

  1. 幀動畫鳞尔。GIF
  2. 補(bǔ)間動畫嬉橙。有透明、大小寥假、位置市框、旋轉(zhuǎn)等,補(bǔ)間動畫的 View 不是真正的移動糕韧,只是視覺意義枫振。
  3. 屬性動畫。真正改變屬性的動畫萤彩。
  4. 觸摸反饋動畫粪滤。如水波紋效果。
  5. 揭露動畫雀扶。背景色擴(kuò)散效果额衙。
  6. 轉(zhuǎn)場動畫。
  7. Vector 矢量動畫。

參考鏈接

18. 屬性動畫的特征

  1. 不局限于 View窍侧。
  2. 真實(shí)改變 View 屬性县踢。
  3. 動畫效果豐富,支持自定義組合屬性伟件。

19. 如何導(dǎo)入已有的外部數(shù)據(jù)庫

android 系統(tǒng)的數(shù)據(jù)庫應(yīng)該存放在 /data/data/(package name)/ 目錄下硼啤,所以我們需要用 FileInputStream 讀取原數(shù)據(jù)庫,再用 FileOutputStream 把讀取到的數(shù)據(jù)寫入到該目錄斧账。

20. Android 中三種播放視頻的方式

  1. 使用自帶播放器谴返。指定 Action 為 ACTION_VIEW,Data 為 uri咧织,Type 為其 MIME 類型嗓袱。
  2. 使用 MediaController + VideoView。
  3. 使用 MediaPlayer + SurfaceView习绢。

21. 介紹下 Android 中數(shù)據(jù)存儲的方式

  1. SharedPreference渠抹;用于保存鍵值對數(shù)據(jù)。
  2. 文件存儲闪萄;
  3. SQLite 數(shù)據(jù)庫梧却;
  4. ContentProvider。為存儲和獲取數(shù)據(jù)提供了統(tǒng)一的接口败去,可以在不同應(yīng)用程序之間共享數(shù)據(jù)放航。
  5. 網(wǎng)絡(luò)存儲。

22. SharedPreference 中 commit 和 apply 的區(qū)別

  1. apply 沒有返回值而 commit 返回 boolean 表明修改是否提交成功圆裕;
  2. apply 是將修改數(shù)據(jù)提交到內(nèi)存, 而后異步真正提交到硬件磁盤, 而 commit 是同步的提交到硬件磁盤广鳍。

效率上,apply > commit
安全上吓妆,commit > apply

23. 什么是混淆赊时,Android 中哪些類不能混淆

混淆,將類名耿战、方法名蛋叼、成員變量等重命名為無意義的簡短名稱,以增加逆向工程的難度剂陡,同時通過移除無用的類減少包的大小狈涮。

以下為不能混淆的類:

  1. 反射使用的類或變量;
  2. bean 對象鸭栖;
  3. 四大組件歌馍;
  4. 注解;
  5. 枚舉晕鹊;
  6. JNI 調(diào)用的 Java 方法松却;
  7. Java 調(diào)用的 Native 方法暴浦;
  8. JS 調(diào)用的 Java 方法;
  9. JavaScript 方法晓锻;
  10. Parcelable 序列化相關(guān)類歌焦;
  11. Gson、EventBus 等砚哆。

Android 混淆機(jī)制基礎(chǔ)

24. 什么是依賴倒置

依賴倒置原則:程序要依賴于抽象接口独撇,不依賴于具體實(shí)現(xiàn)。核心是面向接口編程躁锁。
高層不依賴底層纷铣,應(yīng)該依賴抽象,抽象不應(yīng)該依賴于細(xì)節(jié)战转,細(xì)節(jié)應(yīng)該依賴于抽象搜立。

25. Android 權(quán)限

Android權(quán)限適配(一)
Android權(quán)限適配(二)

1. 權(quán)限的分類
  1. 正常權(quán)限。AndroidManifest 配置即可獲得;
  2. 危險權(quán)限。6.0 之后定義的權(quán)限昂灵,有權(quán)限組的概念。不僅需要需要在 AndroidManifest 中配置社痛,還需要在使用前 check 是否真正擁有權(quán)限见转,以動態(tài)申請命雀。
  3. 特殊權(quán)限。如通知欄權(quán)限斩箫、自啟動吏砂、懸浮窗和無障礙輔助等。嚴(yán)格的來講乘客,這部分內(nèi)容不屬于 Android 權(quán)限部分狐血。因?yàn)樗鼈儾恍枰?app 中配置,而是需要用戶到系統(tǒng)對應(yīng)的設(shè)置頁面打開開關(guān)易核。但是實(shí)際開發(fā)中匈织,確實(shí)有這樣的需求:檢測能不能彈出通知,不能則提示用戶牡直,或直接跳轉(zhuǎn)到對應(yīng)頁面缀匕,引導(dǎo)用戶打開開關(guān)。故把這部分納入權(quán)限的范疇碰逸。
2. Android 中讀寫 SD 卡需要權(quán)限嗎

讀寫權(quán)限雖然是危險權(quán)限乡小,但并不是只要讀寫就要配置這倆個權(quán)限:

內(nèi)置存儲:
Context.getFileDir():/data/data/應(yīng)用包名/files/
Context.getCacheDir():/data/data/應(yīng)用包名/cache/
內(nèi)置存儲讀寫不需要配置任何權(quán)限。

外置sd卡:
Context.getExternalFilesDir():SDCard/Android/data/應(yīng)用包名/files/
Context.getExternalCacheDir():SDCard/Android/data/應(yīng)用包名/cache/
API<19 需要配置權(quán)限饵史,API>=19 不需要配置權(quán)限满钟。
即對于配置了讀寫權(quán)限的 app胜榔,使用"SDCard/Android/data/應(yīng)用包名/"讀寫操作時,4.4 系統(tǒng)以下因?yàn)榕渲昧藱?quán)限而能正常讀寫湃番,4.4 及以上系統(tǒng)因?yàn)椴恍枰獧?quán)限亦能正常讀寫夭织。但是為了不配置多余的權(quán)限,建議如下寫:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18"/>

以上文件夾吠撮,當(dāng) app 卸載時會被系統(tǒng)自動刪除摔癣。
其余 sd 卡位置,6.0 以上需要動態(tài)申請讀寫權(quán)限纬向。

讀寫權(quán)限只是為了限制 app 污染用戶 sd 卡择浊,保護(hù)手機(jī)安全,對于 app 而言逾条,讀寫操作是正常而必要的琢岩,故劃分出以上幾個文件,使 app 在無需權(quán)限的情況下能正常存儲必要的文件师脂,且能在被卸載時自動刪除這些文件担孔,以達(dá)到保護(hù)用戶的目的。

26. Android Profiler

Android Profiler 提供了四種工具吃警,分別對 CPU糕篇、內(nèi)存、流量和電量作監(jiān)測酌心,它們分別是 CPU Profiler拌消、Memory Profiler、Network Profiler 和 Energy Profiler安券。

Android Profiler(一)CPU Profiler
Android Profiler(二)Memory Profiler

CPU Profiler 主要用來檢測這些卡頓:方法耗時過長墩崩、啟動耗時過長、幀渲染過長等侯勉。
Memory Profiler 主要用來檢測這些卡頓:內(nèi)存占用過高鹦筹、內(nèi)存泄漏、內(nèi)存抖動導(dǎo)致的頻繁 GC 卡頓址貌。

使用 Memory Profiler 檢測以下幾種內(nèi)存泄漏:

  1. 累積性內(nèi)存泄漏铐拐。泄漏內(nèi)存不斷累積,最危險练对、最容易導(dǎo)致 OOM 的內(nèi)存泄漏遍蟋,容易檢測。
  2. 覆蓋性內(nèi)存泄露锹淌。泄漏對象會被新泄漏的對象覆蓋匿值,同樣相對容易檢測。
  3. 短期內(nèi)存泄漏赂摆。只會在短時間內(nèi)泄漏挟憔,如子線程耗時任務(wù)钟些、網(wǎng)絡(luò)調(diào)度等,不容易檢測绊谭,如果是重型頁面政恍、又恰好用戶手機(jī)不佳,還可能導(dǎo)致 OOM达传,這種情況推薦使用 LeakCanary 去檢測篙耗。

27. Android 項(xiàng)目圖片文件夾對應(yīng)的分辨率分別是

尺寸 分辨率 dpi
ldpi 240 x 320 120
mdpi 320 x 480 160
hdpi 480 x 800 240
xhdpi 720 x 1280 320
xxhdpi 1080 x 1920 480

Android 源碼機(jī)制

1. LruCache 和 DiskLruCache 原理

源碼分析 LruCache
源碼分析 DiskLruCache

LruCache

  1. LruCache 會創(chuàng)建一個固定大小的緩存池,并維持一個 LinkHashMap 來有序的緩存數(shù)據(jù)宪赶。
  2. 在往緩存池 put 或 get 數(shù)據(jù)的時候宗弯,LinkHashMap 會將最近使用的數(shù)據(jù)移動到隊尾。
  3. 在往緩存池 put 數(shù)據(jù)的時候搂妻,LruCache 會計算當(dāng)前已緩存數(shù)據(jù)的大小蒙保。如果當(dāng)前緩存數(shù)據(jù)超過了限定值,LruCache 會將 LinkHashMap 隊首的數(shù)據(jù)刪除欲主,直到緩存數(shù)據(jù)大小滿足限定值邓厕。

DiskLruCache 和 LruCache 區(qū)別不大,都是利用 LinkHashMap 實(shí)現(xiàn)緩存算法扁瓢。只不過 DiskLruCache 是硬盤緩存宰掉,故需要持久化 LinkHashMap 中維持的 Lru 順序關(guān)系鞠值。

Journal 文件詳細(xì)的記錄了緩存的操作記錄甥郑,以便于 app 啟動時嘱丢,可以根據(jù)之前的操作記錄,恢復(fù) LinkHashMap 的數(shù)據(jù)她紫。這份數(shù)據(jù)包括:有哪些緩存硅堆,以及這些緩存的 Lru 順序屿储。

2. Android 插件化和熱修復(fù)

1. 為什么使用插件化

Android 開發(fā)中經(jīng)常有這樣的情況:模塊化不清晰贿讹、方法數(shù)超限、想在不重新安裝 App 的情況下增加新的模塊够掠,基于這樣的需求民褂,催生出了 Android 插件化技術(shù)。

2. 什么是插件化/插件化的基本原理是什么

利用 DexClassLoader 可以加載外部 dex 文件以及包含 dex 的壓縮文件(apk 和 jar)的特點(diǎn)疯潭,通過 Hook 技術(shù)來啟動外部 dex 文件中的 Activity赊堪。

3. 詳細(xì)的描述下插件化實(shí)現(xiàn)/如何利用插件化啟動外部 apk

Android 啟動 Activity,大致可以分為以下幾步:

  1. 當(dāng)前 Activity 通知 AMS 檢測 AndroidManifest 中是否有目標(biāo) Activity竖哩;
  2. AMS 檢測到有目標(biāo) Activity哭廉,則通知當(dāng)前 Activity 休眠;
  3. AMS 檢測目標(biāo) Activity 進(jìn)程是否存在相叁,不存在通知 Zygote fork 進(jìn)程遵绰;
  4. 啟動目標(biāo) Activity辽幌。創(chuàng)建 Context,并通過 ClassLoader 創(chuàng)建 Activity椿访。

因?yàn)橐獙?shí)現(xiàn)插件化乌企,啟動外部 apk,所以可以依賴第 1成玫、4 步驟實(shí)現(xiàn)加酵。

針對第一步,在 AndroidManifest 中創(chuàng)建占位的 Activity哭当,然后通過 Hook猪腕,將本進(jìn)程里,把要送檢的 Activity 替換為占位 Activity 以繞過檢測钦勘。同時码撰,將真正要啟動的 Activity 暫存在 Intent 中。

針對第四步个盆,會進(jìn)入 ActivityThread 的 performLaunchActivity 方法脖岛,也就是要啟動 Activity 之前,再次利用 Hook 技術(shù)將真正要啟動的 Activity 還原出來颊亮。在啟動目標(biāo) Activity 時柴梆,利用 DexClassLoader 將外部 dex 文件加載進(jìn)來,創(chuàng)建目標(biāo) Activity 的 Class 文件终惑,同時將外部資源等也一并加載傳入绍在,然后啟動目標(biāo) Activity 即可。

實(shí)際過程是復(fù)雜的雹有,插件化的難點(diǎn)在于需要對系統(tǒng)源碼非常熟悉偿渡,以選擇合適的 Hook 點(diǎn),另外就是系統(tǒng)版本的兼容性霸奕。

4. 熱修復(fù)了解嗎溜宽,原理是什么

熱修復(fù)和插件化的原理大同小異,都是利用 DexClassLoader 來加載外部 dex 文件质帅。

Android 加載 Class 文件使用的是 ClassLoader 及其雙親委托原理适揉,即由下至上委托,由上至下查找煤惩,查找本應(yīng)用的類則是在 PathClassLoader 中嫉嘀,系統(tǒng)創(chuàng)建了一個 Dex 文件數(shù)組,會依次遍歷數(shù)組魄揉,直到找到目標(biāo) Class 為止剪侮。

利用這個原理,可以使用 DexClassLoader 將包含修復(fù) Class 的 Dex 文件加載進(jìn)來洛退,然后利用 Hook 技術(shù)瓣俯,將目標(biāo) Dex 插入到數(shù)組前列红淡,這樣加載類時由于先找到,實(shí)現(xiàn)了替換降铸。

當(dāng)然這只是熱修復(fù)方案的一種在旱。其他方法還有:

  1. 底層替換方案,原理是替換 C 中對 Java 方法的調(diào)用推掸,如替換入口桶蝎、替換 ArtMethod 結(jié)構(gòu)體(該結(jié)構(gòu)體包含了 Java 方法的所有信息,包括執(zhí)行入口谅畅、訪問權(quán)限登渣、所屬類和代碼執(zhí)行地址)。優(yōu)點(diǎn)是立即生效不用重啟毡泻。
  2. Instant Run 方案胜茧。利用 ASM 字節(jié)碼操作庫,在方法中注入替換代碼仇味。該代碼會判斷函數(shù)是否有修改呻顽,以決定是否執(zhí)行修改后的代碼。
5. 插件化丹墨、熱修復(fù)系列博客

1. ClassLoader 詳解 (Class 的加載)
2. HookJava 基礎(chǔ) (如何 Hook)
3. Android App 啟動流程源碼分析 (Activity 的啟動源碼)
4. 從零開始的 Android 插件化:宿主 app 加載 sd 卡 apk (如何一步一步實(shí)現(xiàn)基礎(chǔ)插件化)
5. 熱修復(fù)原理 (熱修復(fù)的種類廊遍、原理等)
6. 熱修復(fù)原理與基礎(chǔ)范例

3. Android 組件化

1. 為什么組件化

隨著 app 版本迭代,業(yè)務(wù)越來越復(fù)雜贩挣,單一模塊的功能越來越多喉前,每個工程師需要熟悉大量的代碼旭蠕,而且很難多人協(xié)作员咽,無論是維護(hù)還是編譯都耗時耗力闽铐。而且單一功能耦合嚴(yán)重寨辩,根本無法做單元測試,所以必須要有更加靈活的架構(gòu)贡耽。

安全性现使、可讀性叼屠、可維護(hù)性疯溺、可擴(kuò)展性都差

2. 如何實(shí)現(xiàn)組件化/描述下組件化方案

關(guān)于組件化论颅,可以從以下幾個方面描述:

  1. 組件化基礎(chǔ)架構(gòu)。包括架構(gòu)圖囱嫩,以及參考組件化框架 ComponentLight 源碼,說明如何利用輔助框架更好的實(shí)現(xiàn)架構(gòu)應(yīng)用漏设。(解決切換成本高墨闲、開發(fā)運(yùn)行不同依賴狀態(tài)干擾的問題)
  2. 組件化通信。EventBus 解耦郑口。
  3. 組件化跳轉(zhuǎn)鸳碧。隱式啟動盾鳞、ARouter 或 SimpleRouter
  4. 組件化存儲瞻离。GreenDao 對象關(guān)系型數(shù)據(jù)庫腾仅。
  5. 組件化權(quán)限。一種方式是僅將危險權(quán)限放入到具體子 module套利,好處是移除模塊危險權(quán)限也跟著移除推励;還有一種是將所有權(quán)限都放入到具體子 module,好處是最大程度解耦肉迫,但是增加了編譯合并檢測的消耗验辞。另外權(quán)限申請框架有 AndPermission。
  6. 組件化混淆喊衫。這里推薦是僅在主 module 混淆跌造、子 module 不混淆。因?yàn)檫@樣可以避免重復(fù)混淆而導(dǎo)致資源找不到族购,但缺點(diǎn)是需要子 module 開發(fā)人員與主 module 同步壳贪,存在溝通成本。
  7. 組件化分發(fā)朝墩。參考 Activity 組件化分發(fā)結(jié)構(gòu)收苏。主要優(yōu)勢是:不是通過 Activity 直接控制 Manager 來執(zhí)行相關(guān)模塊的業(yè)務(wù)懦鼠,而是讓模塊通過關(guān)注分發(fā)的生命周期睦袖,獨(dú)立的完成自身業(yè)務(wù)烈和,最大化解耦忘渔。
  8. 組件化流通。打包成 aar对蒲、上傳 maven 等蝠咆。參考 Android 倉庫解析
  9. Gradle 優(yōu)化联喘。也可以參考 Gradle 核心 個人筆記捂蕴。
  10. 其他優(yōu)化。如 findViewById玫荣,可以使用 ButterKnife(不推薦)、DataBinding(不推薦)官边、ViewBinding捐晶。再如編譯流程優(yōu)化,可以使用 gradle-task-tree池凄、InstantRun、并行編譯荔泳、重復(fù)任務(wù)過濾等。

組件化中需要注意的問題:

  1. AndroidManifest 是會合并的昨登,因此要注意包名胚想、Application 沖突等問題鸿吆。
  2. 子 module 編譯的靜態(tài)變量不能是 final 的,因此 switch 場景不能使用乘瓤,需要轉(zhuǎn)換為 if-else 形式危队。
  3. 同名資源叔锐,會保留主 module 資源禀苦,所以布局起名最好以 module 名開頭作區(qū)分。

Android 組件化架構(gòu) 個人筆記
Android 組件化框架 ComponentLight
Activity 組件化分發(fā)結(jié)構(gòu)

4. Handler 機(jī)制

1. 描述下 Handler 的原理

Handler 主要用于跨線程通信裆装。涉及 MessageQueue桅滋、Message、Looper/Handler 這四個類贞远。

首先是 Message擎勘,它是消息且蓬,是線程間通信的數(shù)據(jù)媒介昵仅;然后是 MessageQueue命辖,它是消息隊列,主要功能是向消息池投遞信息(MessageQueue.enqueueMessage)和取走消息池的信息(MessageQueue.next)登颓。它是個阻塞隊列者铜,因此當(dāng)消息為空時藻烤,它會阻塞取消息的線程绷雏。最后是 Looper怖亭,它會不斷循環(huán)的從 MessageQueue 中取出消息并執(zhí)行涎显。

以主線程為例說明,主線程在啟動時(ActivityThread.main)兴猩,會調(diào)用 Looper.prepare余佃、Looper.loop充包,來創(chuàng)建 MessageQueue退客,并開啟死循環(huán)從 MessageQueue 中取出消息,執(zhí)行 Message.target.handleMessage 方法箭跳。當(dāng)消息隊列為空時,此循環(huán)會被阻塞潭千。子線程通過調(diào)用 Handler.sendMessage 方法谱姓,將消息發(fā)送到消息隊列中,此時隊列不為空刨晴,循環(huán)被喚醒屉来,主線程獲取消息并執(zhí)行。

整個過程類似生產(chǎn)-消費(fèi)者模型狈癞。

2. 一個線程可以有幾個 Looper茄靠、幾個 MessageQueue 和幾個 Handler

Looper 利用了 ThreadLocal,因此每個線程只有一個 Looper蝶桶。
Looper 構(gòu)造函數(shù)中創(chuàng)建了 MessageQueue 對象慨绳,因此一個線程只有一個 MessageQueue。
Handler 在創(chuàng)建時與 Looper 和 MessageQueue 產(chǎn)生關(guān)聯(lián)真竖,因此可以有多個脐雪。

3. 可以在子線程直接創(chuàng)建一個 Handler 嗎,會出現(xiàn)什么問題疼邀,那該怎么做

不能喂江,Handler 依賴于 Looper,而 Looper 是消費(fèi)者線程(這里是子線程)消費(fèi) Message 的關(guān)鍵旁振,因此必須在 Looper.prepare 之后。

class MyThread extends Thread {

    public Handler mHandler;

    public void run() {
        Looper.prepare();   // 為線程創(chuàng)建 Looper 對象

        mHandler = new Handler() {  
            public void handleMessage(Message msg) {
               
            }
        };

        Looper.loop();   // 啟動消息循環(huán)
    }
}
4. Looper 死循環(huán)為什么不會導(dǎo)致應(yīng)用卡死涨岁,會消耗大量資源嗎

可以簡單的將 MessageQueue 理解為阻塞隊列拐袜,當(dāng)沒有消息時,主線程會進(jìn)入阻塞梢薪,釋放 CPU 資源蹬铺。binder 線程會通過 Handler 來喚醒主線程,而 binder 線程則會與系統(tǒng) AMS 進(jìn)行通信秉撇。實(shí)際上 Android 四大組件的生命周期也是這么運(yùn)行的甜攀。

深入點(diǎn)講,MessageQueue 實(shí)際上是管道琐馆。涉及到 Linux pipe/epoll 機(jī)制规阀,簡單說就是在主線程的 MessageQueue 沒有消息時,便阻塞在 Loop 的 queue.next() 中的 nativePollOnce() 方法里谁撼,此時主線程會釋放 CPU 資源進(jìn)入休眠狀態(tài),直到下個消息到達(dá)或者有事務(wù)發(fā)生滋饲,通過往 pipe 管道寫端寫入數(shù)據(jù)來喚醒主線程工作厉碟。這里采用的 epoll 機(jī)制喊巍,是一種 IO 多路復(fù)用機(jī)制,可以同時監(jiān)控多個描述符箍鼓,當(dāng)某個描述符就緒(讀或?qū)懢途w)崭参,則立刻通知相應(yīng)程序進(jìn)行讀或?qū)懖僮鳎举|(zhì)同步 I/O款咖,即讀寫是阻塞的何暮。 所以說,主線程大多數(shù)時候都是處于休眠狀態(tài)之剧,并不會消耗大量 CPU 資源郭卫。

真正會卡死主線程的操作是在回調(diào)方法 onCreate/onStart/onResume 等操作時間過長,會導(dǎo)致掉幀背稼,甚至發(fā)生 ANR贰军,looper.loop 本身不會導(dǎo)致應(yīng)用卡死。

5. ANR 的原理

產(chǎn)生 ANR 的條件:

  1. 輸入事件 5s 內(nèi)沒有處理完畢蟹肘;
  2. BroadcastReceiver 的 onReceive 函數(shù)時 10 秒內(nèi)沒有處理完畢词疼;
  3. Service 的各個生命周期函數(shù) 20 秒內(nèi)沒有處理完畢。

導(dǎo)致 ANR 的原因:

  1. 主線程執(zhí)行了耗時操作帘腹;如網(wǎng)絡(luò)請求贰盗、數(shù)據(jù)庫操作、文件操作阳欲。
  2. 其他【進(jìn)程】占用 CPU舵盈,導(dǎo)致本進(jìn)程得不到 CPU 時間片,比如其他進(jìn)程的頻繁讀寫操作可能會導(dǎo)致這個問題球化。
  3. UI 線程等待子線程釋放鎖秽晚,從而無法處理用戶請求;
  4. 耗時動畫導(dǎo)致 CPU 負(fù)載過重筒愚。

ANR 的實(shí)現(xiàn)原理:

在應(yīng)用收到輸入事件赴蝇、廣播或運(yùn)行服務(wù)時,AMS 的 Handler 會收到消息巢掺,并開啟一個定時任務(wù)(收到什么消息句伶、定多長時間,視場景而定)陆淀,如果未超時考余,則會取消掉該任務(wù),否則拋出 ANR 異常倔约。

ANR 的分析:

  1. 本地結(jié)合 logcat 日志分析秃殉。
  2. 遠(yuǎn)程使用保存在手機(jī)本地的 /data/anr/traces.txt 日志文件分析。
6. MessageQueue 是隊列嗎,它是什么數(shù)據(jù)結(jié)構(gòu)

MessageQueue 不是隊列钾军,它內(nèi)部使用一個 Message 鏈表實(shí)現(xiàn)消息的存和取鳄袍。 鏈表的排列依據(jù)是 Message.when,表示 Message 期望被分發(fā)的時間吏恭,該值是 SystemClock. uptimeMillis 與 delayMillis 之和拗小。

7. Handler.postDelayed 函數(shù)延時執(zhí)行計時是否準(zhǔn)確

當(dāng)上一個消息存在耗時任務(wù)的時候,會占用延時任務(wù)執(zhí)行的時機(jī)樱哼,實(shí)際延遲時間可能會超過預(yù)設(shè)延時時間哀九,這時候就不準(zhǔn)確了。

8. Handler.sendMessageDelayed 是延遲的把消息放入到 MessageQueue 中的嗎

不是搅幅。

Handler.sendMessageDelayed 實(shí)際調(diào)用了 sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);阅束,最終是在 queue.next 方法中處理的。

queue.next 在取消息時茄唐,如果頭部 Message 是有延遲且時間未到的息裸,不返回 Message 而且會計算下時間胧奔,然后利用 nativePollOnce 進(jìn)行阻塞馋缅。期間如果有新的消息進(jìn)來,并且這個消息不是延遲盾剩、或是比當(dāng)前延遲時間短蚁廓,這個消息就插入頭部并喚醒線程访圃。

這么設(shè)計的原因是,如果通過延遲將消息放入到 MessageQueue 中相嵌,那么多個延遲消息就需要多個定時器腿时。

參考源碼

if (msg != null) {
    if (now < msg.when) {
        // Next message is not ready.  Set a timeout to wake up when it is ready.
        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
        } else {
        // Got a message.
        mBlocked = false;
        if (prevMsg != null) {
            prevMsg.next = msg.next;
        } else {
            mMessages = msg.next;
        }
        msg.next = null;
        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
        msg.markInUse();
        return msg;
    }
} else {
    // No more messages.
    nextPollTimeoutMillis = -1;
}

SystemClock.uptimeMillis 是系統(tǒng)從開機(jī)啟動到現(xiàn)在的時間,期間不包括休眠的時間饭宾,獲得到的時間是一個相對的時間圈匆。之所以使用這種方式來計算時間,而不是獲得當(dāng)前 CurrentTime 來計算捏雌,在于 handler 會阻塞、掛起笆搓、睡眠等性湿,這些時候是不應(yīng)該執(zhí)行的。如果使用絕對時間的話满败,就會搶占資源來執(zhí)行當(dāng)前 handler 的內(nèi)容肤频,顯然這是不應(yīng)該出現(xiàn)的情況,所以要避免算墨。

MessageQueen.enqueueMessage 插入消息宵荒,MessageQueue.next 取消息。由此也可知:消息在加入消息隊列時就進(jìn)行好排序,鏈表頭的延遲時間小报咳,尾部延遲時間最大侠讯。

9. 你了解 HandlerThread 嗎

HandlerThread 可以作為消費(fèi)者,在子線程里處理 Message暑刃,其原理實(shí)際就是實(shí)現(xiàn)了 Looper.prepare厢漩、Looper.loop。

Handler 小節(jié)參考資料 談?wù)?Handler 機(jī)制和原理
Handler 小節(jié)參考資料 Handler postDelayed 的原理

5. Bundle 機(jī)制

1. 介紹下 Bundle

Bundle 是 Android 中傳遞數(shù)據(jù)的容器岩臣,通常用在 Intent溜嗜、Message.setData、Fragment.setArguments 等相互通信的方法中架谎。

Bundle 使用 final 修飾炸宵,意味著不可繼承。它實(shí)現(xiàn)了 Parcelable 接口谷扣。內(nèi)部則是通過 ArrayMap 去存儲數(shù)據(jù)土全。

2. ArrayMap 原理

ArrayMap 主要是針對內(nèi)存優(yōu)化的數(shù)據(jù)結(jié)構(gòu)。

內(nèi)部有倆個數(shù)組存儲數(shù)據(jù)抑钟,一個數(shù)組負(fù)責(zé)存儲 key 的 hash 值涯曲,另一個數(shù)組負(fù)責(zé)存儲 key 和 value。數(shù)組按照從小到大的順序排序在塔,添加幻件、刪除、查找數(shù)據(jù)的時候都是先使用二分查找法得到相應(yīng)的 index蛔溃,然后通過 index 來進(jìn)行添加绰沥、查找、刪除等操作贺待。

原理圖參考 此鏈接

6. HashMap 源碼徽曲,SparseArray 原理

1. HashMap 的工作原理

HashMap 基于哈希原理,hashCode 最大的特點(diǎn)是:不同的對象麸塞,hashCode 可能相同秃臣;同一個對象,hashCode 一定相同哪工“麓耍基于此,利用數(shù)組 + 鏈表的形式來實(shí)現(xiàn) HashMap雁比。

HashMap 通過 put 和 get 方法儲存和獲取對象稚虎。當(dāng)將鍵值對傳遞給 put 方法時,它調(diào)用鍵對象的 hashCode 方法來計算哈希值偎捎,然后找到 bucket 位置來儲存值對象蠢终。如果當(dāng)前位置已有對象了序攘,對象將會儲存在鏈表的下一個節(jié)點(diǎn)中。 當(dāng)獲取對象時寻拂,同樣是先用鍵對象的 hashCode 定位 bucket 位置程奠,然后利用 equals 找到目標(biāo)對象。

因此重寫 hashCode兜喻,一是要保證同一對象的 hashCode 一定相同梦染,二是要保證不同對象的 hashCode 可以相同、可以不同朴皆,但分布要均勻帕识。

2. HashMap 和 HashTable 的區(qū)別
  1. HashMap 不是線程安全的,HashTable 則相反遂铡;(ConcurrentHashMap 同樣是線程安全的肮疗,且擴(kuò)展性更好)
  2. HashMap 可以接受為 null 的鍵值。
  3. 父類不同扒接。HashMap 是 AbstractMap伪货,而 HashTable 繼承自 Dictionary。

讓 HashMap 轉(zhuǎn)為同步的方法:Map concurrentMap = Collections.synchronizeMap(hashMap);

3. HashMap 和 HashSet 的區(qū)別
  1. 實(shí)現(xiàn)接口不同钾怔。HashMap 實(shí)現(xiàn)了 Map 接口碱呼,HashSet 實(shí)現(xiàn)了 Set 接口;
  2. HashMap 存儲鍵值對宗侦,HashMap 僅存儲對象愚臀;
  3. HashMap 使用 put 添加對象,HashSet 使用 add矾利。

沒什么特別的區(qū)別姑裂,照著 map 和 set 的區(qū)別說即可。

4. HashMap 的大小超過了負(fù)載因子(load factor)定義的容量會怎么辦

HashMap 默認(rèn)的負(fù)載因子大小為 0.75男旗,也就是說舶斧,當(dāng)一個 map 填滿了 75% 的 bucket 時候,和其它集合類(如 ArrayList)一樣察皇,將會創(chuàng)建原來 HashMap 大小的兩倍的 bucket 數(shù)組茴厉,來重新調(diào)整 map 的大小,并將原來的對象放入新的 bucket 數(shù)組中什荣。

5. 為什么 String呀忧、Interger 這樣的類適合作為鍵
  1. 它們是 final、不可變的溃睹,因?yàn)殒I值改變可能導(dǎo)致 hashCode 改變,如果放入時和獲取時返回不同的 hashCode胰坟,那就不能正確找到對象因篇。
  2. 它們已經(jīng)重寫了 hashCode 和 equals 方法泞辐。

引申:什么樣的對象可以作為鍵?

6. 介紹下 SparseArray

稀疏數(shù)組竞滓。

SparseArray 是 android 里為 <Interger, Object> 這樣的 Hashmap 而專門寫的類咐吼,目的是提高內(nèi)存效率,其核心是折半查找函數(shù)(binarySearch)商佑。

SparseArray 有兩個優(yōu)點(diǎn):1.避免了自動裝箱(auto-boxing)2.數(shù)據(jù)結(jié)構(gòu)不依賴于外部對象映射锯茄。(SparseArray 內(nèi)部使用兩個一維數(shù)組來保存數(shù)據(jù),一個用來存 key茶没,一個用來存 value)

原理及參考鏈接

7. 介紹下 SurfaceView

如果繪制過程很復(fù)雜肌幽,并且界面更新還非常頻繁,這時候就會造成界面的卡頓抓半,影響用戶體驗(yàn)喂急,為此 Android 提供了 SurfaceView 來解決這一問題。

View 和 SurfaceView 的區(qū)別:

  1. View 適用于主動更新笛求,SurfaceView 適用于被動更新廊移,比如頻繁刷新界面;
  2. View 在主線程中刷新探入,而 SurfaceView 則開啟一個子線程來進(jìn)行刷新狡孔。
  3. SurfaceVie 在底層機(jī)制中實(shí)現(xiàn)了雙緩沖機(jī)制。

雙緩沖主要是為了解決反復(fù)局部刷屏帶來的閃爍蜂嗽,把要畫的東西先畫到一個內(nèi)存區(qū)域里苗膝,然后整體的一次性畫出來。

8. Android 里有哪些內(nèi)存泄露

長生命周期的對象持有短生命周期對象的引用徒爹,盡管短生命周期的對象不再使用荚醒,但是因?yàn)殚L生命周期對象持有它的引用而導(dǎo)致不能被回收。

  1. 靜態(tài)變量持有 Context隆嗅;
  2. 匿名內(nèi)部類導(dǎo)致內(nèi)存泄漏界阁。如子線程耗時任務(wù)、Handler胖喳、TimerTask 等泡躯。解決辦法:使用靜態(tài)內(nèi)部類代替內(nèi)部類,使用弱引用持有 Context丽焊,或在 onDestroy 中移除 Message较剃;
  3. 監(jiān)聽沒有取消注冊;
  4. 資源對象未關(guān)閉技健。如 Cursor写穴、File、Stream雌贱、Bitmap 等啊送。因?yàn)橘Y源性對象往往都用了一些緩沖偿短,緩沖不僅存在于 java 虛擬機(jī)內(nèi),還存在于 java 虛擬機(jī)外馋没。如果僅僅是把它的引用置 null昔逗,而不關(guān)閉它們,也會造成內(nèi)存泄漏篷朵。
  5. 容器類持有對象勾怒;
  6. WebView。解決方案是單開一個進(jìn)程声旺。

檢測內(nèi)存泄漏的工具:

  1. Memory Profiler笔链;
  2. LeakCanary;
  3. MAT艾少;輸入 HRPOF 文件卡乾,輸出分析結(jié)果。

9. Android app 里有哪些性能優(yōu)化方案

優(yōu)化種類缚够、方案很多幔妨,視情況而定,以下是一些類型及其舉例谍椅。

內(nèi)存優(yōu)化:

  1. 內(nèi)存泄漏的優(yōu)化误堡。靜態(tài)內(nèi)部類 + 弱引用、onDestroy 及時移除 Message雏吭,并把相關(guān)資源置空锁施,取消注冊、及時關(guān)閉 Cursor杖们、Bitmap 記得調(diào)用 recycle 等悉抵。
  2. 內(nèi)存占用的優(yōu)化。Bitmap 壓縮和復(fù)用摘完,枚舉使用注解替代等姥饰。
  3. 內(nèi)存抖動的優(yōu)化。避免在 onDraw 等頻繁調(diào)度的方法或循環(huán)中創(chuàng)建對象孝治。

包大小優(yōu)化:

  1. 刪除重復(fù)的庫炮温。
  2. 刪除無用的資源霎槐。
  3. 使用混淆。(混淆可以刪除未用到的庫)
  4. 圖片使用 SVG笤受、WebP 替代犯戏。

穩(wěn)定性優(yōu)化:

  1. 日志埋點(diǎn)監(jiān)控椒涯。如 Firebase垂蜗、無痕埋點(diǎn)等伟墙。crash 監(jiān)控 + 報警。

流暢性優(yōu)化:

  1. 使用線程池解決耗時問題手素;
  2. 避免過度繪制吕喘,避免繪制層級過高赘那;
  3. 使用 merge、ViewStub氯质、include 等元素。
  4. 列表優(yōu)化祠斧。如將過重的 item 拆分為多個 item闻察,因?yàn)榛瑒又量梢姇r才加載,所以變相優(yōu)化了重量級的 item琢锋。

include:將布局中的公共部分提取出來供其他 layout 復(fù)用辕漂;
merge:用于消除視圖層次結(jié)構(gòu)中的冗余視圖。例如根布局是 LinearLayout吴超,如果又 include 一個 LinerLayout 布局钉嘹,不僅無意義,還會減慢 UI 加載速度鲸阻。
ViewStub:占位 View跋涣。優(yōu)點(diǎn)是需要時才會加載,否則不會占用內(nèi)存鸟悴。通常用于如新手引導(dǎo)陈辱、消息提醒等。

省電性優(yōu)化:

  1. 加鎖等待细诸,讓出 CPU 資源沛贪,避免無意義的循環(huán)等待。
  2. 加緩存震贵,如使用享元模式等利赋。
  3. 對網(wǎng)絡(luò)數(shù)據(jù)進(jìn)行壓縮。

安全性優(yōu)化:

  1. 使用 so 庫存儲密鑰猩系。
  2. 使用 local.properties 存儲密鑰媚送,密鑰本地流通,不上傳 Git蝙眶。
  3. 對本地數(shù)據(jù)季希、網(wǎng)絡(luò)數(shù)據(jù)進(jìn)行加密等。
  4. 額外加固幽纷。如梆梆加固式塌。

構(gòu)建優(yōu)化:

  1. Gradle 優(yōu)化、使用 Gradle 插件友浸、進(jìn)行分包(Gradle3.2.x 分包填坑)等峰尝。

10. Android 的系統(tǒng)架構(gòu)

  1. 應(yīng)用程序?qū)印m攲铀械膽?yīng)用程序收恢,如通訊錄武学、瀏覽器等等祭往。
  2. 應(yīng)用程序框架層。為應(yīng)用程序提供的高級服務(wù)火窒。如 ActivityManager硼补、WindowManager、PackageManager熏矿、ResourceManager已骇、LocationManager 等等。
  3. 系統(tǒng)運(yùn)行庫層票编。一系列 C/C++ 程序庫的集合褪储,包括用于存儲的 SQLite、用于網(wǎng)絡(luò)安全的 SSL慧域、用于播放音視頻的庫鲤竹、瀏覽器引擎等,Android 的一些核心庫(如 android.app)以及虛擬機(jī)也在此層昔榴。
  4. Linux 內(nèi)核層辛藻。Linux 提供了系統(tǒng)最基本的功能:進(jìn)程管理、內(nèi)存管理论泛、設(shè)備管理以及網(wǎng)絡(luò)和大量設(shè)備驅(qū)動揩尸。
Android 系統(tǒng)架構(gòu)

詳細(xì)架構(gòu)解析

11. Android 啟動如何優(yōu)化

App 啟動我們可以優(yōu)化的過程包括【主 Application 啟動】和【主 Activity 啟動】,因此針對這倆個點(diǎn)做優(yōu)化屁奏。

Application:

  1. 延遲加載岩榆。一些耗時的 sdk 可以延遲、或需要時坟瓢、或使用 IdleHandler 在系統(tǒng)空閑時再加載勇边。
  2. 異步加載。把不需要同步等待的業(yè)務(wù)全部放到異步加載折联。
  3. 閃屏優(yōu)化粒褒。因?yàn)槔鋯訒葐涌瞻状翱冢賵?zhí)行 Application 初始化诚镰,所以可以先加背景圖奕坟,給用戶視覺已啟動的感覺。
  4. 緩存數(shù)據(jù)清笨。對于一些更新頻率比較低的配置信息月杉,或者資源等,可以采用緩存的方式避免每次啟動都去下載抠艾,從而節(jié)省啟動時間和 CPU 資源苛萎。
  5. dex 優(yōu)化。通過分包、精簡主包的方式去優(yōu)化啟動讀取 class 的效率腌歉。

MainActivity:

  1. 布局優(yōu)化蛙酪。簡化層級、減少過度繪制翘盖、暫時不需要顯示的 View 可以使用 ViewStub 占位桂塞。
  2. 延遲加載。為了讓用戶盡快看到主界面馍驯,在首頁繪制完畢后再做部分必須要在 UI 線程中執(zhí)行的邏輯藐俺,實(shí)現(xiàn)方式是通過監(jiān)聽 onFirstDrawFinish。
  3. 異步泥彤。

12. 一個應(yīng)用程序安裝到手機(jī)上時發(fā)生了什么

此問題等同于應(yīng)用程序的安裝過程。

  1. 將 apk 復(fù)制到 data/app 目錄下卿啡;
  2. 解壓 apk吟吝,將 dex 拷貝至 data/dalvik-cache,命名為 apk 路徑 + classes.dex颈娜;
  3. 在 data/data 目錄下創(chuàng)建對應(yīng)的包名目錄剑逃,并在此目錄下創(chuàng)建存儲應(yīng)用數(shù)據(jù)的相關(guān)目錄。

13. App 是如何沙箱化官辽,為什么要這么做蛹磺;

如何沙箱化:即每一個 Android 應(yīng)用程序都在自己的進(jìn)程中運(yùn)行,都擁有一個獨(dú)立的虛擬機(jī)實(shí)例同仆。應(yīng)用之間相互隔離萤捆、獨(dú)自運(yùn)行并禁止相互訪問。

至于權(quán)限隔離俗批,基礎(chǔ)是建立在 Linux 已有的 uid俗或、gid 上的 。Android 每安裝一個應(yīng)用程序岁忘,就會為它分配一個 uid辛慰。(其中普通 Android 應(yīng)用程序的 uid 是從 10000 開始分配,10000 以下是系統(tǒng)進(jìn)程的 uid)而對于普通應(yīng)用程序來說干像,gid 等于 uid帅腌。

為什么要沙箱:

主要是出于安全性考慮。

  1. 數(shù)據(jù)安全麻汰。應(yīng)用之間隔離數(shù)據(jù)不可胡亂訪問速客。
  2. 系統(tǒng)安全。一個進(jìn)程什乙、虛擬機(jī)的崩潰不會導(dǎo)致其他應(yīng)用出現(xiàn)問題挽封。
  3. 權(quán)限管理。可以為不同進(jìn)程劃分不同權(quán)限辅愿,方便系統(tǒng)管理智亮。

14. 圖片優(yōu)化,如何壓縮点待、如何緩存

優(yōu)化方案

  1. 壓縮圖片阔蛉;有質(zhì)量壓縮(像素不變,改變位深和透明度)癞埠、尺寸壓縮(降低像素)状原、采樣率壓縮(好處是不會將大圖讀入內(nèi)存,缺點(diǎn)是不能很好的保證圖片質(zhì)量)
  2. 內(nèi)存緩存苗踪;使用 LruCache 最近最少使用緩存颠区。
  3. 使用軟引用;內(nèi)存吃緊則回收對象通铲。

參考鏈接:Android 性能優(yōu)化圖片壓縮

15. 如何降低程序崩潰率

  1. 首先要保證代碼質(zhì)量毕莱。開發(fā)代碼要有統(tǒng)一的約定;開發(fā)時 Lint 全局掃描颅夺,解決報警問題朋截;提交代碼有完善的 review 檢測機(jī)制。
  2. 發(fā)版前測試吧黄。包括 QA 全量回歸測試部服、云平臺各種機(jī)型穩(wěn)定性測試、崩潰分析等等拗慨。
  3. 上線后的系統(tǒng)監(jiān)控廓八。如 Firebase 線上監(jiān)控、Google 統(tǒng)計胆描、無痕埋點(diǎn)等瘫想。
  4. 上線后的解決方案:熱修復(fù)(小 bug)或重發(fā)穩(wěn)定版本(提前準(zhǔn)備穩(wěn)定版本,重大 bug 發(fā)版覆蓋)昌讲。

16. OOM 原因及如何定位

OOM 的原因:

  1. 內(nèi)存泄漏導(dǎo)致国夜。
  2. 加載的文件或圖片過大;

定位方法:

  1. 使用 Memory Profiler短绸,動態(tài)跟蹤內(nèi)存使用信息车吹;
  2. 使用 LeakCanary 定位內(nèi)存泄漏;
  3. 使用 MAT醋闭,分析 hprof 文件窄驹,檢測內(nèi)存泄漏。

17. 描述下 Glide

Android 的圖片加載和緩存庫证逻。

特色優(yōu)化:

1.滑動監(jiān)聽乐埠,滑動時暫停加載,停止時開始加載,對應(yīng)方法:resumeRequests丈咐,pauseRequests瑞眼。
2.默認(rèn)基于 HttpUrlConnection,可以替換為 OkHttp棵逊。

細(xì)節(jié):

1.基于三級緩存伤疙,內(nèi)存、硬盤辆影、網(wǎng)絡(luò)徒像。
2.弱引用 + LruCache 共同完成內(nèi)存緩存功能。
3.內(nèi)存緩存最大為:每個進(jìn)程最大可用內(nèi)存 * 0.4蛙讥,低配手機(jī)會替換為 0.33锯蛀。
4.磁盤緩存為 250m膨疏。

18. AsyncTask 機(jī)制及缺點(diǎn)(了解)

AsyncTask 內(nèi)部其實(shí)就是使用子線程(更準(zhǔn)確的是 FutureTask)+ Handler 實(shí)現(xiàn)的胚鸯。

缺點(diǎn)如下:

  1. AsyncTask 不會隨 Activity 銷毀而銷毀,而一直要等 doInBackground 執(zhí)行完畢硫嘶,可能導(dǎo)致內(nèi)存泄漏经备;
  2. 內(nèi)部線程池上限為 128 個線程,當(dāng)隊列已滿部默,將會拋出 RejectedExecutionException侵蒙,而且還存在大量消耗系統(tǒng)資源導(dǎo)致系統(tǒng)崩潰的風(fēng)險。

在 Android 1.6 之前的版本傅蹂,AsyncTask 是串行的纷闺,在 1.6 至 2.3 的版本,改成了并行的份蝴。在 2.3 之后的版本又做了修改犁功,可以支持并行和串行,當(dāng)想要串行執(zhí)行時婚夫,直接執(zhí)行 execute 方法浸卦,如果需要并行執(zhí)行,則要執(zhí)行 executeOnExecutor(Executor)案糙。

19. 列舉幾種常見的 RuntimeException限嫌,并說明 RuntimeException 和 Exception 區(qū)別

常見的RuntimeException:

  1. NullPointerException 空指針異常
  2. ClassCastException 類型轉(zhuǎn)換異常
  3. IllegalArgumentException 傳遞非法參數(shù)
  4. IndexOutOfBoundsException 數(shù)組下標(biāo)越界
  5. NumberFormatException 數(shù)組格式異常
  6. SecurityException 安全異常

Error 和 Exception 區(qū)別:

Error 是編譯時錯誤和系統(tǒng)錯誤,而 Exception 是可以被拋出的基本類型时捌。

RuntimeException 和 Exception 區(qū)別:

Exception 必須被開發(fā)者解決后才能編譯通過怒医,解決方式可以 throw 到上層,也可以 try-catch 處理奢讨。如 IOException稚叹,而 RuntimeException 是運(yùn)行時異常,不受異常檢查。

20. OOM 是否可以 try-catch

只在特殊情況下可行:即 try 中聲明了很大的對象扒袖,導(dǎo)致 OOM塞茅。

但是通常情況下,OOM 可能是由內(nèi)存泄漏等其他原因引起的僚稿,很難捕獲凡桥。針對大對象,Java 也提供了 SoftReference蚀同、LruCache 解決內(nèi)存問題缅刽。

21. try-catch 實(shí)現(xiàn)原理是什么,對性能有影響嗎

實(shí)現(xiàn)原理:

當(dāng)把 Java 源碼編譯成相應(yīng)的字節(jié)碼蠢络,如果方法內(nèi)有 try-catch 異常處理衰猛,就會產(chǎn)生與該方法相關(guān)聯(lián)的異常表,異常表記錄的是 try 的起點(diǎn)和終點(diǎn)刹孔、catch 方法體所在的位置啡省,以及聲明捕獲的異常種類。通過這些信息髓霞,當(dāng)程序出現(xiàn)異常時卦睹,Java 虛擬機(jī)就會查找方法對應(yīng)的異常表,如果發(fā)現(xiàn)有聲明的異常與拋出的異常類型匹配就會跳轉(zhuǎn)到 catch 處執(zhí)行相應(yīng)的邏輯方库,如果沒有匹配成功结序,就會回到上層調(diào)用方法中繼續(xù)查找,如此反復(fù)纵潦,一直到異常被處理為止徐鹤,或者停止進(jìn)程。所以邀层,try 在反映到字節(jié)碼上的就是產(chǎn)生一張異常表返敬,只有發(fā)生異常時才會被使用。

對性能的影響:

try-catch 與未使用 try-catch 代碼區(qū)別在于寥院,前者阻止 Java 對 try 塊內(nèi)代碼的一些優(yōu)化劲赠,例如重排序。try-catch 里面的代碼是不會被編譯器優(yōu)化重排的秸谢。所以在實(shí)際編程中经磅,提倡 try 代碼塊的范圍盡量小,這樣才可以充分發(fā)揮 Java 對代碼的優(yōu)化钮追。

參考鏈接

Android View

1. View 基礎(chǔ)

View 全解析.pdf 提取碼 7hn2

2. View 的生命周期如何變化预厌,以及生命周期和 Activity 的對應(yīng)關(guān)系

View 生命周期調(diào)用順序

生命周期調(diào)用順序

View 與 Activity 生命周期關(guān)聯(lián)關(guān)系

View 與 Activity 生命周期關(guān)聯(lián)關(guān)系

3. 描述下 Android View 的加載、繪制流程

加載流程:

  1. Activity.setContentView 間接調(diào)用了 PhoneWindow.setContentView元媚;
  2. 在此方法中通過 LayoutInflate 去加載對象轧叽,原理是利用 xml 的 pull 解析去解析 xml 布局文件苗沧,最終通過反射創(chuàng)建 view。

繪制流程:

Activity 啟動時炭晒,會將 DecorView 與 ViewRoot 建立關(guān)聯(lián)待逞,之后 ViewRoot 的 requestLayout 會被調(diào)用,最終將調(diào)用 ViewRootImpl.performTraversals网严,它是整個繪制的起點(diǎn)识樱。

performTraversals 會依次調(diào)用 measure、layout震束、draw 三大繪制方法怜庸。其中 measure 用于計算視圖大小,layout 用于擺放視圖位置垢村,draw 用于繪制視圖割疾。

draw 的繪制過程:

  1. 繪制背景;
  2. 通過 onDraw 繪制自身內(nèi)容;
  3. 通過 dispatchDraw 繪制子 View;
  4. 繪制滾動條;

4. onTouchListener嘉栓、onTouchEvent宏榕、onClickListener 的執(zhí)行順序

優(yōu)先級上,onTouchListener > onTouchEvent > onClickListener侵佃,onTouchListener 返回 true 則攔截事件麻昼。

5. 描述下 View 事件傳遞分發(fā)機(jī)制

Activity → Window(PhoneWindow)→ DecorView → ViewGroup → View

ViewGroup.dispatchTouchEvent → ViewGroup.onInterceptTouchEvent → View.dispatchTouchEvent → View.onTouchEvent → ViewGroup.onTouchEvent

各種場景下的各生命周期方法最好親測熟記。

Android 第三方庫源碼解析

1. Volley

2. OkHttp

3. RxJava

4. Retrofit

5. EventBus

6. Jetpack

持續(xù)更新中...

目錄大綱

Java 基礎(chǔ)

  1. ==馋辈、equals 和 hashCode 的區(qū)別
  2. 基礎(chǔ)數(shù)據(jù)類型各占多少字節(jié)
  3. 對多態(tài)的理解
  4. String涌献、StringBuffer 與 StringBuilder 的區(qū)別
  5. 父類的靜態(tài)方法能否被子類重寫
  6. 進(jìn)程和線程的區(qū)別
  7. final、finally 和 finalize 的區(qū)別
  8. Serializable 和 Parcelable 的區(qū)別
  9. 內(nèi)部類的設(shè)計意圖及種類
  10. 鎖機(jī)制 synchronized首有、volatile、Lock
  11. 常見編碼和字節(jié)占用數(shù)
  12. 深拷貝和淺拷貝的區(qū)別
  13. 靜態(tài)代理和動態(tài)代理的區(qū)別枢劝,什么場景使用
  14. 如何將一個 Java 對象序列化到文件里
  15. 談?wù)剬ψ⒔獾睦斫?/li>
  16. 談?wù)剬σ蕾囎⑷氲睦斫?/li>
  17. 談?wù)剬Ψ盒偷睦斫?/li>

Java 虛擬機(jī)

  1. APK 的生成
  2. APK 的安裝方式(JIT井联、AOT)、Dalvik 和 ART 的異同等
  3. Java 內(nèi)存結(jié)構(gòu)
  4. Java 內(nèi)存模型
  5. Java 引用類型
  6. 垃圾識別策略
  7. 垃圾回收算法
  8. 類的加載過程(如何從 class 轉(zhuǎn)變?yōu)楸惶摂M機(jī)識別的類)
  9. 虛引用的功能及原理

Java 多線程

  1. 開啟線程的幾種方式
  2. 線程的幾種狀態(tài)
  3. 如何控制某個方法允許并發(fā)訪問線程的個數(shù)
  4. wait您旁、sleep 的區(qū)別
  5. 什么可以導(dǎo)致線程阻塞/為什么會出現(xiàn)線程阻塞
  6. 線程如何關(guān)閉
  7. Java 中實(shí)現(xiàn)同步的幾種方法(如何實(shí)現(xiàn)線程同步)
  8. ThreadLocal 原理烙常,實(shí)現(xiàn)及如何保證 Local 屬性?
  9. 什么是線程安全鹤盒?如何保證線程安全蚕脏?(如何實(shí)現(xiàn)線程同步)
  10. 如何保證 List 線程安全?(線程間操作 List)
  11. Java 對象的生命周期
  12. 談?wù)?synchronized 中的類鎖侦锯、方法鎖和可重入鎖
  13. synchronized 的原理
  14. synchronized 與 Lock 的區(qū)別
  15. 什么是死鎖驼鞭,如何避免死鎖
  16. ReentrantLock 的內(nèi)部實(shí)現(xiàn)
  17. 死鎖的四個必要條件
  18. 線程池常見問題
    1. 為什么使用線程池,線程池的優(yōu)勢或好處
    2. 線程池類的繼承關(guān)系
    3. ThreadPoolExecutor 有哪些參數(shù)尺碰,作用分別是什么
    4. 線程池的任務(wù)執(zhí)行規(guī)則
    5. 有哪些固定類型的線程池
  19. 談?wù)剬Χ嗑€程(并發(fā)編程)的理解
    1. 為什么使用多線程(使用多線程的好處挣棕,使用多線程解決了哪些問題)
    2. 如何實(shí)現(xiàn)多線程
    3. 多線程有哪些問題译隘,如何解決多線程問題
  20. 談?wù)勀銓Χ嗑€程同步機(jī)制的理解
  21. 多線程斷點(diǎn)續(xù)傳原理

Android 基礎(chǔ)

  1. 四大組件及其簡單介紹
  2. Activity 在各種場景下的生命周期變化
  3. Activity 的四種啟動模式
  4. Activity 之間的通信方式
  5. App 的啟動流程,以及 Activity 的啟動過程
  6. 橫豎屏切換的時候洛心,Activity 的生命周期
  7. Activity 與 Fragment 之間生命周期比較
  8. Fragment 在各場景下的生命周期變化
  9. Fragment 之間以及和 Activity 的通信
  10. 本地廣播和全局廣播有什么差別固耘?
  11. ContentProvider、ContentResolver词身、ContentObserver 之間的關(guān)系
  12. 廣播使用的方式和場景
  13. 廣播里可以直接啟動 Activity 嗎
  14. AlertDialog厅目、PopupWindow 的區(qū)別
  15. getApplication 和 getApplicationContext 的區(qū)別
  16. Application 和 Activity 的 Context 對象的區(qū)別
  17. 描述下 Android 的幾種動畫
  18. 屬性動畫的特征
  19. 如何導(dǎo)入已有的外部數(shù)據(jù)庫
  20. Android 中三種播放視頻的方式
  21. 介紹下 Android 中數(shù)據(jù)存儲的方式
  22. SharedPreference 中 commit 和 apply 的區(qū)別
  23. 什么是混淆,Android 中哪些類不能混淆
  24. 什么是依賴倒置
  25. Android 權(quán)限
    1. 權(quán)限的分類
    2. Android 中讀寫 SD 卡需要權(quán)限嗎
  26. Android Profiler
  27. Android 項(xiàng)目圖片文件夾對應(yīng)的分辨率

Http 相關(guān)

  1. https 和 http 的區(qū)別
  2. https 的請求過程
  3. 描述下網(wǎng)絡(luò)的幾層結(jié)構(gòu)
  4. http 訪問網(wǎng)頁的完整流程
  5. 描述下 http 的三次握手
  6. 網(wǎng)絡(luò)連接中使用到哪些設(shè)備
  7. http2 和 http1.1 的區(qū)別
  8. 描述下網(wǎng)絡(luò)通信協(xié)議
  9. 描述下 http 的消息結(jié)構(gòu)
  10. http 的請求類型有哪些

Android View

  1. View 基礎(chǔ)
  2. View 的生命周期如何變化法严,以及生命周期和 Activity 的對應(yīng)關(guān)系
  3. 描述下 Android View 的加載损敷、繪制流程
  4. onTouchListener、onTouchEvent渐夸、onClickListener 的執(zhí)行順序
  5. 描述下 View 事件傳遞分發(fā)機(jī)制

Android 源碼機(jī)制

  1. LruCache 和 DiskLruCache 原理
  2. Android 插件化和熱修復(fù)
    1. 為什么使用插件化
    2. 什么是插件化/插件化的基本原理是什么
    3. 詳細(xì)的描述下插件化實(shí)現(xiàn)/如何利用插件化啟動外部 apk
    4. 熱修復(fù)了解嗎嗤锉,原理是什么
    5. 插件化、熱修復(fù)系列博客
  3. Android 組件化
    1. 為什么組件化
    2. 如何實(shí)現(xiàn)組件化/描述下組件化方案
  4. Handler 機(jī)制
    1. 描述下 Handler 的原理
    2. 一個線程可以有幾個 Looper墓塌、幾個 MessageQueue 和幾個 Handler
    3. 可以在子線程直接創(chuàng)建一個 Handler 嗎瘟忱,會出現(xiàn)什么問題,那該怎么做
    4. Looper 死循環(huán)為什么不會導(dǎo)致應(yīng)用卡死苫幢,會消耗大量資源嗎
    5. ANR 的原理
    6. MessageQueue 是隊列嗎访诱,它是什么數(shù)據(jù)結(jié)構(gòu)
    7. Handler.postDelayed 函數(shù)延時執(zhí)行計時是否準(zhǔn)確
    8. Handler.sendMessageDelayed 是延遲的把消息放入到 MessageQueue 中的嗎
    9. 你了解 HandlerThread 嗎
  5. Bundle 機(jī)制
    1. 介紹下 Bundle
    2. ArrayMap 原理
  6. HashMap 源碼,SparseArray 原理
    1. HashMap 的工作原理
    2. HashMap 和 HashTable 的區(qū)別
    3. HashMap 和 HashSet 的區(qū)別
    4. HashMap 的大小超過了負(fù)載因子(load factor)定義的容量會怎么辦
    5. 為什么 String韩肝、Interger 適合作為鍵
    6. 介紹下 SparseArray
  7. 介紹下 SurfaceView
  8. Android 里有哪些內(nèi)存泄露
  9. Android app 里有哪些性能優(yōu)化方案
  10. Android 的系統(tǒng)架構(gòu)
  11. Android 啟動如何優(yōu)化
  12. 一個應(yīng)用程序安裝到手機(jī)上時發(fā)生了什么
  13. App 是如何沙箱化触菜,為什么要這么做;
  14. 圖片優(yōu)化哀峻,如何壓縮涡相、如何緩存
  15. 如何降低程序崩潰率
  16. OOM 原因及如何定位
  17. 描述下 Glide
  18. AsyncTask 機(jī)制及缺點(diǎn)(了解)
  19. 列舉幾種常見的 RuntimeException,并說明 RuntimeException 和 Exception 區(qū)別
  20. OOM 是否可以 try-catch
  21. try-catch 實(shí)現(xiàn)原理是什么剩蟀,對性能有影響嗎

Android 第三方庫源碼解析

  1. Volley
  2. OkHttp
  3. RxJava
  4. Retrofit
  5. EventBus
  6. Jetpack

[TOC]

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末催蝗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子育特,更是在濱河造成了極大的恐慌丙号,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缰冤,死亡現(xiàn)場離奇詭異犬缨,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)棉浸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進(jìn)店門怀薛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人迷郑,你說我怎么就攤上這事乾戏∮乜粒” “怎么了?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵鼓择,是天一觀的道長三幻。 經(jīng)常有香客問我,道長呐能,這世上最難降的妖魔是什么念搬? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮摆出,結(jié)果婚禮上朗徊,老公的妹妹穿的比我還像新娘。我一直安慰自己偎漫,他們只是感情好爷恳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著象踊,像睡著了一般温亲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上杯矩,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天栈虚,我揣著相機(jī)與錄音,去河邊找鬼史隆。 笑死魂务,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的泌射。 我是一名探鬼主播粘姜,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼熔酷!你這毒婦竟也來了孤紧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤纯陨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后留储,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體翼抠,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年获讳,在試婚紗的時候發(fā)現(xiàn)自己被綠了阴颖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡丐膝,死狀恐怖量愧,靈堂內(nèi)的尸體忽然破棺而出钾菊,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站菇用,受9級特大地震影響瞪醋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜坛增,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧料饥,春花似錦、人聲如沸朱监。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赫编。三九已至巡蘸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間沛慢,已是汗流浹背赡若。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留团甲,地道東北人逾冬。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像躺苦,于是被迫代替她去往敵國和親身腻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評論 2 356

推薦閱讀更多精彩內(nèi)容