Android基礎(chǔ)

1、Activity和Fragment生命周期有哪些?


1

oncreate():做一些初始化相關(guān)工作,加載視圖。

onstart():頁面已經(jīng)初始化完成,頁面還處在后臺,看不見也不能交互

onresume():頁面從后臺進入前臺,可以和用戶交互

onpause():頁面還在前臺能看見,但是不能交互,比如彈窗,這里最好不要做緩存持久化操作,因為會影響打開頁面的速度

onstop():頁面進入后臺,看不見,可以做些緩存數(shù)據(jù)持久化操作

ondestroy():頁面進入銷毀階段,可以做些資源回收操作

2、橫豎屏切換時Activity的生命周期

不設(shè)置Activity的android:configChanges時,切屏?xí)匦禄卣{(diào)各個生命周期,切橫屏?xí)r會執(zhí)行一次,切豎屏?xí)r會執(zhí)行兩次。 設(shè)置Activity的android:configChanges=”orientation”時,切屏還是會調(diào)用各個生命周期,切換橫豎屏只會執(zhí)行一次 設(shè)置Activity的android:configChanges=”orientation |keyboardHidden”時,切屏不會重新調(diào)用各個生命周期,只會執(zhí)行onConfigurationChanged方法

3、onSaveInstanceState() 與 onRestoreIntanceState()

Activity的 onSaveInstanceState() 和 onRestoreInstanceState()并不是生命周期方法,它們不同于 onCreate()、onPause()等生命周期方法,它們并不一定會被觸發(fā)。當(dāng)應(yīng)用遇到意外情況(如:內(nèi)存不足、用戶直接按Home鍵)由系統(tǒng)銷毀一個Activity時,onSaveInstanceState() 會被調(diào)用。但是當(dāng)用戶主動去銷毀一個Activity時,例如在應(yīng)用中按返回鍵,onSaveInstanceState()就不會被調(diào)用。因為在這種情況下,用戶的行為決定了不需要保存Activity的狀態(tài)。通常onSaveInstanceState()只適合用于保存一些臨時性的狀態(tài),而onPause()適合用于數(shù)據(jù)的持久化保存。 在activity被殺掉之前調(diào)用保存每個實例的狀態(tài),以保證該狀態(tài)可以在onCreate(Bundle)或者onRestoreInstanceState(Bundle) (傳入的Bundle參數(shù)是由onSaveInstanceState封裝好的)中恢復(fù)。這個方法在一個activity被殺死前調(diào)用,當(dāng)該activity在將來某個時刻回來時可以恢復(fù)其先前狀態(tài)。 例如,如果activity B啟用后位于activity A的前端,在某個時刻activity A因為系統(tǒng)回收資源的問題要被殺掉,A通過onSaveInstanceState將有機會保存其用戶界面狀態(tài),使得將來用戶返回到activity A時能通過onCreate(Bundle)或者onRestoreInstanceState(Bundle)恢復(fù)界面的狀態(tài)

4、Bunder傳遞對象為什么需要序列化?Serialzable和Parcelable的區(qū)別?

因為bundle傳遞數(shù)據(jù)時只支持基本數(shù)據(jù)類型,所以在傳遞對象時需要序列化轉(zhuǎn)換成可存儲或可傳輸?shù)谋举|(zhì)狀態(tài)(字節(jié)流)。序列化后的對象可以在網(wǎng)絡(luò)、IPC(比如啟動另一個進程的Activity、Service和Reciver)之間進行傳輸,也可以存儲到本地。

Serializable(Java自帶):

Serializable 是序列化的意思,表示將一個對象轉(zhuǎn)換成存儲或可傳輸?shù)臓顟B(tài)。序列化后的對象可以在網(wǎng)絡(luò)上進傳輸,也可以存儲到本地。

Parcelable(android專用):

除了Serializable之外,使用Parcelable也可以實現(xiàn)相同的效果,不過不同于將對象進行序列化,Parcelable方式的實現(xiàn)原理是將一個完整的對象進行分解,而分解后的每一部分都是Intent所支持的數(shù)據(jù)類型,這也就實現(xiàn)傳遞對象的功能了。

可以肯定的是,兩者都是支持序列化和反序列化的操作。

兩者最大的區(qū)別在于 存儲媒介的不同,Serializable 使用 I/O 讀寫存儲在硬盤上,而 Parcelable 是直接 在內(nèi)存中讀寫。很明顯,內(nèi)存的讀寫速度通常大于 IO 讀寫,所以在 Android 中傳遞數(shù)據(jù)優(yōu)先選擇 Parcelable。

Serializable 會使用反射,序列化和反序列化過程需要大量 I/O 操作, Parcelable 自已實現(xiàn)封送和解封(marshalled &unmarshalled)操作不需要用反射,數(shù)據(jù)也存放在 Native 內(nèi)存中,效率要快很多。

5、context相關(guān)內(nèi)容:

Context?本身是一個抽象類,主要實現(xiàn)類為?ContextImpl,另外有子類?ContextWrapper?和?ContextThemeWrapper

2


3

