Android面試考點

1锯岖、Activity、Dialog甫何、PopupWindow出吹、Toast 與Window的關(guān)系

簡單的從創(chuàng)建方式的角度來說一說:

Activity。在Activity創(chuàng)建過程中所創(chuàng)建的PhoneWindow辙喂,是層級最小的Window捶牢,叫做應(yīng)用Window,層級范圍1-99巍耗。(層級范圍大的Window可以覆蓋層級小的Window)

Dialog秋麸。Dialog的顯示過程和Activity基本相同,也是創(chuàng)建了PhoneWindow炬太,初始化DecorView,并將Dialog的視圖添加到DecorView中灸蟆,最終通過addView顯示出來。

但是有一點不同的是亲族,Dialog的Window并不是應(yīng)用窗口炒考,而是子窗口可缚,層級范圍1000-1999,子Window的顯示必須依附于應(yīng)用窗口斋枢,也會覆蓋應(yīng)用級Window帘靡。這也就是為什么Dialog傳入的上下文必須為Activity的Context了。

PopupWindow瓤帚。PopupWindow的顯示就有所不同了描姚,它沒有創(chuàng)建PhoneWindow,而是直接創(chuàng)建了一個View(PopupDecorView)戈次,然后通過WindowManager的addView方法顯示出來了轩勘。

沒有創(chuàng)建PhoneWindow,是不是就跟Window沒關(guān)系了呢朝扼?

并不是赃阀,其實只要是調(diào)用了WindowManager的addView方法,那就是創(chuàng)建了Window擎颖,跟你有沒有創(chuàng)建PhoneWindow無關(guān)榛斯。View就是Window的表現(xiàn)形式,只不過PhoneWindow的存在讓W(xué)indow形象更立體了一些搂捧。

所以PopupWindow也是通過Window展示出來的驮俗,而它的Window層級屬于子Window,必須依附與應(yīng)用窗口允跑。

Toast王凑。Toast和PopupWindow比較像,沒有新建PhoneWindow聋丝,直接通過addView方法顯示View即可索烹。不同的是它屬于系統(tǒng)級Window,層級范圍2000-2999,所以無須依附于Activity弱睦。

四個比較下來百姓,可以發(fā)現(xiàn),只要想顯示View况木,就會涉及到WindowManager的addView方法垒拢,也就用到了Window這個概念,然后會根據(jù)不同的分層依次顯示覆蓋到界面上火惊。

不同的是求类,Activity和Dialog涉及到了布局比較復(fù)雜,還會有布局主題等元素屹耐,所以用到了PhoneWindow進行一個解耦尸疆,幫助他們管理View。而PopupWindow和Toast結(jié)構(gòu)比較簡單,所以直接新建一個類似DecorView的View仓技,通過addView顯示到界面鸵贬。

2、onSaveInstanceState()什么時候會被調(diào)用呢脖捻?

概括的講阔逼,onSaveInstanceState 這個方法會在activity 將要被kill之前被調(diào)用以保存每個實例的狀態(tài),以保證在將來的某個時刻回來時可以恢復(fù)到原來的狀態(tài)地沮,但和activity 的生命周期方法onStop 和 onPause 不一樣嗜浮,與兩者并沒有絕對的先后調(diào)用順序,或者說并非所有場景都會調(diào)用onSaveInstanceState 方法摩疑。

那么onSaveInstanceState 方法何時會被調(diào)用呢危融,或者這么問,什么時候activity 會被系統(tǒng)kill 掉呢雷袋?

有以下幾種比較常見的場景:

(1)用戶主動按下home 鍵吉殃,系統(tǒng)不能確認activity 是否會被銷毀,實際上此刻系統(tǒng)也無法預(yù)測將來的場景楷怒,比如說內(nèi)存占用蛋勺,應(yīng)用運行情況等,所以系統(tǒng)會調(diào)用onSaveInstanceState保存activity狀態(tài) 鸠删;

(2)activity位于前臺抱完,按下電源鍵,直接鎖屏刃泡;

(3)橫豎屏切換巧娱;

(4)activity B啟動后位于activity A之前,在某個時刻activity A因為系統(tǒng)回收資源的問題要被kill掉烘贴,A通過onSaveInstanceState保存狀態(tài)禁添。

換句話說,onSaveInstanceState()的調(diào)用遵循一個重要原則桨踪,即當系統(tǒng)存在“未經(jīng)你許可”時銷毀了我們的Activity,則onSaveInstanceState()會被系統(tǒng)調(diào)用上荡,這是系統(tǒng)的職責,因為它必須要提供一個機會讓用戶保存數(shù)據(jù)馒闷。

3、Android 數(shù)據(jù)持久化之 SharedPreferences

Android之SharedPreferences內(nèi)部原理淺析

剖析 SharedPreference apply 引起的 ANR 問題

