前言
有一些基礎(chǔ)的知識移斩,可能搞了很久Android開發(fā)都沒有遇到過,可能只有在面試的時候才會被問到绢馍。但是其實(shí)這些基礎(chǔ)知識都是很重要的向瓷,所以想在這里做一下總結(jié)〗⒂浚可能比較零散猖任,但是至少自己可以有個印象,假如某天需要用到了瓷耙,也可以來這里查一下朱躺〉罄担或者就當(dāng)做是面試題匯總也行,哈哈哈室琢。持續(xù)更新乾闰,也就是想到什么或者看到什么就寫什么的意思吧。
Android部分
Activity
生命周期常用的回調(diào)函數(shù)主要有onCreate()盈滴、onStart()涯肩、onRestart()、onResume()巢钓、onPause()病苗、onStop()和onDestory(),主要是要理解這些回調(diào)發(fā)生在什么時候症汹。
一些例子:
- 啟動Activity: onCreate()—>onStart()—>onResume()硫朦,Activity進(jìn)入運(yùn)行狀態(tài)。
- Activity退居后臺: 當(dāng)前Activity轉(zhuǎn)到新的Activity界面或按Home鍵回到主屏: onPause()—>onStop()背镇,進(jìn)入停滯狀態(tài)咬展。
- Activity返回前臺: onRestart()—>onStart()—>onResume(),再次回到運(yùn)行狀態(tài)瞒斩。
- 當(dāng)失去焦點(diǎn)時破婆,會調(diào)用onPause(),當(dāng)不可見時胸囱,調(diào)用onStop()
與生命周期相關(guān)的還有onSaveInstanceState() 和 onRestoreInstanceState()祷舀,但他們并不是生命周期的回調(diào)函數(shù),也不一定會調(diào)用烹笔,但是如果需要保存一些狀態(tài)時要用到裳扯。
還可以在Application中注冊ActivityLifecycleCallbacks接口,得到各個Activity的生命周期谤职,可以用來做一些統(tǒng)計和控制的工作饰豺。
Activity啟動模式
- stardard
默認(rèn)的啟動模式,特點(diǎn)就是不管任務(wù)棧中是否存在該Activity的實(shí)例允蜈,都會生成一個新的實(shí)例冤吨。 - singleTop
在跳轉(zhuǎn)時會檢查任務(wù)棧的棧頂,如果當(dāng)前Activity的實(shí)例位于棧頂陷寝,則復(fù)用該實(shí)例,否則生成一個新的實(shí)例其馏。 - singleTask
如果任務(wù)棧中有當(dāng)前Activity的實(shí)例凤跑,則會清空該實(shí)例上面的所有其他Activity的實(shí)例,將該實(shí)例至于棧頂叛复。 - singleInstance
會生成一個新的任務(wù)棧仔引,并且不會有其他實(shí)例進(jìn)入該任務(wù)棧扔仓。
Service
Service(服務(wù))是一個一種可以在后臺執(zhí)行長時間運(yùn)行操作而沒有用戶界面的應(yīng)用組件,具體可以看這里
Service的兩種啟動方式
- startService:生命周期和調(diào)用者不同咖耘,啟動后若調(diào)用者未調(diào)用stopService而直接退出翘簇,Service仍會運(yùn)行。
- bindService:生命周期與調(diào)用者綁定儿倒,調(diào)用者一旦退出版保,Service就會調(diào)用unBind() -> onDestory()
Service與IntentService
具體看這里,簡單總結(jié)一下:
- IntentService是Service的子類夫否,是一個異步的彻犁,會自動停止的服務(wù),很好解決了傳統(tǒng)的Service中處理完耗時操作忘記停止并銷毀Service的問題
- 生成一個默認(rèn)的且與線程相互獨(dú)立的工作線程執(zhí)行所有發(fā)送到onStartCommand()方法的Intent,可以在onHandleIntent()中處理
- 串行隊列,每次只運(yùn)行一個任務(wù),不存在線程安全問題,所有任務(wù)執(zhí)行完后自動停止服務(wù),不需要自己手動調(diào)用stopSelf()來停止
Activity,Window,View
Activity 可以說是應(yīng)用程序的載體(也可以理解為界面的載體凰慈,但是不界面)汞幢,用戶能夠在上面繪制界面(Activity本身不繪制界面),并提供用戶處理事件的API微谓,維護(hù)應(yīng)用程序的生命周期(Android應(yīng)用程序是由多個 Activity 堆積而成森篷,而各個 Activity 又有其獨(dú)立的生命周期)。
Activity內(nèi)部組合了一個Window(這是一個抽象類豺型,具體是PhoneWindow)對象仲智。我們自己寫的擴(kuò)展一個Activity時,在onCreate 方法中調(diào)用 setContentView触创,實(shí)際上是調(diào)用Window對象的 setContentView坎藐,所以說界面繪制全部是由Window類的實(shí)現(xiàn)類(PhoneWindow類)來完成的。
Broadcast廣播
本地廣播:
本地廣播在本應(yīng)用范圍內(nèi)傳播,不用擔(dān)心隱私數(shù)據(jù)泄露,不用擔(dān)心別的應(yīng)用偽造廣播.相比全局廣播,本地廣播更高效.
兩種注冊方式:
- 靜態(tài)注冊:在清單文件中注冊哼绑,常見的有監(jiān)聽設(shè)備啟動岩馍,常駐注冊不會隨程序生命周期改變
- 動態(tài)注冊:在代碼中注冊,隨著程序的結(jié)束抖韩,也就停止接受廣播了
Handler
Binder
多進(jìn)程
進(jìn)程等級
- 前臺進(jìn)程(Foreground process)
前臺進(jìn)程是用戶當(dāng)前做的事所必須的進(jìn)程蛀恩,如果滿足下面各種情況中的一種,一個進(jìn)程被認(rèn)為是在前臺:- 進(jìn)程持有一個正在與用戶交互的Activity茂浮。
- 進(jìn)程持有一個Service双谆,這個Service處于這幾種狀態(tài):
- Service與用戶正在交互的Activity綁定。
- Service是在前臺運(yùn)行的席揽,即它調(diào)用了 startForeground()顽馋。
- Service正在執(zhí)行它的生命周期回調(diào)函數(shù)(onCreate(), onStart(), or onDestroy())。
- 進(jìn)程持有一個BroadcastReceiver幌羞,這個BroadcastReceiver正在執(zhí)行它的 onReceive() 方法寸谜。
- 殺死前臺進(jìn)程需要用戶交互,因?yàn)榍芭_進(jìn)程的優(yōu)先級是最高的属桦。
- 可見進(jìn)程(Visible process)
如果一個進(jìn)程不含有任何前臺的組件熊痴,但仍可被用戶在屏幕上所見他爸。可見的進(jìn)程也被認(rèn)為是很重要的果善,一般不會被銷毀诊笤,除非是為了保證所有前臺進(jìn)程的運(yùn)行而不得不殺死可見進(jìn)程的時候。當(dāng)滿足如下任一條件時巾陕,進(jìn)程被認(rèn)為是可見的:- 進(jìn)程持有一個Activity讨跟,這個Activity不在前臺,但是仍然被用戶可見(處于onPause()調(diào)用后又沒有調(diào)用onStop()的狀態(tài)惜论,比如许赃,前臺的activity打開了一個對話框,這樣activity就會在其后可見)馆类。
*進(jìn)程持有一個Service混聊,這個Service和一個可見的(或者前臺的)Activity綁定。
- 進(jìn)程持有一個Activity讨跟,這個Activity不在前臺,但是仍然被用戶可見(處于onPause()調(diào)用后又沒有調(diào)用onStop()的狀態(tài)惜论,比如许赃,前臺的activity打開了一個對話框,這樣activity就會在其后可見)馆类。
- 服務(wù)進(jìn)程 (Service process)
如果一個進(jìn)程中運(yùn)行著一個service乾巧,這個service是通過 startService() 開啟的句喜,并且不屬于上面兩種較高優(yōu)先級的情況,這個進(jìn)程就是一個服務(wù)進(jìn)程沟于。
盡管服務(wù)進(jìn)程沒有和用戶可以看到的東西綁定咳胃,但是它們一般在做的事情是用戶關(guān)心的,比如后臺播放音樂旷太,后臺下載數(shù)據(jù)等展懈。所以系統(tǒng)會盡量維持它們的運(yùn)行,除非系統(tǒng)內(nèi)存不足以維持前臺進(jìn)程和可見進(jìn)程的運(yùn)行需要供璧。 - 后臺進(jìn)程 (Background process)
如果進(jìn)程不屬于上面三種情況存崖,但是進(jìn)程持有一個用戶不可見的activity(activity的onStop()被調(diào)用,但是onDestroy()沒有調(diào)用的狀態(tài))睡毒,就認(rèn)為進(jìn)程是一個后臺進(jìn)程来惧。
后臺進(jìn)程不直接影響用戶體驗(yàn),系統(tǒng)會為了前臺進(jìn)程演顾、可見進(jìn)程供搀、服務(wù)進(jìn)程而任意殺死后臺進(jìn)程。
通常會有很多個后臺進(jìn)程存在钠至,它們會被保存在一個LRU (least recently used)列表中葛虐,這樣就可以確保用戶最近使用的activity最后被銷毀,即最先銷毀時間最遠(yuǎn)的activity棉钧。 - 空進(jìn)程
如果一個進(jìn)程不包含任何活躍的應(yīng)用組件屿脐,則認(rèn)為是空進(jìn)程。
例如:一個進(jìn)程當(dāng)中已經(jīng)沒有數(shù)據(jù)在運(yùn)行了,但是內(nèi)存當(dāng)中還為這個應(yīng)用駐留了一個進(jìn)程空間摄悯。
保存這種進(jìn)程的唯一理由是為了緩存的需要,為了加快下次要啟動這個進(jìn)程中的組件時的啟動時間愧捕。系統(tǒng)為了平衡進(jìn)程緩存和底層內(nèi)核緩存的資源奢驯,經(jīng)常會殺死空進(jìn)程。
進(jìn)程間通信
- Bundle
- 文件共享
- 廣播
- ContentProvider
- Messenger
- AIDL
- Socket
名稱 | 優(yōu)點(diǎn) | 缺點(diǎn) | 使用場景 |
---|---|---|---|
Bundle | 簡單易用 | 只能傳輸Bundle支持的數(shù)據(jù)類型 | 四大組件間的進(jìn)程間通信 |
文件共享 | 簡單易用 | 不適合高并發(fā)場景,并且無法做到進(jìn)程間的即時通信 | 無并發(fā)訪問情形, 交換簡單的數(shù)據(jù)實(shí)時性不高的場景 |
AIDL | 功能強(qiáng)大 | 使用稍復(fù)雜,需要處理好線程同步 | 一對多通信且有RPC需求 |
ContentProvider | 在數(shù)據(jù)源訪問方面功能強(qiáng)大,支持一對多并發(fā)數(shù)據(jù)共享 | 可以理解為受約束的AIDL,主要提供數(shù)據(jù)源的CRUD操作 | 一對多的進(jìn)程間的數(shù)據(jù)共享 |
Messenger | 功能一般, 支持一對多串行通信,支持實(shí)時通信 | 不能很好處理高并發(fā),不支持RPC,數(shù)據(jù)通過Message進(jìn)行傳輸, 因此只能傳輸Bundle支持的數(shù)據(jù)類型 | 低并發(fā)的一對多即時通信,無RPC需求,或者無需要返回結(jié)果的RPC需求 |
Socket | 功能強(qiáng)大,可以通過網(wǎng)絡(luò)傳輸字節(jié)流,支持一對多并發(fā)實(shí)時通信 | 實(shí)現(xiàn)細(xì)節(jié)稍微有點(diǎn)繁瑣,不支持直接的RPC | 網(wǎng)絡(luò)數(shù)據(jù)交換 |
SparseArray
ANR
ANR全稱Application Not Responding次绘,意思就是程序未響應(yīng)
在Android里, App的響應(yīng)能力是由Activity Manager和Window Manager系統(tǒng)服務(wù)來監(jiān)控的. 通常在如下兩種情況下會彈出ANR對話框:
- 5s內(nèi)無法響應(yīng)用戶輸入事件(例如鍵盤輸入, 觸摸屏幕等).
- BroadcastReceiver在10s內(nèi)無法結(jié)束.
造成以上兩種情況的首要原因就是在主線程(UI線程)里面做了太多的阻塞耗時操作, 例如文件讀寫, 數(shù)據(jù)庫讀寫, 網(wǎng)絡(luò)查詢等等.
如何避免ANR瘪阁?簡單來說就是不要在主線程(UI線程)里面做繁重的操作。
內(nèi)存泄露
- 資源對象沒有關(guān)閉造成,如查詢數(shù)據(jù)庫沒有關(guān)閉游標(biāo)
- 構(gòu)造Adapter時,沒有使用緩存ConvertView
- Bitmap對象在不使用時調(diào)用recycle()釋放內(nèi)存
- context逃逸問題
- 注冊沒有取消,如動態(tài)注冊廣播在Activity銷毀前沒有unregisterReceiver
- 集合對象未清理,如無用時沒有釋放對象的引用
- 在Activity中使用非靜態(tài)的內(nèi)部類邮偎,并開啟一個長時間運(yùn)行的線程管跺,因?yàn)閮?nèi)部類持有Activity的引用,會導(dǎo)致Activity本來可以被gc時卻長期得不到回收
性能優(yōu)化
幾乎是史上最全最實(shí)用的Android性能全面分析與優(yōu)化方案研究
LinearLayout和RelativeLayout性能對比
- RelativeLayout會讓子View調(diào)用2次onMeasure禾进,LinearLayout 在有weight時豁跑,也會調(diào)用子View2次onMeasure
- RelativeLayout的子View如果高度和RelativeLayout不同,則會引發(fā)效率問題泻云,當(dāng)子View很復(fù)雜時艇拍,這個問題會更加嚴(yán)重。如果可以宠纯,盡量使用padding代替margin卸夕。
- 在不影響層級深度的情況下,使用LinearLayout和FrameLayout而不是RelativeLayout。
布局優(yōu)化
- 避免OverDraw過渡繪制
- 優(yōu)化布局層級
- 避免嵌套過多無用布局
- 使用<include />標(biāo)簽把復(fù)雜的界面需要抽取出來
- 使用<merge />標(biāo)簽婆瓜,因?yàn)樗趦?yōu)化UI結(jié)構(gòu)時起到很重要的作用快集。目的是通過刪減多余或者額外的層級,從而優(yōu)化整個Android Layout的結(jié)構(gòu)廉白。核心功能就是減少冗余的層次從而達(dá)到優(yōu)化UI的目的
- ViewStub 是一個隱藏的个初,不占用內(nèi)存空間的視圖對象,它可以在運(yùn)行時延遲加載布局資源文件蒙秒。
ListView卡頓的原因以及優(yōu)化策略
重用ConvertView: 通過復(fù)用ConvertView來減少不必要的view的創(chuàng)建勃黍,另外Inflate操作會把xml文件實(shí)例化成相應(yīng)的View實(shí)例,屬于IO操作晕讲,是耗時操作覆获。
減少findViewById()操作: 將xml文件中的元素封裝成viewholder靜態(tài)類,通過ConvertView的setTag和getTag方法將view與相應(yīng)的holder對象綁定在一起瓢省,避免不必要的findViewById操作
避免在 getView 方法中做耗時的操作: 例如加載本地 Image 需要載入內(nèi)存以及解析 Bitmap 弄息,都是比較耗時的操作,如果用戶快速滑動listview勤婚,會因?yàn)間etview邏輯過于復(fù)雜耗時而造成滑動卡頓現(xiàn)象摹量。用戶滑動時候不要加載圖片,待滑動完成再加載
Item的布局層次結(jié)構(gòu)盡量簡單,避免布局太深或者不必要的重繪
-
盡量能保證 Adapter 的 hasStableIds() 返回 true
這樣在 notifyDataSetChanged() 的時候缨称,如果item內(nèi)容并沒有變化凝果,ListView 將不會重新繪制這個 View,達(dá)到優(yōu)化的目的
-
在一些場景中睦尽,ScollView內(nèi)會包含多個ListView器净,可以把listview的高度寫死固定下來。
由于ScollView在快速滑動過程中需要大量計算每一個listview的高度当凡,阻塞了UI線程導(dǎo)致卡頓現(xiàn)象出現(xiàn)山害,如果我們每一個item的高度都是均勻的,可以通過計算把listview的高度確定下來沿量,避免卡頓現(xiàn)象出現(xiàn)
使用 RecycleView 代替listview: 每個item內(nèi)容的變動浪慌,listview都需要去調(diào)用notifyDataSetChanged來更新全部的item,太浪費(fèi)性能了朴则。RecycleView可以實(shí)現(xiàn)當(dāng)個item的局部刷新权纤,并且引入了增加和刪除的動態(tài)效果,在性能上和定制上都有很大的改善
ListView 中元素避免半透明: 半透明繪制需要大量乘法計算乌妒,在滑動時不停重繪會造成大量的計算妖碉,在比較差的機(jī)子上會比較卡。 在設(shè)計上能不半透明就不不半透明芥被。實(shí)在要弄就把在滑動的時候把半透明設(shè)置成不透明欧宜,滑動完再重新設(shè)置成半透明。
盡量開啟硬件加速: 硬件加速提升巨大拴魄,避免使用一些不支持的函數(shù)導(dǎo)致含淚關(guān)閉某個地方的硬件加速冗茸。當(dāng)然這一條不只是對 ListView。
動畫
逐幀動畫(Drawable Animation): 加載一系列Drawable資源來創(chuàng)建動畫匹中,簡單來說就是播放一系列的圖片來實(shí)現(xiàn)動畫效果夏漱,可以自定義每張圖片的持續(xù)時間
補(bǔ)間動畫(Tween Animation): Tween可以對View對象實(shí)現(xiàn)一系列簡單的動畫效果,比如位移顶捷,縮放挂绰,旋轉(zhuǎn),透明度等等服赎。但是它并不會改變View屬性的值葵蒂,只是改變了View的繪制的位置,比如重虑,一個按鈕在動畫過后践付,不在原來的位置,但是觸發(fā)點(diǎn)擊事件的仍然是原來的坐標(biāo)缺厉。
屬性動畫(Property Animation): 動畫的對象除了傳統(tǒng)的View對象永高,還可以是Object對象隧土,動畫結(jié)束后,Object對象的屬性值被實(shí)實(shí)在在的改變了
Java部分
引用類型
- 強(qiáng)引用
- 軟引用
- 弱引用
- 虛引用
WeakReference 與 SoftReference的區(qū)別
雖然 WeakReference 與 SoftReference 都有利于提高 GC 和 內(nèi)存的效率命爬,但是 WeakReference 曹傀,一旦失去最后一個強(qiáng)引用,就會被 GC 回收饲宛,而軟引用雖然不能阻止被回收卖毁,但是可以延遲到 JVM 內(nèi)存不足的時候。
集合
HashMap
強(qiáng)烈推薦Java 8系列之重新認(rèn)識HashMap落萎,看過的最好的一篇文章,感覺看過這一篇之后炭剪,就不需要再看其他介紹HashMap的了
LinkedHashMap
ConcurrentHashMap
sleep 方法和 wait 方法的區(qū)別
雖然兩者都是用來暫停當(dāng)前運(yùn)行的線程练链,但是 sleep() 實(shí)際上只是短暫停頓,因?yàn)樗粫尫沛i奴拦,而 wait() 意味著條件等待媒鼓,這就是為什么該方法要釋放鎖,因?yàn)橹挥羞@樣错妖,其他等待的線程才能在滿足條件時獲取到該鎖绿鸣。
Java 中堆和棧的區(qū)別
JVM 中堆和棧屬于不同的內(nèi)存區(qū)域,使用目的也不同暂氯。
- 棧常用于保存方法幀和局部變量潮模,而對象總是在堆上分配。
- 棧通常都比堆小痴施,也不會在多個線程之間共享擎厢,而堆被整個 JVM 的所有線程共享。
GC
- 引用計數(shù)
- 可達(dá)性分析
垃圾收集算法
- 標(biāo)記清除
- 復(fù)制收集
- 標(biāo)記整理
- 分代收集
你能保證 GC 執(zhí)行嗎
不能辣吃,雖然你可以調(diào)用 System.gc() 或者 Runtime.gc()动遭,但是沒有辦法保證 GC 的執(zhí)行。
String,StringBuilder,StringBuffer
- 如果要操作少量的數(shù)據(jù)用 = String
- 單線程操作字符串緩沖區(qū) 下操作大量數(shù)據(jù) = StringBuilder
- 多線程操作字符串緩沖區(qū) 下操作大量數(shù)據(jù) = StringBuffer