Context是Android應(yīng)用程序與系統(tǒng)環(huán)境進行交互的橋梁,主要實現(xiàn)類是ContextImpl,?可以訪問應(yīng)用程序資源/啟動組件/訪問系統(tǒng)服務(wù)/訪問應(yīng)用程序的文件等,而Context可以分為三種:ContextImpl/ContextWrapper/ContextThemeWrapper,不同ContextImpl?是Context的主要實現(xiàn)類,ContextWrapper是簡單的包裝類,所有的實現(xiàn)都由其內(nèi)部的mBase成員完成,ContextThemeWrapper繼承自ContextWrapper?,它的主要繼承者是Activity,和其他兩個Context不同的是,他內(nèi)部對應(yīng)用資源和主題有不同的行為,在應(yīng)用中使用跟主題相關(guān)的Context時,最好使用activity,而不要使用getBaseContext或者applictaion.

service和application的context啟動activity需要加上FLAG_ACTIVITY_NEW_TASK這個flag,個人理解activity的context啟動activity是默認(rèn)添加到自己所在的棧中,而service和application沒有activity棧的概念。

6、IntentService和HandlerThread

handlerThread是繼承了Thread,創(chuàng)建了自己線程的loop,可以用來執(zhí)行心跳或者定時任務(wù)

IntentService處理異步請求,內(nèi)部是由handlerThread實現(xiàn)的,在onHandleIntent中處理耗時操作,多個耗時任務(wù)會依次執(zhí)行,執(zhí)行完畢自動結(jié)束,已經(jīng)被棄用了,可以使用WorkManage替代

7、怎么在Service中創(chuàng)建Dialog對話框?

1.在我們?nèi)〉肈ialog對象后,需給它設(shè)置類型,即:

dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)

2.在Manifest中加上權(quán)限:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINOW" />

8、Asset目錄與res目錄的區(qū)別?

assets:不會在 R 文件中生成相應(yīng)標(biāo)記,存放到這里的資源在打包時會打包到程序安裝包中。(通過 AssetManager 類訪問這些文件)

res:會在 R 文件中生成 id 標(biāo)記,資源在打包時如果使用到則打包到安裝包中,未用到不會打入安裝包中。

res/anim:存放動畫資源。

res/raw:和 asset 下文件一樣,打包時直接打入程序安裝包中(會映射到 R 文件中)。

9、Handler相關(guān)


4

消息隊列是一個單鏈表,里面的消息是按照消息執(zhí)行時間when排序的,在enqueueMessage中對新加入的消息進行排序。

關(guān)于loop死循環(huán)問題:

這里就涉及到Linux pipe/epoll機制,簡單說就是在主線程的MessageQueue沒有消息時,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此時主線程會釋放CPU資源進入休眠狀態(tài),直到下個消息到達或者有事務(wù)發(fā)生,通過往pipe管道寫端寫入數(shù)據(jù)來喚醒主線程工作。這里采用的epoll機制,是一種IO多路復(fù)用機制,可以同時監(jiān)控多個描述符,當(dāng)某個描述符就緒(讀或?qū)懢途w),則立刻通知相應(yīng)程序進行讀或?qū)懖僮?,本質(zhì)是同步I/O,即讀寫是阻塞的。所以說,主線程大多數(shù)時候都是處于休眠狀態(tài),并不會消耗大量CPU資源。

同步屏障相關(guān)問題:

首先同步屏障是通過一個target為null的message來實現(xiàn)的,也就是說,開啟同步屏障回往消息隊列中添加一個target==null的message,當(dāng)執(zhí)行到這個消息時,會在消息隊列中尋找異步消息,即isAsynchronous==true的消息,然后派發(fā)出去,由對應(yīng)的handler執(zhí)行,如果沒有移除同步屏障那么同步消息一直得不到執(zhí)行,移除同步屏障也就是移除添加同步屏障時的那個target==null的消息。

9、線程池的相關(guān)知識。

Android中的線程池都是直接或間接通過配置ThreadPoolExecutor來實現(xiàn)不同特性的線程池.Android中最常見的類具有不同特性的線程池分別為FixThreadPool、CachedhreadPool、SingleThreadPool、ScheduleThreadExecutr.

1).FixThreadPool

只有核心線程,并且數(shù)量固定的,也不會被回收,所有線程都活動時,因為隊列沒有限制大小,新任務(wù)會等待執(zhí)行.

優(yōu)點:更快的響應(yīng)外界請求.

2).SingleThreadPool

只有一個核心線程,確保所有的任務(wù)都在同一線程中按序完成.因此不需要處理線程同步的問題.

3).CachedThreadPool

只有非核心線程,最大線程數(shù)非常大,所有線程都活動時會為新任務(wù)創(chuàng)建新線程,否則會利用空閑線程(60s空閑時間,過了就會被回收,所以線程池中有0個線程的可能)處理任務(wù).

優(yōu)點:任何任務(wù)都會被立即執(zhí)行(任務(wù)隊列SynchronousQuue相當(dāng)于一個空集合);比較適合執(zhí)行大量的耗時較少的任務(wù).

4).ScheduledThreadPool

核心線程數(shù)固定,非核心線程(閑著沒活干會被立即回收數(shù))沒有限制.

優(yōu)點:執(zhí)行定時任務(wù)以及有固定周期的重復(fù)任務(wù)

10、OOM 是否可以try catch ?

只有在一種情況下,這樣做是可行的:

在try語句中聲明了很大的對象,導(dǎo)致OOM,并且可以確認(rèn)OOM是由try語句中的對象聲明導(dǎo)致的,那么在catch語句中,可以釋放掉這些對象,解決OOM的問題,繼續(xù)執(zhí)行剩余語句。

但是這通常不是合適的做法。