總結(jié):

  1. sSharedPrefsCache 是一個 ArrayMap<String,ArrayMap<File,SharedPreferencesImpl>>叁征,它會保存加載到內(nèi)存中的 SharedPreferences 對象纳账,ContextImpl 類中并沒有定義將 SharedPreferences 對象移除 sSharedPrefsCache 的方法,所以一旦加載到內(nèi)存中捺疼,就會存在直至進程銷毀疏虫。相對的,也就是說,SP 對象一旦加載到內(nèi)存卧秘,后面任何時間使用呢袱,都是從內(nèi)存中獲取,不會再出現(xiàn)讀取磁盤的情況

  2. SharedPreferences 和 Editor 都只是接口翅敌,真正的實現(xiàn)在 SharedPreferencesImpl 和 EditorImpl 羞福,SharedPreferences 只能讀數(shù)據(jù),它是在內(nèi)存中進行的蚯涮,Editor 則負責存數(shù)據(jù)和修改數(shù)據(jù)治专,分為內(nèi)存操作和磁盤操作

  3. 獲取 SP 只能通過 ContextImpl#getSharedPerferences 來獲取,它里面首先通過 mSharedPrefsPaths 根據(jù)傳入的 name 拿到 File 遭顶,然后根據(jù) File 從 ArrayMap<File, SharedPreferencesImpl> cache 里取出對應(yīng)的 SharedPrederenceImpl 實例

  4. SharedPreferencesImpl 實例化的時候會啟動子線程來讀取磁盤文件张峰,但是在此之前如果通過 SharedPreferencesImpl#getXxx 或者 SharedPreferences.Editor 會阻塞 UI 線程,因為在從 SP 文件中讀取數(shù)據(jù)或者往 SP 文件中寫入數(shù)據(jù)的時候必須等待 SP 文件加載完

  5. 在 EditorImpl 中 putXxx 的時候棒旗,是通過 HashMap 來存儲數(shù)據(jù)喘批,提交的時候分為 commit 和 apply,它們都會把修改先提交到內(nèi)存中铣揉,然后在寫入磁盤中饶深。只不過 apply 是異步寫磁盤,而 commit 可能是同步寫磁盤也可能是異步寫磁盤老速,在于前面是否還有寫磁盤任務(wù)粥喜。對于 apply 和 commit 的同步,是通過 CountDownLatch 來實現(xiàn)的橘券,它是一個同步工具類额湘,它允許一個線程或多個線程一致等待,直到其他線程的操作執(zhí)行完之后才執(zhí)行

  6. SP 的讀寫操作是線程安全的旁舰,它對 mMap 的讀寫操作用的是同一把鎖锋华,考慮到 SP 對象的生命周期與進程一致,一旦加載到內(nèi)存中就不會再去讀取磁盤文件箭窜,所以只要保證內(nèi)存中的狀態(tài)是一致的毯焕,就可以保證讀寫的一致性

注意事項以及優(yōu)化建議
  1. 強烈建議不要在 SP 里面存儲特別大的 key/value ,有助于減少卡頓 / ANR

  2. 請不要高頻的使用 apply磺樱,盡可能的批量提交纳猫;commit 直接在主線程操作,更要注意了

  3. 不要使用 MODE_MULTI_PROCESS

  4. 高頻寫操作的 key 與高頻讀操作的 key 可以適當?shù)牟鸱治募褡剑詼p少同步鎖競爭

  5. 不要連續(xù)多次 edit芜辕,每次 edit 就是打開一次文件,應(yīng)該獲取一次 edit块差,然后多次執(zhí)行 putXxx侵续,減少內(nèi)存波動绳匀,所以在封裝方法的時候要注意了

  6. apply 在 QueueWork 維護的單線程池調(diào)用胳施,雖然是異步的但是可能會阻塞 Service.onStop 和 Activity.onPause 方法,可能會導(dǎo)致 ANR

ANR 容易發(fā)生的地方:

  1. sp.getXxx,首先會調(diào)用 awaitLoadedLocked 等待首次 sp 文件創(chuàng)建與讀取操作完成

  2. sp.apply 雖然是異步的但是可能會在 Service Activity 等生命周期期間 mcr.writtenToDiskLatch.await() 等待過久

  3. sp.commit 最終會調(diào)用 sp.writeToFile 方法薪铜,很耗時

  4. ContextImpl.getSharedPreferences含衔,主線程直接調(diào)用的話股耽,如果 sp 文件很大處理時間也就會變成

4颜曾、Activity的啟動過程

image.png

應(yīng)用啟動過程

  1. Launcher通過Binder進程間通信機制通知AMS,它要啟動一個Activity

  2. AMS通過Binder進程間通信機制通知Launcher進入Paused狀態(tài)

  3. Launcher通過Binder進程間通信機制通知AMS眶根,它已經(jīng)準備就緒進入Paused狀態(tài)蜀铲,于是AMS就創(chuàng)建一個新的線程,用來啟動一個ActivityThread實例属百,即將要啟動的Activity就是在這個ActivityThread實例中運行

  4. ActivityThread通過Binder進程間通信機制將一個ApplicationThread類型的Binder對象傳遞給AMS记劝,以便以后AMS能夠通過這個Binder對象和它進行通信

  5. AMS通過Binde進程間通信機制通知ActivityThread,現(xiàn)在一切準備就緒族扰,它可以真正執(zhí)行Activity的啟動操作了

5厌丑、Service生命周期

startService() --> onCreate() --> onStartCommand() --> Service running --> onDestory()

bindService() --> onCreate() --> onBind() --> Service running --> onUnbind() --> onDestory()

onCreate():

系統(tǒng)在Service第一次創(chuàng)建時執(zhí)行此方法,來執(zhí)行只運行一次的初始化工作渔呵,如果service已經(jīng)運行怒竿,這個方法不會調(diào)用。

onStartCommand():

每次客戶端調(diào)用startService()方法啟動該Service都會回調(diào)該方法(多次調(diào)用)扩氢,一旦這個方法執(zhí)行耕驰,service就啟動并且在后臺長期運行,通過調(diào)用stopSelf()或stopService()來停止服務(wù)录豺。

onBind():

當組件調(diào)用bindService()想要綁定到service時朦肘,系統(tǒng)調(diào)用此方法(一次調(diào)用),一旦綁定后双饥,下次在調(diào)用bindService()不會回調(diào)該方法媒抠。在你的實現(xiàn)中,你必須提供一個返回一個IBinder來使客戶端能夠使用它與service通訊咏花,你必須總是實現(xiàn)這個方法趴生,但是如果你不允許綁定,那么你應(yīng)返回null

onUnbind():

當前組件調(diào)用unbindService()昏翰,想要解除與service的綁定時系統(tǒng)調(diào)用此方法(一次調(diào)用苍匆,一旦解除綁定后,下次再調(diào)用unbindService()會拋異常)

