摘要:和體現(xiàn)了對不同異常情況的分類。是程序正常運行中,可以預(yù)料的意外情況,可能并且應(yīng)該被捕獲,進行相應(yīng)的處理。是指在正常情況下,不大可能出現(xiàn)的情況,絕大部分都會使程序處于非正常不可恢復(fù)的狀態(tài)。常見的非對稱加密包括等。
面試,無非都是問上面這些問題(挺多的 - -!),聘請中高級的安卓開發(fā)會往深的去問,并且會問一延伸二。以下我先提出幾點重點,是面試官基本必問的問題,請一定要去了解!
基礎(chǔ)知識 – 四大組件(生命周期,使用場景,如何啟動)
java基礎(chǔ) – 數(shù)據(jù)結(jié)構(gòu),線程,mvc框架
通信 – 網(wǎng)絡(luò)連接(HttpClient,HttpUrlConnetion),Socket
數(shù)據(jù)持久化 – SQLite,SharedPreferences,ContentProvider
性能優(yōu)化 – 布局優(yōu)化,內(nèi)存優(yōu)化,電量優(yōu)化
安全 – 數(shù)據(jù)加密,代碼混淆,WebView/Js調(diào)用,https
UI– 動畫
其他 – JNI,AIDL,Handler,Intent等
開源框架 – Volley,Gilde,RxJava等(簡歷上寫你會的,用過的)
拓展 – Android6.0/7.0/8.0/9.0特性,kotlin語言,I/O大會
急急忙忙投簡歷,趕面試,還不如沉淀一兩天時間,再過一遍以上內(nèi)容。想穩(wěn)妥拿到一個offer,最好能理解實現(xiàn)原理,并且知道使用場景了。不要去背!要去理解!面試官聽了一天這些內(nèi)容是很厭倦的,最好能說出一些自己的見解。
Java中引用類型的區(qū)別,具體的使用場景Java中引用類型分為四類:強引用、軟引用、弱引用、虛引用。
強引用:強引用指的是通過new對象創(chuàng)建的引用,垃圾回收器即使是內(nèi)存不足也不會回收強引用指向的對象。
軟引用:軟引用是通過SoftRefrence實現(xiàn)的,它的生命周期比強引用短,在內(nèi)存不足,拋出OOM之前,垃圾回收器會回收軟引用引用的對象。軟引用常見的使用場景是存儲一些內(nèi)存敏感的緩存,當(dāng)內(nèi)存不足時會被回收。
弱引用:弱引用是通過WeakRefrence實現(xiàn)的,它的生命周期比軟引用還短,GC只要掃描到弱引用的對象就會回收。弱引用常見的使用場景也是存儲一些內(nèi)存敏感的緩存。
虛引用:虛引用是通過FanttomRefrence實現(xiàn)的,它的生命周期最短,隨時可能被回收。如果一個對象只被虛引用引用,我們無法通過虛引用來訪問這個對象的任何屬性和方法。它的作用僅僅是保證對象在finalize后,做某些事情。虛引用常見的使用場景是跟蹤對象被垃圾回收的活動,當(dāng)一個虛引用關(guān)聯(lián)的對象被垃圾回收器回收之前會收到一條系統(tǒng)通知。
Exception和Error的區(qū)別Exception和Error都繼承于Throwable,在Java中,只有Throwable類型的對象才能被throw或者catch,它是異常處理機制的基本組成類型。
Exception和Error體現(xiàn)了Java對不同異常情況的分類。Exception是程序正常運行中,可以預(yù)料的意外情況,可能并且應(yīng)該被捕獲,進行相應(yīng)的處理。
Error是指在正常情況下,不大可能出現(xiàn)的情況,絕大部分Error都會使程序處于非正常、不可恢復(fù)的狀態(tài)。既然是非正常,所以不便于也不需要捕獲,常見的OutOfMemoryError就是Error的子類。
Exception又分為checked Exception和unchecked Exception。checked Exception在代碼里必須顯式的進行捕獲,這是編譯器檢查的一部分。unchecked Exception也就是運行時異常,類似空指針異常、數(shù)組越界等,通常是可以避免的邏輯錯誤,具體根據(jù)需求來判斷是否需要捕獲,并不會在編譯器強制要求。
volatile一般提到volatile,就不得不提到內(nèi)存模型相關(guān)的概念。我們都知道,在程序運行中,每條指令都是由CPU執(zhí)行的,而指令的執(zhí)行過程中,勢必涉及到數(shù)據(jù)的讀取和寫入。程序運行中的數(shù)據(jù)都存放在主存中,這樣會有一個問題,由于CPU的執(zhí)行速度是要遠高于主存的讀寫速度,所以直接從主存中讀寫數(shù)據(jù)會降低CPU的效率。為了解決這個問題,就有了高速緩存的概念,在每個CPU中都有高速緩存,它會事先從主存中讀取數(shù)據(jù),在CPU運算之后在合適的時候刷新到主存中。
這樣的運行模式在單線程中是沒有任何問題的,但在多線程中,會導(dǎo)致緩存一致性的問題。舉個簡單的例子:i=i+1 ,在兩個線程中執(zhí)行這句代碼,假設(shè)i的初始值為0。我們期望兩個線程運行后得到2,那么有這樣的一種情況,兩個線程都從主存中讀取i到各自的高速緩存中,這時候兩個線程中的i都為0。在線程1執(zhí)行完畢得到i=1,將之刷新到主存后,線程2開始執(zhí)行,由于線程2中的i是高速緩存中的0,所以在執(zhí)行完線程2之后刷新到主存的i仍舊是1。
所以這就導(dǎo)致了對共享變量的緩存一致性的問題,那么為了解決這個問題,提出了緩存一致性協(xié)議:當(dāng)CPU在寫數(shù)據(jù)時,如果發(fā)現(xiàn)操作的是共享變量,它會通知其他CPU將它們內(nèi)部的這個共享變量置為無效狀態(tài),當(dāng)其他CPU讀取緩存中的共享變量時,發(fā)現(xiàn)這個變量是無效的,它會從新從主存中讀取最新的值。
在Java的多線程開發(fā)中,有三個重要概念:原子性、可見性、有序性。
原子性:一個或多個操作要么都不執(zhí)行,要么都執(zhí)行。
可見性:一個線程中對共享變量(類中的成員變量或靜態(tài)變量)的修改,在其他線程立即可見。
有序性:程序執(zhí)行的順序按照代碼的順序執(zhí)行。
把一個變量聲明為volatile,其實就是保證了可見性和有序性。
可見性我上面已經(jīng)說過了,在多線程開發(fā)中是很有必要的。這個有序性還是得說一下,為了執(zhí)行的效率,有時候會發(fā)生指令重排,這在單線程中指令重排之后的輸出與我們的代碼邏輯輸出還是一致的。但在多線程中就可能發(fā)生問題,volatile在一定程度上可以避免指令重排。
volatile的原理是在生成的匯編代碼中多了一個lock前綴指令,這個前綴指令相當(dāng)于一個內(nèi)存屏障,這個內(nèi)存屏障有3個作用:
確保指令重排的時候不會把屏障后的指令排在屏障前,確保不會把屏障前的指令排在屏障后。
修改緩存中的共享變量后立即刷新到主存中。
當(dāng)執(zhí)行寫操作時會導(dǎo)致其他CPU中的緩存無效。
網(wǎng)絡(luò)相關(guān)面試題 http 狀態(tài)碼 http 與 https 的區(qū)別?https 是如何工作的?http是超文本傳輸協(xié)議,而https可以簡單理解為安全的http協(xié)議。https通過在http協(xié)議下添加了一層ssl協(xié)議對數(shù)據(jù)進行加密從而保證了安全。https的作用主要有兩點:建立安全的信息傳輸通道,保證數(shù)據(jù)傳輸安全;確認網(wǎng)站的真實性。
http與https的區(qū)別主要如下:
https需要到CA申請證書,很少免費,因而需要一定的費用
http是明文傳輸,安全性低;而https在http的基礎(chǔ)上通過ssl加密,安全性高
二者的默認端口不一樣,http使用的默認端口是80;https使用的默認端口是443
https的工作流程
提到https的話首先要說到加密算法,加密算法分為兩類:對稱加密和非對稱加密。
對稱加密:加密和解密用的都是相同的秘鑰,優(yōu)點是速度快,缺點是安全性低。常見的對稱加密算法有DES、AES等等。
非對稱加密:非對稱加密有一個秘鑰對,分為公鑰和私鑰。一般來說,私鑰自己持有,公鑰可以公開給對方,優(yōu)點是安全性比對稱加密高,缺點是數(shù)據(jù)傳輸效率比對稱加密低。采用公鑰加密的信息只有對應(yīng)的私鑰可以解密。常見的非對稱加密包括RSA等。
在正式的使用場景中一般都是對稱加密和非對稱加密結(jié)合使用,使用非對稱加密完成秘鑰的傳遞,然后使用對稱秘鑰進行數(shù)據(jù)加密和解密。二者結(jié)合既保證了安全性,又提高了數(shù)據(jù)傳輸效率。
https的具體流程如下:
客戶端(通常是瀏覽器)先向服務(wù)器發(fā)出加密通信的請求
支持的協(xié)議版本,比如TLS 1.0版
一個客戶端生成的隨機數(shù) random1,稍后用于生成”對話密鑰”
支持的加密方法,比如RSA公鑰加密
支持的壓縮方法
服務(wù)器收到請求,然后響應(yīng)
確認使用的加密通信協(xié)議版本,比如TLS 1.0版本。如果瀏覽器與服務(wù)器支持的版本不一致,服務(wù)器關(guān)閉加密通信
一個服務(wù)器生成的隨機數(shù)random2,稍后用于生成”對話密鑰”
確認使用的加密方法,比如RSA公鑰加密
服務(wù)器證書
客戶端收到證書之后會首先會進行驗證
首先驗證證書的安全性
驗證通過之后,客戶端會生成一個隨機數(shù)pre-master secret,然后使用證書中的公鑰進行加密,然后傳遞給服務(wù)器端
服務(wù)器收到使用公鑰加密的內(nèi)容,在服務(wù)器端使用私鑰解密之后獲得隨機數(shù)pre-master secret,然后根據(jù)radom1、radom2、pre-master secret通過一定的算法得出一個對稱加密的秘鑰,作為后面交互過程中使用對稱秘鑰。同時客戶端也會使用radom1、radom2、pre-master secret,和同樣的算法生成對稱秘鑰。
然后再后續(xù)的交互中就使用上一步生成的對稱秘鑰對傳輸?shù)膬?nèi)容進行加密和解密。
TCP三次握手流程 Android面試題 進程間通信的方式有哪幾種AIDL 、廣播、文件、socket、管道
廣播靜態(tài)注冊和動態(tài)注冊的區(qū)別動態(tài)注冊廣播不是常駐型廣播,也就是說廣播跟隨Activity的生命周期。注意在Activity結(jié)束前,移除廣播接收器。 靜態(tài)注冊是常駐型,也就是說當(dāng)應(yīng)用程序關(guān)閉后,如果有信息廣播來,程序也會被系統(tǒng)調(diào)用自動運行。
當(dāng)廣播為有序廣播時:優(yōu)先級高的先接收(不分靜態(tài)和動態(tài))。同優(yōu)先級的廣播接收器,動態(tài)優(yōu)先于靜態(tài)
同優(yōu)先級的同類廣播接收器,靜態(tài):先掃描的優(yōu)先于后掃描的,動態(tài):先注冊的優(yōu)先于后注冊的。
當(dāng)廣播為默認廣播時:無視優(yōu)先級,動態(tài)廣播接收器優(yōu)先于靜態(tài)廣播接收器。同優(yōu)先級的同類廣播接收器,靜態(tài):先掃描的優(yōu)先于后掃描的,動態(tài):先注冊的優(yōu)先于后冊的。
Android性能優(yōu)化工具使用(這個問題建議配合Android中的性能優(yōu)化)Android中常用的性能優(yōu)化工具包括這些:Android Studio自帶的Android Profiler、LeakCanary、BlockCanary
Android自帶的Android Profiler其實就很好用,Android Profiler可以檢測三個方面的性能問題:CPU、MEMORY、NETWORK。
LeakCanary是一個第三方的檢測內(nèi)存泄漏的庫,我們的項目集成之后LeakCanary會自動檢測應(yīng)用運行期間的內(nèi)存泄漏,并將之輸出給我們。
BlockCanary也是一個第三方檢測UI卡頓的庫,項目集成后Block也會自動檢測應(yīng)用運行期間的UI卡頓,并將之輸出給我們。
Android中的類加載器PathClassLoader,只能加載系統(tǒng)中已經(jīng)安裝過的apk
DexClassLoader,可以加載jar/apk/dex,可以從SD卡中加載未安裝的apk
Android中動畫大致分為3類:幀動畫、補間動畫(View Animation)、屬性動畫(Object Animation)。
幀動畫:通過xml配置一組圖片,動態(tài)播放。很少會使用。
補間動畫(View Animation):大致分為旋轉(zhuǎn)、透明、縮放、位移四類操作。很少會使用。
屬性動畫(Object Animation):屬性動畫是現(xiàn)在使用的最多的一種動畫,它比補間動畫更加強大。屬性動畫大致分為兩種使用類型,分別是ViewPropertyAnimator和ObjectAnimator。前者適合一些通用的動畫,比如旋轉(zhuǎn)、位移、縮放和透明,使用方式也很簡單通過View.animate()即可得到ViewPropertyAnimator,之后進行相應(yīng)的動畫操作即可。后者適合用于為我們的自定義控件添加動畫,當(dāng)然首先我們應(yīng)該在自定義View中添加相應(yīng)的getXXX()和setXXX()相應(yīng)屬性的getter和setter方法,這里需要注意的是在setter方法內(nèi)改變了自定義View中的屬性后要調(diào)用invalidate()來刷新View的繪制。之后調(diào)用ObjectAnimator.of屬性類型()返回一個ObjectAnimator,調(diào)用start()方法啟動動畫即可。
補間動畫與屬性動畫的區(qū)別:
補間動畫是父容器不斷的繪制view,看起來像移動了效果,其實view沒有變化,還在原地。
是通過不斷改變view內(nèi)部的屬性值,真正的改變view。
Handler機制說到Handler,就不得不提與之密切相關(guān)的這幾個類:Message、MessageQueue,Looper。
Message。Message中有兩個成員變量值得關(guān)注:target和callback。target其實就是發(fā)送消息的Handler對象,callback是當(dāng)調(diào)用handler.post(runnable)時傳入的Runnable類型的任務(wù)。post事件的本質(zhì)也是創(chuàng)建了一個Message,將我們傳入的這個runnable賦值給創(chuàng)建的Message的callback這個成員變量。
MessageQueue。消息隊列很明顯是存放消息的隊列,值得關(guān)注的是MessageQueue中的next()方法,它會返回下一個待處理的消息。
Looper。Looper消息輪詢器其實是連接Handler和消息隊列的核心。首先我們都知道,如果想要在一個線程中創(chuàng)建一個Handler,首先要通過Looper.prepare()創(chuàng)建Looper,之后還得調(diào)用Looper.loop()開啟輪詢。我們著重看一下這兩個方法。
prepare()。這個方法做了兩件事:首先通過ThreadLocal.get()獲取當(dāng)前線程中的Looper,如果不為空,則會拋出一個RunTimeException,意思是一個線程不能創(chuàng)建2個Looper。如果為null則執(zhí)行下一步。第二步是創(chuàng)建了一個Looper,并通過ThreadLocal.set(looper)。將我們創(chuàng)建的Looper與當(dāng)前線程綁定。這里需要提一下的是消息隊列的創(chuàng)建其實就發(fā)生在Looper的構(gòu)造方法中。
loop()。這個方法開啟了整個事件機制的輪詢。它的本質(zhì)是開啟了一個死循環(huán),不斷的通過MessageQueue的next()方法獲取消息。拿到消息后會調(diào)用msg.target.dispatchMessage()來做處理。其實我們在說到Message的時候提到過,msg.target其實就是發(fā)送這個消息的handler。這句代碼的本質(zhì)就是調(diào)用handler的dispatchMessage()。
Handler。上面做了這么多鋪墊,終于到了最重要的部分。Handler的分析著重在兩個部分:發(fā)送消息和處理消息。
發(fā)送消息。其實發(fā)送消息除了sendMessage之外還有sendMessageDelayed和post以及postDelayed等等不同的方式。但它們的本質(zhì)都是調(diào)用了sendMessageAtTime。在sendMessageAtTime這個方法中調(diào)用了enqueueMessage。在enqueueMessage這個方法中做了兩件事:通過msg.target = this實現(xiàn)了消息與當(dāng)前handler的綁定。然后通過queue.enqueueMessage實現(xiàn)了消息入隊。
處理消息。消息處理的核心其實就是dispatchMessage()這個方法。這個方法里面的邏輯很簡單,先判斷msg.callback是否為null,如果不為空則執(zhí)行這個runnable。如果為空則會執(zhí)行我們的handleMessage方法。
Android性能優(yōu)化Android中的性能優(yōu)化在我看來分為以下幾個方面:內(nèi)存優(yōu)化、布局優(yōu)化、網(wǎng)絡(luò)優(yōu)化、安裝包優(yōu)化。
內(nèi)存優(yōu)化:下一個問題就是。
布局優(yōu)化:布局優(yōu)化的本質(zhì)就是減少View的層級。常見的布局優(yōu)化方案如下
在LinearLayout和RelativeLayout都可以完成布局的情況下優(yōu)先選擇RelativeLayout,可以減少View的層級
將常用的布局組件抽取出來使用 < include > 標(biāo)簽
通過 < ViewStub > 標(biāo)簽來加載不常用的布局
使用 < Merge > 標(biāo)簽來減少布局的嵌套層次
網(wǎng)絡(luò)優(yōu)化:常見的網(wǎng)絡(luò)優(yōu)化方案如下
盡量減少網(wǎng)絡(luò)請求,能夠合并的就盡量合并
避免DNS解析,根據(jù)域名查詢可能會耗費上百毫秒的時間,也可能存在DNS劫持的風(fēng)險。可以根據(jù)業(yè)務(wù)需求采用增加動態(tài)更新IP的方式,或者在IP方式訪問失敗時切換到域名訪問方式。
大量數(shù)據(jù)的加載采用分頁的方式
網(wǎng)絡(luò)數(shù)據(jù)傳輸采用GZIP壓縮
加入網(wǎng)絡(luò)數(shù)據(jù)的緩存,避免頻繁請求網(wǎng)絡(luò)
上傳圖片時,在必要的時候壓縮圖片
安裝包優(yōu)化:安裝包優(yōu)化的核心就是減少apk的體積,常見的方案如下
使用混淆,可以在一定程度上減少apk體積,但實際效果微乎其微
減少應(yīng)用中不必要的資源文件,比如圖片,在不影響APP效果的情況下盡量壓縮圖片,有一定的效果
在使用了SO庫的時候優(yōu)先保留v7版本的SO庫,刪掉其他版本的SO庫。原因是在2018年,v7版本的SO庫可以滿足市面上絕大多數(shù)的要求,可能八九年前的手機滿足不了,但我們也沒必要去適配老掉牙的手機。實際開發(fā)中減少apk體積的效果是十分顯著的,如果你使用了很多SO庫,比方說一個版本的SO庫一共10M,那么只保留v7版本,刪掉armeabi和v8版本的SO庫,一共可以減少20M的體積。
Android內(nèi)存優(yōu)化Android的內(nèi)存優(yōu)化在我看來分為兩點:避免內(nèi)存泄漏、擴大內(nèi)存,其實就是開源節(jié)流。
其實內(nèi)存泄漏的本質(zhì)就是較長生命周期的對象引用了較短生命周期的對象。
常見的內(nèi)存泄漏:
單例模式導(dǎo)致的內(nèi)存泄漏。最常見的例子就是創(chuàng)建這個單例對象需要傳入一個Context,這時候傳入了一個Activity類型的Context,由于單例對象的靜態(tài)屬性,導(dǎo)致它的生命周期是從單例類加載到應(yīng)用程序結(jié)束為止,所以即使已經(jīng)finish掉了傳入的Activity,由于我們的單例對象依然持有Activity的引用,所以導(dǎo)致了內(nèi)存泄漏。解決辦法也很簡單,不要使用Activity類型的Context,使用Application類型的Context可以避免內(nèi)存泄漏。
靜態(tài)變量導(dǎo)致的內(nèi)存泄漏。靜態(tài)變量是放在方法區(qū)中的,它的生命周期是從類加載到程序結(jié)束,可以看到靜態(tài)變量生命周期是非常久的。最常見的因靜態(tài)變量導(dǎo)致內(nèi)存泄漏的例子是我們在Activity中創(chuàng)建了一個靜態(tài)變量,而這個靜態(tài)變量的創(chuàng)建需要傳入Activity的引用this。在這種情況下即使Activity調(diào)用了finish也會導(dǎo)致內(nèi)存泄漏。原因就是因為這個靜態(tài)變量的生命周期幾乎和整個應(yīng)用程序的生命周期一致,它一直持有Activity的引用,從而導(dǎo)致了內(nèi)存泄漏。
非靜態(tài)內(nèi)部類導(dǎo)致的內(nèi)存泄漏。非靜態(tài)內(nèi)部類導(dǎo)致內(nèi)存泄漏的原因是非靜態(tài)內(nèi)部類持有外部類的引用,最常見的例子就是在Activity中使用Handler和Thread了。使用非靜態(tài)內(nèi)部類創(chuàng)建的Handler和Thread在執(zhí)行延時操作的時候會一直持有當(dāng)前Activity的引用,如果在執(zhí)行延時操作的時候就結(jié)束Activity,這樣就會導(dǎo)致內(nèi)存泄漏。解決辦法有兩種:第一種是使用靜態(tài)內(nèi)部類,在靜態(tài)內(nèi)部類中使用弱引用調(diào)用Activity。第二種方法是在Activity的onDestroy中調(diào)用handler.removeCallbacksAndMessages來取消延時事件。
使用資源未及時關(guān)閉導(dǎo)致的內(nèi)存泄漏。常見的例子有:操作各種數(shù)據(jù)流未及時關(guān)閉,操作Bitmap未及時recycle等等。
使用第三方庫未能及時解綁。有的三方庫提供了注冊和解綁的功能,最常見的就是EventBus了,我們都知道使用EventBus要在onCreate中注冊,在onDestroy中解綁。如果沒有解綁的話,EventBus其實是一個單例模式,他會一直持有Activity的引用,導(dǎo)致內(nèi)存泄漏。同樣常見的還有RxJava,在使用Timer操作符做了一些延時操作后也要注意在onDestroy方法中調(diào)用disposable.dispose()來取消操作。
屬性動畫導(dǎo)致的內(nèi)存泄漏。常見的例子就是在屬性動畫執(zhí)行的過程中退出了Activity,這時View對象依然持有Activity的引用從而導(dǎo)致了內(nèi)存泄漏。解決辦法就是在onDestroy中調(diào)用動畫的cancel方法取消屬性動畫。
WebView導(dǎo)致的內(nèi)存泄漏。WebView比較特殊,即使是調(diào)用了它的destroy方法,依然會導(dǎo)致內(nèi)存泄漏。其實避免WebView導(dǎo)致內(nèi)存泄漏的最好方法就是讓W(xué)ebView所在的Activity處于另一個進程中,當(dāng)這個Activity結(jié)束時殺死當(dāng)前WebView所處的進程即可,我記得阿里釘釘?shù)腤ebView就是另外開啟的一個進程,應(yīng)該也是采用這種方法避免內(nèi)存泄漏。
擴大內(nèi)存,為什么要擴大我們的內(nèi)存呢?有時候我們實際開發(fā)中不可避免的要使用很多第三方商業(yè)的SDK,這些SDK其實有好有壞,大廠的SDK可能內(nèi)存泄漏會少一些,但一些小廠的SDK質(zhì)量也就不太靠譜一些。那應(yīng)對這種我們無法改變的情況,最好的辦法就是擴大內(nèi)存。
擴大內(nèi)存通常有兩種方法:一個是在清單文件中的Application下添加largeHeap=”true”這個屬性,另一個就是同一個應(yīng)用開啟多個進程來擴大一個應(yīng)用的總內(nèi)存空間。第二種方法其實就很常見了,比方說我使用過個推的SDK,個推的Service其實就是處在另外一個多帶帶的進程中。
Android中的內(nèi)存優(yōu)化總的來說就是開源和節(jié)流,開源就是擴大內(nèi)存,節(jié)流就是避免內(nèi)存泄漏。
Binder機制在Linux中,為了避免一個進程對其他進程的干擾,進程之間是相互獨立的。在一個進程中其實還分為用戶空間和內(nèi)核空間。這里的隔離分為兩個部分,進程間的隔離和進程內(nèi)的隔離。
既然進程間存在隔離,那其實也是存在著交互。進程間通信就是IPC,用戶空間和內(nèi)核空間的通信就是系統(tǒng)調(diào)用。
Linux為了保證獨立性和安全性,進程之間不能直接相互訪問,Android是基于Linux的,所以也是需要解決進程間通信的問題。
其實Linux進程間通信有很多方式,比如管道、socket等等。為什么Android進程間通信采用了Binder而不是Linux已有的方式,主要是有這么兩點考慮:性能和安全
性能。在移動設(shè)備上對性能要求是比較嚴苛的。Linux傳統(tǒng)的進程間通信比如管道、socket等等進程間通信是需要復(fù)制兩次數(shù)據(jù),而Binder則只需要一次。所以Binder在性能上是優(yōu)于傳統(tǒng)進程通信的。
安全。傳統(tǒng)的Linux進程通信是不包含通信雙方的身份驗證的,這樣會導(dǎo)致一些安全性問題。而Binder機制自帶身份驗證,從而有效的提高了安全性。
Binder是基于CS架構(gòu)的,有四個主要組成部分。
Client。客戶端進程。
Server。服務(wù)端進程。
ServiceManager。提供注冊、查詢和返回代理服務(wù)對象的功能。
Binder驅(qū)動。主要負責(zé)建立進程間的Binder連接,進程間的數(shù)據(jù)交互等等底層操作。
Binder機制主要的流程是這樣的:
服務(wù)端通過Binder驅(qū)動在ServiceManager中注冊我們的服務(wù)。
客戶端通過Binder驅(qū)動查詢在ServiceManager中注冊的服務(wù)。
ServiceManager通過Binder驅(qū)動返回服務(wù)端的代理對象。
客戶端拿到服務(wù)端的代理對象后即可進行進程間通信。
LruCache的原理LruCache的核心原理就是對LinkedHashMap的有效利用,它的內(nèi)部存在一個LinkedHashMap成員變量。值得我們關(guān)注的有四個方法:構(gòu)造方法、get、put、trimToSize。
構(gòu)造方法:在LruCache的構(gòu)造方法中做了兩件事,設(shè)置了maxSize、創(chuàng)建了一個LinkedHashMap。這里值得注意的是LruCache將LinkedHashMap的accessOrder設(shè)置為了true,accessOrder就是遍歷這個LinkedHashMap的輸出順序。true代表按照訪問順序輸出,false代表按添加順序輸出,因為通常都是按照添加順序輸出,所以accessOrder這個屬性默認是false,但我們的LruCache需要按訪問順序輸出,所以顯式的將accessOrder設(shè)置為true。
get方法:本質(zhì)上是調(diào)用LinkedHashMap的get方法,由于我們將accessOrder設(shè)置為了true,所以每調(diào)用一次get方法,就會將我們訪問的當(dāng)前元素放置到這個LinkedHashMap的尾部。
put方法:本質(zhì)上也是調(diào)用了LinkedHashMap的put方法,由于LinkedHashMap的特性,每調(diào)用一次put方法,也會將新加入的元素放置到LinkedHashMap的尾部。添加之后會調(diào)用trimToSize方法來保證添加后的內(nèi)存不超過maxSize。
trimToSize方法:trimToSize方法的內(nèi)部其實是開啟了一個while(true)的死循環(huán),不斷的從LinkedHashMap的首部刪除元素,直到刪除之后的內(nèi)存小于maxSize之后使用break跳出循環(huán)。
其實到這里我們可以總結(jié)一下,為什么這個算法叫?最近最少使用?算法呢?原理很簡單,我們的每次put或者get都可以看做一次訪問,由于LinkedHashMap的特性,會將每次訪問到的元素放置到尾部。當(dāng)我們的內(nèi)存達到閾值后,會觸發(fā)trimToSize方法來刪除LinkedHashMap首部的元素,直到當(dāng)前內(nèi)存小于maxSize。為什么刪除首部的元素,原因很明顯:我們最近經(jīng)常訪問的元素都會放置到尾部,那首部的元素肯定就是?最近最少使用?的元素了,因此當(dāng)內(nèi)存不足時應(yīng)當(dāng)優(yōu)先刪除這些元素。
DiskLruCache原理 設(shè)計一個圖片的異步加載框架設(shè)計一個圖片加載框架,肯定要用到圖片加載的三級緩存的思想。三級緩存分為內(nèi)存緩存、本地緩存和網(wǎng)絡(luò)緩存。
內(nèi)存緩存:將Bitmap緩存到內(nèi)存中,運行速度快,但是內(nèi)存容量小。
本地緩存:將圖片緩存到文件中,速度較慢,但容量較大。
網(wǎng)絡(luò)緩存:從網(wǎng)絡(luò)獲取圖片,速度受網(wǎng)絡(luò)影響。
如果我們設(shè)計一個圖片加載框架,流程一定是這樣的:
拿到圖片url后首先從內(nèi)存中查找BItmap,如果找到直接加載。
內(nèi)存中沒有找到,會從本地緩存中查找,如果本地緩存可以找到,則直接加載。
內(nèi)存和本地都沒有找到,這時會從網(wǎng)絡(luò)下載圖片,下載到后會加載圖片,并且將下載到的圖片放到內(nèi)存緩存和本地緩存中。
上面是一些基本的概念,如果是具體的代碼實現(xiàn)的話,大概需要這么幾個方面的文件:
首先需要確定我們的內(nèi)存緩存,這里一般用的都是LruCache。
確定本地緩存,通常用的是DiskLruCache,這里需要注意的是圖片緩存的文件名一般是url被MD5加密后的字符串,為了避免文件名直接暴露圖片的url。
內(nèi)存緩存和本地緩存確定之后,需要我們創(chuàng)建一個新的類MemeryAndDiskCache,當(dāng)然,名字隨便起,這個類包含了之前提到的LruCache和DiskLruCache。在MemeryAndDiskCache這個類中我們定義兩個方法,一個是getBitmap,另一個是putBitmap,對應(yīng)著圖片的獲取和緩存,內(nèi)部的邏輯也很簡單。getBitmap中按內(nèi)存、本地的優(yōu)先級去取BItmap,putBitmap中先緩存內(nèi)存,之后緩存到本地。
在緩存策略類確定好之后,我們創(chuàng)建一個ImageLoader類,這個類必須包含兩個方法,一個是展示圖片displayImage(url,imageView),另一個是從網(wǎng)絡(luò)獲取圖片downloadImage(url,imageView)。在展示圖片方法中首先要通過ImageView.setTag(url),將url和imageView進行綁定,這是為了避免在列表中加載網(wǎng)絡(luò)圖片時會由于ImageView的復(fù)用導(dǎo)致的圖片錯位的bug。之后會從MemeryAndDiskCache中獲取緩存,如果存在,直接加載;如果不存在,則調(diào)用從網(wǎng)絡(luò)獲取圖片這個方法。從網(wǎng)絡(luò)獲取圖片方法很多,這里我一般都會使用OkHttp+Retrofit。當(dāng)從網(wǎng)絡(luò)中獲取到圖片之后,首先判斷一下imageView.getTag()與圖片的url是否一致,如果一致則加載圖片,如果不一致則不加載圖片,通過這樣的方式避免了列表中異步加載圖片的錯位。同時在獲取到圖片之后會通過MemeryAndDiskCache來緩存圖片。
Android中的事件分發(fā)機制在我們的手指觸摸到屏幕的時候,事件其實是通過 Activity -> ViewGroup -> View 這樣的流程到達最后響應(yīng)我們觸摸事件的View。
說到事件分發(fā),必不可少的是這幾個方法:dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent。接下來就按照 Activity -> ViewGroup -> View 的流程來大致說一下事件分發(fā)機制。
我們的手指觸摸到屏幕的時候,會觸發(fā)一個Action_Down類型的事件,當(dāng)前頁面的Activity會首先做出響應(yīng),也就是說會走到Activity的dispatchTouchEvent()方法內(nèi)。在這個方法內(nèi)部簡單來說是這么一個邏輯:
調(diào)用getWindow.superDispatchTouchEvent()。
如果上一步返回true,直接返回true;否則就return自己的onTouchEvent()。
這個邏輯很好理解,getWindow().superDispatchTouchEvent()如果返回true代表當(dāng)前事件已經(jīng)被處理,無需調(diào)用自己的onTouchEvent;否則代表事件并沒有被處理,需要Activity自己處理,也就是調(diào)用自己的onTouchEvent。
getWindow()方法返回了一個Window類型的對象,這個我們都知道,在Android中,PhoneWindow是Window的唯一實現(xiàn)類。所以這句本質(zhì)上是調(diào)用了PhoneWindow中的superDispatchTouchEvent()。
而在PhoneWindow的這個方法中實際調(diào)用了mDecor.superDispatchTouchEvent(event)。這個mDecor就是DecorView,它是FrameLayout的一個子類,在DecorView中的superDispatchTouchEvent()中調(diào)用的是super.dispatchTouchEvent()。到這里就很明顯了,DecorView是一個FrameLayout的子類,F(xiàn)rameLayout是一個ViewGroup的子類,本質(zhì)上調(diào)用的還是ViewGroup的dispatchTouchEvent()。
分析到這里,我們的事件已經(jīng)從Activity傳遞到了ViewGroup,接下來我們來分析下ViewGroup中的這幾個事件處理方法。
在ViewGroup中的dispatchTouchEvent()中的邏輯大致如下:
通過onInterceptTouchEvent()判斷當(dāng)前ViewGroup是否攔截事件,默認的ViewGroup都是不攔截的;
如果攔截,則return自己的onTouchEvent();
如果不攔截,則根據(jù) child.dispatchTouchEvent()的返回值判斷。如果返回true,則return true;否則return自己的onTouchEvent(),在這里實現(xiàn)了未處理事件的向上傳遞。
通常情況下ViewGroup的onInterceptTouchEvent()都返回false,也就是不攔截。這里需要注意的是事件序列,比如Down事件、Move事件……Up事件,從Down到Up是一個完整的事件序列,對應(yīng)著手指從按下到抬起這一系列的事件,如果ViewGroup攔截了Down事件,那么后續(xù)事件都會交給這個ViewGroup的onTouchEvent。如果ViewGroup攔截的不是Down事件,那么會給之前處理這個Down事件的View發(fā)送一個Action_Cancel類型的事件,通知子View這個后續(xù)的事件序列已經(jīng)被ViewGroup接管了,子View恢復(fù)之前的狀態(tài)即可。
這里舉一個常見的例子:在一個Recyclerview鐘有很多的Button,我們首先按下了一個button,然后滑動一段距離再松開,這時候Recyclerview會跟著滑動,并不會觸發(fā)這個button的點擊事件。這個例子中,當(dāng)我們按下button時,這個button接收到了Action_Down事件,正常情況下后續(xù)的事件序列應(yīng)該由這個button處理。但我們滑動了一段距離,這時Recyclerview察覺到這是一個滑動操作,攔截了這個事件序列,走了自身的onTouchEvent()方法,反映在屏幕上就是列表的滑動。而這時button仍然處于按下的狀態(tài),所以在攔截的時候需要發(fā)送一個Action_Cancel來通知button恢復(fù)之前狀態(tài)。
事件分發(fā)最終會走到View的dispatchTouchEvent()中。在View的dispatchTouchEvent()中沒有onInterceptTouchEvent(),這也很容易理解,View不是ViewGroup,不會包含其他子View,所以也不存在攔截不攔截這一說。忽略一些細節(jié),View的dispatchTouchEvent()中直接return了自己的onTouchEvent()。如果onTouchEvent()返回true代表事件被處理,否則未處理的事件會向上傳遞,直到有View處理了事件或者一直沒有處理,最終到達了Activity的onTouchEvent()終止。
這里經(jīng)常有人問onTouch和onTouchEvent的區(qū)別。首先,這兩個方法都在View的dispatchTouchEvent()中,是這么一個邏輯:
如果touchListener不為null,并且這個View是enable的,而且onTouch返回的是true,滿足這三個條件時會直接return true,不會走onTouchEvent()方法。
上面只要有一個條件不滿足,就會走到onTouchEvent()方法中。所以onTouch的順序是在onTouchEvent之前的。
View的繪制流程視圖繪制的起點在ViewRootImpl類的performTraversals()方法,在這個方法內(nèi)其實是按照順序依次調(diào)用了mView.measure()、mView.layout()、mView.draw()
View的繪制流程分為3步:測量、布局、繪制,分別對應(yīng)3個方法measure、layout、draw。
測量階段。measure方法會被父View調(diào)用,在measure方法中做一些優(yōu)化和準(zhǔn)備工作后會調(diào)用onMeasure方法進行實際的自我測量。onMeasure方法在View和ViewGroup做的事情是不一樣的:
View。View中的onMeasure方法會計算自己的尺寸并通過setMeasureDimension保存。
ViewGroup。ViewGroup中的onMeasure方法會調(diào)用所有子View的measure方法進行自我測量并保存。然后通過子View的尺寸和位置計算出自己的尺寸并保存。
布局階段。layout方法會被父View調(diào)用,layout方法會保存父View傳進來的尺寸和位置,并調(diào)用onLayout進行實際的內(nèi)部布局。onLayout在View和ViewGroup中做的事情也是不一樣的:
View。因為View是沒有子View的,所以View的onLayout里面什么都不做。
ViewGroup。ViewGroup中的onLayout方法會調(diào)用所有子View的layout方法,把尺寸和位置傳給他們,讓他們完成自我的內(nèi)部布局。
繪制階段。draw方法會做一些調(diào)度工作,然后會調(diào)用onDraw方法進行View的自我繪制。draw方法的調(diào)度流程大致是這樣的:
繪制背景。對應(yīng)drawBackground(Canvas)方法。
繪制主體。對應(yīng)onDraw(Canvas)方法。
繪制子View。對應(yīng)dispatchDraw(Canvas)方法。
繪制滑動相關(guān)和前景。對應(yīng)onDrawForeground(Canvas)。
Android源碼中常見的設(shè)計模式以及自己在開發(fā)中常用的設(shè)計模式 Android與js是如何交互的在Android中,Android與js的交互分為兩個方面:Android調(diào)用js里的方法、js調(diào)用Android中的方法。
Android調(diào)js。Android調(diào)js有兩種方法:
WebView.loadUrl(“javascript:js中的方法名”)。這種方法的優(yōu)點是很簡潔,缺點是沒有返回值,如果需要拿到j(luò)s方法的返回值則需要js調(diào)用Android中的方法來拿到這個返回值。
WebView.evaluateJavaScript(“javascript:js中的方法名”,ValueCallback)。這種方法比loadUrl好的是可以通過ValueCallback這個回調(diào)拿到j(luò)s方法的返回值。缺點是這個方法Android4.4才有,兼容性較差。不過放在2018年來說,市面上絕大多數(shù)App都要求最低版本是4.4了,所以我認為這個兼容性問題不大。
js調(diào)Android。js調(diào)Android有三種方法:
WebView.addJavascriptInterface()。這是官方解決js調(diào)用Android方法的方案,需要注意的是要在供js調(diào)用的Android方法上加上?@JavascriptInterface?注解,以避免安全漏洞。這種方案的缺點是Android4.2以前會有安全漏洞,不過在4.2以后已經(jīng)修復(fù)了。同樣,在2018年來說,兼容性問題不大。
重寫WebViewClient的shouldOverrideUrlLoading()方法來攔截url,拿到url后進行解析,如果符合雙方的規(guī)定,即可調(diào)用Android方法。優(yōu)點是避免了Android4.2以前的安全漏洞,缺點也很明顯,無法直接拿到調(diào)用Android方法的返回值,只能通過Android調(diào)用js方法來獲取返回值。
重寫WebChromClient的onJsPrompt()方法,同前一個方式一樣,拿到url之后先進行解析,如果符合雙方規(guī)定,即可調(diào)用Android方法。最后如果需要返回值,通過result.confirm(“Android方法返回值”)即可將Android的返回值返回給js。方法的優(yōu)點是沒有漏洞,也沒有兼容性限制,同時還可以方便的獲取Android方法的返回值。其實這里需要注意的是在WebChromeClient中除了onJsPrompt之外還有onJsAlert和onJsConfirm方法。那么為什么不選擇另兩個方法呢?原因在于onJsAlert是沒有返回值的,而onJsConfirm只有true和false兩個返回值,同時在前端開發(fā)中prompt方法基本不會被調(diào)用,所以才會采用onJsPrompt。
熱修復(fù)原理 Activity啟動過程 SparseArray原理SparseArray,通常來講是Android中用來替代HashMap的一個數(shù)據(jù)結(jié)構(gòu)。
準(zhǔn)確來講,是用來替換key為Integer類型,value為Object類型的HashMap。需要注意的是SparseArray僅僅實現(xiàn)了Cloneable接口,所以不能用Map來聲明。
從內(nèi)部結(jié)構(gòu)來講,SparseArray內(nèi)部由兩個數(shù)組組成,一個是int[]類型的mKeys,用來存放所有的鍵;另一個是Object[]類型的mValues,用來存放所有的值。
最常見的是拿SparseArray跟HashMap來做對比,由于SparseArray內(nèi)部組成是兩個數(shù)組,所以占用內(nèi)存比HashMap要小。我們都知道,增刪改查等操作都首先需要找到相應(yīng)的鍵值對,而SparseArray內(nèi)部是通過二分查找來尋址的,效率很明顯要低于HashMap的常數(shù)級別的時間復(fù)雜度。提到二分查找,這里還需要提一下的是二分查找的前提是數(shù)組已經(jīng)是排好序的,沒錯,SparseArray中就是按照key進行升序排列的。
綜合起來來說,SparseArray所占空間優(yōu)于HashMap,而效率低于HashMap,是典型的時間換空間,適合較小容量的存儲。
從源碼角度來說,我認為需要注意的是SparseArray的remove()、put()和gc()方法。
remove()。SparseArray的remove()方法并不是直接刪除之后再壓縮數(shù)組,而是將要刪除的value設(shè)置為DELETE這個SparseArray的靜態(tài)屬性,這個DELETE其實就是一個Object對象,同時會將SparseArray中的mGarbage這個屬性設(shè)置為true,這個屬性是便于在合適的時候調(diào)用自身的gc()方法壓縮數(shù)組來避免浪費空間。這樣可以提高效率,如果將來要添加的key等于刪除的key,那么會將要添加的value覆蓋DELETE。
gc()。SparseArray中的gc()方法跟JVM的GC其實完全沒有任何關(guān)系。gc()方法的內(nèi)部實際上就是一個for循環(huán),將value不為DELETE的鍵值對往前移動覆蓋value為DELETE的鍵值對來實現(xiàn)數(shù)組的壓縮,同時將mGarbage置為false,避免內(nèi)存的浪費。
put()。put方法是這么一個邏輯,如果通過二分查找在mKeys數(shù)組中找到了key,那么直接覆蓋value即可。如果沒有找到,會拿到與數(shù)組中與要添加的key最接近的key索引,如果這個索引對應(yīng)的value為DELETE,則直接把新的value覆蓋DELETE即可,在這里可以避免數(shù)組元素的移動,從而提高了效率。如果value不為DELETE,會判斷mGarbage,如果為true,則會調(diào)用gc()方法壓縮數(shù)組,之后會找到合適的索引,將索引之后的鍵值對后移,插入新的鍵值對,這個過程中可能會觸發(fā)數(shù)組的擴容。
圖片加載如何避免OOM我們知道內(nèi)存中的Bitmap大小的計算公式是:長所占像素?寬所占像素?每個像素所占內(nèi)存。想避免OOM有兩種方法:等比例縮小長寬、減少每個像素所占的內(nèi)存。
等比縮小長寬。我們知道Bitmap的創(chuàng)建是通過BitmapFactory的工廠方法,decodeFile()、decodeStream()、decodeByteArray()、decodeResource()。這些方法中都有一個Options類型的參數(shù),這個Options是BitmapFactory的內(nèi)部類,存儲著BItmap的一些信息。Options中有一個屬性:inSampleSize。我們通過修改inSampleSize可以縮小圖片的長寬,從而減少BItmap所占內(nèi)存。需要注意的是這個inSampleSize大小需要是2的冪次方,如果小于1,代碼會強制讓inSampleSize為1。
減少像素所占內(nèi)存。Options中有一個屬性inPreferredConfig,默認是ARGB_8888,代表每個像素所占尺寸。我們可以通過將之修改為RGB_565或者ARGB_4444來減少一半內(nèi)存。
大圖加載加載高清大圖,比如清明上河圖,首先屏幕是顯示不下的,而且考慮到內(nèi)存情況,也不可能一次性全部加載到內(nèi)存。這時候就需要局部加載了,Android中有一個負責(zé)局部加載的類:BitmapRegionDecoder。使用方法很簡單,通過BitmapRegionDecoder.newInstance()創(chuàng)建對象,之后調(diào)用decodeRegion(Rect rect, BitmapFactory.Options options)即可。第一個參數(shù)rect是要顯示的區(qū)域,第二個參數(shù)是BitmapFactory中的內(nèi)部類Options。
Android三方庫的源碼分析由于源碼分析篇幅太大,所以這里之貼出我的源碼分析的鏈接(掘金)。
OkHttpOkHttp源碼分析
RetrofitRetrofit源碼分析1
Retrofit源碼分析2
Retrofit源碼分析3
RxJava源碼分析
GlideGlide源碼分析
EventBusEventBus源碼分析
大致是這么一個流程:
register:
獲取訂閱者的Class對象
使用反射查找訂閱者中的事件處理方法集合
遍歷事件處理方法集合,調(diào)用subscribe(subscriber,subscriberMethod)方法,在subscribe方法內(nèi):
如果事件繼承性為true,遍歷這個Map類型的stickEvents,通過isAssignableFrom方法判斷當(dāng)前事件是否是遍歷事件的父類,如果是則發(fā)送事件
如果事件繼承性為false,通過stickyEvents.get(eventType)獲取事件并發(fā)送
如果事件類型集合為空則創(chuàng)建一個新的集合,這一步目的是延遲集合的初始化
拿到事件類型集合后將新的事件類型加入到集合中
如果Subscription集合為空則創(chuàng)建一個新的集合,這一步目的是延遲集合的初始化
拿到Subscription集合后遍歷這個集合,通過比較事件處理的優(yōu)先級,將新的Subscription對象加入合適的位置
通過subscriberMethod獲取處理的事件類型eventType
將訂閱者subscriber和方法subscriberMethod綁在一起形成一個Subscription對象
通過subscriptionsByEventType.get(eventType)獲取Subscription集合
通過typesBySubscriber.get(subscriber)獲取事件類型集合
判斷當(dāng)前事件類型是否是sticky
如果當(dāng)前事件類型不是sticky(粘性事件),subscribe(subscriber,subscriberMethod)到此終結(jié)
如果是sticky,判斷EventBus中的一個事件繼承性的屬性,默認是true
post:
postSticky
將事件加入到stickyEvents這個Map類型的集合中
調(diào)用post方法
post
事件繼承性為true,找到當(dāng)前事件所有的父類型并調(diào)用postSingleEventForEventType方法發(fā)送事件
事件繼承性為false,只發(fā)送當(dāng)前事件類型的事件
在postToSubscription中分為四種情況
POSTING,調(diào)用invokeSubscriber(subscription, event)處理事件,本質(zhì)是method.invoke()反射
MAIN,如果在主線程直接invokeSubscriber處理;反之通過handler切換到主線程調(diào)用invokeSubscriber處理事件
BACKGROUND,如果不在主線程直接invokeSubscriber處理事件;反之開啟一條線程,在線程中調(diào)用invokeSubscriber處理事件
ASYNC,開啟一條線程,在線程中調(diào)用invokeSubscriber處理事件
在postSingleEventForEventType中,通過subscriptionsByEventType.get(eventClass)獲取Subscription類型集合
遍歷這個集合,調(diào)用postToSubscription發(fā)送事件
將事件加入當(dāng)前線程的事件隊列中
通過while循環(huán)不斷從事件隊列中取出事件并調(diào)用postSingleEvent方法發(fā)送事件
在postSingleEvent中,判斷事件繼承性,默認為true
unregister:
刪除subscriptionsByEventType中與訂閱者相關(guān)的所有subscription
刪除typesBySubscriber中與訂閱者相關(guān)的所有類型
數(shù)據(jù)結(jié)構(gòu)與算法 手寫快排 手寫歸并排序 手寫堆以及堆排序 說一下排序算法的區(qū)別(時間復(fù)雜度和空間復(fù)雜度)1.Activity的啟動過程(不要回答生命周期)1.2.Activity的啟動模式以及使用場景http://blog.csdn.net/luosheng...
(1)manifest設(shè)置,(2)startActivity flag3.Service的兩種啟動方式
http://blog.csdn.net/CodeEmpe...
此處延伸:棧(First In Last Out)與隊列(First In First Out)的區(qū)別
(1)startService(),(2)bindService()4.Broadcast注冊方式與區(qū)別
http://www.jianshu.com/p/2fb6...
(1)靜態(tài)注冊(minifest),(2)動態(tài)注冊5.HttpClient與HttpUrlConnection的區(qū)別
http://www.jianshu.com/p/ea5e...
此處延伸:什么情況下用動態(tài)注冊
http://blog.csdn.net/guolin_b...6.http與https的區(qū)別
此處延伸:Volley里用的哪種請求方式(2.3前HttpClient,2.3后HttpUrlConnection)
http://blog.csdn.net/whatday/...7.手寫算法(選擇冒泡必須要會)
此處延伸:https的實現(xiàn)原理
http://www.jianshu.com/p/ae97...8.進程保活(不死進程)
http://www.jianshu.com/p/63aa...9.進程間通信的方式
此處延伸:進程的優(yōu)先級是什么(下面這篇文章,都有說)
https://segmentfault.com/a/11...
(1)AIDL,(2)廣播,(3)Messenger10.加載大圖
AIDL :?https://www.jianshu.com/p/a8e...
https://www.jianshu.com/p/0cc...
Messenger :
http://blog.csdn.net/lmj62356...
此處延伸:簡述Binder ,
http://blog.csdn.net/luosheng...
PS:有家小公司(規(guī)模寫假的,給騙過去了),直接把項目給我看,讓我說實現(xiàn)原理。。11.三級緩存(各大圖片框架都可以扯到這上面來)
最讓我無語的一次面試,就一個點問的我底褲都快穿了,就差幫他們寫代碼了。。
http://blog.csdn.net/lmj62356...
(1)內(nèi)存緩存,(2)本地緩存,(3)網(wǎng)絡(luò)12.MVP框架(必問)
內(nèi)存:http://blog.csdn.net/guolin_b...
本地:http://blog.csdn.net/guolin_b...
http://blog.csdn.net/lmj62356...13.講解一下Context
此處延伸:手寫mvp例子,與mvc之間的區(qū)別,mvp的優(yōu)勢
http://blog.csdn.net/lmj62356...14.JNI
http://www.jianshu.com/p/aba7...15.java虛擬機和Dalvik虛擬機的區(qū)別
此處延伸:項目中使用JNI的地方,如:核心邏輯,密鑰,加密邏輯
http://www.jianshu.com/p/923a...16.線程sleep和wait有什么區(qū)別
http://blog.csdn.net/liuzhenw...17.View,ViewGroup事件分發(fā)
http://blog.csdn.net/guolin_b...18.保存Activity狀態(tài)
http://blog.csdn.net/guolin_b...
onSaveInstanceState()19.WebView與js交互(調(diào)用哪些API)
http://blog.csdn.net/yuzhiboy...
http://blog.csdn.net/cappucci...20.內(nèi)存泄露檢測,內(nèi)存性能優(yōu)化
22.自定義view和動畫http://blog.csdn.net/guolin_b...
21.布局優(yōu)化http://blog.csdn.net/guolin_b...
以下兩個講解都講得很透徹,這部分面試官多數(shù)不會問很深,要么就給你一個效果讓你講原理。總結(jié)
(1)http://www.gcssloop.com/custo...
(2)http://blog.csdn.net/yanbober...
工作中解決了什么難題,做了什么有成就感的項目(這個問題一定會問到,所以肯定要做準(zhǔn)備)
這個些問題其實還是靠平時的積累,對我來說的話,最有成就感的就是開發(fā)了KCommon這個項目,它大大提升了我的開發(fā)效率。
參考
https://www.jianshu.com/p/564...
閱讀更多
除程序員,除了寫好代碼,你更應(yīng)該學(xué)會這些!
技術(shù)精華總結(jié),說說我上半年都干了什么
一招教你打造一個滑動置頂?shù)囊曈X特效
NDK項目實戰(zhàn)—高仿360手機助手之卸載監(jiān)聽
如果您覺得不錯,轉(zhuǎn)發(fā)是對我很大的支持!在這里獲得的不僅僅是技術(shù)!
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/72640.html
1、介紹一下標(biāo)準(zhǔn)的CSS的盒子模型?與低版本IE的盒子模型有什么不同的? 標(biāo)準(zhǔn)盒子模型:寬度=內(nèi)容的寬度(content)+ border + padding + margin低版本IE盒子模型:寬度=內(nèi)容寬度(content+border+padding)+ margin 2、box-sizing屬性? 用來控制元素的盒子模型的解析模式,默認為content-boxcontext-box:W3C...
摘要:但有時候我們希望關(guān)閉輸入框的自動完成功能,例如當(dāng)用戶輸入內(nèi)容的時候,我們希望使用技術(shù)從數(shù)據(jù)庫搜索并列舉而不是在用戶的歷史記錄中搜索。 以下是我整理的一些HTML的基礎(chǔ)面試體,并自己整理了答案。 1 DOCTYPE有什么作用?標(biāo)準(zhǔn)模式與混雜模式如何區(qū)分?它們有何意義? 告訴瀏覽器使用哪個版本的HTML規(guī)范來渲染文檔。DOCTYPE不存在或形式不正確會導(dǎo)致HTML文檔以混雜模式呈現(xiàn)。標(biāo)準(zhǔn)模...
閱讀 2222·2023-04-26 01:57
閱讀 3240·2023-04-25 16:30
閱讀 2324·2021-11-17 09:38
閱讀 1068·2021-10-08 10:14
閱讀 1382·2021-09-23 11:21
閱讀 3678·2019-08-29 17:28
閱讀 3450·2019-08-29 15:27
閱讀 944·2019-08-29 13:04