Java中管理內(nèi)存除了顯式地catch OOM之外還有更多有效的方法:比如SoftReference, WeakReference, 硬盤緩存等。 在JVM用光內(nèi)存之前,會多次觸發(fā)GC,這些GC會降低程序運行的效率。 如果OOM的原因不是try語句中的對象(比如內(nèi)存泄漏),那么在catch語句中會繼續(xù)拋出OOM。

11、廣播傳輸?shù)臄?shù)據(jù)是否有限制,是多少,為什么要限制?

Intent在傳遞數(shù)據(jù)時是有大小限制的,大約限制在1MB之內(nèi),你用Intent傳遞數(shù)據(jù),實際上走的是跨進程通信(IPC),跨進程通信需要把數(shù)據(jù)從內(nèi)核copy到進程中,每一個進程有一個接收內(nèi)核數(shù)據(jù)的緩沖區(qū),默認(rèn)是1M;如果一次傳遞的數(shù)據(jù)超過限制,就會出現(xiàn)異常。

不同廠商表現(xiàn)不一樣有可能是廠商修改了此限制的大小,也可能同樣的對象在不同的機器上大小不一樣。

傳遞大數(shù)據(jù),不應(yīng)該用Intent;考慮使用ContentProvider或者直接匿名共享內(nèi)存。簡單情況下可以考慮分段傳輸。

12、Bitmap相關(guān)

ALPHA_8 每個像素占用1byte內(nèi)存

ARGB_4444 每個像素占用2byte內(nèi)存? ? ?

ARGB_8888 每個像素占用4byte內(nèi)存(默認(rèn))? ? ?

RGB_565 每個像素占用2byte內(nèi)存

Bitamp 占用內(nèi)存大小 = 寬度像素 x (inTargetDensity / inDensity) x 高度像素 x (inTargetDensity / inDensity)x 一個像素所占的內(nèi)存

BitmapFactory.Options 參數(shù)inSampleSize的使用,先把options.inJustDecodeBounds設(shè)為true,只是去讀取圖片的大小,在拿到圖片的大小之后和要顯示的大小做比較通過calculateInSampleSize()函數(shù)計算inSampleSize的具體值,得到值之后。options.inJustDecodeBounds設(shè)為false讀圖片資源

13、請解釋安卓為啥要加簽名機制。

1、發(fā)送者的身份認(rèn)證 由于開發(fā)商可能通過使用相同的 Package Name 來混淆替換已經(jīng)安裝的程序,以此保證簽名不同的包不被替換。

2、保證信息傳輸?shù)耐暾?簽名對于包中的每個文件進行處理,以此確保包中內(nèi)容不被替換。

3、防止交易中的抵賴發(fā)生, Market 對軟件的要求。

簽名類型分為v1、v2、v3類型,v1到v2變化大,v2到v3只是小優(yōu)化,主要了解v1、v2:

apk 本質(zhì)是個 zip 文件,解壓縮后,在?META-INFO?文件夾中可以看到有?MANIFEST.MF、CERT.SF、CERT.RSA?三個文件。這三個文件在簽名時創(chuàng)建,在安裝時用于驗證簽名。

v1簽名:打包過程中,對每個文件進行sha1數(shù)字摘要存放在MANIFEST.MF中,這樣保證了所有文件不被篡改,然后又對MANIFEST.MF文件整體及每個文件摘要再次進行sha1數(shù)字摘要,生成了CERT.SF文件;為了保證CERT.SF不被篡改,?CERT.SF 文件用私鑰計算出簽名, 然后將簽名以及包含公鑰信息的數(shù)字證書一同寫入 CERT.RSA 中保存。

v2簽名:首先v1簽名存在的缺點有兩個:1、文件過多會導(dǎo)致摘要時間過長,2、沒法保證整個apk的完整性(簽名文件所在的META-INFO目錄沒有校驗,前期的渠道包信息就是通過這里將含有渠道名的空文件寫到這個目錄的)

在了解下zip文件格式:


5

V2 簽名時,會將 簽名信息塊 插入到 Zip 文件的「文件信息」和「中央目錄」之間,


6

摘要、簽名與數(shù)字證書都是什么?

14、計算一個view的嵌套層級

private int getParents(ViewParents view){

? ? if(view.getParents() == null)

? ? ? ? return 0;

? ? } else {

? ? return (1 + getParents(view.getParents));

? }

}

15、SharedPrefrences的apply和commit有什么區(qū)別?

這兩個方法的區(qū)別在于:

apply沒有返回值而commit返回boolean表明修改是否提交成功。

apply是將修改數(shù)據(jù)原子提交到內(nèi)存, 而后異步真正提交到硬件磁盤, 而commit是同步的提交到硬件磁盤,因此,在多個并發(fā)的提交commit的時候,他們會等待正在處理的commit保存到磁盤后在操作,從而降低了效率。而apply只是原子的提交到內(nèi)容,后面有調(diào)用apply的函數(shù)的將會直接覆蓋前面的內(nèi)存數(shù)據(jù),這樣從一定程度上提高了很多效率。

apply方法不會提示任何失敗的提示。 由于在一個進程中,sharedPreference是單實例,一般不會出現(xiàn)并發(fā)沖突,如果對提交的結(jié)果不關(guān)心的話,建議使用apply,當(dāng)然需要確保提交成功且有后續(xù)操作的話,還是需要用commit的。

16、Base64、MD5是加密方法么?

Base64是什么?