onDestory():

系統(tǒng)在service不在被使用并且要銷毀的時候調(diào)用此方法(一次調(diào)用)棚菊。service應(yīng)在此方法中釋放資源锉桑,比如線程,已注冊的監(jiān)聽器窍株、接收器等等民轴。

三種情況下Service的生命周期
  1. startService / stopService

    生命周期:onCreate --> onStartCommand --> onDestory

    如果一個Service被某個Activity調(diào)用Context.startService 方法啟動,那么不管是否有Activity使用bindService綁定或unbindService解除綁定到該Service球订,該Service都在后臺運行后裸,直到被調(diào)用stopService,或自身的stopSelf方法冒滩。當然如果系統(tǒng)資源不足微驶,Android系統(tǒng)也可能結(jié)束服務(wù),還有一種方法可以關(guān)閉服務(wù)开睡,在設(shè)置中因苹,通過應(yīng)用 --> 找到自己應(yīng)用 --> 停止。

    注意:

    第一次startService會觸發(fā)onCreate和onStartCommand篇恒,以后在服務(wù)運行過程中扶檐,每次startService都只會觸發(fā)onStartCommand

    不論startService多少次,stopService一次就會停止服務(wù)

  2. bindService / unbindService

    生命周期:onCreate --> onBind --> onUnbind --> onDestory

    如果一個Service在某個Activity中被調(diào)用bindService方法啟動胁艰,不論bindService被調(diào)用幾次款筑,Service的onCreate方法只會執(zhí)行一次,同時onStartCommand方法始終不會調(diào)用腾么。

    當建立連接后奈梳,Service會一直運行,除非調(diào)用unbindService來解除綁定解虱、斷開連接或調(diào)用該Service的Context不存在了(如Activity被finish --- 即通過bindService啟動的Service的生命周期依附于啟動它的Context)攘须,系統(tǒng)會在這時候自動停止該Service。

    注意:

    第一次bindService會觸發(fā)onCreate和inBind殴泰,以后在服務(wù)運行過程中于宙,每次bindService都不會觸發(fā)任何回調(diào)

  3. 混合型

    當一個Service再被啟動(startService)的同時又被綁定(bindService),該Service將會一直在后臺運行艰匙,不管調(diào)用幾次限煞,onCreate方法始終只會調(diào)用一次,onStartCommand的調(diào)用次數(shù)與startService調(diào)用的次數(shù)一致(使用bindService方法不會調(diào)用onStartCommand)员凝。同時署驻,調(diào)用unBindService將不會停止Service,必須調(diào)用stopService或Service自身的stopSelf來停止服務(wù)健霹。

三種情況下的應(yīng)用場景

如果你只是想啟動一個后臺服務(wù)長期進行某項任務(wù)旺上,那么使用startService便可以了。

如果你想與正在運行的Service取的聯(lián)系糖埋,那么有兩種方法宣吱,一種是使用broadcast,另外是使用bindService瞳别。前者的缺點是如果交流較為頻繁征候,容易造成性能上的問題杭攻,并且BroadcastReceiver本身執(zhí)行代碼的時間是很短的(也許執(zhí)行到一半,后面的代碼便不會執(zhí)行)疤坝,而后者則沒有這些問題兆解,因此我們肯定選擇使用bindService(這個時候便同時使用了startService和bindService了,這在Activity中更新Service的某些運行狀態(tài)是相當有用的)

如果你的服務(wù)只是公開了一個遠程接口跑揉,供連接上的客戶端(Android的Service是C/S架構(gòu))遠程調(diào)用執(zhí)行方法锅睛。這個時候你可以不讓服務(wù)一開始就運行,而只用bindService历谍,這樣在第一次bindService的時候才會創(chuàng)建服務(wù)的實例運行它现拒,這會節(jié)約很多系統(tǒng)資源,特別是如果你的服務(wù)是Remote Service望侈,那么該效果會越明顯印蔬。

6、BroadcastReceiver

應(yīng)用場景:

  1. 不同組件之間的通信(包括應(yīng)用內(nèi) / 不同應(yīng)用之間)

  2. 與Android系統(tǒng)在特定情況下的通信甜无,如當電話呼入時扛点,網(wǎng)絡(luò)可用時

  3. 多線程通信

實現(xiàn)原理
  • 使用了觀察者模式:基于消息的發(fā)布/訂閱事件模型。

  • 模型中有三個角色:消息訂閱者(廣播接收者)岂丘、消息發(fā)布者(廣播發(fā)布者)和消息中心(AMS陵究,即Activity Manager Service)

    [圖片上傳中...(image-100ae0-1663300893576-0)]

  • 原理描述

    1. 廣播接收者通過Binder機制在AMS注冊

    2. 廣播發(fā)送者通過Binder機制向AMS發(fā)送廣播

    3. AMS根據(jù)廣播發(fā)送者要求,在已注冊列表中奥帘,尋找合適的廣播接收者铜邮,尋找依據(jù):IntentFilter / Permission

    4. AMS將廣播發(fā)送到合適的廣播接收者相應(yīng)的消息循環(huán)隊列

    5. 廣播接收者通過消息循環(huán)拿到此廣播,并回調(diào)onReceive()

    注意:廣播發(fā)送者和廣播接收者的執(zhí)行是異步的寨蹋,發(fā)出去的廣播不會關(guān)心有沒有接收者接收松蒜,也不確定接收者何時能接受到。

    廣播的類型主要分為5類:

    • 普通廣播(Normal Broadcast)

    • 系統(tǒng)廣播(System Broadcast)

    • 有序廣播(Ordered Broadcast)

    • 粘性廣播(Sticky Broadcast)Android 5.0 & API 21中已經(jīng)失效

    • App應(yīng)用內(nèi)廣播(Local Broadcast)

