最近在準(zhǔn)備android面試,整理了下相關(guān)的面試題评架,分為如下三個部分:android部分、Java部分炕泳、算法面試題纵诞,后續(xù)有新內(nèi)容直接在對應(yīng)的文章中補充。
android部分:本文
Java部分:
http://www.reibang.com/p/c2c8f5019c8f
算法部分:
http://www.reibang.com/p/d9bfc440ada3
0培遵、android系統(tǒng)架構(gòu)圖
Android系統(tǒng)啟動過程過程如下:Loder--Kernel--Native--FrameWork--App浙芙。
Loader層:分為Boot ROM和Boot Loader,其中Boot ROM是當(dāng)手機處于關(guān)機狀態(tài)時籽腕,長按Power鍵開機嗡呼,引導(dǎo)芯片開始從固化ROM里的預(yù)設(shè)代碼開始執(zhí)行,然后加載引導(dǎo)程序到RAM皇耗;Boot Loader是啟動Android系統(tǒng)之前的引導(dǎo)程序南窗,主要是檢查RAM,初始化應(yīng)急參數(shù)等功能郎楼。
Kernel層:指的是android內(nèi)核層矾瘾,到這里才剛剛開始進入android系統(tǒng)。啟動Kernel的Swapper進程箭启,該進程也成為idle進程壕翩,用于初始化進程管理、內(nèi)存管理傅寡,加載Display放妈、Camera Driver、Binder Driver等荐操。然后接著啟動kthhreadd進程芜抒,該進程是Linux的內(nèi)核進程,用于創(chuàng)建內(nèi)核工作線程kworker等托启,kthreadd進程是所有內(nèi)核進程的鼻祖宅倒。
Native層:進入到Native層主要包括啟動init進程,init是linux系統(tǒng)的用戶進程屯耸,init進程是所有用戶進程的鼻祖拐迁。Init進程會啟動ServiceManager(binder服務(wù)大管家)蹭劈,Init進程還會會孵化出zygote進程,zygote是android系統(tǒng)的第一個java進程线召,zygote進程是所有java進程的父進程铺韧。
Framework層:zygote進程是右init進程通過解析init.rc文件后fork生成的,zygote進程主要包括加載ZygoteInit類缓淹,注冊Zygote Socket套接字哈打;加載虛擬機;preloadclass讯壶;preloadresource料仗。隨后zygote孵化SystemServer進程,SystemServer是zygote孵化的第一個進程,SystemServer負責(zé)啟動和管理整個java framework伏蚊,包含AMS罢维、WMS、PMS等丙挽。
APP層:zygote進程孵化出的第一個APP進程是Launcher(SystemServer啟動完成調(diào)用AMS的systemReady會啟動Launcher)肺孵,他還會孵化出Browser、Phone等颜阐;所有的APP進程都是有zygote進程孵化出來的平窘。
1、Http請求流程
http://www.reibang.com/p/24c320699dd8
2凳怨、android如何實現(xiàn)Https單向認證
http://www.reibang.com/p/0a392c38b711
3瑰艘、如何導(dǎo)入外部數(shù)據(jù)庫
可以把數(shù)據(jù)導(dǎo)入data目錄下,然后通過SQLiteDatabase.openOrCreateDatabase來打開數(shù)據(jù)庫肤舞。
4紫新、Proguard代碼混淆,混淆后的Class是如何查找的(加載類李剖,調(diào)用方法)
Proguard有以下四個功能:
? ? 壓縮:檢測并移除代碼中無用的類芒率、字段、方法和特性篙顺。
? ? 優(yōu)化:對字節(jié)碼進行優(yōu)化偶芍,移除無用的指令。
? ? 混淆:使用a b c這樣簡短無意義的名字對類德玫、字段匪蟀、方法進行重命名。
? ? 預(yù)驗:Java平臺對處理后的代碼進行預(yù)驗宰僧,確保加載后的class是可以執(zhí)行的材彪。
? ? 混淆后的class是一些無意義的名稱,類的引用關(guān)系沒有變,增加了反編譯的難度段化∴医荩混淆會生成mapping文件,該文件描述了混淆后的名稱和原來名稱的映射關(guān)系穗泵。
5普气、本地廣播和全局廣播的區(qū)別
http://www.reibang.com/p/71b973997ddf
本地廣播通過LocalBroadcastManager來實現(xiàn)廣播的注冊和發(fā)送谜疤,只在本應(yīng)用范圍內(nèi)傳播佃延,不必擔(dān)心隱私數(shù)據(jù)的泄漏。全局廣播會在整個系統(tǒng)內(nèi)發(fā)布夷磕。
6履肃、Window View Activity三者之間的區(qū)別
Activity像一個工匠(控制單元),Window像窗戶(承載模型)坐桩,View像窗花(顯示視圖)尺棋,LayoutInflater像剪刀,XML配置窗花圖紙绵跷。
? ? 在Activity的attach會創(chuàng)建PhoneWindow類型的Window膘螟。
? ? Activity中調(diào)用setContentView其實是調(diào)用PhoneWindow.setContentView。
? ? PhoneWindow.setContentView會創(chuàng)建DectorView碾局,DectorView是FrameLayout類型的ViewGroup
? ? 之后通過LayoutInflater加載XML布局荆残,并將View加入到DectorView中。
7净当、進程間通信的方式
Activity Service Receiver通過Intent通信内斯;Socket方式(zygote通過sockect接受消息);基于文件共享像啼;AIDL方式俘闯;Messanger方式
8、Binder原理
http://www.reibang.com/p/b26c7bcef5e4
9忽冻、Java ClassLoader和Android ClassLoder的對比
http://www.reibang.com/p/bce29536a602
10真朗、Messanger和AIDL的區(qū)別
Messenger不適用大量并發(fā)的請求,它以傳下的方式來處理客戶端發(fā)來的消息僧诚,如果大量消息發(fā)送到服務(wù)端蜜猾,服務(wù)端由于是通過Handler來處理消息,所以只能一個個順序執(zhí)行振诬。Messenger主要是為了傳遞Message蹭睡,對于跨進程調(diào)用服務(wù)端的方法,messenger不適合赶么。Messenegr的底層實現(xiàn)是AIDL肩豁,系統(tǒng)為了跨進程傳輸Message提供了這個API方便使用。AIDL適用于大量并發(fā)請求,以及涉及服務(wù)端方法調(diào)用的情況。
Messenger實現(xiàn)跨進程雙向通信示例:
Client通過bindService在onServiceConnected中創(chuàng)建一個Messenger,創(chuàng)建一個含有replyTo的message饭入,通過Messenger發(fā)送這個Message到Service悬嗓。
Service端創(chuàng)建一個Messenger,并在onBind中將Messenger.getBinder返回給客戶端琅催,Service端的Messager在收到Client傳來的Message后,取出replyTo字段,通過replyTo回復(fù)消息給客戶端谜叹,由此就實現(xiàn)了雙向通信。
11搬葬、OOM和內(nèi)存泄漏的區(qū)別
OOM(Out Of Memory)荷腊,也叫內(nèi)存溢出,即內(nèi)存不夠用了急凰。申請新內(nèi)存的時候女仰,發(fā)現(xiàn)剩余內(nèi)存不夠,就會出現(xiàn)OOM內(nèi)存溢出抡锈。
內(nèi)存泄漏(MemoryLeak)疾忍,只的是申請的內(nèi)存沒法釋放(引用無法釋放),如果內(nèi)存泄漏嚴重床三,最后可能導(dǎo)致內(nèi)存溢出一罩。
OOM可以try catch嗎? 可以
12勿璃、產(chǎn)生內(nèi)存泄漏的原因
資源對象沒有關(guān)閉擒抛,Cursor或者File往往使用了一些緩沖,在不使用的時候需要及時關(guān)閉补疑∑缁Γ可以定義一個公共的Closoable接口來執(zhí)行這些關(guān)閉。
使用Adapter時沒有使用緩存convertView
Bitmap不使用時沒有recycle
注冊的監(jiān)聽器沒有取消
Handler造成的內(nèi)存泄漏:在newHandler時莲组,非靜態(tài)內(nèi)部類會持有外部類的引用诊胞。下圖的代碼延遲10分鐘發(fā)送消息,如果發(fā)送消息后關(guān)閉界面锹杈,由于message引用了handler撵孤,handler又引用了activity,就導(dǎo)致activity無法被回收竭望,出現(xiàn)泄漏邪码。
13、內(nèi)存泄漏分析步驟
代碼里面集成LeakCanary工具咬清,在測試的時候顯示引用沒有釋放
使用android studio的內(nèi)存監(jiān)測工具闭专,監(jiān)測內(nèi)存波動奴潘,例如內(nèi)存突然變大釋放,就可能存在內(nèi)存抖動
Monkey測試影钉,抓取hprof文件画髓,使用mat工具進行分析,首先使用hprof-conf進行轉(zhuǎn)換平委,然后mat打開文件奈虾,會展示的內(nèi)存問題,然后查看detail可以查看GC根元素到內(nèi)存消耗聚集點的最短路徑廉赔。使用List Object / Path to GC roots可以查看對象被誰引用肉微,當(dāng)然在查看時要過濾掉soft引用和weak引用。
幾個概念:
Shallow Size:對象自身占用的內(nèi)存大小昂勉,不包括它引用的對象浪册。針對非數(shù)組類型的對象扫腺,它的大小就是對象與它所有的成員變量大小的總和岗照。當(dāng)然這里面還會包括一些java語言特性的數(shù)據(jù)存儲單元。針對數(shù)組類型的對象笆环,它的大小是數(shù)組元素對象的大小總和攒至。
Retained Size:Retained Size=當(dāng)前對象大小+當(dāng)前對象可直接或間接引用到的對象的大小總和。(間接引用的含義:A->B->C躁劣;C就是間接引用)
換句話說迫吐,Retained Size就是當(dāng)前對象被GC后,從Heap上總共能釋放掉的內(nèi)存账忘。不過志膀,釋放的時候還要排除被GC Roots直接或間接引用的對象。他們暫時不會被被當(dāng)做Garbage鳖擒。
14溉浙、低版本SDK如何實現(xiàn)高版本API
例如File.getTotalSpace()在API Level 9及其以上才會存在,在API level8調(diào)用這個方法就會出現(xiàn)Call requires API level 9(current min is 8)的錯誤蒋荚,通過設(shè)置@ TargetApi(9)可以在8的SDK完成編譯戳稽,但是在低版本機器運行會報出NoSushMethodError;所以在代碼上要加一個版本判斷以保證在低版本不允許該代碼執(zhí)行期升。
在低版本SDK下使用該版本的API需要按照如下三步進行代碼編寫:
使用@TargetApi($API_LEVEL)使可以通過編譯惊奇;運行時判斷API Level,僅在足夠高且有此方法的系統(tǒng)中調(diào)用此方法播赁;保證功能完整性颂郎,針對低版本需要自己實現(xiàn)API的功能。
15容为、Ubuntu下編譯android的步驟
下載aosp源碼(清華或者科大網(wǎng)站下載鏡像)—進入根目錄—source ./build/envsetup.sh—lunch—選擇版本— make–j8(開啟8個線程編譯)
16乓序、ANR產(chǎn)生的原因和分析步驟
ANR(Application Not responding)诞吱,指應(yīng)用程序未響應(yīng),android系統(tǒng)對于一些事情需要在一定時間內(nèi)完成竭缝,如果超過時間還沒有響應(yīng)房维,就會產(chǎn)生ANR。以下場景會產(chǎn)生ANR:
? ? ? ? ·ServiceTimeout:前臺服務(wù)需要在20秒內(nèi)完成抬纸。
? ? ? ? ·BroadcastQueue Timeout:前臺廣播需要在10秒內(nèi)完成咙俩。
? ? ? ? ·ContentProvider Timeout:provider在publish后需要在10秒內(nèi)完成。
? ? ? ? ·InputDispatching Timeout:輸入事件分發(fā)不能超過5秒湿故,包含按鍵和觸摸事件阿趁。
Service Timeout:位于ActivityManager線程中的AMS.MainHandler收到SERVICE_TIME_OUT消息時觸發(fā)。對于Service有兩類:前臺服務(wù)的超時時間為20S坛猪,后臺服務(wù)的超時時間為200s脖阵。
BroadcastReceiver Timeout:位于ActivityManager線程中的Broadcast.Queue.BroadcastHandler收到BROADCAST_TIMEOUT_MSG消息時觸發(fā),對于廣播有兩個隊列:前臺隊列的超時時間為10秒墅茉,后臺隊列的超時時間為60s命黔。
ContentProvider Timeout:位于ActivityManager線程中的AMS.MainHandler收到CONTENT_PROVIDER_PUBLISH_TIME_MSG消息時觸發(fā),超時時間為CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10s就斤,這和前面的Service和BroadcastQueue完全不同悍募,由Provider進程啟動過程相關(guān)。
Input事件處理慢觸發(fā)ANR洋机,一般有如下原因:無窗口坠宴,有應(yīng)用;窗口暫停绷旗;窗口未連接喜鼓;窗口連接已死亡;窗口連接已滿衔肢;按鍵事件庄岖,輸出隊列或事件等待隊列不為空;非按鍵事件膀懈,事件等待隊列不為空且頭事件分發(fā)超時500ms顿锰。
如何分析ANR呢?
系統(tǒng)針對ANR启搂,會生成一個trace.txt文件硼控,log文件找到tid=1的線程,該線程為主線程胳赌±魏常可以看到是什么原因?qū)е碌腁NR,查看主線程的狀態(tài)疑苫,是主線程等待了熏版、死鎖了還是其他什么原因纷责。然后查看main log,搜索ANR關(guān)鍵字撼短,查看當(dāng)前的CPU狀態(tài)再膳、IO wait、message的延時時間曲横、block喂柒、memory leak等。如果是死鎖了禾嫉,可以通過trace文件看到對象被哪個線程占用著灾杰,如果內(nèi)存泄漏了,需要dump內(nèi)存文件進行分析熙参。我們也可以借助于第三方工具BlockCanary在測試的時候進行慢操作分析艳吠,該組件利用了Looper類的loop函數(shù)的特性,如下所示孽椰,loop函數(shù)在調(diào)用dispatchMessage函數(shù)(會調(diào)用handleMessage)的時候會可以打印handleMessage的執(zhí)行日志昭娩,通過自定義Printer類,將printer類設(shè)置到當(dāng)前l(fā)ooper對象弄屡,這樣在就可以打印每個message的執(zhí)行時間题禀,進而找到慢操作鞋诗。
如何避免ANR膀捷?
要避免ANR可以這樣做:數(shù)據(jù)庫操作或者文件讀寫等IO耗時操作使用異步;UI線程只做UI的事削彬;廣播如果需要耗時操作可以在onReceiver啟動一個IntentService全庸;如果要使用Service,勁量使用IntentService融痛;線程間交互使用Handler來做壶笼;后臺線程使用Background優(yōu)先級;圖片質(zhì)量勁量降低雁刷,如使用565圖片覆劈;界面層次布局降低,減少繪制時間沛励;數(shù)據(jù)庫打開并發(fā)讀寫模式责语;release環(huán)境關(guān)閉log。
17目派、log打印的buffer溢出了怎么解決
https://mp.weixin.qq.com/s?__biz=MzA3NTYzODYzMg%3D%3D&mid=2653578220&idx=3&sn=5691bdd82ae0715ab12fd6b849f74aee&chksm=84b3b1ebb3c438fddf86bf74e232fa14222932ebd6d6439bed04ad17d5e64e9270d4ab460f64&scene=4
參考微信mars的xlog坤候,xlog是微信使用的獨立日志模塊,android平臺使用java實現(xiàn)日志模塊企蹭,每有一句日志就寫入文件白筹,這樣在使用的過程中存在大量的GC智末,而且大量的IO操作容易導(dǎo)致程序卡頓。Xlog2.0引入mmap徒河,mmap是使用邏輯內(nèi)存對磁盤文件進行映射系馆,中間只是進行映射沒有任何拷貝操作,避免了寫文件的數(shù)據(jù)拷貝顽照,操作內(nèi)存就相當(dāng)于在操作文件它呀,避免內(nèi)核空間和用戶空間的頻繁切換。
Mmap的回寫時機如下:內(nèi)存不足棒厘、進程退出纵穿、調(diào)用msync或者munmap、不設(shè)置MAP_NOSYNC
18奢人、如何降低應(yīng)用功耗
常見的導(dǎo)致功耗問題的原因如下:阻止手機休眠(Wake_Lock)谓媒、時常喚醒手機(Alarm)、后臺頻繁運行何乎、過渡繪制句惯。
Wakelock有以下類型:
PARTIAL_WAKE_LOCK:保持CPU運轉(zhuǎn),屏幕和鍵盤燈有可能是關(guān)閉的支救。SCREEN_DIM_WAKE_LOCK:保持CPU運轉(zhuǎn)抢野,允許保持屏幕顯示但有可能是灰的,允許關(guān)閉鍵盤燈各墨。
SCREEN_BRIGHT_WAKE_LOCK:保持CPU運轉(zhuǎn)指孤,允許保持屏幕高亮,允許關(guān)閉鍵盤燈
FULL_WAKE_LOCK:保持CPU運轉(zhuǎn)贬堵,保持屏幕高亮顯示恃轩,鍵盤燈也保持亮度
Wakelock默認是引用計數(shù)(reference counted)行為,同一wakelock 呼叫幾次acquire()黎做,就需搭配幾次release() 才能真正釋放
若wakelock 已釋放叉跛,再呼叫release() 就會丟出RuntimeException
可以使用setReferenceCounted(boolean value)方法,傳入false設(shè)置不使用引用計數(shù)方式蒸殿,release一次之后即可釋放所有wakelock
acquire(long timeout)筷厘,使用這種方式,傳入超時時間宏所,在timeout后自動釋放鎖酥艳。
Activity在使用時如果需要阻止屏幕熄滅,例如播放視頻楣铁,可以使用:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
AlarmManager類型:
應(yīng)用進程及時不存在玖雁,也想在后臺做一些事,這時候就需要用到AlarmManager盖腕,相當(dāng)于定時器赫冬。有如下四種類型Alarm:
ELAPSED_REALTIME:在多少ms 后呼叫指定的PendingIntent浓镜。若當(dāng)時手機處于休眠,會延遲到手機醒來時 (屏幕可能還是關(guān)的)才做劲厌。(不一定準(zhǔn)時)
ELAPSED_REALTIME_WAKEUP:在多少ms 后呼叫指定的PendingIntent膛薛。若當(dāng)時手機處于休眠,會喚醒手機做事补鼻,同時休眠期間被延遲的事也會一起執(zhí)行哄啄。是最常見的耗電兇手。
RTC:在指定的某段時間做事风范。若當(dāng)時手機處于休眠咨跌,會延遲到手機醒來時 (屏幕可能還是關(guān)的)才做。(不一定準(zhǔn)時)
RTC_WAKEUP:在指定的某段時間做事硼婿。若當(dāng)時手機處于休眠锌半,會喚醒手機做事,同時休眠期間被延遲的事也會一起執(zhí)行寇漫。通常用于鬧鐘刊殉、記事提醒等。
降低功耗的方式:
勁量不使用靜態(tài)廣播州胳,以減少后臺自啟動工作记焊;如果要使用靜態(tài)廣播,可以先設(shè)置disable栓撞,然后在進入應(yīng)用后再設(shè)置enable遍膜。
少用alarm,尤其是wakeup的alarm腐缤。
使用partial wakelock 需設(shè)定timeout捌归,在超過時間后自動釋放wakelock。
網(wǎng)絡(luò)請求如果對及時性要求不高可以對請求進行組合岭粤。
考慮使用指數(shù)退避算法。
避免2G情況傳輸數(shù)據(jù)特笋。
Activity Pause時勁量釋放資源剃浇。
避免動畫不停運行。
減少view的過渡繪制猎物。
19虎囚、CRASH的產(chǎn)生和處理
Crash分為native crash和framework crash,如果沒有try…catch蔫磨,就會導(dǎo)致crash淘讥,系統(tǒng)會自己捕獲,進入crash流程堤如。對應(yīng)進程來說蒲列,在啟動的時候會運行RuntimeInit的commonInit函數(shù)窒朋,該函數(shù)方法設(shè)置了UncaughExceptionHandler,
UncaughExceptionHandler會觸發(fā)AMS的handleApplicationCrash函數(shù)蝗岖,處理crash侥猩。應(yīng)用可以通過自定義UncaughExceptionHandler來設(shè)置自定義的crash后續(xù)處理,例如保存log等待下次上傳抵赢。
60秒內(nèi)連續(xù)crash兩次的非persistent進程被認定為bad進程欺劳,如果第三次從后臺啟動該進程(Intent.getFlags判斷),則會拒絕創(chuàng)建進程铅鲤。
當(dāng)crash次數(shù)達到兩次的非persistent進程划提,再次殺死該進程,即使允許自啟動的service也會在被殺后拒絕啟動邢享。
20腔剂、App進程的創(chuàng)建流程
App的進程是由zygote fork出來的,運行在Android Runtime驼仪,進程創(chuàng)建圖如下:
當(dāng)點擊桌面應(yīng)用圖標(biāo)時掸犬,會觸發(fā)startActivity,startActivity發(fā)起進程便是Launcher所在進程绪爸。發(fā)起進程通過binder發(fā)送消息到system_server進程湾碎。
system_server進程檢測到目標(biāo)應(yīng)用所在進程沒有啟動的話,就會調(diào)用Process.start來啟動目標(biāo)進程奠货,該函數(shù)通過socket向zygote進程發(fā)送創(chuàng)建新進程的請求介褥。
Zygote進程啟動的時候運行了ZygoteInit.main函數(shù),并進入runSelectLoop循環(huán)递惋,該循環(huán)用來檢測外部的socket請求柔滔,當(dāng)運行Process.start的時候,該循環(huán)檢測到請求萍虽,就會調(diào)用ZygoteConnection.runOnce函數(shù)睛廊,最后創(chuàng)建出新的目標(biāo)進程。
新的進程的入口函數(shù)就是ActivityThread.main函數(shù)杉编。
上圖所示為具體調(diào)用的函數(shù)和進程間關(guān)系:
system_server通過Process.start發(fā)起進程創(chuàng)建請求超全,會先收集新進程的uid、gid邓馒、nice-name等參數(shù)嘶朱,然后通過socket發(fā)給zygote進程。
Zygote在接受到system_server進程發(fā)送過來的參數(shù)后進入forkAndSpecialize函數(shù)開始創(chuàng)建進行光酣。
新進程創(chuàng)建時進入handleChildProc疏遏,設(shè)置進程名,打開binder驅(qū)動,啟動binder線程财异,設(shè)置art虛擬機參數(shù)倘零,然后反射調(diào)用ActivityThread的main函數(shù),該函數(shù)是新apk的入口函數(shù)宝当。
21视事、殺進程的方法
Process.java提供了三個方法來殺死用戶態(tài)進程。
killProcess和killProcessQuite的唯一區(qū)別在于是否輸出log庆揩,最終調(diào)用的都是kill(pid,sig)方法俐东。
22、android如何開啟一個新的進程
app都是單進程架構(gòu)订晌,對于多進程架構(gòu)的app一般是通過在AndroidManifest.xml中android:process屬性來實現(xiàn)的虏辫。
當(dāng)android:process屬性值以”:”開頭,則代表該進程是私有的锈拨,只有該app可以使用砌庄,其他應(yīng)用無法訪問;
當(dāng)android:process屬性值不以”:“開頭奕枢,則代表的是全局型進程娄昆,但這種情況需要注意的是進程名必須至少包含“.”字符。
23缝彬、如何做過度繪制優(yōu)化
大多數(shù)手機的刷新頻率為60hz萌焰,如果在1000/60=16.67ms內(nèi)沒有辦法把這一幀的任務(wù)執(zhí)行完成,就會發(fā)生丟幀現(xiàn)象谷浅,丟幀越多兰英,卡頓越明顯街州。
RefreshRate:一秒內(nèi)刷新屏幕的次數(shù)活尊,取決于硬件的固定參數(shù)炬搭,例如60Hz
Frame Rate:幀率,代表GPU一秒內(nèi)繪制的幀數(shù)墩邀,例如30fps/60fps
GPU會獲取圖形數(shù)據(jù)進行渲染掌猛,然后硬件負責(zé)把渲染后的內(nèi)容呈現(xiàn)到屏幕上,兩者之間不停地協(xié)作磕蒲。理想狀態(tài)如下圖所示:
理想狀態(tài)下留潦,GPU渲染完成的時間和硬件準(zhǔn)備呈現(xiàn)時間保持一致,但不幸的是辣往,刷新頻率和幀率并不是總能保持相同的節(jié)奏。如果發(fā)生幀率和刷新頻率不一致殖卑,就容易出現(xiàn)Tearing現(xiàn)象(畫面上線兩部分顯示的內(nèi)容發(fā)生斷裂站削,來做不同的數(shù)據(jù)發(fā)生重疊),當(dāng)幀率小于刷新屬性頻率的時候孵稽,即GPU渲染內(nèi)容慢了许起,如下所示:
在這種情況下十偶,某些幀顯示的畫面內(nèi)容就和上一幀畫面一樣,出現(xiàn)掉幀現(xiàn)象园细,用戶會感覺卡頓惦积。
掉幀的原因很多,例如布局過于復(fù)雜猛频、內(nèi)存抖動狮崩、主線程做太多的事等導(dǎo)致GPU渲染太慢。我們可以使用View Hierarchy英[?ha??rɑ:ki]來查看界面的布局結(jié)構(gòu)鹿寻,打開lint工具進行檢查睦柴。勁量使用RelativeLayout,使用merge標(biāo)簽毡熏,使用ViewStub延遲布局加載坦敌,background和主題不應(yīng)該重復(fù)使用。
24痢法、IdleHandler
該方法是在消息隊列的消息全部處理完或者在MessageQueue在阻塞的過程中等待更多的消息的時候調(diào)用的狱窘,返回值用于判斷處理一次后是否保存這個接口,如果為true表示每次消息處理完或者消息阻塞了都會處理這個接口财搁;反之只處理一次蘸炸。調(diào)用IdelHandler的代碼如下所示:
Looper里面loop函數(shù)通過調(diào)用MessageQueue.next來獲取消息,在nativePollOnce來獲取一個message,當(dāng)出現(xiàn)超時或者獲取的消息為空妇拯,說明當(dāng)前沒有消息要處理幻馁,就idler.queueIdle()函數(shù)來處理IdelHandelr。Android源碼里也用到了IdelHandler越锈,例如ActivityThread中:
25仗嗦、應(yīng)用啟動速度如何優(yōu)化
通常我們使用TraceView在進行啟動速度優(yōu)化的分析工具,但該工具有個缺點甘凭,在冷啟動的情況下只能通過代碼的方式來埋點稀拐,例如在Application的onCreate中埋入開始抓取Trace文件的代碼:Debug.startTrace,在主界面的onResume中埋入抓取結(jié)束的代碼:Debug.stopTrace丹弱。咋抓取出trace文件后德撬,可以通過android studio打開,打開后分為上下兩個面板躲胳,上面如下:
該面板記錄了各個線程的函數(shù)調(diào)用時間蜓洪,main表示主線程,圖中不同的顏色代表不同的方法執(zhí)行坯苹,同一個顏色時間越長點執(zhí)行的時間越久隆檀,空白表示這個時間段沒有方法執(zhí)行。下面板詳細記錄了各個方法的執(zhí)行時間。
關(guān)鍵指標(biāo)如下:
定位問題的時候按照如下步驟:
我們一般只關(guān)心兩點:一類是調(diào)用次數(shù)不多恐仑,但每次卻花費很長時間的函數(shù)(Cpi
time/Call)泉坐;第二類是本身占用時間不長,但卻頻繁調(diào)用的函數(shù)(Calls+Recur&Calls/Total)裳仆。
從上面板查看哪些線程執(zhí)行時間長腕让,什么時間執(zhí)行,與主線程的交互如何歧斟。
點擊下面板的Cpu Time/Call纯丸,按照占用Cpu的時間進行排序。
哪些方法調(diào)用次數(shù)頻繁构捡,按照Calls+Recur和Calls/Total次數(shù)排序液南。
冷啟動優(yōu)化
當(dāng)app進程不存在的時候,系統(tǒng)啟動app會先創(chuàng)建進程勾徽,執(zhí)行Application的onCreate方法滑凉,在進程的初始化中勢必會消耗一段時間,這段時間WindowManager會先加載app主題樣式的窗口背景windowBackground作為預(yù)覽顏色喘帚,然后才會去加載真正的布局畅姊,如果這個時間很長,而默認的背景又是黑色或者白色吹由,就會給用戶造成一種卡頓錯覺若未。一般有兩種方式解決該問題:
使用閃屏頁,將啟動界面的windowBackground設(shè)置為APP的logo背景圖片
設(shè)置windowBackground為透明色倾鲫,這樣當(dāng)用戶點擊桌面app的時候粗合,會先顯示出透明的界面,給用戶造成一種假象乌昔,以為是rom的卡頓隙疚,微信就是這么干的。在Activity.onCreate之前再設(shè)置本來的theme
26磕道、Systrace
http://www.reibang.com/p/fba521349f19
27供屉、RemoteViews原理
http://www.reibang.com/p/f666d7ca23d7
28、IntentService
Android的Service是在處理任務(wù)時都是同步執(zhí)行的溺蕉,IntentService在執(zhí)行任務(wù)時卻是異步的伶丐,原因是IntentService內(nèi)部使用HandlerThread來執(zhí)行任務(wù),在onCreate會創(chuàng)建一個HandlerThread疯特,并將HandlerThread的looper傳給ServiceHandler:
這樣在onStart中處理任務(wù)時哗魂,可以直接通過ServiceHandler進行異步處理。ServiceHandler在handleMessage的后就自動stop掉IntentService漓雅。
29啡彬、HandlerThread
HandleThread繼承了Thread羹与,在new HandlerThread后通過start方法開啟這個線程故硅;HandlerThread.run會開啟一個Looper庶灿,并調(diào)用Looper.prepare,最終調(diào)用Looper.loop開啟消息循環(huán)吃衅。
HandlerThread提供一個getLooper方法往踢,在創(chuàng)建Handler時傳入這個Looper對象可以保證Looper.loop運行在異步線程。
30徘层、AsyncTask
Android提供的異步處理API峻呕,有三個主要方法,onPreExecute趣效、doInBackground瘦癌、onPostExecute,onpre在異步執(zhí)行前調(diào)用跷敬、doInBackground是異步調(diào)用讯私、onpost是在異步執(zhí)行后調(diào)用。AsyncTask提供兩個方法執(zhí)行異步任務(wù):execute/executeOnExecutor西傀,execute方法異步任務(wù)運行在SerialExecutor線程池上斤寇,如下:
當(dāng)多個AsyncTask同時執(zhí)行execute方法時,如果前面一個任務(wù)沒有執(zhí)行完成拥褂,也就是run方法沒有執(zhí)行完成娘锁,而此時mActive又不為空,所以后續(xù)的任務(wù)都會在mTasks中進行等待饺鹃,SerialExecutor也就是單線程的順序執(zhí)行莫秆。
executeOnExecutor執(zhí)行的任務(wù)運行在自己定義的線程池上,我們可以使用AsyncTask提供的默認THREAD_POOL_EXECUTOR線程池悔详,該線程池如下:
由于該線程池沒有實現(xiàn)RejectedExecutionHandler拒絕任務(wù)處理策略镊屎,所以可能會拋出異常。
AsyncTask如何獲取處理結(jié)果:AsyncTask通過Java的FutureTask來獲取處理結(jié)果伟端,在異步處理完成后調(diào)用AsyncTask的post方法杯道。
如何取消AsyncTask?三步:
1、如果AsyncTask的狀態(tài)不是RUNNING责蝠,調(diào)用cancel(true)
2党巾、在doInBackground中判斷isCancelled,如果已經(jīng)取消霜医,直接return
3齿拂、在onProgressUpdate中判斷isCancelled,如果已經(jīng)取消肴敛,直接return
31署海、Handler機制
http://www.reibang.com/p/b0c8cfab59ef
Looper.prepare:創(chuàng)建一個Looper對象吗购,并將該對象設(shè)置給ThreadLocal,這樣每個Thread有自己的Looper對象砸狞。創(chuàng)建Looper對象會創(chuàng)建一個MessageQueue捻勉,所以每個Looper對應(yīng)一個MessageQueue。
? ? ? Looper.loop的時候會進入死循環(huán)刀森,獲取MessageQueue踱启,調(diào)用其next方法來獲取里面的消息進行處理。Next函數(shù)在阻塞或者沒有消息處理的時候會運行所有的IdelHandler研底。
ActivityThread的Loop.loop為啥沒有阻塞主線程:
Main函數(shù)中埠偿,在調(diào)用Loop.loop后有一個throw語句,該語句不會執(zhí)行榜晦,說明Looper.loop將函數(shù)阻塞了冠蒋。那為啥主線程還能運行?
在主線程使用Looper.loop可以保證主線程一直在運行乾胶,事實上抖剿,在Looper.loop死循環(huán)之前,已經(jīng)創(chuàng)建了一個Binder線程:
thread.attach會建立Binder通道胚吁,創(chuàng)建新線程牙躺。attach(false)會創(chuàng)建一個ApplicaitonThread的Binder線程,用于接受AMS發(fā)來的消息腕扶,該Binder線程通過ActivityThread的H類型Handler將消息發(fā)送給主線程孽拷。ActivityThread并不是線程類,只是它運行在主線程半抱。
主線程的死循環(huán)是否一致耗費CPU資源脓恕?
Handler底層采用Linux的pipe/epoll機制,MessageQueue沒有消息的時候窿侈,便阻塞在Looper.mQueue.next方法中炼幔,此時主線程會釋放CPU資源進入休眠,直到下個事件到達史简,當(dāng)有新消息的時候乃秀,通過往pipe管道寫數(shù)據(jù)來喚醒主線程工作。所以主線程大多數(shù)時候處于休眠狀態(tài)圆兵,不會阻塞跺讯。
32、如何展示一張超大的圖殉农,并實現(xiàn)手指縮放
1刀脏、 使用BitmapRegionDecoder進行局部加載,用戶手勢拖動在加載其余部分超凳。使用BitmapRegionDecoder.newInstance(inputStream,false)創(chuàng)建對象愈污,使用decodeRegion(rect,options)加載局部矩形耀态。
2、 參照圖庫的實現(xiàn)
33暂雹、View touch事件分發(fā)
http://www.reibang.com/p/62b638e1712a
34首装、OnTouchListener .onTouch()和View.onTouchEvent()的區(qū)別
這2個方法都是在View.dispatchTouchEvent()中調(diào)用,但onTouch()優(yōu)先于onTouchEvent執(zhí)行擎析;若手動復(fù)寫在onTouch()中返回true(將事件消費掉)簿盅,將不會再執(zhí)行onTouchEvent。
當(dāng)代碼運行到View.dispatchTouchEvent的時候揍魂,如下所示:
可以看到,如果設(shè)置了TouchListener棚瘟,且View的Enable屬性為true现斋,就會先運行OnTouchListener.onTouch,并根據(jù)其返回值來判斷是否需要進一步運行onTouchEvent偎蘸。如果沒有設(shè)置OnTouchListener或者OnTouchListener.onTouch函數(shù)返回false,就會運行onTouchEvent庄蹋,此時onTouchEvent的返回值就是View.dispatchTouchEvent的返回值。
35迷雪、View繪制過程
http://www.reibang.com/p/ddf78cc6296a
36限书、如何優(yōu)化自定義控件
onDraw中不應(yīng)該做內(nèi)存分配
減少onDraw次數(shù),例如減少invalidate調(diào)用章咧,調(diào)用含有四個參數(shù)的invalidate函數(shù)倦西,沒有參數(shù)的invalidate會強制刷新整個view
減少requestLayout的調(diào)用,view層級勁量扁平化
37赁严、LinnearLayout和RelativeLayout的區(qū)別
正常情況下LinnearLayout的性能更好點扰柠,比較兩者的性能,關(guān)鍵在于比較Measure函數(shù)的執(zhí)行疼约。RelativeLayout的onMeasure如下:
可以發(fā)現(xiàn)RelativeLayout會根據(jù)二次排列的結(jié)果對子View做一次measure卤档,因為ReleativeLayout的子View的排列方式存在彼此的依賴關(guān)系,而這個依賴關(guān)系可能和Xml布局中的View順序不同程剥,在確定每個View的位置時劝枣,需要先給所有的子View排序,又因為RelativeLayout允許ViewB在橫向上依賴ViewA织鲸,ViewA在縱向上依賴ViewB舔腾,所以橫向和縱向都需要測量一次。
LinnearLayout的onMeasure過程如下:
每次測量完child后昙沦,都會調(diào)用child.getMeasuredHeight獲取child的高度琢唾,并將高度加入mTotalLength中,但是該方法暫時避開了lp.weight>0且高度為0的子view盾饮,后面會將剩余的高度按照weight分配給對應(yīng)的子View采桃。因此懒熙,如果LinearLayout沒有使用weight屬性,將只進行一次measure普办;如果使用了weight屬性工扎,LinearLayout在第一次測量時會獲取所有子View的高度,之后再將剩余高度按照weight加入到子View衔蹲,可見weight會影響LinearLayout的測量性能肢娘。
總結(jié):1、RelativeLayout慢于LinearLayout是因為它會讓子View執(zhí)行兩次measure舆驶,而LinearLayout只需要執(zhí)行一次橱健;但是如果LinearLayout有了weight屬性,那么LinearLayout也需要兩次measure
2沙廉、在不影響層級深度的情況下拘荡,優(yōu)先使用LinearLayout。
38撬陵、Volley總結(jié)
http://www.reibang.com/p/fcae3633a7e9
39珊皿、Activity的啟動模式
http://www.reibang.com/p/18d1917acbb8
standard:默認的啟動模式,每次啟動actvity都會創(chuàng)建一個新的activity實例
singleTop:一般情況下和standard模式一樣巨税,但如果某個singleTop的activity已經(jīng)在某task的棧頂蟋定,此時再嘗試啟動該activity是不會創(chuàng)建新的activity對象的。
singleTask:當(dāng)啟動一個模式為singleTask的activity時草添,系統(tǒng)會檢查是否存在與這個activity的taskAffinify相同的task驶兜。有以下兩種情況:
1、如果存在這樣的task果元,就去檢查該activity是否已經(jīng)實例化促王,如果已經(jīng)實例化,就銷毀該activity以上的其余activity而晒,然后調(diào)用這個activity的onNewIntent方法蝇狼;如果該activity沒有實例化,那么就創(chuàng)建該activity實例倡怎,并壓入到該棧迅耘。
2、如果不存在這樣的task监署,那么就重新創(chuàng)建該task颤专,然后創(chuàng)建該activity的實例,然后入棧钠乏,最后將該task提到最頂位置栖秕。
singleInstance:是對singleTask的進一步加強,當(dāng)啟動該模式的activity時晓避,如果該activity沒有被實例化簇捍,那么就重新創(chuàng)建一個task只壳,然后實例化activity,并入棧暑塑;如果activity已經(jīng)被實例化吼句,那么就調(diào)用activity的onNewIntent。
該模式的activity所在task不允許存在其余activity事格,任何從該activity加載的其它activity(Activity2)都會被放到其它task中惕艳,如果存在于Activity2相同affinity相同的task,則在該task內(nèi)創(chuàng)建Activity2驹愚,否則重新生成task远搪。
40、Glide源碼解析
http://www.reibang.com/p/c555a4fe5fd1
41么鹤、OOM_ADJ
Out of memory adjustment(OOM_ADJ)是app“進程屬性”的其中一種;根據(jù)運行狀態(tài)终娃,system會動態(tài)調(diào)整app的ADJ值;根據(jù)內(nèi)存狀態(tài),會kill掉adj值較大的app;Linux本身存在oom機制蒸甜,android進行了擴展,每個app相關(guān)余佛。
1.cat /proc/$pid/oom_adj柠新,此文件內(nèi)容為所有進程都可以讀,但只有進程自己可以寫自己辉巡,也就是更改此值恨憎;oom_adj的作用,是當(dāng)手機內(nèi)存free不足會觸發(fā)oomkiller郊楣,作為執(zhí)行時的決策依據(jù)因素憔恳,值越大越先被考慮殺死;當(dāng)然決策時也考慮app的內(nèi)存占用大小净蚤,同等oom的優(yōu)先考慮size大的钥组;(參考ProcessList.java定義和lowmemorykiller.c判斷)
2.oom_adj為1:比如“設(shè)置”界面,點開360的懸浮小窗口,此時“設(shè)置”的oom_adj的值今瀑;以及當(dāng)前正在對外提供服務(wù)的provider或service程梦;
3.oom_adj為2:可以在狀態(tài)欄等位置感知到的情況;例如豌豆莢橘荠、360屿附、音樂播放器,在狀態(tài)欄有條目哥童;
4.oom_adj為6:通俗說法此時launcher存在在后臺挺份,比如從launcher點擊進入了設(shè)置或者長按HOME,此時launcher作為后臺值是6贮懈;launcher在前臺時為0匀泊;
5.oom_adj為7:打開“我的文件”优训,此時我的文件oom_adj為0(當(dāng)前正在運行的應(yīng)用);當(dāng)按下home鍵探赫,此時變?yōu)?型宙;當(dāng)按下返回或者切換了別的應(yīng)用后,“我的文件”可能變?yōu)?或者9+伦吠;
6.oom_adj為-12:此apk(包括system_server)永遠為-12妆兑,無論在前臺還是后臺;所以期望殺死后重新被調(diào)起來而采用的persistent毛仪,副作用會是持續(xù)占據(jù)內(nèi)存搁嗓;除了Android默認的電話等模塊,其它模塊若要用需要審核方可采用此方案箱靴;
7.A/B
service:通過startService()啟動的腺逛,默認是Aservice,其中舊的1/3會成為Bservice衡怀;B>A棍矛;默認啟動的service也是backgroundservice,可以set抛杨;
8.純粹的activity够委、接收過廣播、執(zhí)行過provider等的到后臺后默認oom_adj都比較大怖现,會優(yōu)先考慮被殺茁帽;
9.oom_adj小于0:這塊主要是一些如后臺二進制app、常駐后臺的socket如installd屈嗤、vold潘拨、netd、zygote等使用,為-16(android5.0+:-17)饶号;當(dāng)在shell下執(zhí)行二進制命令時铁追,oom值默認是0;像牛盾后臺root服務(wù)占據(jù)40M讨韭,但因為也是-16(android5.0+:-17)不會被殺脂信;因為有些二進制程序是不受Android管控的,但是會受Linux管控透硝;
42狰闪、動畫的分類、實現(xiàn)原理濒生,插值器埋泵、估值器
動畫分為View動畫、幀動畫、屬性動畫丽声。
屬性動畫通過時間插值器和屬性估值器類進行工作礁蔗,插值器根據(jù)時間流失的百分比計算屬性變化的百分比,估值器根據(jù)屬性變化的百分比計算屬性值雁社。圖中所示為隨著時間t的編號绊含,屬性x的對應(yīng)的值墓律,兩者使用了不同的插值器,上面是線性插值器,下面使用的是AccelerateDecelerateInterpolator盈蛮。
下面的t=10ms時缚去,為啥x=6羞酗,計算方式如下:
Step1:計算時間流失百分比摧找,elapsed fraction = 10/40 = 0.25
Step2:計算插值器:
getInteroilation就是根據(jù)時間流失百分比input還計算屬性變化的百分比。
Step3:計算屬性值喇完,這時候就用到估值器
這里是有整型估值器伦泥,計算方式為 開始屬性值+屬性變化百分比*(結(jié)束屬性值-開始屬性)
插值器:本質(zhì)是時間的函數(shù),定義了動畫的變化規(guī)律(提前/延遲執(zhí)行默認的時間點來達到加速/減速的效果)锦溪。將消失的時間因子轉(zhuǎn)化為插值因子不脯,只需要實現(xiàn)getInterpolation函數(shù),輸入的是當(dāng)前動畫執(zhí)行了多久了刻诊,根據(jù)公式將時間執(zhí)行的百分比進行修改跨新,來達到加速或者減速的目的,例如動畫已經(jīng)運行了一半時間0.5坏逢,如果getInterpolation計算后返回0.8,就達到了加速效果赘被,如果返回0.3就達到減速的效果是整。
插值器:用來決定屬性的計算方式,實現(xiàn)evalute的計算方式即可民假,android提供了IntEvalutor浮入、FloatEvalutor等
43、Bitmap占用的內(nèi)存
http://blog.csdn.net/u010652002/article/details/72676723
https://blog.csdn.net/smileiam/article/details/68946182
本地圖片:
內(nèi)存=width * height*(手機屏幕密度/資源圖片文件密度)^2 * 每一象素占用字節(jié)數(shù)羊异。
本地圖片占用內(nèi)存跟圖片本身大小事秀、手機屏幕密度、圖片所在的文件夾密度野舶,圖片編碼的色彩格式有關(guān)
網(wǎng)絡(luò)圖片:
內(nèi)存=width*height*ARGB_Config
網(wǎng)絡(luò)圖片易迹,在不同屏幕密度的手機上加載出來,占用內(nèi)存是一樣的平道。只取決于ARGB值睹欲。
44、Activity的生命周期
如果啟動一個透明Activity或者Dialog,前面Activity的生命周期只會運行到onPause窘疮,而不會運行onStop袋哼。當(dāng)ActivityA啟動Activity時,如果ActvityB不是透明的闸衫,那么先運行ActivityB的onCreate后才運行ActivityA的onStop涛贯。
onPause和onResume對應(yīng).
onStop對應(yīng)了onRestart->onStart->…
onSaveInstanceState調(diào)用的時機不確定,但肯定在onStop前面蔚出,在不在onPause后面不得而知弟翘,onSaveInstanceState是當(dāng)系統(tǒng)覺得Activity要被可能會被回收的時候進行調(diào)用,例如按下home鍵或者鎖屏身冬,正常的點擊返回鍵不會運行這個方法衅胀,與之對應(yīng)的方法是onRestoreInstanceState。
下拉狀態(tài)欄不會調(diào)用Activity的生命周期酥筝。
45滚躯、Fragment的生命周期
ViewPager如何實現(xiàn)Fragment的懶加載:setUserVisibleToUser
Fragment之間如何通信:
可以通過觀察者模式來實現(xiàn)Fragment之間的通信,實現(xiàn)方式如下:
46嘿歌、Service的生命周期
Service生命周期分為statService和bindService兩種:
startService生命周期為onCreate->onStartCommand->onDestroy掸掏;如果多次startService,onCreate之后運行一次宙帝,onStartCommand會運行多次丧凤,次數(shù)和startService相對應(yīng)。
BindService生命周期為onCreate->onBind->onUnbind->onDestroy步脓,不論調(diào)用bindDervice幾次愿待,onCreate之后運行,onStartCommand方法始終不會被調(diào)用靴患,有如下場景仍侥。
1、appA多次bind appB的Service鸳君,如果不執(zhí)行unBind农渊,appB的Service只會onBind一次。
2或颊、appA appB同時去bind appC的Service砸紊,如果不執(zhí)行unBind,appB的onBind只會執(zhí)行一次囱挑。
可以這樣理解醉顽,如果appA appB同時去bind AMS,假如AMS的onBind執(zhí)行了多次看铆,那么每個APP豈不是拿到了不同的ActivityManagerNative
47徽鼎、SharePreference如何實現(xiàn)進程同步
SP不是進程同步的,多進程并發(fā)讀取會有問題》裼伲可以使用MODE_MULTI_PROCESS屬性悄但,但是該屬性在androidM中已經(jīng)廢棄,建議使用ContentProvider來做多進程數(shù)據(jù)共享石抡。利用Provider的增刪改查特性檐嚣,來完成對SP的文件的增刪改查。
48啰扛、Push的實現(xiàn)
可以使用Socket長連接形式嚎京,通過心跳包維持長連接。
Push采用TCP長連接隐解,如果出現(xiàn)粘包和分包怎么辦鞍帝?
https://www.cnblogs.com/wade-luffy/p/6165671.html
粘包產(chǎn)生的原因:
(1)發(fā)送方引起的粘包是由TCP協(xié)議本身造成的,TCP為提高傳輸效率煞茫,發(fā)送方往往要收集到足夠多的數(shù)據(jù)后才發(fā)送一包數(shù)據(jù)帕涌。若連續(xù)幾次發(fā)送的數(shù)據(jù)都很少,通常TCP會根據(jù)優(yōu)化算法把這些數(shù)據(jù)合成一包后一次發(fā)送出去续徽,這樣接收方就收到了粘包數(shù)據(jù)蚓曼。
(2)接收方引起的粘包是由于接收方用戶進程不及時接收數(shù)據(jù),從而導(dǎo)致粘包現(xiàn)象钦扭。這是因為接收方先把收到的數(shù)據(jù)放在系統(tǒng)接收緩沖區(qū)纫版,用戶進程從該緩沖區(qū)取數(shù)據(jù),若下一包數(shù)據(jù)到達時前面數(shù)據(jù)尚未被用戶進程取走客情,則下一包數(shù)據(jù)放到系統(tǒng)接收緩沖區(qū)時就接到前一包數(shù)據(jù)之后其弊,而用戶進程根據(jù)預(yù)先設(shè)定的緩沖區(qū)大小從系統(tǒng)接收緩沖區(qū)取數(shù)據(jù),這樣就一次取到了多包數(shù)據(jù)膀斋。
粘包情況有兩種瑞凑,一種是粘在一起的包都是完整的數(shù)據(jù)包,另一種情況是粘在一起的包有不完整的包概页。
分包產(chǎn)生的原因:
可能是IP分片傳輸導(dǎo)致的,也可能是傳輸過程中丟失部分包導(dǎo)致出現(xiàn)的半包练慕,還有可能就是一個包可能被分成了兩次傳輸惰匙,在取數(shù)據(jù)的時候,先取到了一部分(還可能與接收的緩沖區(qū)大小有關(guān)系)铃将,總之就是一個數(shù)據(jù)包被分成了多次接收项鬼。
如何解決粘包和分包問題
一個是采用分隔符的方式,即我們在封裝要傳輸?shù)臄?shù)據(jù)包的時候劲阎,采用固定的符號作為結(jié)尾符(數(shù)據(jù)中不能含結(jié)尾符)绘盟,這樣我們接收到數(shù)據(jù)后,如果出現(xiàn)結(jié)尾標(biāo)識,即人為的將粘包分開龄毡,如果一個包中沒有出現(xiàn)結(jié)尾符吠卷,認為出現(xiàn)了分包,則等待下個包中出現(xiàn)后 組合成一個完整的數(shù)據(jù)包沦零,這種方式適合于文本傳輸?shù)臄?shù)據(jù)祭隔,如采用/r/n之類的分隔符;
另一種是采用在數(shù)據(jù)包中添加長度的方式路操,即在數(shù)據(jù)包中的固定位置封裝數(shù)據(jù)包的長度信息(或可計算數(shù)據(jù)包總長度的信息)疾渴,服務(wù)器接收到數(shù)據(jù)后,先是解析包長度屯仗,然后根據(jù)包長度截取數(shù)據(jù)包(此種方式常出現(xiàn)于自定義協(xié)議中)搞坝。
nio有內(nèi)置的解碼器來處理粘包分包。
49魁袜、Websocket和Socket的區(qū)別
Http協(xié)議有一個缺陷桩撮,就是通信只能由客戶端發(fā)起,是單向的慌核,服務(wù)器端如果有什么變化就沒法立即告知客戶端距境。要想及時了解服務(wù)器的變化只能通過輪詢的方式,效率非常低垮卓,很費資源垫桂。
WebSock就是為了解決這一溫柔引入的,最大的特點就是服務(wù)器可以主動向客戶端推送消息粟按,客戶端也可以主動向服務(wù)器推消息诬滩。如下圖所示:
WebSocket處于應(yīng)用層,都是基于TCP協(xié)議來傳輸數(shù)據(jù)的灭将;與HTTP協(xié)議有很好的兼容性疼鸟;Socket其實不是一個協(xié)議,工作在會話層庙曙,是為了大家方便直接使用底層TCP協(xié)議而做的一層封裝空镜,它是應(yīng)用層和TCP協(xié)議族通信的中間軟件抽象層。
50捌朴、Android簽名機制和UID
簽名的好處:
應(yīng)用升級:應(yīng)用升級時吴攒,具有同樣證書的應(yīng)用才能安裝成功,不同簽名的應(yīng)用包名必須不一樣砂蔽。
應(yīng)用程序模塊化:android系統(tǒng)允許同一個簽名的多個應(yīng)用程序運行在同一個進程里面洼怔。
代碼或數(shù)據(jù)共享:一個應(yīng)用程序可以為另一個相同證書簽名的應(yīng)用程序公開自己的功能,以同一個證書對多個應(yīng)用簽名左驾,利用基于簽名的權(quán)限檢查镣隶,應(yīng)用間就可以共享代碼和數(shù)據(jù)了极谊。Pid即進程ID,UID即用戶ID安岂,Android中每個程序都有一個UID轻猖,將Activity、Service嗜闻、Provider進行共享有以下方式:
1蜕依、完全暴露,android:exported=”true”琉雳,一旦設(shè)置了IntentFilter样眠,exported就被設(shè)置為true
2、權(quán)限提示暴露:定義Permission翠肘,使用者需要使用use-permission才能訪問檐束。
私有暴露:使用shareUserId,android每個app有自己的UID束倍,這樣權(quán)限就被設(shè)置為只對該用戶可見被丧,如果要想其他應(yīng)用程序可見,就可以使用shareUserId绪妹,也就是讓兩個APP使用同樣的UID甥桂,讓他們運行在同一個進程中,這樣他們就可以互相看到對方的內(nèi)容邮旷,當(dāng)然要保證兩個APP的簽名一致黄选,具有相同簽名和相同sharedUserId的app才能共享UID,否則會出現(xiàn)Package com.test.MyTest has no signatures that match those in shared user android.uid.system錯誤婶肩。例如android:shareUserId=”android.uid.system”需要配合LOCAL_CERTIFICATE:=platform使用办陷。
51、EventBus源碼解析
EventBus.getInstance().register(this)注冊律歼,通過EventBus.getInstance().unRregister(this)反注冊民镜。通過@Subscriber來注解方法,表示方法需要接受某個事件险毁。在register的時候制圈,反射獲取this類中所有@Subscriber的方法,并存儲起來畔况,存儲格式:
@Subscriber方法的參數(shù)類型EventType---(方法method,方法所屬對象)离唐;當(dāng)進行post事件時,根據(jù)事件類型EventType找到對應(yīng)的Method和Method所屬對象问窃,然后反射調(diào)用Method,并傳入事件完沪。
52域庇、Apk打包流程
Android項目經(jīng)過編譯打包嵌戈,形成如下文件:.dex文件、resource.arsc資源表听皿、uncompiled resources(xml熟呛、drawable等)、AndroidManifest.xml尉姨。具體的詳細打包流程如下:
aapt階段:使用aapt打包res資源文件庵朝,生成R文件、resource.arsc文件又厉、res文件九府;resource.arsc文件記錄了所有應(yīng)用程序的資源目錄信息,可以當(dāng)成一個資源索引表覆致,根據(jù)資源ID可以快速找到資源侄旬。
aidl階段:將aidi文件生成對應(yīng)的java文件。
java compiler階段:編譯java代碼煌妈,生成class文件儡羔。
dex階段:將class文件打包到dex文件中,生成classes.dex璧诵。
apk builder階段:將classes.dex汰蜘、resource.arsc、res帶包到APK中
Jar Signer階段:給apk進行簽名
zipaligin階段:對簽名后的apk進行對齊處理之宿。
53族操、APK的安裝過程
Apk安裝有如下步驟:
將apk拷貝到data/app目錄
解析apk信息
Dexopt操作
更新權(quán)限信息
完成安裝,發(fā)送ACTION_PACKAGED_ADDED廣播
54澈缺、如何降低后臺service被殺的概率
結(jié)合OOM_ADJ屬性坪创,adj越大越容易被殺死,可以通過以下方式降低被殺概率姐赡。
優(yōu)化內(nèi)存使用
將程序進行拆分莱预,減少每個進程的資源占用貢獻
Service.startForeground使后臺服務(wù)獲取前臺優(yōu)先級(在通知欄顯示)
Context.bindSevice使后臺服務(wù)優(yōu)先級不低于前臺activity,先startService项滑,然后再bind依沮。service不在前臺時通過bindService無法有效提升優(yōu)先級。bindService只保證其與unbindService直接的時間段內(nèi)服務(wù)為運行狀態(tài)枪狂,startService保證在stopService/stopSelf之前的服務(wù)為運行狀態(tài)危喉,系統(tǒng)會勁量保持服務(wù)的運行狀態(tài)(程序異常終止會嘗試重啟STICKY服務(wù),crash 2次后就不會再重啟州疾,但是Setting里面強行停止的服務(wù)不會重啟)辜限,所以一般先startService然后在bindService。
Service設(shè)置android:priority严蓖,只會影響service的啟動優(yōu)先級薄嫡,不會影響運行期的生存優(yōu)先級氧急。
被殺之后怎么辦?
onStartCommand返回START_STICKY
多應(yīng)用抱成團毫深,進程互拉吩坝,類似于個推,比應(yīng)用內(nèi)兩個service互相監(jiān)督更靠譜哑蔫。
Native fork進程來定時啟動我們的service钉寝,am startservice …,但5.0后沒有用闸迷,5.0引入了forceStopPackage會殺死子進程嵌纲;通過獨立apk里面實現(xiàn)兩個service互相監(jiān)督,兩個service互相bind稿黍,同時監(jiān)聽linkToDeath疹瘦,監(jiān)聽到對方掛掉后立馬把對方開啟來,不過好像也沒用巡球。
通過注冊各種系統(tǒng)廣播來尋求重生
使用AlarmManager定時發(fā)廣播言沐,4.2之后Setting強制殺死后沒用。應(yīng)用初始安裝或者forceStopPackage后是收不到廣播的酣栈,可以通過非廣播類的組件來進行重啟险胰,例如startService
結(jié)論:所有的手段只能保證勁量維持服務(wù),都不是可靠的矿筝,作為廠商可以通過自身的優(yōu)勢來實現(xiàn)防殺起便。設(shè)置為persistent,會在開機第一時間啟動窖维,通過預(yù)置的應(yīng)用來組團喚起我的服務(wù)……
55榆综、RXJava解析
http://www.reibang.com/p/d45c65f022de
56、Activity的啟動流程
http://www.reibang.com/p/a768810c3ff8
57铸史、Service的啟動流程
http://www.reibang.com/p/f0108309761b
58鼻疮、Receiver的注冊和發(fā)送流程
http://www.reibang.com/p/71b973997ddf
59、Provider的啟動流程
http://www.reibang.com/p/31fbd5ecdddc
60琳轿、VirtualApk代碼解析
http://www.reibang.com/nb/25534574
61判沟、Gradle編譯Apk的過程
http://www.reibang.com/p/ed4ef3b96a29
Gradle里有Project,表示一個待編譯打包的工程崭篡,可以是apk挪哄,可以是Jar,Project里面可以包含多個Project,每個Project由很多Task構(gòu)成琉闪,每個Task又有不同的Action和一系列要執(zhí)行的操作迹炼。Project中的Task執(zhí)行順序由dependsOn控制。
Gardle執(zhí)行時以Task為執(zhí)行單位颠毙,執(zhí)行過程分為三個階段:
初始化階段:判斷包含哪些工程斯入,創(chuàng)建對應(yīng)的Project實例拿霉,最先執(zhí)行的就是setting.gradle中的語句。
配置階段:創(chuàng)建不同的Task咱扣,并根據(jù)Task之間的dependsOn確定Task的執(zhí)行順序。此階段后Task之間的依賴關(guān)系也就確定下來涵防。
執(zhí)行階段:配置結(jié)束后闹伪,按照依賴關(guān)系,按順序執(zhí)行Task壮池。
62偏瓤、SystemServer啟動過程
SystemServer是zygote孵化的第一個進程,ZygoteInit.java中椰憋,通過startSystemServer函數(shù)啟動SystemServer進程:
啟動SystemServer進程后厅克,運行SystemServer.run():
run()函數(shù)啟動了android系統(tǒng)的核心Service并注冊到ServiceManager,最后調(diào)用了Looper.loop()使得該線程一直處于運行狀態(tài)橙依。startOtherServices啟動最后運行AMS.systemReady表示SystemServer啟動完成证舟。
AMS.systemReady()如下:
startHomeActivityLocked啟動Launcher。
63窗骑、MVP架構(gòu)描述
MVP架構(gòu)將Activity中復(fù)雜的業(yè)務(wù)移動到Presenter層女责,Activity和Presenter采用雙向關(guān)聯(lián),Activity實現(xiàn)IView接口并持有IPresenter的實現(xiàn)创译,Presenter實現(xiàn)IPresenter并持有IView的實現(xiàn)已經(jīng)IModle的實現(xiàn)抵知。以一次完整的數(shù)據(jù)請求為例,運行流程如下:
關(guān)于MVP demo實現(xiàn)可以參照如下項目:
https://github.com/JasmineBen/GankImitation_MVP
基于"干貨集中營"的開放API软族,采用MVP架構(gòu)刷喜、RxJava、dagger2立砸、glide掖疮、retrofit、GreenDao仰禽、butterknife氮墨、rxpermissions2等技術(shù)實現(xiàn)了一個簡單的Gank客戶端
65.ButterKnife原理
http://www.reibang.com/p/036a635da941
66.EventBus原理
http://www.reibang.com/p/8d6c377ff026