Base64是用文本表示二進制的編碼方式,它使用4個字節(jié)的文本來表示3個字節(jié)的原始二進制數(shù)據(jù)。 它將二進制數(shù)據(jù)轉(zhuǎn)換成一個由64個可打印的字符組成的序列:A-Za-z0-9+/

MD5是什么?

MD5是哈希算法的一種,可以將任意數(shù)據(jù)產(chǎn)生出一個128位(16字節(jié))的散列值,用于確保信息傳輸完整一致。我們常在注冊登錄模塊使用MD5,用戶密碼可以使用MD5加密的方式進行存儲。如:md5(hello world,32) = 5eb63bbbe01eeed093cb22bb8f5acdc3

加密,指的是對數(shù)據(jù)進行轉(zhuǎn)換以后,數(shù)據(jù)變成了另一種格式,并且除了拿到解密方法的人,沒人能把數(shù)據(jù)轉(zhuǎn)換回來。 MD5是一種信息摘要算法,它是不可逆的,不可以解密。所以它只能算的上是一種單向加密算法。 Base64也不是加密算法,它是一種數(shù)據(jù)編碼方式,雖然是可逆的,但是它的編碼方式是公開的,無所謂加密。

17、啟動模式

standard,singleTop,singleTask,singleInstance

需要注意的是:taskAffinity 默認(rèn)值是應(yīng)用包名,這個屬性不能單獨決定activity所屬任務(wù)棧,需要結(jié)合FLAG_ACTIVITY_NEW_TASK ,也就是說同一個任務(wù)棧中可以包含不同taskAffinity 的activity。

非 Activity 啟動 Activity 都必須添加 Intent.FLAG_ACTIVITY_NEW_TASK 才行,為啥?

如果某個活動不是通過 Activity 啟動的,說明不是用戶主動的行為,也就是說這個活動可能會出現(xiàn)在任何 APP 的活動之上,這時如果不用 Intent.FLAG_ACTIVITY_NEW_TASK 將這個活動限制在自己的 Task 中,就可能讓用戶誤以為新的活動是屬于當(dāng)前 APP,這是不合理的。

FLAG_ACTIVITY_CLEAR_TASK 單獨使用沒用,需要結(jié)合其它flag,常見和FLAG_ACTIVITY_NEW_TASK 一起:

如果目標(biāo) Task 已存在,就先清空目標(biāo) Task(將目標(biāo) Task 原有的 Activity 全部出棧),然后新 Activity 稱為目標(biāo) Task 的根 Activity。如果目標(biāo) Task 還不存在,就新建目標(biāo) Task,新 Activity 成為根 Activity(這和單獨設(shè)置 Intent.FLAG_ACTIVITY_NEW_TASK 的效果一樣)。

詳情參考

18、對于?SharedPreferences?的?apply?和?commit?方法,在使用上存在一些區(qū)別。

commit():

commit?方法是同步提交,它會將修改的數(shù)據(jù)立即寫入磁盤,并返回一個布爾值來表示提交是否成功。同時,commit?方法會阻塞當(dāng)前線程,直到寫入磁盤操作完成。如果寫入操作耗時較長,可能會對性能產(chǎn)生影響。

apply():

apply?方法是異步提交,它會將修改的數(shù)據(jù)放入內(nèi)存緩存,并啟動一個新的線程用于將數(shù)據(jù)異步寫入磁盤。因為使用了異步線程,所以?apply?方法不會阻塞當(dāng)前線程,不會對性能造成太大的影響。注意,apply?方法沒有返回值,也無法得知寫入是否成功。

總結(jié)來說,commit?方法是同步的,會阻塞當(dāng)前線程,立即將數(shù)據(jù)寫入磁盤,而?apply?方法則是異步的,不會阻塞當(dāng)前線程,將數(shù)據(jù)寫入內(nèi)存緩存后,再通過新的線程異步寫入磁盤。

19、如何保證Service不被殺死?

Android 進程不死從3個層面入手:

A.提供進程優(yōu)先級,降低進程被殺死的概率

方法一:監(jiān)控手機鎖屏解鎖事件,在屏幕鎖屏?xí)r啟動1個像素的 Activity,在用戶解鎖時將 Activity 銷毀掉。

方法二:啟動前臺service。

方法三:提升service優(yōu)先級:

在AndroidManifest.xml文件中對于intent-filter可以通過android:priority = "1000"這個屬性設(shè)置最高優(yōu)先級,1000是最高值,如果數(shù)字越小則優(yōu)先級越低,同時適用于廣播。

B. 在進程被殺死后,進行拉活

方法一:注冊高頻率廣播接收器,喚起進程。如網(wǎng)絡(luò)變化,解鎖屏幕,開機等

方法二:雙進程相互喚起。

方法三:依靠系統(tǒng)喚起。

方法四:onDestroy方法里重啟service:service + broadcast 方式,就是當(dāng)service走ondestory的時候,發(fā)送一個自定義的廣播,當(dāng)收到廣播的時候,重新啟動service;

20、dp與px

計算的公式為:1dp=(屏幕的dpi/160)px,如果以我們的1080*1920的5英寸屏幕為例那就是1dp=(441/160)px=2.8px。

21、View的事件分發(fā)機制?滑動沖突怎么解決?

了解Activity的構(gòu)成