動態(tài)廣播最好在Activity的onResume()注冊已旧,onPause()注銷秸苗,否則會導(dǎo)致內(nèi)存泄漏,當然运褪,重復(fù)注冊和重復(fù)注銷也不允許惊楼。

7、ContentProvider

作用

進程間進行數(shù)據(jù)交互&共享秸讹,即跨進程通信

image.png
原理

ContentProvider的底層采用Android中的Binder機制

統(tǒng)一資源標識符(RUI)

作用:唯一標識ContentProvider & 其中的數(shù)據(jù)檀咙,外界進程通過URI找到對應(yīng)的ContentProvider & 其中的數(shù)據(jù),再進行數(shù)據(jù)操作

具體使用:

URI分為系統(tǒng)預(yù)置 & 自定義璃诀,分別對應(yīng)系統(tǒng)內(nèi)置的數(shù)據(jù)(如通訊錄弧可、日程表等等)和自定義數(shù)據(jù)庫。

8劣欢、Context

Android應(yīng)用模型是基于組件的應(yīng)用設(shè)計模式棕诵,組件的運行要有一個完整的Android工程環(huán)境裁良。在這個工程環(huán)境下,Activity校套、Service等系統(tǒng)組件才能夠正常工作,而這些組件并不能采用普通的Java對象創(chuàng)建方式搔确,new一下就能創(chuàng)建實例了,而是要有它們各自的上下文環(huán)境灭忠,也就是Context膳算,Context是維持Android程序中各組件能夠正常工作的一個核心功能類。

如何生動形象的理解Context弛作?

一個Android程序可以理解為一部電影涕蜂,Activity、Service映琳、BroadcastReceiver和ContentProvider這四大組件就好比戲了的四個主角机隙,它們是劇組(系統(tǒng))一開始定好的,主角并不是大街上隨便拉個人(new 一個對象)都能演的萨西。有了演員當然也得有攝像機拍攝啊有鹿,它們必須通過鏡頭(Context)才能將戲傳給觀眾,這也就正對應(yīng)說四大組件必須工作在Context環(huán)境下谎脯。那么Button葱跋、TextView等等控件就相當于群演,顯然沒那么重用源梭,隨便一個路人甲都能演(可以new一個對象)娱俺,但是它們也必須在面對鏡頭(工作在Context環(huán)境下),所以Button mButtom = new Button(context) 是可以的废麻。

[圖片上傳中...(image-df38f2-1663300893577-4)]

它有兩個具體實現(xiàn)類:ContextImpl和ContextWrapper荠卷。

其中ContextWrapper類,是一個包裝類而已烛愧,ContextWrapper構(gòu)造函數(shù)中必須包含一個真正的Context引用油宜,同時ContextWrapper中提供了attachBaseContext()用于給ContextWrapper對象指定真正的Context對象,調(diào)用ContextWrapper的方法都會被轉(zhuǎn)向其包含的真正的Context對象屑彻。ContextThemeWrapper類验庙,其內(nèi)部包含了與主題Theme相關(guān)的接口,這里所說的主題就是指在AndroidManifest,xml中通過android:theme為Application元素或者Activity元素指定的主題社牲。當然粪薛,只有Activity才需要主題,Service是不需要主題的搏恤,所以Service直接繼承與ContextWrapper违寿,Application同理湃交。而ContextImpl類則真正實現(xiàn)了Context中的所有函數(shù),應(yīng)用程序中所調(diào)用的各種Context類的方法藤巢,其實現(xiàn)均來源于該類搞莺。Context得兩個子類分工明確,其中ContextImpl是Context的具體實現(xiàn)類掂咒,ContextWrapper是Context的包裝類才沧。 Activity、Application绍刮、Service雖都繼承自ContextWrapper(Activity繼承自ContextWrapper的子類ContextThemeWrapper)温圆,但它們初始化的過程中都會創(chuàng)建ContextImpl對象,由ContextImpl實現(xiàn)Context中的方法孩革。

一個應(yīng)用程序有幾個Context岁歉?

在應(yīng)用程序中Context的具體實現(xiàn)子類就是:Activity、Service和Application膝蜈。那么Context數(shù)量=Activity數(shù)量+Service數(shù)量+1锅移。那么為什么四大組件中只有Activity和Service持有Context呢?BroadcastReceiver和ContextPrivider并不是Context的子類饱搏,它們所持有的Context都是其他地方傳過去的非剃,所以并不計入Context總數(shù)。

Context的作用域

雖然Context神通廣大窍帝,但并不是隨便拿到一個Context實例就可以為所欲為努潘,它的使用還是有一些規(guī)則限制的。由于Context的具體實例是由ContextImpl類去實現(xiàn)的坤学,因此在絕大多數(shù)場景下疯坤,Activity、Service和Application這三種類型的Context都是可以通用的深浮。不過有幾種場景比較特殊压怠,比如啟動Activity,還有彈出Dialog飞苇。出于安全原因的考慮菌瘫,Android是不允許Activity或Dialog憑空出現(xiàn)的,一個Activity的啟動必須要建立在另一個Activity的基礎(chǔ)之上布卡,也就是以此形成返回棧雨让。而Dialog則必須在一個Activity上面彈出(除非是System Alert類型的Dialog),因此在這種場景下忿等,我們只能使用Activity類型的Context栖忠,否則將會報錯。

[圖片上傳中...(image-d795d0-1663300893576-2)]

