-
Android篇
-
Activity
- 1、說下Activity生命周期 筛婉?
- 2判沟、Activity A 啟動(dòng)另一個(gè)Activity B 會(huì)調(diào)用哪些方法在孝?如果B是透明主題的又或則是個(gè)DialogActivity呢 唾糯?
- 3怠硼、說下onSaveInstanceState()方法的作用 ? 何時(shí)會(huì)被調(diào)用鬼贱?
- 4、說下 Activity的四種啟動(dòng)模式香璃、應(yīng)用場景 这难?
- 5、了解哪些Activity常用的標(biāo)記位Flags葡秒?
- 6姻乓、說下 Activity跟window,view之間的關(guān)系眯牧?
- 7糖权、橫豎屏切換的Activity生命周期變化?
- 8炸站、如何啟動(dòng)其他應(yīng)用的Activity?
- 9疚顷、Activity的啟動(dòng)過程旱易?(重點(diǎn))
- Fragment
- Service
- Broadcast Receiver
- ContentProvider
- 數(shù)據(jù)存儲(chǔ)
-
IPC
- 1、Android中進(jìn)程和線程的關(guān)系酣衷? 區(qū)別交惯?
- 2、如何開啟多進(jìn)程 穿仪? 應(yīng)用是否可以開啟N個(gè)進(jìn)程 席爽?
- 3、為何需要IPC牡借?多進(jìn)程通信可能會(huì)出現(xiàn)的問題拳昌?
- 4、Android沙箱機(jī)制
- 5钠龙、Android中IPC方式炬藤、各種方式優(yōu)缺點(diǎn),為什么選擇Binder碴里?
- 6沈矿、Binder機(jī)制的作用和原理?
- 7咬腋、Binder框架中ServiceManager的作用羹膳?
- 8、Bundle傳遞對(duì)象為什么需要序列化根竿?Serialzable和Parcelable的區(qū)別陵像?
- 9就珠、講講AIDL?原理是什么醒颖?如何優(yōu)化多模塊都使用AIDL的情況妻怎?
- View
-
Activity
Android篇
Activity
1啄巧、說下Activity生命周期 寻歧?
在正常情況下,Activity的常用生命周期就只有如下7個(gè)
onCreate():表示Activity正在被創(chuàng)建秩仆,常用來初始化工作码泛,比如調(diào)用setContentView加載界面布局資源,初始化Activity所需數(shù)據(jù)等澄耍;
onRestart():表示Activity正在重新啟動(dòng)噪珊,一般情況下,當(dāng)前Acitivty從不可見重新變?yōu)榭梢姇r(shí)齐莲,OnRestart就會(huì)被調(diào)用痢站;
onStart():表示Activity正在被啟動(dòng),此時(shí)Activity可見但不在前臺(tái)选酗,還處于后臺(tái)阵难,無法與用戶交互;
onResume():表示Activity獲得焦點(diǎn)芒填,此時(shí)Activity可見且在前臺(tái)并開始活動(dòng)呜叫,這是與onStart的區(qū)別所在空繁;
onPause():表示Activity正在停止,此時(shí)可做一些存儲(chǔ)數(shù)據(jù)朱庆、停止動(dòng)畫等工作盛泡,但是不能太耗時(shí),因?yàn)檫@會(huì)影響到新Activity的顯示椎工,onPause必須先執(zhí)行完饭于,新Activity的onResume才會(huì)執(zhí)行;
onStop():表示Activity即將停止维蒙,可以做一些稍微重量級(jí)的回收工作掰吕,比如注銷廣播接收器、關(guān)閉網(wǎng)絡(luò)連接等颅痊,同樣不能太耗時(shí)殖熟;
onDestroy():表示Activity即將被銷毀,這是Activity生命周期中的最后一個(gè)回調(diào)斑响,常做回收工作菱属、資源釋放;
- 延伸:從整個(gè)生命周期來看舰罚,
onCreate
和onDestroy
是配對(duì)的纽门,分別標(biāo)識(shí)著Activity的創(chuàng)建和銷毀,并且只可能有一次調(diào)用营罢;
從Activity是否可見
來說赏陵,onStart
和onStop
是配對(duì)的,這兩個(gè)方法可能被調(diào)用多次饲漾;
從Activity是否在前臺(tái)
來說蝙搔,onResume
和onPause
是配對(duì)的,這兩個(gè)方法可能被調(diào)用多次;
除了這種區(qū)別,在實(shí)際使用中沒有其他明顯區(qū)別拿愧; - 因?yàn)閐ialog與popupwindow實(shí)際上是一個(gè)存在于這個(gè)activity上的控件敷存,所以它并不會(huì)影響activity本身的生命周期
生命周期-詳細(xì)參考
2、Activity A 啟動(dòng)另一個(gè)Activity B 會(huì)調(diào)用哪些方法?如果B是透明主題的又或則是個(gè)DialogActivity呢 ?
Activity A 啟動(dòng)另一個(gè)Activity B,回調(diào)如下
Activity A 的onPause() → Activity B的onCreate() → onStart() → onResume() → Activity A的onStop()运翼;
如果B是透明主題又或則是個(gè)DialogActivity,則不會(huì)回調(diào)A的onStop兴枯,調(diào)用onPause后就結(jié)束了血淌,關(guān)閉后直接走onResume;
3、說下onSaveInstanceState()方法的作用 ? 何時(shí)會(huì)被調(diào)用悠夯?
發(fā)生條件:異常情況下(系統(tǒng)配置發(fā)生改變時(shí)導(dǎo)致Activity被殺死并重新創(chuàng)建癌淮、資源內(nèi)存不足導(dǎo)致低優(yōu)先級(jí)的Activity被殺死)
系統(tǒng)會(huì)調(diào)用onSaveInstanceState來保存當(dāng)前Activity的狀態(tài),此方法調(diào)用在onStop之前沦补,與onPause沒有既定的時(shí)序關(guān)系乳蓄;
當(dāng)Activity被重建后,系統(tǒng)會(huì)調(diào)用onRestoreInstanceState夕膀,并且把onSave(簡稱)方法所保存的Bundle對(duì)象同時(shí)傳參給onRestore(簡稱)和onCreate()虚倒,因此可以通過這兩個(gè)方法判斷Activity是否被重建,調(diào)用在onStart之后产舞;
- 當(dāng)targetSdkVersion小于3時(shí)onSaveInstanceState是在onPause方法中調(diào)用的魂奥,而大于3時(shí)是在onStop方法中調(diào)用的。而onRestoreInstanceState是在onStart之后易猫、onResume之前調(diào)用的耻煤。
4、說下 Activity的四種啟動(dòng)模式准颓、應(yīng)用場景 哈蝇?
- standard標(biāo)準(zhǔn)模式:每次啟動(dòng)一個(gè)Activity都會(huì)重新創(chuàng)建一個(gè)新的實(shí)例,不管這個(gè)實(shí)例是否已經(jīng)存在攘已,此模式的Activity默認(rèn)會(huì)進(jìn)入啟動(dòng)它的Activity所屬的任務(wù)棧中炮赦;
- singleTop棧頂復(fù)用模式:如果新Activity已經(jīng)位于任務(wù)棧的棧頂,那么此Activity不會(huì)被重新創(chuàng)建样勃,同時(shí)會(huì)回調(diào)onNewIntent方法眼五,如果新Activity實(shí)例已經(jīng)存在但不在棧頂,那么Activity依然會(huì)被重新創(chuàng)建彤灶;
- singleTask棧內(nèi)復(fù)用模式:只要Activity在一個(gè)任務(wù)棧中存在,那么多次啟動(dòng)此Activity都不會(huì)重新創(chuàng)建實(shí)例批旺,并回調(diào)onNewIntent方法幌陕,此模式啟動(dòng)Activity A,系統(tǒng)首先會(huì)尋找是否存在A想要的任務(wù)棧汽煮,如果不存在搏熄,就會(huì)重新創(chuàng)建一個(gè)任務(wù)棧,然后把創(chuàng)建好A的實(shí)例放到棧中暇赤;
- singleInstance單實(shí)例模式:這是一種加強(qiáng)的singleTask模式心例,具有此種模式的Activity只能單獨(dú)地位于一個(gè)任務(wù)棧中,且此任務(wù)棧中只有唯一一個(gè)實(shí)例鞋囊;
啟動(dòng)模式參考
5止后、了解哪些Activity常用的標(biāo)記位Flags?
FLAG_ACTIVITY_NEW_TASK
: 對(duì)應(yīng)singleTask啟動(dòng)模式,其效果和在XML中指定該啟動(dòng)模式相同译株;
FLAG_ACTIVITY_SINGLE_TOP
: 對(duì)應(yīng)singleTop啟動(dòng)模式瓜喇,其效果和在XML中指定該啟動(dòng)模式相同;
FLAG_ACTIVITY_CLEAR_TOP
: 具有此標(biāo)記位的Activity歉糜,當(dāng)它啟動(dòng)時(shí)乘寒,在同一個(gè)任務(wù)棧中所有位于它上面的Activity都要出棧。這個(gè)標(biāo)記位一般會(huì)和singleTask模式一起出現(xiàn)匪补,在這種情況下伞辛,被啟動(dòng)Activity的實(shí)例如果已經(jīng)存在,那么系統(tǒng)就會(huì)回調(diào)onNewIntent夯缺。如果被啟動(dòng)的Activity采用standard模式啟動(dòng)蚤氏,那么它以及連同它之上的Activity都要出棧,系統(tǒng)會(huì)創(chuàng)建新的Activity實(shí)例并放入棧中喳逛;
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
: 具有這個(gè)標(biāo)記的 Activity 不會(huì)出現(xiàn)在歷史 Activity 列表中瞧捌;
6、說下 Activity跟window润文,view之間的關(guān)系姐呐?
Activity在創(chuàng)建時(shí)會(huì)調(diào)用 attach() 方法初始化一個(gè)PhoneWindow(繼承于Window),每一個(gè)Activity都包含了唯一一個(gè)PhoneWindow
Activity通過setContentView實(shí)際上是調(diào)用的 getWindow().setContentView將View設(shè)置到PhoneWindow上典蝌,而PhoneWindow內(nèi)部是通過 WindowManager 的addView曙砂、removeView、updateViewLayout這三個(gè)方法來管理View骏掀,WindowManager本質(zhì)是接口鸠澈,最終由WindowManagerImpl實(shí)現(xiàn)-
WindowManager為每個(gè)Window創(chuàng)建Surface對(duì)象,然后應(yīng)用就可以通過這個(gè)Surface來繪制任何它想要繪制的東西截驮。而對(duì)于WindowManager來說笑陈,這只不過是一塊矩形區(qū)域而已
Surface其實(shí)就是一個(gè)持有像素點(diǎn)矩陣的對(duì)象,這個(gè)像素點(diǎn)矩陣是組成顯示在屏幕的圖像的一部分葵袭。 我們看到顯示的每個(gè)Window(包括對(duì)話框涵妥、全屏的Activity、狀態(tài)欄等)都有他自己繪制的Surface坡锡。 而最終的顯示可能存在Window之間遮擋的問題蓬网,此時(shí)就是通過`SurfaceFlinger`對(duì)象渲染最終的顯示,使他們以正確的Z-order顯示出來鹉勒。 一般Surface擁有一個(gè)或多個(gè)緩存(一般2個(gè))帆锋,通過雙緩存來刷新,這樣就可以一邊繪制一邊加新緩存禽额。
View是Window里面用于交互的UI元素锯厢。Window只attach一個(gè)View Tree(組合模式),當(dāng)Window需要重繪(如,當(dāng)View調(diào)用invalidate)時(shí)哲鸳,最終轉(zhuǎn)為Window的Surface臣疑,Surface被鎖住(locked)并返回Canvas對(duì)象徙菠,此時(shí)View拿到Canvas對(duì)象來繪制自己讯沈。當(dāng)所有View繪制完成后,Surface解鎖(unlock)婿奔,并且post到繪制緩存用于繪制缺狠,通過Surface Flinger來組織各個(gè)Window,顯示最終的整個(gè)屏幕
推薦文章:
Activity萍摊、View挤茄、Window的理解一篇文章就夠了
7、橫豎屏切換的Activity生命周期變化冰木?
不設(shè)置Activity的android:configChanges時(shí)穷劈,切屏?xí)N毀當(dāng)前Activity,然后重新加載調(diào)用各個(gè)生命周期踊沸,切橫屏?xí)r會(huì)執(zhí)行一次歇终,切豎屏?xí)r會(huì)執(zhí)行兩次;
onPause() →onStop()→onDestory()→onCreate()→onStart()→onResume()
設(shè)置Activity的android:configChanges="orientation"逼龟,經(jīng)過機(jī)型測試在Android5.1 即API 23級(jí)別下评凝,切屏還是會(huì)重新調(diào)用各個(gè)生命周期,切橫腺律、豎屏?xí)r只會(huì)執(zhí)行一次
在Android9 即API 28級(jí)別下奕短,切屏不會(huì)重新調(diào)用各個(gè)生命周期,只會(huì)執(zhí)行onConfigurationChanged方法
后經(jīng)官方查正匀钧,原話如下如果您的應(yīng)用面向Android 3.2即API 級(jí)別 13或更高級(jí)別(按照 minSdkVersion 和 targetSdkVersion 屬性所聲明的級(jí)別)翎碑,則還應(yīng)聲明 "screenSize" 配置,因?yàn)楫?dāng)設(shè)備在橫向與縱向之間切換時(shí)之斯,該配置也會(huì)發(fā)生變化日杈。即便是在 Android 3.2 或更高版本的設(shè)備上運(yùn)行,此配置變更也不會(huì)重新啟動(dòng) Activity
設(shè)置Activity的android:configChanges="orientation|keyboardHidden|screenSize"時(shí)吊圾,機(jī)型測試通過,切屏不會(huì)重新調(diào)用各個(gè)生命周期翰蠢,只會(huì)執(zhí)行onConfigurationChanged方法项乒;
Android 橫豎屏切換加載不同的布局
8、如何啟動(dòng)其他應(yīng)用的Activity梁沧?
共享uid的App
使用exported:例如:WXEntryActivity
-
在保證有權(quán)限訪問的情況下檀何,通過隱式Intent進(jìn)行目標(biāo)Activity的IntentFilter匹配,原則是:
一個(gè)intent只有同時(shí)匹配某個(gè)Activity的intent-filter中的action、category频鉴、data才算完全匹配栓辜,才能啟動(dòng)該Activity; 一個(gè)Activity可以有多個(gè) intent-filter垛孔,一個(gè) intent只要成功匹配任意一組 intent-filter藕甩,就可以啟動(dòng)該Activity;
android:exported 屬性詳解
android將自己的應(yīng)用和系統(tǒng)應(yīng)用共享UID
intent-filter的action周荐,category狭莱,data匹配規(guī)則
9、Activity的啟動(dòng)過程概作?(重點(diǎn))
- ?先還是得當(dāng)前系統(tǒng)中有沒有擁有這個(gè) Application 的進(jìn)程腋妙。如果沒有,則需要處理 APP 的啟動(dòng)過
程讯榕。在經(jīng)過創(chuàng)建進(jìn)程骤素、綁定 Application 步驟后,才真正開始啟動(dòng) Activity 的?法愚屁。 startActivity() ?
法最終還是調(diào)?的 startActivityForResult()济竹。
在 startActivityForResult() 中,真正去打開 Activity 的實(shí)現(xiàn)是在 Instrumentation 的
execStartActivivity() ?法中集绰。
在 execStartActivity() 中采? checkStartActivityResult() 檢查在 manifest 中是否已經(jīng)注冊(cè)规辱,如果沒
有注冊(cè)則拋出異常。否則把打開 Activity 的任務(wù)交給 ActivityThread 的內(nèi)部類 ApplicationThread栽燕,
該類實(shí)現(xiàn)了 IApplicationThread 接?罕袋。這個(gè)類完全搞定了 onCreate()、onStart() 等 Activity 的?命
周期回調(diào)?法碍岔。
在 ApplicationThread 類中浴讯,有?個(gè)?法叫 scheduleLaunchActivity(),它可以構(gòu)造?個(gè) Activity 記
錄蔼啦,然后發(fā)送?個(gè)消息給事先定義好的 Handler榆纽。
這個(gè) Handler 負(fù)責(zé)根據(jù) LAUNCH_ACTIVITY 的類型來做不同的 Activity 啟動(dòng)?式。其中有?個(gè)?要的
?法 handleLaunchActivity() 捏肢。
在 handleLaunchActivity() 中奈籽,會(huì)把啟動(dòng) Activity 交給 performLaunchActivity() ?法。
在 performLaunchActivity() ?法中鸵赫,?先從 Intent 中解析出?標(biāo) Activity 的啟動(dòng)參數(shù)衣屏,然后?
ClassLoader 將?標(biāo) Activity 的類通過類名加載出來并? newInstance() 來實(shí)例化?個(gè)對(duì)象。
創(chuàng)建完畢后辩棒, 開始調(diào)? Activity 的 onCreate() ?法狼忱,?此膨疏,Activity 被成功啟動(dòng)。
Android四大組件啟動(dòng)機(jī)制之Activity啟動(dòng)過程
Fragment
1钻弄、談一談Fragment的生命周期佃却?
-
Fragment從創(chuàng)建到銷毀整個(gè)生命周期中涉及到的方法依次為:onAttach()→onCreate()→ onCreateView()→onActivityCreated()→onStart()→onResume()→onPause()→onStop()→onDestroyView()→onDestroy()→onDetach(),其中和Activity有不少名稱相同作用相似的方法窘俺,而不同的方法有:
onAttach():當(dāng)Fragment和Activity建立關(guān)聯(lián)時(shí)調(diào)用饲帅; onCreateView():當(dāng)fragment創(chuàng)建視圖調(diào)用,在onCreate之后批销; onActivityCreated():當(dāng)與Fragment相關(guān)聯(lián)的Activity完成onCreate()之后調(diào)用洒闸; onDestroyView():在Fragment中的布局被移除時(shí)調(diào)用; onDetach():當(dāng)Fragment和Activity解除關(guān)聯(lián)時(shí)調(diào)用均芽;
2丘逸、談?wù)凙ctivity和Fragment的區(qū)別?
相似點(diǎn):
都可包含布局掀宋、可有自己的生命周期
不同點(diǎn):
- Fragment相比較于Activity多出4個(gè)回調(diào)周期深纲,在控制操作上更靈活;
Fragment可以在XML文件中直接進(jìn)行寫入劲妙,也可以在Activity中動(dòng)態(tài)添加湃鹊; - Fragment可以使用show()/hide()或者replace()隨時(shí)對(duì)Fragment進(jìn)行切換,并且切換的時(shí)候不會(huì)出現(xiàn)明顯的效果镣奋,用戶體驗(yàn)會(huì)好币呵;Activity雖然也可以進(jìn)行切換,但是Activity之間切換會(huì)有明顯的翻頁或者其他的效果侨颈,在小部分內(nèi)容的切換上給用戶的感覺不是很好余赢;
3、Fragment中add與replace的區(qū)別(Fragment重疊)
add不會(huì)重新初始化fragment哈垢,replace每次都會(huì)妻柒。所以如果在fragment生命周期內(nèi)獲取獲取數(shù)據(jù),使用replace會(huì)重復(fù)獲取耘分;
添加相同的fragment時(shí)举塔,replace不會(huì)有任何變化,add會(huì)報(bào)IllegalStateException異常求泰;
replace先remove掉相同id的所有fragment央渣,然后在add當(dāng)前的這個(gè)fragment,而add是覆蓋前一個(gè)fragment渴频。所以如果使用add一般會(huì)伴隨hide()和show()芽丹,避免布局重疊;
使用add枉氮,如果應(yīng)用放在后臺(tái)志衍,或以其他方式被系統(tǒng)銷毀,再打開時(shí)聊替,hide()中引用的fragment會(huì)銷毀楼肪,所以依然會(huì)出現(xiàn)布局重疊bug,可以使用replace或使用add時(shí)惹悄,添加一個(gè)tag參數(shù)春叫;
4、getFragmentManager泣港、getSupportFragmentManager 暂殖、getChildFragmentManager之間的區(qū)別?
-
getFragmentManager()
所得到的是所在fragment 的父容器
的管理器当纱,
getChildFragmentManager()
所得到的是在fragment里面子容器
的管理器呛每,如果是fragment嵌套fragment,那么就需要利用getChildFragmentManager()坡氯; - 因?yàn)镕ragment是3.0 Android系統(tǒng)API版本才出現(xiàn)的組件,所以3.0以上系統(tǒng)可以直接調(diào)用getFragmentManager()來獲取FragmentManager()對(duì)象,而3.0以下則需要調(diào)用getSupportFragmentManager() 來間接獲人赣俊帐姻;
5、FragmentPagerAdapter與FragmentStatePagerAdapter的區(qū)別與使用場景
- 相同點(diǎn) :二者都繼承PagerAdapter
- 不同點(diǎn) :
FragmentPagerAdapter
的每個(gè)Fragment會(huì)持久的保存在FragmentManager中悯恍,只要用戶可以返回到頁面中库糠,它都不會(huì)被銷毀。因此適用于那些數(shù)據(jù)相對(duì)靜態(tài)的頁涮毫,F(xiàn)ragment數(shù)量也比較少的那種瞬欧;
FragmentStatePagerAdapter
只保留當(dāng)前頁面,當(dāng)頁面不可見時(shí)窒百,該Fragment就會(huì)被消除黍判,釋放其資源。因此適用于那些數(shù)據(jù)動(dòng)態(tài)性較大篙梢、占用內(nèi)存較多顷帖,多Fragment的情況;
Service
1渤滞、談一談Service的生命周期贬墩?
Service是一個(gè)可以在后臺(tái)執(zhí)行長時(shí)間運(yùn)行操作而沒有用戶界面的應(yīng)用組件,沒有界面的組件妄呕,運(yùn)行于UI線程陶舞,不能做耗時(shí)操作,如果要做耗時(shí)操作绪励,要新開線程肿孵。服務(wù)一旦啟動(dòng)唠粥,就會(huì)在后臺(tái)一直運(yùn)行,即使啟動(dòng)它的Activity/BroadcastReceiver被銷毀了停做,也不會(huì)被影響晤愧,但可以主動(dòng)調(diào)用方法關(guān)閉服務(wù)。使用bindService方式開啟Service蛉腌,便可以與Activity綁定官份,并進(jìn)行Binder通信
生命周期:
①start方式啟動(dòng):onCreate()->onStartCommand()->運(yùn)行->onDestory(),其中每次startService,都會(huì)調(diào)用onStartCommand()
②bind方式啟動(dòng):onCreate()->onBind()->運(yùn)行->onUnBind()->onDestory()
③同時(shí)使用 startService 與 bindService烙丛,需要unbindService與stopService同時(shí)調(diào)用舅巷,才能終止 Service
onCreate()
:如果service沒被創(chuàng)建過,調(diào)用startService()后會(huì)執(zhí)行onCreate()回調(diào)河咽;如果service已處于運(yùn)行中钠右,調(diào)用startService()不會(huì)執(zhí)行onCreate()方法。也就是說忘蟹,onCreate()只會(huì)在第一次創(chuàng)建service時(shí)候調(diào)用爬舰,多次執(zhí)行startService()不會(huì)重復(fù)調(diào)用onCreate(),此方法適合完成一些初始化工作寒瓦;onStartComand()
:服務(wù)啟動(dòng)時(shí)調(diào)用情屹,此方法適合完成一些數(shù)據(jù)加載工作,比如會(huì)在此處創(chuàng)建一個(gè)線程用于下載數(shù)據(jù)或播放音樂杂腰;onBind()
:服務(wù)被綁定時(shí)調(diào)用垃你;onUnBind()
:服務(wù)被解綁時(shí)調(diào)用;onDestroy()
:服務(wù)停止時(shí)調(diào)用喂很;-
需要注意的是:
服務(wù)對(duì)象同時(shí)只會(huì)有一個(gè) 默認(rèn)情況下惜颇,一個(gè)started的Service與啟動(dòng)他的組件在同一個(gè)線程中。如果是服務(wù)在主線程中完成耗時(shí)操作的話少辣,容易造成主線程阻塞凌摄。
Android組件系列----Android Service組件深入解析
2、Service的兩種啟動(dòng)方式漓帅?區(qū)別在哪锨亏?
startService():
通過這種方式調(diào)用startService,onCreate()只會(huì)被調(diào)用一次忙干,多次調(diào)用startSercie會(huì)多次執(zhí)行onStartCommand()和onStart()方法器予。如果外部沒有調(diào)用stopService()或stopSelf()方法,service會(huì)一直運(yùn)行捐迫。
bindService():
如果該服務(wù)之前還沒創(chuàng)建乾翔,系統(tǒng)回調(diào)順序?yàn)閛nCreate()→onBind()。如果調(diào)用bindService()方法前服務(wù)已經(jīng)被綁定施戴,多次調(diào)用bindService()方法不會(huì)多次創(chuàng)建服務(wù)及綁定反浓。如果調(diào)用者希望與正在綁定的服務(wù)解除綁定萌丈,可以調(diào)用unbindService()方法,回調(diào)順序?yàn)閛nUnbind()→onDestroy()雷则;
浓瞪、如何保證Service不被殺死 ?
-
onStartCommand方式中巧婶,返回
START_STICKY
或則START_REDELIVER_INTENT
START_STICKY:如果返回START_STICKY,表示Service運(yùn)行的進(jìn)程被Android系統(tǒng)強(qiáng)制殺掉之后涂乌,Android系統(tǒng)會(huì)將該Service依然設(shè)置為started狀態(tài)(即運(yùn)行狀態(tài))艺栈,但是不再保存onStartCommand方法傳入的intent對(duì)象 START_NOT_STICKY:如果返回START_NOT_STICKY,表示當(dāng)Service運(yùn)行的進(jìn)程被Android系統(tǒng)強(qiáng)制殺掉之后湾盒,不會(huì)重新創(chuàng)建該Service START_REDELIVER_INTENT:如果返回START_REDELIVER_INTENT湿右,其返回情況與START_STICKY類似,但不同的是系統(tǒng)會(huì)保留最后一次傳入onStartCommand方法中的Intent再次保留下來并再次傳入到重新創(chuàng)建后的Service的onStartCommand方法中
提高Service的優(yōu)先級(jí)
在AndroidManifest.xml文件中對(duì)于intent-filter可以通過android:priority = "1000"這個(gè)屬性設(shè)置最高優(yōu)先級(jí)罚勾,1000是最高值毅人,如果數(shù)字越小則優(yōu)先級(jí)越低,同時(shí)適用于廣播尖殃;在onDestroy方法里重啟Service
當(dāng)service走到onDestroy()時(shí)丈莺,發(fā)送一個(gè)自定義廣播,當(dāng)收到廣播時(shí)送丰,重新啟動(dòng)service缔俄;提升Service進(jìn)程的優(yōu)先級(jí)
進(jìn)程優(yōu)先級(jí)由高到低:前臺(tái)進(jìn)程 一 可視進(jìn)程 一 服務(wù)進(jìn)程 一 后臺(tái)進(jìn)程 一 空進(jìn)程
可以使用startForeground將service放到前臺(tái)狀態(tài),這樣低內(nèi)存時(shí)器躏,被殺死的概率會(huì)低一些俐载;系統(tǒng)廣播監(jiān)聽Service狀態(tài)
將APK安裝到/system/app,變身為系統(tǒng)級(jí)應(yīng)用
注意:以上機(jī)制都不能百分百保證Service不被殺死登失,除非做到系統(tǒng)白名單遏佣,與系統(tǒng)同生共死
4、能否在Service開啟耗時(shí)操作 揽浙? 怎么做 状婶?
- Service默認(rèn)并不會(huì)運(yùn)行在子線程中,也不運(yùn)行在一個(gè)獨(dú)立的進(jìn)程中馅巷,它同樣執(zhí)行在主線程中(UI線程)太抓。換句話說,不要在Service里執(zhí)行耗時(shí)操作令杈,除非手動(dòng)打開一個(gè)子線程走敌,否則有可能出現(xiàn)主線程被阻塞(ANR)的情況;
5逗噩、用過哪些系統(tǒng)Service 掉丽?
[圖片上傳失敗...(image-2d89c6-1595590156419)]
6跌榔、了解ActivityManagerService嗎?發(fā)揮什么作用
- ActivityManagerService是Android中最核心的服務(wù) 捶障, 主要負(fù)責(zé)系統(tǒng)中四大組件的啟動(dòng)僧须、切換、調(diào)度及應(yīng)用進(jìn)程的管理和調(diào)度等工作项炼,其職責(zé)與操作系統(tǒng)中的進(jìn)程管理和調(diào)度模塊類似担平;
ActivityManagerService分析——AMS啟動(dòng)流程
Broadcast Receiver
BroadcastReceiver是android四大組件之一,是一個(gè)全局的監(jiān)聽器锭部,采用觀察者模式暂论,有三個(gè)角色參與,廣播發(fā)布者拌禾,廣播取胎,廣播接受者。廣播可以分為:普通廣播湃窍,系統(tǒng)廣播闻蛀,有序廣播,粘性廣播您市,本地廣播觉痛。通過廣播發(fā)布者發(fā)布廣播,廣播接受者通過intent-filter的action過濾出自己要接收的廣播茵休。
1秧饮、廣播有幾種形式 ? 都有什么特點(diǎn) ?
普通廣播:
開發(fā)者自身定義 intent的廣播(最常用)泽篮,所有的廣播接收器幾乎會(huì)在同一時(shí)刻接受到此廣播信息盗尸,接受的先后順序隨機(jī);
有序廣播:
發(fā)送出去的廣播被廣播接收者按照先后順序接收帽撑,同一時(shí)刻只會(huì)有一個(gè)廣播接收器能夠收到這條廣播消息泼各,當(dāng)這個(gè)廣播接收器中的邏輯執(zhí)行完畢后,廣播才會(huì)繼續(xù)傳遞亏拉,且優(yōu)先級(jí)(priority)高的廣播接收器會(huì)先收到廣播消息扣蜻。有序廣播可以被接收器截?cái)嗍沟煤竺娴慕邮掌鳠o法收到它;
本地廣播:
僅在自己的應(yīng)用內(nèi)發(fā)送接收廣播及塘,也就是只有自己的應(yīng)用能收到莽使,數(shù)據(jù)更加安全,效率更高笙僚,但只能采用動(dòng)態(tài)注冊(cè)的方式芳肌;
粘性廣播:這種廣播會(huì)一直滯留,當(dāng)有匹配該廣播的接收器被注冊(cè)后,該接收器就會(huì)收到此條廣播亿笤;
Android四大組件:BroadcastReceiver史上最全面解析
2翎迁、廣播的兩種注冊(cè)方式 ?
3净薛、廣播發(fā)送和接收的原理了解嗎 汪榔?(Binder機(jī)制、AMS)
- 首先通過Binder機(jī)制向AMS進(jìn)行注冊(cè)廣播
- 廣播發(fā)送者通過Binder機(jī)制向AMS發(fā)送廣播
- AMS查找符合相應(yīng)條件(IntentFilter/Permission)的BroadcastReceiver,將廣播發(fā)送到BroadcastReceiver(一般情況是Activity)相應(yīng)的消息循環(huán)隊(duì)列中
- 消息循環(huán)執(zhí)行拿到此廣播肃拜,回調(diào)BroadcastReceiver中的onReceive()方法
- 廣播和廣播接收者都在AMS中進(jìn)行登記
本地廣播:
1痴腌、使用它發(fā)送的廣播只在自身APP內(nèi)傳播,因此你不必?fù)?dān)心泄露隱私數(shù)據(jù)
其他APP無法對(duì)你的APP發(fā)送廣播燃领,因?yàn)槟愕腁PP根本就不可能接收到非自身應(yīng)用發(fā)送的該廣播士聪,因此你不必?fù)?dān)心有安全漏洞可以利用
2、比系統(tǒng)的全局廣播更加高效
LocalBroadcastManager高效的原因主要是因?yàn)閮?nèi)部是通過Handler實(shí)現(xiàn)的柿菩,它的sendBroadcast()方法含義并非和我們平時(shí)所用的一樣,它的sendBroadcast()方法其實(shí)是通過handler發(fā)送一個(gè)Message實(shí)現(xiàn)的雨涛。
既然它內(nèi)部是通過Handler來實(shí)現(xiàn)廣播的發(fā)送的枢舶,那么相比與系統(tǒng)廣播通過Binder實(shí)現(xiàn)肯定是更高效了,同時(shí)使用Handler來實(shí)現(xiàn)替久,別的應(yīng)用無法向我們的應(yīng)用發(fā)送廣播凉泄,而我們應(yīng)用內(nèi)發(fā)送的廣播也不會(huì)離開我們的應(yīng)用。
ContentProvider
1蚯根、ContentProvider了解多少后众?
ContentProvider作為四大組件之一,其主要負(fù)責(zé)存儲(chǔ)和共享數(shù)據(jù)颅拦。與文件存儲(chǔ)蒂誉、SharedPreferences存儲(chǔ)、SQLite數(shù)據(jù)庫存儲(chǔ)這幾種數(shù)據(jù)存儲(chǔ)方法不同的是距帅,后者保存下的數(shù)據(jù)只能被該應(yīng)用程序使用右锨,而前者可以讓不同應(yīng)用程序之間進(jìn)行數(shù)據(jù)共享,它還可以選擇只對(duì)哪一部分?jǐn)?shù)據(jù)進(jìn)行共享碌秸,從而保證程序中的隱私數(shù)據(jù)不會(huì)有泄漏風(fēng)險(xiǎn)绍移。
- ContentProvider 和 sql 在實(shí)現(xiàn)上有什么區(qū)別
①ContentProvider 屏蔽了數(shù)據(jù)存儲(chǔ)的細(xì)節(jié),內(nèi)部實(shí)現(xiàn)透明化讥电,用戶只需關(guān)心 uri 即可(是否匹配)
②ContentProvider 能實(shí)現(xiàn)不同 app 的數(shù)據(jù)共享蹂窖,sql 只能是自己程序才能訪問
③Contentprovider 還能增刪本地的文件,xml等信息
Android:關(guān)于ContentProvider的知識(shí)都在這里了!
2恩敌、說說ContentProvider瞬测、ContentResolver、ContentObserver 之間的關(guān)系?
ContentProvider:
管理數(shù)據(jù)涣楷,提供數(shù)據(jù)的增刪改查操作分唾,數(shù)據(jù)源可以是數(shù)據(jù)庫、文件狮斗、XML绽乔、網(wǎng)絡(luò)等,ContentProvider為這些數(shù)據(jù)的訪問提供了統(tǒng)一的接口碳褒,可以用來做進(jìn)程間數(shù)據(jù)共享折砸。
ContentResolver:
ContentResolver可以為不同URI操作不同的ContentProvider中的數(shù)據(jù),外部進(jìn)程
可以通過ContentResolver與ContentProvider進(jìn)行交互沙峻。
ContentObserver:
觀察ContentProvider中的數(shù)據(jù)變化睦授,并將變化通知給外界。
數(shù)據(jù)存儲(chǔ)
1摔寨、描述一下Android數(shù)據(jù)持久存儲(chǔ)方式去枷?
- SharedPreferences存儲(chǔ):一種輕型的數(shù)據(jù)存儲(chǔ)方式,本質(zhì)是基于XML文件存儲(chǔ)的key-value鍵值對(duì)數(shù)據(jù)是复,通常用來存儲(chǔ)一些簡單的配置信息(如應(yīng)用程序的各種配置信息)删顶;
- SQLite數(shù)據(jù)庫存儲(chǔ):一種輕量級(jí)嵌入式數(shù)據(jù)庫引擎,它的運(yùn)算速度非呈缋龋快逗余,占用資源很少,常用來存儲(chǔ)大量復(fù)雜的關(guān)系數(shù)據(jù)季惩;
- ContentProvider:四大組件之一录粱,用于數(shù)據(jù)的存儲(chǔ)和共享,不僅可以讓不同應(yīng)用程序之間進(jìn)行數(shù)據(jù)共享画拾,還可以選擇只對(duì)哪一部分?jǐn)?shù)據(jù)進(jìn)行共享啥繁,可保證程序中的隱私數(shù)據(jù)不會(huì)有泄漏風(fēng)險(xiǎn);
- File文件存儲(chǔ):寫入和讀取文件的方法和 Java中實(shí)現(xiàn)I/O的程序一樣青抛;
- 網(wǎng)絡(luò)存儲(chǔ):主要在遠(yuǎn)程的服務(wù)器中存儲(chǔ)相關(guān)數(shù)據(jù)输虱,用戶操作的相關(guān)數(shù)據(jù)可以同步到服務(wù)器上;
2脂凶、SharedPreferences的應(yīng)用場景?注意事項(xiàng)亭病?
SharedPreferences是一種輕型的數(shù)據(jù)存儲(chǔ)方式嘶居,本質(zhì)是基于XML文件存儲(chǔ)的key-value鍵值對(duì)數(shù)據(jù)促煮,通常用來存儲(chǔ)一些簡單的配置信息整袁,如int菠齿,String,boolean疾棵、float和long是尔;
-
注意事項(xiàng):
勿存儲(chǔ)大型復(fù)雜數(shù)據(jù),這會(huì)引起內(nèi)存GC恩溅、阻塞主線程使頁面卡頓產(chǎn)生ANR 勿在多進(jìn)程模式下鞍恢,操作Sp 不要多次edit和apply帮掉,盡量批量修改一次提交 建議apply稽莉,少用commit 不支持進(jìn)程同步
apply和commit的異同
1、apply沒有返回值而commit返回boolean表明修改是否提交成功良拼。
2、apply是將修改數(shù)據(jù)原子提交到內(nèi)存, 而后異步真正提交到硬件磁盤, 而commit是同步的提交到硬件磁盤贬媒,因此坡倔,在多個(gè)并發(fā)的提交commit的時(shí)候致讥,他們會(huì)等待正在處理的commit保存到磁盤后在操作,從而降低了效率请契。而apply只是原子的提交到內(nèi)容,后面有調(diào)用apply的函數(shù)的將會(huì)直接覆蓋前面的內(nèi)存數(shù)據(jù)氯夷,這樣從一定程度上提高了很多效率饶米。
3、apply方法不會(huì)提示任何失敗的提示。 由于在一個(gè)進(jìn)程中福也,sharedPreference是單實(shí)例,一般不會(huì)出現(xiàn)并發(fā)沖突搬设,如果對(duì)提交的結(jié)果不關(guān)心的話泣洞,建議使用apply,當(dāng)然需要確保提交成功且有后續(xù)操作的話呕诉,還是需要用commit的。
史上最全面伊者,清晰的SharedPreferences解析
3、SQLite
SQLite中的事務(wù)操作:
SQLite在做CRDU操作時(shí)都默認(rèn)開啟了事務(wù),然后把SQL語句翻譯成對(duì)應(yīng)的SQLiteStatement并調(diào)用其相應(yīng)的CRUD方法搂蜓,此時(shí)整個(gè)操作還是在rollback journal這個(gè)臨時(shí)文件上進(jìn)行粘秆,只有操作順利完成才會(huì)更新db數(shù)據(jù)庫殷勘,否則會(huì)被回滾玲销;使用SQLite做批量操作:
使用SQLiteDatabase的beginTransaction方法開啟一個(gè)事務(wù)策吠,將批量操作SQL語句轉(zhuǎn)化為SQLiteStatement并進(jìn)行批量操作,結(jié)束后endTransaction()刪除SQLite中表的個(gè)別字段L:
SQLite數(shù)據(jù)庫只允許增加字段而不允許修改和刪除表字段,只能創(chuàng)建新表保留原有字段跋理,刪除原表-
使用SQLite時(shí)會(huì)有哪些優(yōu)化操作:
使用事務(wù)做批量操作 及時(shí)關(guān)閉Cursor,避免內(nèi)存泄露 耗時(shí)操作異步化:數(shù)據(jù)庫的操作屬于本地IO耗時(shí)操作,建議放入異步線程中處理 ContentValues的容量調(diào)整:ContentValues內(nèi)部采用HashMap來存儲(chǔ)Key-Value數(shù)據(jù)记劈,ContentValues初始容量為8,擴(kuò)容時(shí)翻倍刽射。因此建議對(duì)ContentValues填入的內(nèi)容進(jìn)行估量肾档,設(shè)置合理的初始化容量怒见,減少不必要的內(nèi)部擴(kuò)容操作 使用索引加快檢索速度:對(duì)于查詢操作量級(jí)較大闺阱、業(yè)務(wù)對(duì)查詢要求較高的推薦使用索引
IPC
1瘦穆、Android中進(jìn)程和線程的關(guān)系? 區(qū)別?
線程是CPU調(diào)度的最小單元黔姜,同時(shí)線程是一種有限的系統(tǒng)資源
進(jìn)程一般指一個(gè)執(zhí)行單元,在PC和移動(dòng)設(shè)備上一個(gè)程序或則一個(gè)應(yīng)用
一般來說,一個(gè)App程序至少有一個(gè)進(jìn)程毙芜,一個(gè)進(jìn)程至少有一個(gè)線程(包含與被包含的關(guān)系),通俗來講就是,在App這個(gè)工廠里面有一個(gè)進(jìn)程展辞,線程就是里面的生產(chǎn)線杏愤,但主線程(主生產(chǎn)線)只有一條珊楼,而子線程(副生產(chǎn)線)可以有多個(gè)
進(jìn)程有自己獨(dú)立的地址空間,而進(jìn)程中的線程共享此地址空間曲聂,都可以并發(fā)執(zhí)行-
進(jìn)程與線程的區(qū)別
1. 線程是程序執(zhí)行的最小單位,而進(jìn)程是操作系統(tǒng)分配資源的最小單位; 2. 一個(gè)進(jìn)程由一個(gè)或多個(gè)線程組成穷绵,線程是一個(gè)進(jìn)程中代碼的不同執(zhí)行路線 3. 進(jìn)程之間相互獨(dú)立,但同一進(jìn)程下的各個(gè)線程之間共享程序的內(nèi)存空間(包括代碼段,數(shù)據(jù)集,堆等)及一些進(jìn)程級(jí)的資源(如打開文件和信號(hào)等)匈勋,某進(jìn)程內(nèi)的線程在其他進(jìn)程不可見; 4. 調(diào)度和切換:線程上下文切換比進(jìn)程上下文切換要快得多
各類組件元素的清單文件條目<activity>、<service>、<receiver> 和 <provider>—均支持 android:process 屬性烛卧,此屬性可以指定該組件應(yīng)在哪個(gè)進(jìn)程運(yùn)行呈宇。
-
進(jìn)程優(yōu)先級(jí)
第一高:前臺(tái)進(jìn)程 前臺(tái)進(jìn)程是Android系統(tǒng)中最重要的進(jìn)程炬搭,是與用戶正在交互的進(jìn)程迎变。 第二高:可見進(jìn)程 可見進(jìn)程指部分程序界面能夠被用戶看見,卻不在前臺(tái)與用戶交互。 第三高:服務(wù)進(jìn)程 一個(gè)包含已啟動(dòng)服務(wù)的進(jìn)程就是服務(wù)進(jìn)程句狼,服務(wù)沒有用戶界面,不與用 戶直接交互,但能夠在后臺(tái)長期運(yùn)行,提供用戶所關(guān)心的重要功能洋侨。 第四高:后臺(tái)進(jìn)程 如果一個(gè)進(jìn)程不包含任何已經(jīng)啟動(dòng)的服務(wù)陵且,而且沒有用戶可見的 Activity锅知,則這個(gè)進(jìn)程就是后臺(tái)進(jìn)程。 第五高:空進(jìn)程 空進(jìn)程是不包含任何活躍組件的進(jìn)程。在系統(tǒng)資源緊張時(shí)會(huì)被首先清楚。
2固歪、如何開啟多進(jìn)程 叶沛? 應(yīng)用是否可以開啟N個(gè)進(jìn)程 判帮?
在AndroidMenifest中給四大組件指定屬性android:process開啟多進(jìn)程模式
在內(nèi)存允許的條件下可以開啟N個(gè)進(jìn)程
-
開啟多進(jìn)程的好處:
1肴茄、分擔(dān)主進(jìn)程的內(nèi)存壓力 2踩麦、可以利用守護(hù)進(jìn)程進(jìn)行拉活贪婉,使應(yīng)用盡可能得常駐后臺(tái)才顿,但是也不絕對(duì)
-
多進(jìn)程會(huì)造成以下幾個(gè)方面的問題
1、靜態(tài)成員和單例模式完全失效 2、線程同步機(jī)制完全失效呵萨,多進(jìn)程同時(shí)訪問文件可能會(huì)造成數(shù)據(jù)錯(cuò) 亂勇婴,因?yàn)槎噙M(jìn)程不像多線程一樣德谅,可以加鎖進(jìn)行保護(hù)數(shù)據(jù) 3慰技、SharedPreferences 的可靠性下降 4掏颊、Application 會(huì)多次創(chuàng)建,數(shù)據(jù)也會(huì)被初始化多次 5艾帐、進(jìn)程間通信也比較麻煩 6乌叶、多進(jìn)程比較占系統(tǒng)內(nèi)存
如何開啟多進(jìn)程?應(yīng)用是否可以開啟N個(gè)進(jìn)程?
3柒爸、為何需要IPC?多進(jìn)程通信可能會(huì)出現(xiàn)的問題捎稚?
所有運(yùn)行在不同進(jìn)程的四大組件(Activity乐横、Service求橄、Receiver、ContentProvider)共享數(shù)據(jù)都會(huì)失敗葡公,這是由于Android為每個(gè)應(yīng)用分配了
獨(dú)立的虛擬機(jī)
罐农,不同的虛擬機(jī)在內(nèi)存分配上有不同的地址空間
,這會(huì)導(dǎo)致在不同的虛擬機(jī)中訪問同一個(gè)類的對(duì)象會(huì)產(chǎn)生多份副本催什。比如常用例子(通過開啟多進(jìn)程獲取更大內(nèi)存空間涵亏、兩個(gè)或則多個(gè)應(yīng)用之間共享數(shù)據(jù)、微信全家桶)-
一般來說蛆楞,使用多進(jìn)程通信會(huì)造成如下幾方面的問題
1溯乒、靜態(tài)成員和單例模式完全失效:獨(dú)立的虛擬機(jī)造成 2夹厌、線程同步機(jī)制完全實(shí)效:獨(dú)立的虛擬機(jī)造成 3豹爹、SharedPreferences的可靠性下降:這是因?yàn)镾p不支持兩個(gè)進(jìn)程并 發(fā)進(jìn)行讀寫,有一定幾率導(dǎo)致數(shù)據(jù)丟失 4矛纹、Application會(huì)多次創(chuàng)建:Android系統(tǒng)在創(chuàng)建新的進(jìn)程會(huì)分配獨(dú)立的 虛擬機(jī)臂聋,所以這個(gè)過程其實(shí)就是啟動(dòng)一個(gè)應(yīng)用的過程,自然也會(huì)創(chuàng)建新 的Application
4或南、Android沙箱機(jī)制
android的沙箱機(jī)制是一種安全機(jī)制孩等,主要是為了實(shí)現(xiàn)應(yīng)用程序之間的相互隔離。在android里采够,每個(gè)應(yīng)用都運(yùn)行在一個(gè)獨(dú)立的Dalvik虛擬機(jī)里肄方,擁有獨(dú)立的地址空間和資源,被分配一個(gè)唯一并固定的UID蹬癌,不同應(yīng)用不能訪問其他應(yīng)用資源权她。保證了數(shù)據(jù)安全。
補(bǔ)充:如果兩個(gè)應(yīng)用想共享UID實(shí)現(xiàn)數(shù)據(jù)共享逝薪,需要在清單文件添加相同的UID隅要,并且擁有相同的簽名。
補(bǔ)充:Linux下UID是對(duì)用戶分配id董济,android是單用戶系統(tǒng)步清,UID是對(duì)應(yīng)用程序分配id
- Android為每個(gè)安裝好的應(yīng)用程序分配了自己的UID,故進(jìn)程的UID是鑒別進(jìn)程身份的重要標(biāo)志虏肾。使用傳統(tǒng)IPC只能由用戶在數(shù)據(jù)包里填入U(xiǎn)ID/PID廓啊,但這樣不可靠,容易被惡意程序利用封豪∏绰郑可靠的身份標(biāo)記只有由IPC機(jī)制本身在內(nèi)核中添加。其次傳統(tǒng)IPC訪問接入點(diǎn)是開放的撑毛,無法建立私有通道书聚。比如命名管道的名稱唧领、system V的鍵值、socket的ip地址或文件名都是開放的雌续,只要知道這些接入點(diǎn)的程序都可以和對(duì)端建立連接斩个,不管怎樣都無法阻止惡意程序通過猜測接收方地址獲得連接。
5驯杜、Android中IPC方式受啥、各種方式優(yōu)缺點(diǎn),為什么選擇Binder鸽心?
- Linux 出于安全考慮滚局,不同進(jìn)程間不能之間操作對(duì)方的數(shù)據(jù),這叫做“進(jìn)程隔離”也就是:在 Linux 系統(tǒng)中顽频,虛擬內(nèi)存機(jī)制為每個(gè)進(jìn)程分配了線性連續(xù)的內(nèi)存空間藤肢,操作系統(tǒng)將這種虛擬內(nèi)存空間映射到物理內(nèi)存空間,每個(gè)進(jìn)程有自己的虛擬內(nèi)存空間糯景,進(jìn)而不能操作其他進(jìn)程的內(nèi)存空間嘁圈,只有操作系統(tǒng)才有權(quán)限操作物理內(nèi)存空間。 進(jìn)程隔離保證了每個(gè)進(jìn)程的內(nèi)存安全蟀淮。
[圖片上傳失敗...(image-5628d6-1595590156419)]
-
與Linux上傳統(tǒng)的IPC機(jī)制最住,比如System V,Socket相比怠惶,Binder好在哪呢涨缚?
1、傳輸效率高策治、可操作性強(qiáng):傳輸效率主要影響因素是內(nèi)存拷貝的次數(shù)脓魏,拷貝次數(shù)越少,傳輸速率越高览妖。從Android進(jìn)程架構(gòu)角度分析:對(duì)于消息隊(duì)列轧拄、Socket和管道來說,數(shù)據(jù)先從發(fā)送方的緩存區(qū)拷貝到內(nèi)核開辟的緩存區(qū)中讽膏,再從內(nèi)核緩存區(qū)拷貝到接收方的緩存區(qū)檩电,一共兩次拷貝,如圖:
[圖片上傳失敗...(image-7e428-1595590156419)]
而對(duì)于Binder來說府树,數(shù)據(jù)從發(fā)送方的緩存區(qū)拷貝到內(nèi)核的緩存區(qū)俐末,而接收方的緩存區(qū)與內(nèi)核的緩存區(qū)是映射到同一塊物理地址的,節(jié)省了一次數(shù)據(jù)拷貝的過程奄侠,如圖:
[圖片上傳失敗...(image-3f636c-1595590156419)]
由于共享內(nèi)存操作復(fù)雜卓箫,綜合來看,Binder的傳輸效率是最好的垄潮。
提高數(shù)據(jù)的讀烹卒、寫 & 傳輸?shù)臅r(shí)間性能
減少了數(shù)據(jù)拷貝次數(shù)
用戶空間 & 內(nèi)核空間的高效交互(通過映射的區(qū)域 直接交互)
用內(nèi)存讀寫 代替 I/O讀寫
提高內(nèi)存利用率:通過虛擬內(nèi)存 & 共享對(duì)象
2闷盔、Binder是基于C/S架構(gòu)的,簡單解釋下C/S架構(gòu)旅急,是指客戶端(Client)和服務(wù)端(Server)組成的架構(gòu)逢勾,Client端有什么需求,直接發(fā)送給Server端去完成藐吮,架構(gòu)清晰明朗溺拱,Server端與Client端相對(duì)獨(dú)立,穩(wěn)定性較好谣辞;而共享內(nèi)存實(shí)現(xiàn)方式復(fù)雜迫摔,沒有客戶與服務(wù)端之別, 需要充分考慮到訪問臨界資源的并發(fā)同步問題泥从,否則可能會(huì)出現(xiàn)死鎖等問題句占;從這穩(wěn)定性角度看,Binder架構(gòu)優(yōu)越于共享內(nèi)存
實(shí)現(xiàn)C/S架構(gòu)方便:Linux的眾IPC方式除了Socket以外都不是基于C/S架構(gòu)歉闰,而Socket主要用于網(wǎng)絡(luò)間的通信且傳輸效率較低辖众。Binder基于C/S架構(gòu) 卓起,Server端與Client端相對(duì)獨(dú)立和敬,穩(wěn)定性較好。
3戏阅、安全性高:傳統(tǒng)Linux IPC的接收方無法獲得對(duì)方進(jìn)程可靠的UID/PID昼弟,從而無法鑒別對(duì)方身份;而Binder機(jī)制為每個(gè)進(jìn)程分配了UID/PID且在Binder通信時(shí)會(huì)根據(jù)UID/PID進(jìn)行有效性檢測奕筐。
為什么 Android 要采用 Binder 作為 IPC 機(jī)制舱痘?
6、Binder機(jī)制的作用和原理离赫?
- Linux系統(tǒng)將一個(gè)進(jìn)程分為
用戶空間
和內(nèi)核空間
芭逝。對(duì)于進(jìn)程之間來說,用戶空間的數(shù)據(jù)不可共享渊胸,內(nèi)核空間的數(shù)據(jù)可共享旬盯,為了保證安全性和獨(dú)立性,一個(gè)進(jìn)程不能直接操作或者訪問另一個(gè)進(jìn)程翎猛,即Android的進(jìn)程是相互獨(dú)立胖翰、隔離的,這就需要跨進(jìn)程之間的數(shù)據(jù)通信方式
-
一次完整的 Binder IPC 通信過程通常是這樣:
1切厘、首先 Binder 驅(qū)動(dòng)在內(nèi)核空間創(chuàng)建一個(gè)數(shù)據(jù)接收緩存區(qū)萨咳; 2、接著在內(nèi)核空間開辟一塊內(nèi)核緩存區(qū)疫稿,建立內(nèi)核緩存區(qū)和內(nèi)核中數(shù)據(jù)接收緩存區(qū)之間的映射關(guān)系培他,以及內(nèi)核中數(shù)據(jù)接收緩存區(qū)和接收進(jìn)程用戶空間地址的映射關(guān)系鹃两; 3、發(fā)送方進(jìn)程通過系統(tǒng)調(diào)用 copyfromuser() 將數(shù)據(jù) copy 到內(nèi)核中的內(nèi)核緩存區(qū)舀凛,由于內(nèi)核緩存區(qū)和接收進(jìn)程的用戶空間存在內(nèi)存映射怔毛,因此也就相當(dāng)于把數(shù)據(jù)發(fā)送到了接收進(jìn)程的用戶空間,這樣便完成了一次進(jìn)程間的通信腾降。
[圖片上傳失敗...(image-3627ce-1595590156419)]
-
內(nèi)存映射:
關(guān)聯(lián) 進(jìn)程中的1個(gè)虛擬內(nèi)存區(qū)域 & 1個(gè)磁盤上的對(duì)象拣度,使得二者存在映射關(guān)系 上述的映射過程 = 初始化該虛擬內(nèi)存區(qū)域 虛擬內(nèi)存區(qū)域被初始化后,就會(huì)在交換空間中換你來還去 被映射的對(duì)象稱為:共享對(duì)象(普通文件 / 匿名文件) 1. 作用 若存在上述映射關(guān)系螃壤,則具備以下特征 在多個(gè)進(jìn)程的虛擬內(nèi)存區(qū)域 已和同1個(gè)共享對(duì)象 建立映射關(guān)系的前提下 若 其中1個(gè)進(jìn)程對(duì)該虛擬區(qū)域進(jìn)行寫操作 那么抗果,對(duì)于 也把該共享對(duì)象映射到其自身虛擬內(nèi)存區(qū)域的進(jìn)程 也是可見的
/**
* 函數(shù)原型
*/
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
/**
* 具體使用(用戶進(jìn)程調(diào)用mmap())
* 下述代碼即常見了一片大小 = MAP_SIZE的接收緩存區(qū) & 關(guān)聯(lián)到共享對(duì)象中(即建立映射)
*/
mmap(NULL, MAP_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
/**
* 內(nèi)部原理
* 步驟1:創(chuàng)建虛擬內(nèi)存區(qū)域
* 步驟2:實(shí)現(xiàn)地址映射關(guān)系,即:進(jìn)程的虛擬地址空間 ->> 共享對(duì)象
* 注:
* a. 此時(shí)奸晴,該虛擬地址并沒有任何數(shù)據(jù)關(guān)聯(lián)到文件中冤馏,僅僅只是建立映射關(guān)系
* b. 當(dāng)其中1個(gè)進(jìn)程對(duì)虛擬內(nèi)存寫入數(shù)據(jù)時(shí),則真正實(shí)現(xiàn)了數(shù)據(jù)的可見
*/
-
Binder的使用場景:
①系統(tǒng)廣播 ②與AMS交互寄啼,比如Intent啟動(dòng)其他組件逮光,會(huì)離開當(dāng)前進(jìn)程,進(jìn)入AMS進(jìn)程墩划,所以Intent傳值屬于IPC通信涕刚,要序列化 ③aidl ④使用Parcelable序列化是就是過Binder機(jī)制保存到共享內(nèi)存中 ⑤ContentProvider的底層是采用Android中的Binder機(jī)制存儲(chǔ) ⑥系統(tǒng)廣播也是利用了Binder進(jìn)行進(jìn)程間通信
Android跨進(jìn)程通信:圖文詳解 Binder機(jī)制 原理
7、Binder框架中ServiceManager的作用乙帮?
首先服務(wù)端需要在ServiceManager注冊(cè)服務(wù)杜漠,客戶端想要訪問服務(wù)端,首先去ServiceManager查詢要調(diào)用的方法察净,并返回服務(wù)端的代理對(duì)象驾茴,代理對(duì)象執(zhí)行方法其實(shí)就是把參數(shù)傳給Binder驅(qū)動(dòng),Binder驅(qū)動(dòng)會(huì)通知服務(wù)端調(diào)用相應(yīng)方法氢卡,并將結(jié)果返回給Binder驅(qū)動(dòng)锈至,Binder驅(qū)動(dòng)再將結(jié)果返回給客戶端
-
Binder框架 是基于 C/S 架構(gòu)的。由一系列的組件組成译秦,包括 Client峡捡、Server、ServiceManager诀浪、Binder驅(qū)動(dòng)棋返,其中 Client、Server雷猪、Service Manager 運(yùn)行在用戶空間睛竣,Binder 驅(qū)動(dòng)運(yùn)行在內(nèi)核空間
[圖片上傳失敗...(image-239f32-1595590156419)]Server&Client:服務(wù)器&客戶端。在Binder驅(qū)動(dòng)和Service Manager提供的基礎(chǔ)設(shè)施上求摇,進(jìn)行Client-Server之間的通信射沟。 ServiceManager(如同DNS域名服務(wù)器)服務(wù)的管理者殊者,將Binder名字轉(zhuǎn)換為Client中對(duì)該Binder的引用,使得Client可以通過Binder名字獲得Server中Binder實(shí)體的引用验夯。 Binder驅(qū)動(dòng)(如同路由器):負(fù)責(zé)進(jìn)程之間binder通信的建立猖吴,傳遞,計(jì)數(shù)管理以及數(shù)據(jù)的傳遞交互等底層支持挥转。
-
一個(gè)進(jìn)程的Binder線程數(shù)默認(rèn)最大是16海蔽,超過的請(qǐng)求會(huì)被阻塞等待空閑的Binder線程。
所以绑谣,在進(jìn)程間通信時(shí)處理并發(fā)問題時(shí)党窜,如使用ContentProvider時(shí),它的CRUD(創(chuàng)建、檢索、更新和刪除)方法只能同時(shí)有16個(gè)線程同時(shí)工作
8艳馒、Bundle傳遞對(duì)象為什么需要序列化?Serialzable和Parcelable的區(qū)別杆融?
- 因?yàn)閎undle傳遞數(shù)據(jù)時(shí)只支持基本數(shù)據(jù)類型,所以在傳遞對(duì)象時(shí)需要序列化轉(zhuǎn)換成可存儲(chǔ)或可傳輸?shù)谋举|(zhì)狀態(tài)(字節(jié)流)。序列化后的對(duì)象可以在網(wǎng)絡(luò)、IPC(比如啟動(dòng)另一個(gè)進(jìn)程的Activity楚里、Service和Reciver)之間進(jìn)行傳輸,也可以存儲(chǔ)到本地括改。
序列化實(shí)現(xiàn)的兩種方式:實(shí)現(xiàn)Serializable/Parcelable接口腻豌。不同點(diǎn)如圖:
[圖片上傳失敗...(image-b1f79b-1595590156419)]
9、講講AIDL嘱能?原理是什么?如何優(yōu)化多模塊都使用AIDL的情況虱疏?
-
AIDL(Android Interface Definition Language惹骂,Android接口定義語言):如果在一個(gè)進(jìn)程中要調(diào)用另一個(gè)進(jìn)程中對(duì)象的方法,可使用AIDL生成可序列化的參數(shù)做瞪,AIDL會(huì)生成一個(gè)服務(wù)端對(duì)象的代理類对粪,通過它客戶端實(shí)現(xiàn)間接調(diào)用服務(wù)端對(duì)象的方法。
為什么返回代理對(duì)象:進(jìn)程間通信時(shí)装蓬,數(shù)據(jù)是在內(nèi)核空間里著拭,無法返回服務(wù)端真正的對(duì)象。
AIDL的本質(zhì)是系統(tǒng)提供了一套可快速實(shí)現(xiàn)Binder的工具牍帚。關(guān)鍵類和方法:
AIDL接口:繼承IInterface儡遮。
Stub類:Binder的實(shí)現(xiàn)類,服務(wù)端通過這個(gè)類來提供服務(wù)暗赶。
Proxy類:服務(wù)器的本地代理鄙币,客戶端通過這個(gè)類調(diào)用服務(wù)器的方法肃叶。
asInterface():客戶端調(diào)用,將服務(wù)端的返回的Binder對(duì)象十嘿,轉(zhuǎn)換成客戶端所需要的AIDL接口類型對(duì)象因惭。如果客戶端和服務(wù)端位于統(tǒng)一進(jìn)程,則直接返回Stub對(duì)象本身绩衷,否則返回系統(tǒng)封裝后的Stub.proxy對(duì)象
asBinder():根據(jù)當(dāng)前調(diào)用情況返回代理Proxy的Binder對(duì)象蹦魔。
onTransact():運(yùn)行服務(wù)端的Binder線程池中,當(dāng)客戶端發(fā)起跨進(jìn)程請(qǐng)求時(shí)咳燕,遠(yuǎn)程請(qǐng)求會(huì)通過系統(tǒng)底層封裝后交由此方法來處理版姑。
transact():運(yùn)行在客戶端,當(dāng)客戶端發(fā)起遠(yuǎn)程請(qǐng)求的同時(shí)將當(dāng)前線程掛起迟郎。之后調(diào)用服務(wù)端的onTransact()直到遠(yuǎn)程請(qǐng)求返回剥险,當(dāng)前線程才繼續(xù)執(zhí)行。
當(dāng)有多個(gè)業(yè)務(wù)模塊都需要AIDL來進(jìn)行IPC宪肖,此時(shí)需要為每個(gè)模塊創(chuàng)建特定的aidl文件表制,那么相應(yīng)的Service就會(huì)很多。必然會(huì)出現(xiàn)系統(tǒng)資源耗費(fèi)嚴(yán)重控乾、應(yīng)用過度重量級(jí)的問題么介。解決辦法是建立Binder連接池,即將每個(gè)業(yè)務(wù)模塊的Binder請(qǐng)求統(tǒng)一轉(zhuǎn)發(fā)到一個(gè)遠(yuǎn)程Service中去執(zhí)行蜕衡,從而避免重復(fù)創(chuàng)建Service壤短。
工作原理:每個(gè)業(yè)務(wù)模塊創(chuàng)建自己的AIDL接口并實(shí)現(xiàn)此接口,然后向服務(wù)端提供自己的唯一標(biāo)識(shí)和其對(duì)應(yīng)的Binder對(duì)象慨仿。服務(wù)端只需要一個(gè)Service久脯,服務(wù)器提供一個(gè)queryBinder接口,它會(huì)根據(jù)業(yè)務(wù)模塊的特征來返回相應(yīng)的Binder對(duì)象镰吆,不同的業(yè)務(wù)模塊拿到所需的Binder對(duì)象后就可進(jìn)行遠(yuǎn)程方法的調(diào)用了
View
1帘撰、講下View的繪制流程?
View的工作流程主要是指measure万皿、layout摧找、draw這三大流程,即測量牢硅、布局和繪制蹬耘,其中measure確定View的測量寬/高,layout確定View的最終寬/高和四個(gè)頂點(diǎn)的位置减余,而draw則將View繪制到屏幕上
-
View的繪制過程遵循如下幾步:
繪制背景 background.draw(canvas) 繪制自己(onDraw) 繪制 children(dispatchDraw) 繪制裝飾(onDrawScollBars)
[圖片上傳失敗...(image-752d89-1595590156419)]
-
Activity#attach方法综苔,創(chuàng)建一個(gè)PhoneWindow,執(zhí)行Activity#onCreate,調(diào)用setContentView加載布局休里,初始化DecorView蛆挫,然后調(diào)用Activity#onStart,Activity#onResume妙黍,最后將DecorView添加到Window中悴侵,在將DecorView addView()到window中時(shí),addView()中創(chuàng)建了一個(gè)對(duì)象ViewRoot拭嫁,調(diào)用performTraversals()可免,啟動(dòng)View的繪制。繪制之后才可以拿到控件的寬高做粤。內(nèi)部會(huì)調(diào)用performMeasure()浇借、performLayout、performDraw()怕品。performMeasure()會(huì)調(diào)用最外層的ViewGroup的measure()-->onMeasure(),如果是View的話妇垢,最終會(huì)調(diào)用setMeasuredDimension()方法來設(shè)定測量出的大小,如果是ViewGroup的話肉康,會(huì)調(diào)用measureChildren()方法來去測量子視圖的大小闯估。performLayout()會(huì)調(diào)用最外層的ViewGroup的layout(),使用setFrame()設(shè)置本View的四個(gè)頂點(diǎn)位置。onLayout()是空方法吼和,繼承ViewGroup需要重寫這個(gè)方法涨薪,獲取子控件,調(diào)用childView.layout()進(jìn)行擺放炫乓。performDraw()會(huì)調(diào)用最外層ViewGroup的draw():首先會(huì)先繪制背景刚夺,然后OnDraw()繪制自己,是個(gè)空方法末捣,需要自己實(shí)現(xiàn)侠姑,然后繪制子控件dispatchDraw(),在View中是個(gè)空方法塔粒,在ViewGroup中有具體的繪制代碼结借,最后會(huì)繪制一些裝飾,比如滾動(dòng)條卒茬。
measure:是否需要重新進(jìn)行測量視圖大小 measure()方法是fainl的,接收兩個(gè)參數(shù)咖熟,widthMeasureSpec和heightMeasureSpec圃酵,這兩個(gè)值分別用于確定視圖的寬度和高度的規(guī)格和大小,是由父視圖經(jīng)過計(jì)算后傳遞給子視圖的。然后會(huì)調(diào)用onMeasure(widthMeasureSpec, heightMeasureSpec)馍管,如果是View的話郭赐,最終會(huì)調(diào)用setMeasuredDimension()方法來設(shè)定測量出的大小,如果是ViewGroup的話,會(huì)調(diào)用measureChildren()方法來去測量子視圖的大小捌锭,根據(jù)父控件的MeasureSpec和子控件的LayoutParams獲取子控件的MeasureSpec俘陷,調(diào)用child.measure()進(jìn)行測量。一層一層得向下傳遞观谦。 ①M(fèi)easureSpec的值由specSize和specMode共同組成的拉盾,MeasureSpec表示的是一個(gè)32位的整形值,它的高2位表示測量模式SpecMode豁状,低30位表示某種測量模式下的規(guī)格大小SpecSize捉偏。其中specSize記錄的是大小,specMode記錄的是規(guī)格(UNSPECIFIED泻红、EXACTLY(對(duì)應(yīng)精確值和match_parent)夭禽、AT_MOST(對(duì)應(yīng)warp_content)) ②在setMeasuredDimension()方法調(diào)用之后,我們才能使用getMeasuredWidth()和getMeasuredHeight()來獲取視圖測量出的寬高 ④DecorView的MeasureSpec由窗口大小和其LayoutParams決定谊路,其他View由父View的MeasureSpec和本View的LayoutParams決定讹躯。 layout:是否需要重新放置視圖位置 和measure一樣,通過對(duì)view樹的自上而下進(jìn)行遍歷缠劝。layout()方法接收四個(gè)參數(shù)潮梯,分別代表著左、上剩彬、右酷麦、下的坐標(biāo),是相對(duì)于父視圖的喉恋。將measure測量的寬高傳了進(jìn)去沃饶,也就是host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight)。在layout()中轻黑,首先調(diào)用setFrame()方法判斷視圖大小是否發(fā)生變化糊肤,來確定有沒有必要對(duì)當(dāng)前的視圖進(jìn)行重繪,同時(shí)設(shè)置自己四個(gè)頂點(diǎn)位置氓鄙。接著調(diào)用onLayout()方法馆揉,是個(gè)空方法,繼承ViewGroup需要重寫這個(gè)方法抖拦,獲取子控件升酣,調(diào)用childView.layout()進(jìn)行擺放。(這個(gè)時(shí)候可以拿到子控件的寬高) draw:是否需要進(jìn)行重繪 首先會(huì)先繪制背景态罪,然后OnDraw()繪制自己噩茄,是個(gè)空方法,需要自己實(shí)現(xiàn)复颈,然后繪制子控件dispatchDraw()绩聘,在View中是個(gè)空方法,在ViewGroup中有具體的繪制代碼,最后會(huì)繪制一些裝飾凿菩,比如滾動(dòng)條(大多數(shù)情況下沒有顯示)机杜。 ③在onLayout()過程結(jié)束后,我們就可以調(diào)用getWidth()方法和getHeight()方法來獲取視圖的寬高衅谷。 ④invalidate():如果視圖大小沒有發(fā)生變化椒拗,就不會(huì)調(diào)用layout放置這個(gè)過程。requestLayout():當(dāng)布局發(fā)生變化的時(shí)候会喝,觸發(fā)measure和layout方法陡叠,不會(huì)調(diào)用draw方法 自定義View ①繼承 View 重寫OnMeasure、onDraw方法 ②繼承 ViewGroup 重寫OnMeasure肢执、OnLayout方法 ③繼承特定的 View ④繼承特定的 ViewGroup
2枉阵、MotionEvent是什么?包含幾種事件预茄?什么條件下會(huì)產(chǎn)生兴溜?
-
MotionEvent是手指接觸屏幕后所產(chǎn)生的一系列事件。典型的事件類型有如下:
ACTION_DOWN:手指剛接觸屏幕 ACTION_MOVE:手指在屏幕上移動(dòng) ACTION_UP:手指從屏幕上松開的一瞬間 ACTION_CANCELL:手指保持按下操作耻陕,并從當(dāng)前控件轉(zhuǎn)移到外層控件時(shí)觸發(fā)
-
正常情況下拙徽,一次手指觸摸屏幕的行為會(huì)觸發(fā)一系列點(diǎn)擊事件,考慮如下幾種情況:
點(diǎn)擊屏幕后松開诗宣,事件序列:DOWN→UP 點(diǎn)擊屏幕滑動(dòng)一會(huì)再松開膘怕,事件序列為DOWN→MOVE→.....→MOVE→UP
3、描述一下View事件傳遞分發(fā)機(jī)制召庞?
- View事件分發(fā)本質(zhì)就是對(duì)MotionEvent事件分發(fā)的過程岛心。即當(dāng)一個(gè)MotionEvent發(fā)生后,系統(tǒng)將這個(gè)點(diǎn)擊事件傳遞到一個(gè)具體的View上
- 點(diǎn)擊事件的傳遞順序:Activity(Window)→ViewGroup→ View
- 事件分發(fā)過程由三個(gè)方法共同完成:
dispatchTouchEvent:
用來進(jìn)行事件的分發(fā)篮灼。如果事件能夠傳遞給當(dāng)前View忘古,那么此方法一定會(huì)被調(diào)用,返回結(jié)果受當(dāng)前View的onTouchEvent和下級(jí)View的dispatchTouchEvent方法的影響诅诱,表示是否消耗當(dāng)前事件
onInterceptTouchEvent:
在上述方法內(nèi)部調(diào)用髓堪,對(duì)事件進(jìn)行攔截。該方法只在ViewGroup中有娘荡,View(不包含 ViewGroup)是沒有的干旁。一旦攔截,則執(zhí)行ViewGroup的onTouchEvent炮沐,在ViewGroup中處理事件疤孕,而不接著分發(fā)給View。且只調(diào)用一次央拖,返回結(jié)果表示是否攔截當(dāng)前事件
onTouchEvent:
在dispatchTouchEvent方法中調(diào)用,用來處理點(diǎn)擊事件,返回結(jié)果表示是否消耗當(dāng)前事件
**詳細(xì)內(nèi)容**
- Activity -> PhoneWindow -> DecorView -> ViewGroup ->... -> View鲜戒,事件分發(fā)是個(gè)典型的責(zé)任鏈模式专控。
- 當(dāng)屏幕被點(diǎn)擊的時(shí)候,事件首先會(huì)傳遞給Activity遏餐,然后會(huì)傳遞給PhoneWindow伦腐,PhoneWindow是通過內(nèi)部類DecorView進(jìn)行事件傳遞的,DecorView拿到事件之后,調(diào)用dispatchTouchEvent分發(fā)事件,內(nèi)部調(diào)用onInterceptTouchEvent方法失都,根據(jù)onInterceptTouchEvent的返回值判斷自己是否需要攔截,如果攔截,就調(diào)用onTouchEvent消費(fèi)事件,如果不攔截,調(diào)用子控件的dispatchTouchEvent傳遞到子控件,如果子控件是ViewGroup就還需要判斷,如果是view,就調(diào)用onTouchEvent判斷是否消費(fèi)事件,如果子控件消費(fèi)事件,則事件不再回傳,如果不消費(fèi),則會(huì)回傳到ViewGroup的onTouchEvent消費(fèi)事件,ViewGroup也不消費(fèi)的話,則會(huì)依次向上傳遞事件柏蘑,如果最后Activity還沒有消費(fèi),就會(huì)被拋棄粹庞。
①在dispatchTouchEvent方法中會(huì)先執(zhí)行onTouch方法咳焚,如果onTouch不消費(fèi)才會(huì)執(zhí)行onTouchEvent,如果是點(diǎn)擊事件庞溜,在onTouchEvent中會(huì)調(diào)用onClick方法革半。
②ViewGroup包含dispatchTouchEvent分發(fā)事件,onInterceptTouchEvent攔截事件,onTouchEvent 三個(gè)相關(guān)事件.View包含dispatchTouchEvent,onTouchEvent兩個(gè)相關(guān)事件。Activity和View是沒有onInterceptTouchEvent攔截事件的流码。
①根據(jù)返回值又官,onTouchEvent如果返回true就代表消費(fèi),onInterceptTouchEvent返回true就代表攔截
②ViewGroup 的攔截器onInterceptTouchEvent 默認(rèn)是不攔截的
③子View可以利用getParent().requestDisallowInterceptTouchEvent(false)方法,請(qǐng)求父控件不要攔截事件
④ViewGroup中可能有多個(gè)ChildView漫试,如何判斷應(yīng)該分配給哪一個(gè)六敬?
將ChildView遍歷一下,分發(fā)給手指觸摸的ChildView
當(dāng)該點(diǎn)的ChildView有重疊時(shí)應(yīng)該如何分配驾荣?一般分配給顯示在最上面的ChildView外构。
⑤View為什么會(huì)有dispatchTouchEvent方法?
View有很多事件監(jiān)聽器秘车,單擊事件典勇,長按事件,觸摸事件叮趴。
⑥View事件分發(fā)四個(gè)方法的順序:
onTouch-> onTouchEvent -> onLongClickListener -> onClickListener
onTouch能夠得到執(zhí)行需要兩個(gè)前提條件,第一mOnTouchListener的值不能為空,第二當(dāng)前點(diǎn)擊的控件必須是enable的.因此如果你有一個(gè)控件是非enable的,那么給它注onTouch事件將永遠(yuǎn)得不到執(zhí)行.對(duì)于這一類控件,如果我們想要監(jiān)聽它的touch事件,就必須通過在該控件中重寫onTouchEvent方法來實(shí)現(xiàn)割笙。View 在可點(diǎn)擊狀態(tài)下,onTouchEvent 默認(rèn)會(huì)消耗事件
⑦事件是以down move up為一組的眯亦,在傳遞過程中如果沒有消費(fèi)down事件伤溉,就不會(huì)接收到move up事件
ACTION_CANCEL機(jī)制及作用
一個(gè)子View處理了Down事件,那么隨之而來的Move和Up事件也會(huì)交給它處理妻率。但是交給它處理之前乱顾,父View還是可以攔截事件的,如果攔截了事件宫静,那么子View就會(huì)收到一個(gè)Cancel事件走净,并且不會(huì)收到后續(xù)的Move和Up事件券时。
子View可以通過設(shè)置requestDisallowInterceptTouchEvent(true)來達(dá)到禁止父ViewGroup攔截事件的目的
⑨其他
MotionEvent
①getX/getY 返回相對(duì)于當(dāng)前View左上角的坐標(biāo),getRawX/getRawY 返回相對(duì)于屏幕左上角的坐標(biāo)
②TouchSlop是系統(tǒng)所能識(shí)別出的被認(rèn)為滑動(dòng)的最小距離伏伯,不同設(shè)備值可能不相同橘洞,可通過 ViewConfiguration.get(getContext()).getScaledTouchSlop() 獲取。
VelocityTracker
VelocityTracker 可用于追蹤手指在滑動(dòng)中的速度
GestureDetector
GestureDetector 輔助檢測用戶的單擊说搅、滑動(dòng)炸枣、長按、雙擊等行為弄唧。如果是監(jiān)聽滑動(dòng)相關(guān)适肠,建議在 onTouchEvent 中實(shí)現(xiàn),如果要監(jiān)聽雙擊候引,那么就使用 GestureDectector
Scroller
彈性滑動(dòng)對(duì)象侯养,用于實(shí)現(xiàn) View 的彈性滑動(dòng),Scroller 本身無法讓 View 彈性滑動(dòng)背伴,需要和 View 的 computeScroll 方法配合使用沸毁。startScroll 方法是無法讓 View 滑動(dòng)的,invalidate 會(huì)導(dǎo)致 View 重繪傻寂,重回后會(huì)在 draw 方法中又會(huì)去調(diào)用 computeScroll 方法息尺,computeScroll 方法又會(huì)去向 Scroller 獲取當(dāng)前的 scrollX 和 scrollY,然后通過 scrollTo 方法實(shí)現(xiàn)滑動(dòng)疾掰,接著又調(diào)用 postInvalidate 方法如此反復(fù)
4搂誉、如何解決View的事件沖突 ? 舉個(gè)開發(fā)中遇到的例子 静檬?
-
常見開發(fā)中事件沖突的有ScrollView與RecyclerView的滑動(dòng)沖突炭懊、RecyclerView內(nèi)嵌同時(shí)滑動(dòng)同一方向
滑動(dòng)沖突的處理規(guī)則:1、對(duì)于由于外部滑動(dòng)和內(nèi)部滑動(dòng)方向不一致導(dǎo)致的滑動(dòng)沖突拂檩,可以根據(jù)滑動(dòng)的方向判斷誰來攔截事件侮腹。 2、對(duì)于由于外部滑動(dòng)方向和內(nèi)部滑動(dòng)方向一致導(dǎo)致的滑動(dòng)沖突稻励,可以根據(jù)業(yè)務(wù)需求父阻,規(guī)定何時(shí)讓外部View攔截事件,何時(shí)由內(nèi)部View攔截事件望抽。 對(duì)于上面兩種情況的嵌套加矛,相對(duì)復(fù)雜,可同樣根據(jù)需求在業(yè)務(wù)上找到突破點(diǎn)煤篙。
-
滑動(dòng)沖突的實(shí)現(xiàn)方法:
外部攔截法:
指點(diǎn)擊事件都先經(jīng)過父容器的攔截處理斟览,如果父容器需要此事件就攔截,否則就不攔截辑奈。具體方法:需要重寫父容器的onInterceptTouchEvent方法苛茂,在內(nèi)部做出相應(yīng)的攔截已烤。內(nèi)部攔截法:
指父容器不攔截任何事件,而將所有的事件都傳遞給子容器味悄,如果子容器需要此事件就直接消耗草戈,否則就交由父容器進(jìn)行處理。具體方法:需要配合requestDisallowInterceptTouchEvent方法侍瑟。
5、scrollTo()和scollBy()的區(qū)別丙猬?
- scollBy內(nèi)部調(diào)用了scrollTo涨颜,它是基于當(dāng)前位置的相對(duì)滑動(dòng);
- scrollTo是絕對(duì)滑動(dòng)茧球,因此如果使用相同輸入?yún)?shù)多次調(diào)用scrollTo方法庭瑰,由于View的初始位置是不變的,所以只會(huì)出現(xiàn)一次View滾動(dòng)的效果
- 兩者都只能對(duì)View內(nèi)容的滑動(dòng)抢埋,而非使View本身滑動(dòng)弹灭。可以使用Scroller有過度滑動(dòng)的效果
View 的滑動(dòng)原理和實(shí)現(xiàn)方式
6揪垄、Scroller是怎么實(shí)現(xiàn)View的彈性滑動(dòng)穷吮?
- 在MotionEvent.ACTION_UP事件觸發(fā)時(shí)調(diào)用startScroll()方法,該方法并沒有進(jìn)行實(shí)際的滑動(dòng)操作饥努,而是記錄滑動(dòng)相關(guān)量(滑動(dòng)距離捡鱼、滑動(dòng)時(shí)間)
- 接著調(diào)用invalidate/postInvalidate()方法,請(qǐng)求View重繪酷愧,導(dǎo)致View.draw方法被執(zhí)行
- 當(dāng)View重繪后會(huì)在draw方法中調(diào)用computeScroll方法驾诈,而computeScroll又會(huì)去向Scroller獲取當(dāng)前的scrollX和scrollY病毡;然后通過scrollTo方法實(shí)現(xiàn)滑動(dòng)财饥;接著又調(diào)用postInvalidate方法來進(jìn)行第二次重繪,和之前流程一樣闸迷,如此反復(fù)導(dǎo)致View不斷進(jìn)行小幅度的滑動(dòng)士败,而多次的小幅度滑動(dòng)就組成了彈性滑動(dòng)闯两,直到整個(gè)滑動(dòng)過成結(jié)束
[圖片上傳失敗...(image-ad8df8-1595590156419)]
7、SurfaceView和View的區(qū)別拱烁?
普通view和它的宿主窗口共享一個(gè)繪圖表面(Surface)生蚁,SurfaceView雖然也在View的樹形結(jié)構(gòu)中,但是它有屬于自己的繪圖表面戏自,Surface 內(nèi)部持有一個(gè)Canvas邦投,可以利用這個(gè)Canvas繪制。SurefaceView的UI可以在一個(gè)獨(dú)立的線程中進(jìn)行繪制擅笔。因?yàn)椴粫?huì)占用主線程資源志衣,一方面可以實(shí)現(xiàn)復(fù)雜而高效的UI屯援,二是不會(huì)導(dǎo)致用戶輸入得不到及時(shí)響應(yīng)。
-
View和SurfaceView的區(qū)別:
View適用主動(dòng)更新念脯,SurfaceView 適用被動(dòng)更新狞洋,如頻繁的刷新
View在UI線程更新,在非UI線程更新會(huì)報(bào)錯(cuò)绿店,當(dāng)在主線程更新view時(shí)如果耗時(shí)過長也會(huì)出錯(cuò), SurfaceView在子線程刷新不會(huì)阻塞主線程吉懊,適用于界面頻繁更新、對(duì)幀率要求較高的情況假勿。
SurfaceView可以控制刷新頻率借嗽。
-
SurfaceView底層利用雙緩存機(jī)制,繪圖時(shí)不會(huì)出現(xiàn)閃爍問題转培。
雙緩沖技術(shù)是游戲開發(fā)中的一個(gè)重要的技術(shù)恶导,主要是為了解決 反復(fù)局部刷屏帶來的閃爍。游戲浸须,視頻等畫面變化較頻繁惨寿,前面還沒有顯示完,程序又請(qǐng)求重新繪制删窒,這樣屏幕就會(huì)不停地閃爍裂垦。雙緩沖技術(shù)會(huì)把要處理的圖片在內(nèi)存中處理好之后,把要畫的東西先畫到一個(gè)內(nèi)存區(qū)域里易稠,然后整體的一次性畫出來缸废,將其顯示在屏幕上。 SurfaceView不支持平移驶社,縮放企量,旋轉(zhuǎn)等動(dòng)畫,但是當(dāng)我們利用SurfaceView測試這些不支持的動(dòng)畫時(shí)亡电,如果使用的是7.0 甚至更高版本的Android系統(tǒng)届巩,會(huì)發(fā)現(xiàn)SurfaceView也支持平移,縮放的動(dòng)畫操作份乒。
-
簡化整理:
View需要在UI線程對(duì)畫面進(jìn)行刷新恕汇,而SurfaceView可在子線程進(jìn)行頁面的刷新 View適用于主動(dòng)更新的情況,而SurfaceView適用于被動(dòng)更新或辖,如頻繁刷新瘾英,這是因?yàn)槿绻褂肰iew頻繁刷新會(huì)阻塞主線程,導(dǎo)致界面卡頓 SurfaceView在底層已實(shí)現(xiàn)雙緩沖機(jī)制颂暇,而View沒有缺谴,因此SurfaceView更適用于需要頻繁刷新、刷新時(shí)數(shù)據(jù)處理量很大的頁面(如視頻播放界面)
8耳鸯、自定義View如何考慮機(jī)型適配 ?
- 合理使用warp_content湿蛔,match_parent
- 盡可能的是使用RelativeLayout
- 針對(duì)不同的機(jī)型膀曾,使用不同的布局文件放在對(duì)應(yīng)的目錄下,android會(huì)自動(dòng)匹配阳啥。
- 盡量使用點(diǎn)9圖片添谊。
- 使用與密度無關(guān)的像素單位dp,sp
- 引入android的百分比布局察迟。
- 切圖的時(shí)候切大分辨率的圖斩狱,應(yīng)用到布局當(dāng)中。在小分辨率的手機(jī)上也會(huì)有很好的顯示效果卷拘。