一個Activity包含了一個Window對象,這個對象是由PhoneWindow來實現(xiàn)的。PhoneWindow將DecorView作為整個應(yīng)用窗口的根View,而這個DecorView又將屏幕劃分為兩個區(qū)域:一個是TitleView,另一個是ContentView,而我們平時所寫的就是展示在ContentView中的。

觸摸事件的類型

觸摸事件對應(yīng)的是MotionEvent類,事件的類型主要有如下三種:

ACTION_DOWN

ACTION_MOVE(移動的距離超過一定的閾值會被判定為ACTION_MOVE操作)

ACTION_UP

ACTION_CANCEL

View事件分發(fā)本質(zhì)就是對MotionEvent事件分發(fā)的過程。即當(dāng)一個MotionEvent發(fā)生后,系統(tǒng)將這個點擊事件傳遞到一個具體的View上。

一些重要的結(jié)論:

1、事件傳遞優(yōu)先級:onTouchListener.onTouch > onTouchEvent > onClickListener.onClick。

2、正常情況下,一個時間序列只能被一個View攔截且消耗。因為一旦一個元素攔截了此事件,那么同一個事件序列內(nèi)的所有事件都會直接交給它處理(即不會再調(diào)用這個View的攔截方法去詢問它是否要攔截了,而是把剩余的ACTION_MOVE、ACTION_DOWN等事件直接交給它來處理)。特例:通過將重寫View的onTouchEvent返回false可強行將事件轉(zhuǎn)交給其他View處理。

3、如果View不消耗除ACTION_DOWN以外的其他事件,那么這個點擊事件會消失,此時父元素的onTouchEvent并不會被調(diào)用,并且當(dāng)前View可以持續(xù)收到后續(xù)的事件,最終這些消失的點擊事件會傳遞給Activity處理。

4、ViewGroup默認(rèn)不攔截任何事件(返回false)。

5、View的onTouchEvent默認(rèn)都會消耗事件(返回true),除非它是不可點擊的(clickable和longClickable同時為false)。View的longClickable屬性默認(rèn)都為false,clickable屬性要分情況,比如Button的clickable屬性默認(rèn)為true,而TextView的clickable默認(rèn)為false。

6、View的enable屬性不影響onTouchEvent的默認(rèn)返回值。

7、通過requestDisallowInterceptTouchEvent方法可以在子元素中干預(yù)父元素的事件分發(fā)過程,但是ACTION_DOWN事件除外。

記住這個圖的傳遞順序,面試的時候能夠畫出來,就很詳細(xì)了:


0

ACTION_CANCEL什么時候觸發(fā),觸摸button然后滑動到外部抬起會觸發(fā)點擊事件嗎,再滑動回去抬起會么?

一般ACTION_CANCEL和ACTION_UP都作為View一段事件處理的結(jié)束。如果在父View中攔截ACTION_UP或ACTION_MOVE,在第一次父視圖攔截消息的瞬間,父視圖指定子視圖不接受后續(xù)消息了,同時子視圖會收到ACTION_CANCEL事件。

如果觸摸某個控件,但是又不是在這個控件的區(qū)域上抬起(移動到別的地方了),就會出現(xiàn)action_cancel。

點擊事件被攔截,但是想傳到下面的View,如何操作?

重寫子類的requestDisallowInterceptTouchEvent()方法返回true就不會執(zhí)行父類的onInterceptTouchEvent(),即可將點擊事件傳到下面的View。

如何解決View的事件沖突?舉個開發(fā)中遇到的例子?

常見開發(fā)中事件沖突的有ScrollView與RecyclerView的滑動沖突、RecyclerView內(nèi)嵌同時滑動同一方向。

滑動沖突的處理規(guī)則:

對于由于外部滑動和內(nèi)部滑動方向不一致導(dǎo)致的滑動沖突,可以根據(jù)滑動的方向判斷誰來攔截事件。

對于由于外部滑動方向和內(nèi)部滑動方向一致導(dǎo)致的滑動沖突,可以根據(jù)業(yè)務(wù)需求,規(guī)定何時讓外部View攔截事件,何時由內(nèi)部View攔截事件。

對于上面兩種情況的嵌套,相對復(fù)雜,可同樣根據(jù)需求在業(yè)務(wù)上找到突破點。

滑動沖突的實現(xiàn)方法:

外部攔截法:指點擊事件都先經(jīng)過父容器的攔截處理,如果父容器需要此事件就攔截,否則就不攔截。具體方法:需要重寫父容器的onInterceptTouchEvent方法,在內(nèi)部做出相應(yīng)的攔截。

內(nèi)部攔截法:指父容器不攔截任何事件,而將所有的事件都傳遞給子容器,如果子容器需要此事件就直接消耗,否則就交由父容器進行處理。具體方法:需要配合requestDisallowInterceptTouchEvent方法。

加深理解,GOGOGO

滑動沖突

22、View的繪制流程

view的生命周期:參考

1

從ViewRootImpl的performTraversals開始從上到下進行遍歷整個view樹,調(diào)用onMeasure,onLayout,onDraw進行測量布局繪制。

理解MeasureSpec

MeasureSpec表示的是一個32位的整形值,它的高2位表示測量模式SpecMode,低30位表示某種測量模式下的規(guī)格大小SpecSize。MeasureSpec是View類的一個靜態(tài)內(nèi)部類,用來說明應(yīng)該如何測量這個View。它由三種測量模式,如下:

EXACTLY:精確測量模式,視圖寬高指定為match_parent或具體數(shù)值時生效,表示父視圖已經(jīng)決定了子視圖的精確大小,這種模式下View的測量值就是SpecSize的值。