從上圖我們可以發(fā)現(xiàn)Activity所持有的Context的作用域最廣,無所不能庵寞,因此Activity繼承至ContextThemeWrapper狸相,而Application和Service繼承至ContextWrapper,很顯然ContextThemeWrapper在ContextWrapper的基礎(chǔ)上又做了一些操作使得Activity變得更強大捐川。著重講一下不推薦使用的兩種情況:

  1. 如果我們用ApplicationContext去啟動一個LaunchMode為standard的Activity的時候會報錯:

    android.util.AndroidRuntimeException: Calling startActivity from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

    這是因為非Activity類型的Context并沒有所謂的任務(wù)棧脓鹃,所以待啟動的Activity就找不到棧了。解決這個問題的方法就是為待啟動的Activity指定FLAG_ACTIVITY_NEW_TASK標記位古沥,這樣啟動的時候就為它創(chuàng)建一個新的任務(wù)棧瘸右,而此時Activity是以singleTask模式啟動的。所有這種用Application啟動Activity的方式都不推薦岩齿,Service同Application尊浓。

  2. 在Application和Service中去LayoutInflate也是合法的,但是會使用系統(tǒng)默認的主題樣式纯衍,如果你自定義了某些樣式可能不會被使用,這種方式也不推薦使用苗胀。

一句話總結(jié):凡是跟UI相關(guān)的襟诸,都應(yīng)該使用Activity作為Context來處理;其他的一些操作基协,Service歌亲、Activity、Application等實例都可以澜驮,當然了注意Context引用的持有陷揪,防止內(nèi)存泄露。

如何獲取Context杂穷?

有四種方法:

  1. View.getContext 返回當前View對象的Context對象悍缠,通常是當前正在展示的Activity對象。

  2. Activity.getApplicationContext 獲取當前Activity所在的進程的Context對象耐量,通常我們使用Context對象時飞蚓,要優(yōu)先考慮這個全局的進程Context。

  3. ContextWrapper.getBaseContext() 用來獲取一個ContextWrapper進行裝飾之前的Context廊蜒,可以使用這個方法趴拧,這個方法在實際開發(fā)中使用的不多,也不建議使用山叮。

  4. Activity.this 返回當前Activity實例著榴,如果是UI控件需要使用Activity作為Context對象,但是默認的Toast實際上使用ApplicationContext也可以屁倔。

getApplication()和getApplicationContext()的區(qū)別脑又?

其內(nèi)存地址是一樣的。Application本身就是一個Context,這里獲取getApplicationContext得到的結(jié)果就是Application本身的實例挂谍。getApplication方法的語義性很強叔壤,就是用來獲取Application實例的,但是這個方法只有在Activity和Service中才能調(diào)用的到口叙。那么也許在絕大多數(shù)情況下我們都是在Activity或者Service中使用Application炼绘,但是如果在一些其他的場景,比如BroadcastReceiver中也想獲取Application實例妄田,這時就可以借助getApplicationContext方法了俺亮。

9、Android APK編譯打包流程

[圖片上傳中...(image-e1bab6-1663300893576-1)]

  1. AAPT(Android Asset Packaging Tools)工具會打包應(yīng)用中的資源文件疟呐,如AndroidManifest.xml脚曾、layout布局中的xml等,并將xml文件編譯成二進制形式启具,當然assets文件夾中的文件不會被編譯本讥,圖片以及raw文件夾中的資源也會保持原有的形態(tài),需要注意的是raw文件夾中的資源也會生成資源ID鲁冯。AAPT編譯完成后會生成R.java文件拷沸。

  2. AIDL工會將所有的aidl接口轉(zhuǎn)換為java接口。

  3. 所有的Java源代碼薯演、R文件撞芍、接口都會編譯器編譯成.class文件。

  4. Dex工具會將上述產(chǎn)生的.class文件以及第三方庫和其他class文件轉(zhuǎn)化為dex(Dalvik虛擬機可執(zhí)行文件)文件跨扮,dex文件最終會被打包進APK文件序无。

  5. apkbuilder會把編譯后的資源和其他資源文件同dex文件一起打入APK中。

  6. 生成APK文件之后衡创,帝嗡,需要對其簽名才能安裝到設(shè)備上,平時測試都會使用debug keystore璃氢,當發(fā)布應(yīng)用時必須使用release版的keystore對應(yīng)用進行簽名丈探。

  7. 如果對APK正式簽名,還需要使用zipalign工具對APK進行對齊操作拔莱,這樣做的好處是當應(yīng)用運行時能提高速度碗降,但是會相應(yīng)的增加內(nèi)存開銷。

總結(jié):編譯 --> DEX --> 打包 --> 簽名和對齊

10塘秦、Window讼渊、Activity、DecorView以及ViewRoot之間的關(guān)系

Activity

Activity并不負者視圖控制尊剔,它只是控制生命周期和處理事件爪幻。真正控制視圖的是Window。一個Activity包含了一個Window,Window才是真正代表一個窗口挨稿。Activity就像一個控制器仇轻,統(tǒng)籌視圖的添加與顯示,以及通過其他回調(diào)方法奶甘,來與Window以及View進行交互篷店。

Window

Window是視圖的承載器,內(nèi)部持有一個DecorView臭家,而這個DecorView才是view的跟布局疲陕。Window是一個抽象類,實際在Activity中持有的是其子類PhoneWindow钉赁。PhoneWindow中有個內(nèi)部類DecorView蹄殃,通過創(chuàng)建DecorView來加載Activity中設(shè)置的布局。Window通過WindowManager將DecorView加載其中你踩,并將DecorView交給ViewRoot诅岩,進行視圖繪制以及其他交互。

DecorView

DecorView是FrameLayout的子類带膜,它可以被認為是Android視圖樹的根節(jié)點視圖按厘。

DecorView作為頂級View,一般情況下它內(nèi)部包含一個豎直方向的LinearLayout钱慢,在這個LinearLayout里面有上下三個部分,上面是個ViewStub卿堂,延遲加載的視圖(應(yīng)該是設(shè)置ActionBar束莫,根據(jù)Theme設(shè)置),中間的是標題欄(根據(jù)Theme設(shè)置草描,有的布局沒有)览绿,下面是內(nèi)容欄。在Activity中通過setContentView所設(shè)置的布局文件其實就是被加到內(nèi)容欄之中的穗慕,成為其唯一子View饿敲。

ViewRoot

ViewRoot可能比較陌生,但是其作用非常重大逛绵。所有View的繪制以及事件分發(fā)等交互都是通過它來執(zhí)行或傳遞的怀各。

ViewRoot對應(yīng)ViewRootImpl類,它是連接WindowManagerService和DecorView的紐帶术浪,View的三大流程(測量瓢对、布局、繪制)均通過ViewRoot來完成胰苏。

ViewRoot并不屬于View樹的一份子硕蛹。從源碼實現(xiàn)上來看,它既是非View的子類,也是非View的父類法焰,但是秧荆,它實現(xiàn)了ViewParent接口,這讓它可以作為View的名義上的父視圖埃仪。RootView繼承了Handler類乙濒,可以接收事件并分發(fā),Android的所有觸屏事件贵试,按鍵事件琉兜、界面刷新等事件都是通過ViewRoot來進行分發(fā)的。

[圖片上傳中...(image-6f8e3a-1663300893576-3)]

總結(jié)

Activity就像個控制器毙玻,不負責視圖部分豌蟋。Window像個承載器,裝著內(nèi)部視圖桑滩。DecorView就是個頂級視圖梧疲,是所有View的最外層布局。ViewRoot像個連接器运准,負者溝通幌氮,通過硬件感知來通知視圖,進行用戶之間的交互胁澳。

11该互、Assets目錄與res目錄的區(qū)別

assets目錄與res下的raw、drawable目錄一樣韭畸,也可用來存放資源文件宇智,但它們?nèi)邊^(qū)別如下:


image.png

res/raw和assets的區(qū)別:

res/raw中的文件會被映射到R.java文件中,訪問的時候直接使用資源ID即可胰丁,assets文件夾下的文件不會被映射到R文件中随橘,

訪問的時候需要AssetManager類。

res/raw不可以有目錄結(jié)構(gòu)锦庸,而assets則可以有目錄結(jié)構(gòu)机蔗,也就是assets目錄下可以再建立文件夾。

讀取res/raw下的文件資源甘萧,通過以下方式獲取輸入流來進行寫操作:

InputStream is = getResources().openRawResource(R.id.filename)

注意:

AssertManager中不能處理單個超過1M的文件萝嘁,而raw沒有這個限制

assets文件夾是存放不進行編譯加工的原生文件,即該文件夾里面的文件不會像xml扬卷、java文件被預(yù)編譯酿愧,可以存放一些圖片、html邀泉、js等等

112嬉挡、View視圖繪制過程原理

View視圖繪制需要搞清楚兩個問題钝鸽,一個是從哪里開始繪制,一個是怎么繪制庞钢?

從哪里開始繪制拔恰?我們平常使用Activity的時候,都會調(diào)用setContentView來設(shè)置布局文件基括,沒錯颜懊,視圖繪制就是從這個方法開始。

怎么繪制风皿?

在我們的Activity中調(diào)用了setContentView之后河爹,會轉(zhuǎn)而執(zhí)行PhoneWindow的setContentView,在這個方法里面會判斷我們存放內(nèi)容的ViewGroup(這個ViewGroup可以是DecorView也可以是DecorView的子View)是否存在桐款。不存在的話咸这,則會創(chuàng)建一個DecorView處理,并且會創(chuàng)建出相應(yīng)的窗體風格魔眨,存在的話則會刪除原先的ViewGroup上面已有的View媳维,接著會調(diào)用LayoutInflater的inflate方法以pull解析的方式將當前布局文件中存在的View通過addView的方式添加到ViewGroup上面來,接著在addView方法里面就會執(zhí)行我們常見的invalidate方法了遏暴,這個方法不只是在View視圖繪制的過程中經(jīng)常用到侄刽,其實動畫的實現(xiàn)原理也是不斷的調(diào)用這個方法來實現(xiàn)視圖不斷重繪的,執(zhí)行這個方法的時候會調(diào)用父View的invalidateChild方法朋凉,這個方法是屬于ViewParent的州丹,ViewGroup以及ViewRootImpl中都會他進行了實現(xiàn),invalidateChild里面主要做的是就是通過do while循環(huán)一層一層計算出當前View的四個點所對應(yīng)的矩陣在ViewRoot中所對應(yīng)的位置杂彭,那么有了這個矩陣的位置之后最終都會執(zhí)行ViewRootImpl的invalidateChildInParent方法墓毒,執(zhí)行這個方法的時候首先會檢查當前線程是不是主線程,因為我們要開始準備更新UI了盖灸,不是主線程的話是不允許更新UI的,接著就會執(zhí)行scheduleTraversals方法了磺芭,這個方法會通過handler來執(zhí)行doTraversal方法赁炎,在這個方法里面就見到了我們平常所熟悉的View視圖繪制的起點方法performTraversals了。