AT_MOST:最大值測量模式,當(dāng)視圖的寬高指定為wrap_content時生效,此時子視圖的尺寸可以是不超過父視圖允許的最大尺寸的任何尺寸。

UNSPECIFIED:不指定測量模式, 父視圖沒有限制子視圖的大小,子視圖可以是想要的任何尺寸,通常用于系統(tǒng)內(nèi)部,應(yīng)用開發(fā)中很少用到。

MeasureSpec通過將SpecMode和SpecSize打包成一個int值來避免過多的對象內(nèi)存分配,為了方便操作,其提供了打包和解包的方法,打包方法為makeMeasureSpec,解包方法為getMode和getSize。

普通View的MeasureSpec的創(chuàng)建規(guī)則如下:


1

對于DecorView而言,它的MeasureSpec由窗口尺寸和其自身的LayoutParams共同決定;對于普通的View,它的MeasureSpec由父視圖的MeasureSpec和其自身的LayoutParams共同決定。

View繪制流程之Measure

首先,在ViewGroup中的measureChildren()方法中會遍歷測量ViewGroup中所有的View,當(dāng)View的可見性處于GONE狀態(tài)時,不對其進行測量。

然后,測量某個指定的View時,根據(jù)父容器的MeasureSpec和子View的LayoutParams等信息計算子View的MeasureSpec。

最后,將計算出的MeasureSpec傳入View的measure方法,這里ViewGroup沒有定義測量的具體過程,因為ViewGroup是一個抽象類,其測量過程的onMeasure方法需要各個子類去實現(xiàn)。不同的ViewGroup子類有不同的布局特性,這導(dǎo)致它們的測量細(xì)節(jié)各不相同,如果需要自定義測量過程,則子類可以重寫這個方法。(setMeasureDimension方法用于設(shè)置View的測量寬高,如果View沒有重寫onMeasure方法,則會默認(rèn)調(diào)用getDefaultSize來獲得View的寬高)

自定義View時手動處理wrap_content時的情形

直接繼承View的控件需要重寫onMeasure方法并設(shè)置wrap_content時的自身大小,否則在布局中使用wrap_content就相當(dāng)于使用match_parent。此時,可以在wrap_content的情況下(對應(yīng)MeasureSpec.AT_MOST)指定內(nèi)部寬/高(mWidth和mHeight)。

View的繪制流程之Layout

首先,會通過setFrame方法來設(shè)定View的四個頂點的位置,即View在父容器中的位置。然后,會執(zhí)行到onLayout空方法,子類如果是ViewGroup類型,則重寫這個方法,實現(xiàn)ViewGroup中所有View控件布局流程。

View的繪制流程之Draw

Draw的基本流程:

繪制基本上可以分為六個步驟:

首先繪制View的背景;

如果需要的話,保持canvas的圖層,為fading做準(zhǔn)備;

然后,繪制View的內(nèi)容;

接著,繪制View的子View;

如果需要的話,繪制View的fading邊緣并恢復(fù)圖層;

最后,繪制View的裝飾(例如滾動條等等)。

invalidate() 和 postInvalidate()、requestLayout的區(qū)別 ?

invalidate()與postInvalidate()都會調(diào)用ondraw()用于刷新View,主要區(qū)別是invalidate()在主線程中調(diào)用,若在子線程中使用需要配合handler;而postInvalidate()可在子線程中直接調(diào)用。

requestLayout會觸發(fā)三大流程。

23、進程間通信IPC

Android中進程和線程的關(guān)系?區(qū)別?

線程是CPU調(diào)度的最小單元,同時線程是一種有限的系統(tǒng)資源;而進程一般指一個執(zhí)行單元,在PC和移動設(shè)備上指一個程序或者一個應(yīng)用。

一般來說,一個App程序至少有一個進程,一個進程至少有一個線程(包含與被包含的關(guān)系),通俗來講就是,在App這個工廠里面有一個進程,線程就是里面的生產(chǎn)線,但主線程(即主生產(chǎn)線)只有一條,而子線程(即副生產(chǎn)線)可以有多個。

進程有自己獨立的地址空間,而進程中的線程共享此地址空間,都可以并發(fā)執(zhí)行。

管道、消息隊列、共享內(nèi)存,Binder

小結(jié):client進程、service進程,serviceManager、Binder驅(qū)動

Binder驅(qū)動主要負(fù)責(zé)建立從client進程內(nèi)核緩沖區(qū)到service進程用戶空間內(nèi)存之間的內(nèi)存映射mmap。

ServiceManager負(fù)責(zé)service進程的注冊以及查找client進程需要的service進程。

Binder需要一次拷貝,管道、消息隊列需要兩次拷貝,共享內(nèi)存不需要拷貝但需要解決進程同步問題。

1

Binder詳情

24、app啟動流程

1、點擊桌面應(yīng)用圖標(biāo),Launcher進程將啟動Activity(MainActivity)的請求以Binder的方式發(fā)送給了AMS。

2、AMS接收到啟動請求后,交付ActivityStarter處理Intent和Flag等信息,然后再交給ActivityStackSupervisior/ActivityStack 處理Activity進棧相關(guān)流程。同時以Socket方式請求Zygote進程fork新進程。

3、Zygote接收到新進程創(chuàng)建請求后fork出新進程。