那么接下來就是真正的視圖繪制流程了钾腺,大體上講View的繪制流程經(jīng)歷了Measure測量徙垫、Layout布局以及Draw繪制的三個過程,具體來講是從ViewRootImpl的performTraversals方法開始放棒,首先執(zhí)行的將是performMeasure方法姻报,這個方法里面會傳入兩個MeasureSpec類型的參數(shù),它在很大程度上決定了View的尺寸規(guī)格间螟,對于DecorView來說寬高的MeasureSpec值的獲取與窗口尺寸以及自身的LayoutParams有關(guān)吴旋,對于普通View來說其寬高的MeasureSpec值獲取由父容器以及自身的LayoutParams屬性共同決定损肛,在performMeasure里面會執(zhí)行measure方法,在measure方法里面會執(zhí)行onMeasure方法荣瑟,到這里Measure測量過程對View與ViewGroup來說是沒有區(qū)別的治拿,但是從onMeasure開始兩者有差別了,因為View本身已經(jīng)不存在子View了笆焰,所以他onMeasure方法將執(zhí)行setMeasuredDimension方法劫谅,該方法會設(shè)置View的測量值,但是對于ViewGroup來說嚷掠,因為它里面還存在著子View捏检,那么我們就需要繼續(xù)測量它里面的子View了,調(diào)用的方法是measureChild方法不皆,該方法內(nèi)部又會執(zhí)行measure方法贯城,而measure方法轉(zhuǎn)而又會執(zhí)行onMeasure方法,這樣不斷的遞歸進行下去粟焊,直到整個View樹測量結(jié)束冤狡,這樣performMeasure方法執(zhí)行結(jié)束了。接著便是執(zhí)行performLayout方法了项棠,performMeasure只是測量出了View樹中View的大小了悲雳,但是還不知道View的位置,所以也就出現(xiàn)了performLayout方法了香追,performLayout方法首先會執(zhí)行l(wèi)ayout方法合瓢,以確定View自身的位置,如果當前View是ViewGroup的話透典,則會執(zhí)行onLayout方法晴楔。在onLayout方法里面又會遞歸的執(zhí)行l(wèi)ayout方法,直到當前遍歷到的View不再是ViewGroup為止峭咒,這樣整個layout布局過程就結(jié)束了税弃。在View樹中View的大小以及位置都確定之后,接下來就是真正的繪制View顯示在界面的過程了凑队,該過程首先從performDraw方法開始则果,performDraw首先會執(zhí)行draw方法,在draw方法中首先繪制背景漩氨,接著調(diào)用onDraw方法繪制自己西壮,如果當前View是ViewGroup的話,還要調(diào)用dispatchDraw方法繪制當前ViewGroup的子View叫惊,而dispatchDraw方法里面實際上是通過drawChild方法間接調(diào)用draw方法形成遞歸繪制整個View樹款青,直到當前View不再是ViewGroup為止,這樣整個View的繪制過程就結(jié)束了霍狰。

總結(jié):

  • ViewRootImpl會調(diào)用performTraversals()抡草,其內(nèi)部會調(diào)用performMeasure()饰及、performLayout、performDraw

  • performMeasure會調(diào)用最外層的ViewGroup的measure() --> onMeasure() 渠牲,ViewGroup的onMeasure()是抽象方法旋炒,但其提供了measureChildren(),這之中會遍歷子View然后循環(huán)調(diào)用measureChild()签杈,傳入MeasureSpec參數(shù)瘫镇,然后調(diào)用子View的measure()到View的onMeasure() -->setMeasureDimension(getDefaultSize(),getDefaultSize()),getDefaultSize()默然返回measureSpec的測量數(shù)值答姥,所以繼承View進行自定義的wrap_content需要重寫铣除。

  • performLayout()會調(diào)用最外層的ViewGroup的layout(l,t,r,b)无埃,本View在其中使用setFrame()設(shè)置本View的四個頂點位置柱蟀。在onLayout(抽象方法)中確定子View的位置,如LinearLayout會遍歷子View蝇庭,循環(huán)調(diào)用setChildFrame() --> 子View.layout()

  • performDraw()會調(diào)用最外層的ViewGroup的draw()方法敲长,其中會先后調(diào)用background.draw()繪制背景郎嫁,onDraw(繪制自己),dispatchDraw(繪制子View)祈噪、onDrawScrollBars(繪制裝飾)

  • MeasureSpec由兩位SpecMode(UNSPECIFIED泽铛、EXACTLY(對于精確值和match_parent)、AL_MOST(對應(yīng)warp_content))和三十位SpecSize組成一個int辑鲤,DecorView的MeasureSpec由窗口大小和其LayoutParams決定盔腔,其他View有父View的MeasureSpec和本View的LayoutParams決定。ViewGroup中有g(shù)etChildMeasureSpec()來獲取子View的MeasureSpec月褥。

113弛随、IntentService

IntentService是繼承并處理異步請求的一個類,其本質(zhì)上是一個Service宁赤,因為它是繼承至Service舀透,所以開啟IntentService和普通的Service一致。但是他和普通的Service不同之處在于它可以處理異步任務(wù)决左,在任務(wù)處理完之后會自動結(jié)束愕够。另外,我們可以啟動多次IntentService哆窿,而每一個耗時任務(wù)會以工作隊列的方式在IntentService的onHandleIntent回調(diào)方法中執(zhí)行链烈,并且是串行執(zhí)行厉斟。其實IntentService的內(nèi)部是通過HandleThread和Handle來實現(xiàn)異步操作的挚躯。

14、requestLayout擦秽、invalidate码荔、postInvalidate 的區(qū)別

  1. requestLayout 會回掉 onMeasure漩勤、onLayout、onDraw(ViewGroup.setWillNotDraw(fasle)情況下)方法

  2. invalidate 只會回掉 onDraw 方法

  3. postInvalidate 只會回掉 onDraw 方法(可以在非 UI 線程中調(diào)用)

15缩搅、深入理解Android插件化技術(shù)

16越败、美團外賣Android Crash治理之路

17、對 Activity.runOnUiThread 的理解

當前線程不是ui線程硼瓣,即發(fā)送post消息切換到ui線程(這個和sendMessage是有區(qū)別的究飞,sendMessage是在非ui線程發(fā)送消息,這個在當前線程發(fā)送消息堂鲤,然后因為activity初始化的時候就有l(wèi)ooper亿傅、和MessageQueue,就能直接處理消息瘟栖,從而將mUiThread切換到當前線程葵擎,再次執(zhí)行就直接進行action.run())

是ui線程,即直接實現(xiàn)方法

18半哟、什么是 RemoteViews酬滤?使用場景有哪些?

RemoteViews