4、在新進程里創(chuàng)建ActivityThread對象,新創(chuàng)建的進程就是應(yīng)用的主線程,在主線程里開啟Looper消息循環(huán),開始處理創(chuàng)建Activity。

5、ActivityThread利用ClassLoader去加載Activity、創(chuàng)建Activity實例,并回調(diào)Activity的onCreate()方法,這樣便完成了Activity的啟動。

1

啟動流程詳情

25、說下安卓虛擬機和java虛擬機的原理和不同點?(JVM、Davilk、ART三者的原理和區(qū)別)

JVM 和Dalvik虛擬機的區(qū)別

JVM:.java -> javac -> .class -> jar -> .jar

架構(gòu): 堆和棧的架構(gòu).

DVM:.java -> javac -> .class -> dx.bat -> .dex

架構(gòu): 寄存器(cpu上的一塊高速緩存)

Android2個虛擬機的區(qū)別(一個5.0之前,一個5.0之后)

Dalvik

谷歌設(shè)計專用于 Android 平臺的 Java 虛擬機,可直接運行 .dex 文件,適合內(nèi)存和處理速度有限的系統(tǒng)

JVM 指令集是基于棧的;Dalvik 指令集是基于寄存器的,代碼執(zhí)行效率更優(yōu)

ART

Dalvik 每次運行都要將字節(jié)碼轉(zhuǎn)換成機器碼;ART 在應(yīng)用安裝時就會轉(zhuǎn)換成機器碼,執(zhí)行速度更快

ART 存儲機器碼占用空間更大,空間換時間

什么是Dalvik:Dalvik是Google公司自己設(shè)計用于Android平臺的Java虛擬機。Dalvik虛擬機是Google等廠商合作開發(fā)的Android移動設(shè)備平臺的核心組成部分之一,它可以支持已轉(zhuǎn)換為.dex(即Dalvik Executable)格式的Java應(yīng)用程序的運行,.dex格式是專為Dalvik應(yīng)用設(shè)計的一種壓縮格式,適合內(nèi)存和處理器速度有限的系統(tǒng)。Dalvik經(jīng)過優(yōu)化,允許在有限的內(nèi)存中同時運行多個虛擬機的實例,并且每一個Dalvik應(yīng)用作為獨立的Linux進程執(zhí)行。獨立的進程可以防止在虛擬機崩潰的時候所有程序都被關(guān)閉。

什么是ART:Android操作系統(tǒng)已經(jīng)成熟,Google的Android團隊開始將注意力轉(zhuǎn)向一些底層組件,其中之一是負(fù)責(zé)應(yīng)用程序運行的Dalvik運行時。Google開發(fā)者已經(jīng)花了兩年時間開發(fā)更快執(zhí)行效率更高更省電的替代ART運行時。ART代表Android Runtime,其處理應(yīng)用程序執(zhí)行的方式完全不同于Dalvik,Dalvik是依靠一個Just-In-Time(JIT)編譯器去解釋字節(jié)碼。開發(fā)者編譯后的應(yīng)用代碼需要通過一個解釋器在用戶的設(shè)備上運行,這一機制并不高效,但讓應(yīng)用能更容易在不同硬件和架構(gòu)上運行。ART則完全改變了這套做法,在應(yīng)用安裝的時候就預(yù)編譯字節(jié)碼為機器語言,這一機制叫Ahead-Of-Time(AOT)編譯。在移除解釋代碼這一過程后,應(yīng)用程序執(zhí)行將更有效率,啟動更快。

ART優(yōu)點:

系統(tǒng)性能的顯著提升。

應(yīng)用啟動更快、運行更快、體驗更流暢、觸感反饋更及時。

更長的電池續(xù)航能力。

支持更低的硬件。

ART缺點:

更大的存儲空間占用,可能會增加10%-20%。

更長的應(yīng)用安裝時間。

AOT和JIT以及混合編譯的區(qū)別、優(yōu)勢

AOT和JIT是什么?AOT,即Ahead-of-time,指預(yù)先編譯. JIT,即Just-In-Time,指即時編譯.

區(qū)別: 主要區(qū)別在于是否在“運行時”進行編譯.

優(yōu)劣: AOT優(yōu)點:1.在程序運行前編譯,可以避免在運行時的編譯性能消耗和內(nèi)存消耗. 2.可以在程序運行初期就達到最高性能. 3.可以顯著的加快程序的啟動. AOT缺點:1.在程序運行前編譯會使程序安裝的時間增加. 2.犧牲Java的一致性. 3.將提前編譯的內(nèi)容保存會占用更多的外存.

JIT優(yōu)點:1.可以根據(jù)當(dāng)前硬件情況實時編譯生成最優(yōu)機器指令(ps:AOT也可以做到,在用戶使用是使用字節(jié)碼根據(jù)機器情況在做一次編譯). 2.可以根據(jù)當(dāng)前程序的運行情況生成最優(yōu)的機器指令序列. 3.當(dāng)程序需要支持動態(tài)鏈接時,只能使用JIT. 4.可以根據(jù)進程中內(nèi)存的實際情況調(diào)整代碼,使內(nèi)存能夠更充分的利用. JIT缺點:1.編譯需要占用運行時資源,會導(dǎo)致進程卡頓. 2.由于編譯時間需要占用運行時間,對于某些代碼的編譯優(yōu)化不能完全支持,需要在程序流暢和編譯時間之間做權(quán)衡. 3.在編譯準(zhǔn)備和識別頻繁使用的方法需要占用時間,使得初始編譯不能達到最高性能.

混合編譯: Android N引入了使用編譯+解釋+JIT的混合運行時,以獲得安裝時間,內(nèi)存占用,電池消耗和性能之間的最佳折衷. 優(yōu)點: 即使是大型應(yīng)用程序的安裝時間也減少到幾秒鐘. 系統(tǒng)更新安裝得更快,因為它們不需要優(yōu)化步驟. 應(yīng)用程序的RAM占用空間較小,在某些情況下降至50%. 改善了表現(xiàn). 降低電池消耗.

25、一個圖片在app中調(diào)用R.id后是如何找到的?

在編譯的時候,AAPT會掃描你所定義的所有資源(在不同文件中定義的以及單獨的資源文件),然后給它們指定不同的資源ID。

資源ID 是一個32bit的數(shù)字,格式是PPTTNNNN , PP代表資源所屬的包(package) ,TT代表資源的類型(type),NNNN代表這個類型下面的資源的名稱。 對于應(yīng)用程序的資源來說,PP的取值是0×77。

TT 和NNNN 的取值是由AAPT工具隨意指定的–基本上每一種新的資源類型的數(shù)字都是從上一個數(shù)字累加的(從1開始);而每一個新的資源條目也是從數(shù)字1開始向上累加的。

所以如果我們的這幾個資源文件按照下面的順序排列,AAPT會依次處理:

?<code>layout/main.xml </code>

<code>drawable/icon.xml </code>

<code>layout/listitem.xml</code>

按照順序,第一個資源的類型是”layout” 所以指定TT==1, 這個類型下面的第一個資源是”main” ,所以指定NNNN==1 ,最后這個資源就是0x7f010001。

第二個資源類型是”drawable”,所以指定TT==2,這個類型下的”icon” 指定NNNN ==1,所以最終的資源ID 是 0x7f020001。

第三個資源類型是”layout”,而這個資源類型在前面已經(jīng)有定義了,所以TT仍然是1,但是”listitem”這個名字是新出現(xiàn)的,所以指定NNNN==2,因此最終的資源ID 就是 0x7f010002。

注意的是,AAPT在每一次編譯的時候不會去保存上一次生成的資源ID標(biāo)示,每當(dāng)/res目錄發(fā)生變化的時候,AAPT可能會去重新給資源指定ID號,然后重新生成一個R.java文件。因此,在做開發(fā)的時候,你不應(yīng)該在程序中將資源ID持久化保存到文件或者數(shù)據(jù)庫。而資源ID在每一次編譯后都有可能變化。

一旦資源被編譯成二進制文件的時候,AAPT會生成R.java 文件和“resources.arsc”文件,“R.java”用于代碼的編譯,而”resources.arsc”則包含了全部的資源名稱、資源ID和資源的內(nèi)容(對于單獨文件類型的資源,這個內(nèi)容代表的是這個文件在其.apk 文件中的路徑信息)。這樣就把運行環(huán)境中的資源ID 和具體的資源對應(yīng)起來了。

26、Recycleview和ListView緩存區(qū)別

ListView的緩存機制相對比較好理解,它只有兩級緩存,一級緩存Active View是負(fù)責(zé)屏幕內(nèi)的ItemView快速復(fù)用,而Scrap View是緩存屏幕外的數(shù)據(jù),當(dāng)該數(shù)據(jù)從屏幕外滑動到屏幕內(nèi)的時候需要走一遍getView()方法。

ListView有兩級緩存,分別是Active View和Scrap View,緩存的對象是ItemView;而RecyclerView有四級緩存,分別是Scrap、Cache、ViewCacheExtension和RecycledViewPool,緩存的對象是ViewHolder。Scrap和Cache分別是通過position去找ViewHolder可以直接復(fù)用;ViewCacheExtension自定義緩存,目前來說應(yīng)用場景比較少卻需慎用;RecycledViewPool通過type來獲取ViewHolder,獲取的ViewHolder是個全新,需要重新綁定數(shù)據(jù)。

詳情

27、非UI線程可以更新UI嗎?

可以,當(dāng)訪問UI時,ViewRootImpl會調(diào)用checkThread方法去檢查當(dāng)前訪問UI的線程是哪個,如果不是UI線程則會拋出異常。執(zhí)行onCreate方法的那個時候ViewRootImpl還沒創(chuàng)建,無法去檢查當(dāng)前線程.ViewRootImpl的創(chuàng)建在onResume方法回調(diào)之后。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Context繼承關(guān)系A(chǔ)ctivity/Service/Application都是繼承自ContextWrappe...
    tt2019閱讀 271評論 0 0
  • Handler handler在android應(yīng)用 1.在卡頓監(jiān)測會用到消息機制;主要是發(fā)送一個延時消息來監(jiān)測是否...
    hom03閱讀 743評論 0 0
  • 描述清點擊 Android Studio 的 build 按鈕后發(fā)生了什么 build[https://jueji...
    CHSmile閱讀 802評論 0 1
  • 抽象類和接口的區(qū)別 1.接口是行為的抽象,是一種行為的規(guī)范,接口是like a 的關(guān)系;抽象是對類的抽象,是一種模...
    任振銘閱讀 480評論 0 4
  • 博文博文 一、Activity 1.什么是activity? Activity一個應(yīng)用程序的組件,它提供一個屏幕來...
    瓦雷閱讀 461評論 0 3

友情鏈接更多精彩內(nèi)容