RemoteViews翻譯過來就是遠程視圖.顧名思義,RemoteViews不是當前進程的View,是屬于SystemServer進程.應(yīng)用程序與RemoteViews之間依賴Binder實現(xiàn)了進程間通信.

用法

通常是在通知欄

//1.創(chuàng)建RemoteViews實例

    RemoteViews mRemoteViews=new RemoteViews("com.example.remoteviewdemo", R.layout.remoteview_layout);

    //2.構(gòu)建一個打開Activity的PendingIntent

    Intent intent=new Intent(MainActivity.this,MainActivity.class);

    PendingIntent mPendingIntent=PendingIntent.getActivity(MainActivity.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

    //3.創(chuàng)建一個Notification

    mNotification = new Notification.Builder(this)

    .setSmallIcon(R.drawable.ic_launcher)

    .setContentIntent(mPendingIntent)

    .setContent(mRemoteViews)

    .build();

    //4.獲取NotificationManager

    manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

    Button button1 = (Button) findViewById(R.id.button1);

    button1.setOnClickListener(new OnClickListener() {

        @Override

        public void onClick(View v) {

            //彈出通知

            manager.notify(1, mNotification);

        }

    });

19寓涨、談?wù)?AIDL

AIDL 是一種輔助工具,不用AIDL ,一樣可以實現(xiàn)跨進程通訊

AIDL 的原理是binder,真正有跨進程通訊能力的也是 Binder,所以 AIDL 只是一個能幫你少寫代碼,少出錯的輔助工具,由于設(shè)計的太好,使用太方便,所以非常常用

就像 retrofit 和okhttp 關(guān)系一樣, retrofit 提供 更加友好的api,真正的網(wǎng)絡(luò)請求還是由 okhttp發(fā)起的

20盯串、Android進程間的通信方式?

image.png

21缅茉、Binder機制:

1.為了保證進程空間不被其他進程破壞或干擾嘴脾,Linux中的進程是相互獨立或相互隔離的。

2.進程空間分為用戶空間和內(nèi)核空間蔬墩。用戶空間不可以進行數(shù)據(jù)交互译打;內(nèi)核空間可以進行數(shù)據(jù)交互,所有進程共用一個內(nèi)核空間拇颅。

3.Binder機制相對于Linux內(nèi)傳統(tǒng)的進程間通信方式:(1)性能更好奏司;Binder機制只需要拷貝數(shù)據(jù)一次,管道樟插、消息隊列韵洋、Socket等都需要拷貝數(shù)據(jù)兩次;而共享內(nèi)存雖然不需要拷貝黄锤,但實現(xiàn)復(fù)雜度高搪缨。(2)安全性更高;Binder機制通過UID/PID在內(nèi)核空間添加了身份標識鸵熟,安全性更高副编。

4.Binder跨進程通信機制:基于C/S架構(gòu),由Client流强、Server痹届、Server Manager和Binder驅(qū)動組成呻待。

5.Binder驅(qū)動實現(xiàn)的原理:通過內(nèi)存映射,即系統(tǒng)調(diào)用了mmap()函數(shù)队腐。

6.Server Manager的作用:管理Service的注冊和查詢蚕捉。

7.Binder驅(qū)動的作用:(1)傳遞進程間的數(shù)據(jù),通過系統(tǒng)調(diào)用mmap()函數(shù)柴淘;(2)實現(xiàn)線程的控制迫淹,通過Binder驅(qū)動的線程池,并由Binder驅(qū)動自身進行管理为严。

8.Server進程會創(chuàng)建很多線程處理Binder請求千绪,這些線程采用Binder驅(qū)動的線程池,由Binder驅(qū)動自身進行管理梗脾。一個進程的Binder線程池默認最大是16個荸型,超過的請求會阻塞等待空閑的線程。

9.Android中進行進程間通信主要通過Binder類(已經(jīng)實現(xiàn)了IBinder接口)炸茧,即具備了跨進程通信的能力瑞妇。

首先 所有的server都會在 serviceManager 中注冊

client 訪問 server時 要向 serviceManager 發(fā)起請求,

serviceManager 找到 這個 server 并通過 binder驅(qū)動 生成一個 server代理 返回給client

client得到了 代理之后 訪問 代理server

代理server 相應(yīng)方法被調(diào)用后 會向 binder驅(qū)動中去調(diào)用真正的server方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末梭冠,一起剝皮案震驚了整個濱河市辕狰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌控漠,老刑警劉巖蔓倍,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異盐捷,居然都是意外死亡偶翅,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門碉渡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來聚谁,“玉大人,你說我怎么就攤上這事滞诺⌒蔚迹” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵习霹,是天一觀的道長朵耕。 經(jīng)常有香客問我,道長淋叶,這世上最難降的妖魔是什么阎曹? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上芬膝,老公的妹妹穿的比我還像新娘。我一直安慰自己形娇,他們只是感情好锰霜,可當我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著桐早,像睡著了一般癣缅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上哄酝,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天友存,我揣著相機與錄音,去河邊找鬼陶衅。 笑死屡立,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的搀军。 我是一名探鬼主播膨俐,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼罩句!你這毒婦竟也來了焚刺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤门烂,失蹤者是張志新(化名)和其女友劉穎乳愉,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屯远,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡蔓姚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了慨丐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赂乐。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖咖气,靈堂內(nèi)的尸體忽然破棺而出挨措,到底是詐尸還是另有隱情,我是刑警寧澤崩溪,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布浅役,位于F島的核電站,受9級特大地震影響伶唯,放射性物質(zhì)發(fā)生泄漏觉既。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瞪讼。 院中可真熱鬧钧椰,春花似錦、人聲如沸符欠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽希柿。三九已至诊沪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間曾撤,已是汗流浹背端姚。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留挤悉,地道東北人渐裸。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像装悲,于是被迫代替她去往敵國和親橄仆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,573評論 2 353

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