2018Android中高級面試題總結
雖然本人不搞Android了,但是對於Android還是時常關注的,這裡根據網上的資料對2018的Android面試做一個總結。
1、Activity生命周期? Android的生命周期主要有七個,按其創建到銷毀主要有以下幾個階段:onCreate() -> onStart() -> onResume() -> onPause() -> onStop() -> onDetroy()2、Service生命周期?
service 啟動方式有兩種,一種是通過startService()方式進行啟動,另一種是通過bindService()方式進行啟動。不同的啟動方式他們的生命周期是不一樣.
通過startService()這種方式啟動的service,生命周期是這樣:調用startService() –> onCreate()–> onStartConmon()–> onDestroy()。這種方式啟動的話,需要注意一下幾個問題,第一:當我們通過startService被調用以後,多次在調用startService(),onCreate()方法也只會被調用一次,而onStartConmon()會被多次調用當我們調用stopService()的時候,onDestroy()就會被調用,從而銷毀服務。第二:當我們通過startService啟動時候,通過intent傳值,在onStartConmon()方法中獲取值的時候,一定要先判斷intent是否為null。
通過bindService()方式進行綁定,這種方式綁定service,生命周期走法:bindService–>onCreate()–>onBind()–>unBind()–>onDestroy() bingservice 這種方式進行啟動service好處是更加便利activity中操作service,比如加入service中有幾個方法,a,b ,如果要在activity中調用,在需要在activity獲取ServiceConnection對象,通過ServiceConnection來獲取service中內部類的類對象,然後通過這個類對象就可以調用類中的方法,當然這個類需要繼承Binder對象。
3、Activity的啟動過程
app啟動的過程有兩種情況,第一種是從桌面launcher上點擊相應的應用圖標,第二種是在activity中通過調用startActivity來啟動一個新的activity。
我們創建一個新的項目,默認的根activity都是MainActivity,而所有的activity都是保存在堆棧中的,我們啟動一個新的activity就會放在上一個activity上面,而我們從桌面點擊應用圖標的時候,由於launcher本身也是一個應用,當我們點擊圖標的時候,系統就會調用startActivitySately(),一般情況下,我們所啟動的activity的相關信息都會保存在intent中,比如action,category等等。
我們在安裝這個應用的時候,系統也會啟動一個PackaManagerService的管理服務,這個管理服務會對AndroidManifest.xml文件進行解析,從而得到應用程序中的相關信息,比如service,activity,Broadcast等等,然後獲得相關組件的信息。當我們點擊應用圖標的時候,就會調用startActivitySately()方法,而這個方法內部則是調用startActivty(),而startActivity()方法最終還是會調用startActivityForResult()這個方法。而在startActivityForResult()這個方法。因為startActivityForResult()方法是有返回結果的,所以系統就直接給一個-1,就表示不需要結果返回了。
而startActivityForResult()這個方法實際是通過Instrumentation類中的execStartActivity()方法來啟動activity,Instrumentation這個類主要作用就是監控程序和系統之間的交互。而在這個execStartActivity()方法中會獲取ActivityManagerService的代理對象,通過這個代理對象進行啟動activity。啟動會就會調用一個checkStartActivityResult()方法,如果說沒有在配置清單中配置有這個組件,就會在這個方法中拋出異常了。
當然最後是調用的是Application.scheduleLaunchActivity()進行啟動activity,而這個方法中通過獲取得到一個ActivityClientRecord對象,而這個ActivityClientRecord通過handler來進行消息的發送,系統內部會將每一個activity組件使用ActivityClientRecord對象來進行描述,而ActivityClientRecord對象中保存有一個LoaderApk對象,通過這個對象調用handleLaunchActivity來啟動activity組件,而頁面的生命周期方法也就是在這個方法中進行調用。
4、Broadcast註冊方式與區別?什麼情況下用動態註冊?
Broadcast廣播,註冊方式主要有兩種.
第一種是靜態註冊,也可成為常駐型廣播,這種廣播需要在Androidmanifest.xml中進行註冊,這中方式註冊的廣播,不受頁面生命周期的影響,即使退出了頁面,也可以收到廣播這種廣播一般用於想開機自啟動啊等等,由於這種註冊的方式的廣播是常駐型廣播,所以會佔用CPU的資源。
第二種是動態註冊,而動態註冊的話,是在代碼中註冊的,這種註冊方式也叫非常駐型廣播,收到生命周期的影響,退出頁面後,就不會收到廣播,我們通常運用在更新UI方面。這種註冊方式優先順序較高。最後需要解綁,否會會內存泄露
廣播是分為有序廣播和無序廣播。
5、HttpClient與HttpUrlConnection的區別
此處延伸:Volley里用的哪種請求方式(2.3前HttpClient,2.3後HttpUrlConnection)
首先HttpClient和HttpUrlConnection 這兩種方式都支持Https協議,都是以流的形式進行上傳或者下載數據,也可以說是以流的形式進行數據的傳輸,還有ipv6,以及連接池等功能。HttpClient這個擁有非常多的API,所以如果想要進行擴展的話,並且不破壞它的兼容性的話,很難進行擴展,也就是這個原因,Google在Android6.0的時候,直接就棄用了這個HttpClient.
而HttpUrlConnection相對來說就是比較輕量級了,API比較少,容易擴展,並且能夠滿足Android大部分的數據傳輸。比較經典的一個框架volley,在2.3版本以前都是使用HttpClient,在2.3以後就使用了HttpUrlConnection。
6、java虛擬機和Dalvik虛擬機的區別
Java虛擬機:
1、java虛擬機基於棧。 基於棧的機器必須使用指令來載入和操作棧上數據,所需指令更多更多。
2、java虛擬機運行的是java位元組碼。(java類會被編譯成一個或多個位元組碼.class文件)
Dalvik虛擬機:
1、dalvik虛擬機是基於寄存器的
2、Dalvik運行的是自定義的.dex位元組碼格式。(java類被編譯成.class文件後,會通過一個dx工具將所有的.class文件轉換成一個.dex文件,然後dalvik虛擬機會從其中讀取指令和數據
3、常量池已被修改為只使用32位的索引,以簡化解釋器。
4、一個應用,一個虛擬機實例,一個進程(所有android應用的線程都是對應一個linux線程,都運行在自己的沙盒中,不同的應用在不同的進程中運行。每個android dalvik應用程序都被賦予了一個獨立的linux PID(app_*))
7、系統怎麼進程保活(不死進程)
此處延伸:進程的優先順序是什麼
當前業界的Android進程保活手段主要分為* 黑、白、灰 *三種,其大致的實現思路如下:
黑色保活:不同的app進程,用廣播相互喚醒(包括利用系統提供的廣播進行喚醒)
白色保活:啟動前台Service
灰色保活:利用系統的漏洞啟動前台Service
黑色保活
所謂黑色保活,就是利用不同的app進程使用廣播來進行相互喚醒。舉個3個比較常見的場景:
場景1:開機,網路切換、拍照、拍視頻時候,利用系統產生的廣播喚醒app
場景2:接入第三方SDK也會喚醒相應的app進程,如微信sdk會喚醒微信,支付寶sdk會喚醒支付寶。由此發散開去,就會直接觸發了下面的 場景3
場景3:假如你手機里裝了支付寶、淘寶、天貓、UC等阿里系的app,那麼你打開任意一個阿里系的app後,有可能就順便把其他阿里系的app給喚醒了。(只是拿阿里打個比方,其實BAT系都差不多)
白色保活
白色保活手段非常簡單,就是調用系統api啟動一個前台的Service進程,這樣會在系統的通知欄生成一個Notification,用來讓用戶知道有這樣一個app在運行著,哪怕當前的app退到了後台。如下方的LBE和QQ音樂這樣:
灰色保活
灰色保活,這種保活手段是應用範圍最廣泛。它是利用系統的漏洞來啟動一個前台的Service進程,與普通的啟動方式區別在於,它不會在系統通知欄處出現一個Notification,看起來就如同運行著一個後台Service進程一樣。這樣做帶來的好處就是,用戶無法察覺到你運行著一個前台進程(因為看不到Notification),但你的進程優先順序又是高於普通後台進程的。那麼如何利用系統的漏洞呢,大致的實現思路和代碼如下:
思路一:API < 18,啟動前台Service時直接傳入new Notification();
思路二:API >= 18,同時啟動兩個id相同的前台Service,然後再將後啟動的Service做stop處理
熟悉Android系統的童鞋都知道,系統出於體驗和性能上的考慮,app在退到後台時系統並不會真正的kill掉這個進程,而是將其緩存起來。打開的應用越多,後台緩存的進程也越多。在系統內存不足的情況下,系統開始依據自身的一套進程回收機制來判斷要kill掉哪些進程,以騰出內存來供給需要的app。這套殺進程回收內存的機制就叫 Low Memory Killer ,它是基於Linux內核的 OOM Killer(Out-Of-Memory killer)機制誕生。
進程的重要性,劃分5級:
前台進程 (Foreground process)
可見進程 (Visible process)
服務進程 (Service process)
後台進程 (Background process)
空進程 (Empty process)
了解完 Low Memory Killer,再科普一下oom_adj。什麼是oom_adj?它是linux內核分配給每個系統進程的一個值,代表進程的優先順序,進程回收機制就是根據這個優先順序來決定是否進行回收。對於oom_adj的作用,你只需要記住以下幾點即可:
進程的oom_adj越大,表示此進程優先順序越低,越容易被殺回收;越小,表示進程優先順序越高,越不容易被殺回收
普通app進程的oom_adj>=0,系統進程的oom_adj才可能<0
有些手機廠商把這些知名的app放入了自己的白名單中,保證了進程不死來提高用戶體驗(如微信、QQ、陌陌都在小米的白名單中)。如果從白名單中移除,他們終究還是和普通app一樣躲避不了被殺的命運,為了盡量避免被殺,還是老老實實去做好優化工作吧。
所以,進程保活的根本方案終究還是回到了性能優化上,進程永生不死終究是個徹頭徹尾的偽命題!
8、講解一下Context
Context是一個抽象基類。在翻譯為上下文,也可以理解為環境,是提供一些程序的運行環境基礎信息。Context下有兩個子類,ContextWrapper是上下文功能的封裝類,而ContextImpl則是上下文功能的實現類。而ContextWrapper又有三個直接的子類, ContextThemeWrapper、Service和Application。其中,ContextThemeWrapper是一個帶主題的封裝類,而它有一個直接子類就是Activity,所以Activity和Service以及Application的Context是不一樣的,只有Activity需要主題,Service不需要主題。
Context一共有三種類型,分別是Application、Activity和Service。這三個類雖然分別各種承擔著不同的作用,但它們都屬於Context的一種,而它們具體Context的功能則是由ContextImpl類去實現的,因此在絕大多數場景下,Activity、Service和Application這三種類型的Context都是可以通用的。不過有幾種場景比較特殊,比如啟動Activity,還有彈出Dialog。出於安全原因的考慮,Android是不允許Activity或Dialog憑空出現的,一個Activity的啟動必須要建立在另一個Activity的基礎之上,也就是以此形成的返回棧。而Dialog則必須在一個Activity上面彈出(除非是System Alert類型的Dialog),因此在這種場景下,我們只能使用Activity類型的Context,否則將會出錯。
getApplicationContext()和getApplication()方法得到的對象都是同一個application對象,只是對象的類型不一樣。
Context數量 = Activity數量 + Service數量 + 1 (1為Application)
9、理解Activity,View,Window三者關係
這個問題真的很不好回答。所以這裡先來個算是比較恰當的比喻來形容下它們的關係吧。Activity像一個工匠(控制單元),Window像窗戶(承載模型),View像窗花(顯示視圖)LayoutInflater像剪刀,Xml配置像窗花圖紙。
1:Activity構造的時候會初始化一個Window,準確的說是PhoneWindow。
2:這個PhoneWindow有一個「ViewRoot」,這個「ViewRoot」是一個View或者說ViewGroup,是最初始的根視圖。
3:「ViewRoot」通過addView方法來一個個的添加View。比如TextView,Button等
4:這些View的事件監聽,是由WindowManagerService來接受消息,並且回調Activity函數。比如onClickListener,onKeyDown等。
10、四種LaunchMode及其使用場景
此處延伸:棧(First In Last Out)與隊列(First In First Out)的區別
棧與隊列的區別:
- 隊列先進先出,棧先進後出
- 對插入和刪除操作的」限定」。 棧是限定只能在表的一端進行插入和刪除操作的線性表。 隊列是限定只能在表的一端進行插入和在另一端進行刪除操作的線性表。
- 遍曆數據速度不同
standard 模式
這是默認模式,每次激活Activity時都會創建Activity實例,並放入任務棧中。使用場景:大多數Activity。
singleTop 模式
如果在任務的棧頂正好存在該Activity的實例,就重用該實例( 會調用實例的 onNewIntent() ),否則就會創建新的實例並放入棧頂,即使棧中已經存在該Activity的實例,只要不在棧頂,都會創建新的實例。使用場景如新聞類或者閱讀類App的內容頁面。
singleTask 模式
如果在棧中已經有該Activity的實例,就重用該實例(會調用實例的 onNewIntent() )。重用時,會讓該實例回到棧頂,因此在它上面的實例將會被移出棧。如果棧中不存在該實例,將會創建新的實例放入棧中。使用場景如瀏覽器的主界面。不管從多少個應用啟動瀏覽器,只會啟動主界面一次,其餘情況都會走onNewIntent,並且會清空主界面上面的其他頁面。
singleInstance 模式
在一個新棧中創建該Activity的實例,並讓多個應用共享該棧中的該Activity實例。一旦該模式的Activity實例已經存在於某個棧中,任何應用再激活該Activity時都會重用該棧中的實例( 會調用實例的 onNewIntent() )。其效果相當於多個應用共享一個應用,不管誰激活該 Activity 都會進入同一個應用中。使用場景如鬧鈴提醒,將鬧鈴提醒與鬧鈴設置分離。singleInstance不要用於中間頁面,如果用於中間頁面,跳轉會有問題,比如:A -> B (singleInstance) -> C,完全退出後,在此啟動,首先打開的是B。
11、簡述View的繪製流程
自定義控制項:
1、組合控制項。這種自定義控制項不需要我們自己繪製,而是使用原生控制項組合成的新控制項。如標題欄。
2、繼承原有的控制項。這種自定義控制項在原生控制項提供的方法外,可以自己添加一些方法。如製作圓角,圓形圖片。
3、完全自定義控制項:這個View上所展現的內容全部都是我們自己繪製出來的。比如說製作水波紋進度條。
View的繪製流程:OnMeasure()——>OnLayout()——>OnDraw()
第一步:OnMeasure():測量視圖大小。從頂層父View到子View遞歸調用measure方法,measure方法又回調OnMeasure。
第二步:OnLayout():確定View位置,進行頁面布局。從頂層父View向子View的遞歸調用view.layout方法的過程,即父View根據上一步measure子View所得到的布局大小和布局參數,將子View放在合適的位置上。
第三步:OnDraw():繪製視圖。ViewRoot創建一個Canvas對象,然後調用OnDraw()。六個步驟:①、繪製視圖的背景;②、保存畫布的圖層(Layer);③、繪製View的內容;④、繪製View子視圖,如果沒有就不用;
⑤、還原圖層(Layer);⑥、繪製滾動條。
12、View,ViewGroup事件分發
- Touch事件分發中只有兩個主角:ViewGroup和View。ViewGroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent三個相關事件。View包含dispatchTouchEvent、onTouchEvent兩個相關事件。其中ViewGroup又繼承於View。
2.ViewGroup和View組成了一個樹狀結構,根節點為Activity內部包含的一個ViwGroup。
3.觸摸事件由Action_Down、Action_Move、Aciton_UP組成,其中一次完整的觸摸事件中,Down和Up都只有一個,Move有若干個,可以為0個。
4.當Acitivty接收到Touch事件時,將遍歷子View進行Down事件的分發。ViewGroup的遍歷可以看成是遞歸的。分發的目的是為了找到真正要處理本次完整觸摸事件的View,這個View會在onTouchuEvent結果返回true。
5.當某個子View返回true時,會中止Down事件的分發,同時在ViewGroup中記錄該子View。接下去的Move和Up事件將由該子View直接進行處理。由於子View是保存在ViewGroup中的,多層ViewGroup的節點結構時,上級ViewGroup保存的會是真實處理事件的View所在的ViewGroup對象:如ViewGroup0-ViewGroup1-TextView的結構中,TextView返回了true,它將被保存在ViewGroup1中,而ViewGroup1也會返回true,被保存在ViewGroup0中。當Move和UP事件來時,會先從ViewGroup0傳遞至ViewGroup1,再由ViewGroup1傳遞至TextView。
6.當ViewGroup中所有子View都不捕獲Down事件時,將觸發ViewGroup自身的onTouch事件。觸發的方式是調用super.dispatchTouchEvent函數,即父類View的dispatchTouchEvent方法。在所有子View都不處理的情況下,觸發Acitivity的onTouchEvent方法。
7.onInterceptTouchEvent有兩個作用:1.攔截Down事件的分發。2.中止Up和Move事件向目標View傳遞,使得目標View所在的ViewGroup捕獲Up和Move事件。
13、保存Activity狀態
onSaveInstanceState(Bundle)會在activity轉入後台狀態之前被調用,也就是onStop()方法之前,onPause方法之後被調用;
14、Android中的幾種動畫
幀動畫:指通過指定每一幀的圖片和播放時間,有序的進行播放而形成動畫效果,比如想聽的律動條。
補間動畫:指通過指定View的初始狀態、變化時間、方式,通過一系列的演算法去進行圖形變換,從而形成動畫效果,主要有Alpha、Scale、Translate、Rotate四種效果。注意:只是在視圖層實現了動畫效果,並沒有真正改變View的屬性,比如滑動列表,改變標題欄的透明度。
屬性動畫:在Android3.0的時候才支持,通過不斷的改變View的屬性,不斷的重繪而形成動畫效果。相比於視圖動畫,View的屬性是真正改變了。比如view的旋轉,放大,縮小。
15、Android中跨進程通訊的幾種方式
Android 跨進程通信,像intent,contentProvider,廣播,service,Socket都可以跨進程通信。intent:這種跨進程方式並不是訪問內存的形式,它需要傳遞一個uri,比如說打電話。
contentProvider:這種形式,是使用數據共享的形式進行數據共享。
service:遠程服務,aidl
廣播
16、AIDL理解,並簡述Binder機制
AIDL: 每一個進程都有自己的Dalvik VM實例,都有自己的一塊獨立的內存,都在自己的內存上存儲自己的數據,執行著自己的操作,都在自己的那片狹小的空間里過完自己的一生。而aidl就類似與兩個進程之間的橋樑,使得兩個進程之間可以進行數據的傳輸,跨進程通信有多種選擇,比如 BroadcastReceiver , Messenger 等,但是 BroadcastReceiver 佔用的系統資源比較多,如果是頻繁的跨進程通信的話顯然是不可取的;Messenger 進行跨進程通信時請求隊列是同步進行的,無法並發執行。
Binder機制簡單理解:
在Android系統的Binder機制中,是有Client,Service,ServiceManager,Binder驅動程序組成的,其中Client,service,Service Manager運行在用戶空間,Binder驅動程序是運行在內核空間的。而Binder就是把這4種組件粘合在一塊的粘合劑,其中核心的組件就是Binder驅動程序,Service Manager提供輔助管理的功能,而Client和Service正是在Binder驅動程序和Service Manager提供的基礎設施上實現C/S 之間的通信。其中Binder驅動程序提供設備文件/dev/binder與用戶控制項進行交互,
Client、Service,Service Manager通過open和ioctl文件操作相應的方法與Binder驅動程序進行通信。而Client和Service之間的進程間通信是通過Binder驅動程序間接實現的。而Binder Manager是一個守護進程,用來管理Service,並向Client提供查詢Service介面的能力。
17、Handler的原理
Android中主線程是不能進行耗時操作的,子線程是不能進行更新UI的。所以就有了handler,它的作用就是實現線程之間的通信。
handler整個流程中,主要有四個對象,handler,Message,MessageQueue,Looper。當應用創建的時候,就會在主線程中創建handler對象,
我們通過要傳送的消息保存到Message中,handler通過調用sendMessage方法將Message發送到MessageQueue中,Looper對象就會不斷的調用loop()方法
不斷的從MessageQueue中取出Message交給handler進行處理。從而實現線程之間的通信。
18、Binder機制原理
在Android系統的Binder機制中,是有Client,Service,ServiceManager,Binder驅動程序組成的,其中Client,service,Service Manager運行在用戶空間,Binder驅動程序是運行在內核空間的。而Binder就是把這4種組件粘合在一塊的粘合劑,其中核心的組件就是Binder驅動程序,Service Manager提供輔助管理的功能,而Client和Service正是在Binder驅動程序和Service Manager提供的基礎設施上實現C/S 之間的通信。其中Binder驅動程序提供設備文件/dev/binder與用戶控制項進行交互,Client、Service,Service Manager通過open和ioctl文件操作相應的方法與Binder驅動程序進行通信。而Client和Service之間的進程間通信是通過Binder驅動程序間接實現的。而Binder Manager是一個守護進程,用來管理Service,並向Client提供查詢Service介面的能力。
19、熱修復的原理
我們知道Java虛擬機 —— JVM 是載入類的class文件的,而Android虛擬機——Dalvik/ART VM 是載入類的dex文件,
而他們載入類的時候都需要ClassLoader,ClassLoader有一個子類BaseDexClassLoader,而BaseDexClassLoader下有一個數組——DexPathList,是用來存放dex文件,當BaseDexClassLoader通過調用findClass方法時,實際上就是遍曆數組,
找到相應的dex文件,找到,則直接將它return。而熱修復的解決方法就是將新的dex添加到該集合中,並且是在舊的dex的前面,
所以就會優先被取出來並且return返回。
當然除了這種方式外,還有Instant run等方案,請大家自行查找資料學習。
20、Android內存泄露及管理
(1)內存溢出(OOM)和內存泄露(對象無法被回收)的區別。
(2)引起內存泄露的原因
(3) 內存泄露檢測工具 ——>LeakCanary
內存溢出 out of memory:是指程序在申請內存時,沒有足夠的內存空間供其使用,出現out of memory;比如申請了一個integer,但給它存了long才能存下的數,那就是內存溢出。內存溢出通俗的講就是內存不夠用。
內存泄露 memory leak:是指程序在申請內存後,無法釋放已申請的內存空間,一次內存泄露危害可以忽略,但內存泄露堆積後果很嚴重,無論多少內存,遲早會被佔光
內存泄露原因:
一、Handler 引起的內存泄漏。
解決:將Handler聲明為靜態內部類,就不會持有外部類SecondActivity的引用,其生命周期就和外部類無關,
如果Handler裡面需要context的話,可以通過弱引用方式引用外部類
二、單例模式引起的內存泄漏。
解決:Context是ApplicationContext,由於ApplicationContext的生命周期是和app一致的,不會導致內存泄漏
三、非靜態內部類創建靜態實例引起的內存泄漏。
解決:把內部類修改為靜態的就可以避免內存泄漏了
四、非靜態匿名內部類引起的內存泄漏。
解決:將匿名內部類設置為靜態的。
五、註冊/反註冊未成對使用引起的內存泄漏。
註冊廣播接受器、EventBus等,記得解綁。
六、資源對象沒有關閉引起的內存泄漏。
在這些資源不使用的時候,記得調用相應的類似close()、destroy()、recycler()、release()等方法釋放。
七、集合對象沒有及時清理引起的內存泄漏。
通常會把一些對象裝入到集合中,當不使用的時候一定要記得及時清理集合,讓相關對象不再被引用。
21、Fragment與Fragment、Activity通信的方式
1.直接在一個Fragment中調用另外一個Fragment中的方法
2.使用介面回調
3.使用廣播
4.Fragment直接調用Activity中的public方法
22、Android UI適配
字體使用sp,使用dp,多使用match_parent,wrap_content,weight
圖片資源,不同圖片的的解析度,放在相應的文件夾下可使用百分比代替。
23、app優化
app優化:(工具:Hierarchy Viewer 分析布局 工具:TraceView 測試分析耗時的),app優化主要從以下幾個方面展開:
App啟動優化
布局優化
響應優化
內存優化
電池使用優化
網路優化
App啟動優化(針對冷啟動)
App啟動的方式有三種:
冷啟動:App沒有啟動過或App進程被killed, 系統中不存在該App進程, 此時啟動App即為冷啟動。
熱啟動:熱啟動意味著你的App進程只是處於後台, 系統只是將其從後台帶到前台, 展示給用戶。
介於冷啟動和熱啟動之間, 一般來說在以下兩種情況下發生:
(1)用戶back退出了App, 然後又啟動. App進程可能還在運行, 但是activity需要重建。
(2)用戶退出App後, 系統可能由於內存原因將App殺死, 進程和activity都需要重啟, 但是可以在onCreate中將被動殺死鎖保存的狀態(saved instance state)恢復。
優化:
Application的onCreate(特別是第三方SDK初始化),首屏Activity的渲染都不要進行耗時操作,如果有,就可以放到子線程或者IntentService中
布局優化
盡量不要過於複雜的嵌套。可以使用,,
響應優化
Android系統每隔16ms會發出VSYNC信號重繪我們的界面(Activity)。
頁面卡頓的原因:
(1)過於複雜的布局.
(2)UI線程的複雜運算
(3)頻繁的GC,導致頻繁GC有兩個原因:1、內存抖動, 即大量的對象被創建又在短時間內馬上被釋放.2、瞬間產生大量的對象會嚴重佔用內存區域。
內存優化:參考內存泄露和內存溢出部分
電池使用優化(使用工具:Batterystats & bugreport)
(1)優化網路請求
(2)定位中使用GPS, 請記得及時關閉
網路優化(網路連接對用戶的影響:流量,電量,用戶等待)可在Android studio下方logcat旁邊那個工具Network Monitor檢測
API設計:App與Server之間的API設計要考慮網路請求的頻次, 資源的狀態等. 以便App可以以較少的請求來完成業務需求和界面的展示.
Gzip壓縮:使用Gzip來壓縮request和response, 減少傳輸數據量, 從而減少流量消耗.
圖片的Size:可以在獲取圖片時告知伺服器需要的圖片的寬高, 以便伺服器給出合適的圖片, 避免浪費.
網路緩存:適當的緩存, 既可以讓我們的應用看起來更快, 也能避免一些不必要的流量消耗。
24、圖片優化
(1)對圖片本身進行操作。盡量不要使用setImageBitmap、setImageResource、BitmapFactory.decodeResource來設置一張大圖,因為這些方法在完成decode後,
最終都是通過java層的createBitmap來完成的,需要消耗更多內存.
(2)圖片進行縮放的比例,SDK中建議其值是2的指數值,值越大會導致圖片不清晰。
(3)不用的圖片記得調用圖片的recycle()方法。
25、HybridApp WebView和JS交互
Android與JS通過WebView互相調用方法,實際上是:
Android去調用JS的代碼
- 通過WebView的loadUrl(),使用該方法比較簡潔,方便。但是效率比較低,獲取返回值比較困難。
- 通過WebView的evaluateJavascript(),該方法效率高,但是4.4以上的版本才支持,4.4以下版本不支持。所以建議兩者混合使用。
JS去調用Android的代碼
- 通過WebView的addJavascriptInterface()進行對象映射 ,該方法使用簡單,僅將Android對象和JS對象映射即可,但是存在比較大的漏洞。漏洞產生原因是:當JS拿到Android這個對象後,就可以調用這個Android對象中所有的方法,包括系統類(java.lang.Runtime 類),從而進行任意代碼執行。
解決方式:
(1)Google 在Android 4.2 版本中規定對被調用的函數以 @JavascriptInterface進行註解從而避免漏洞攻擊。
(2)在Android 4.2版本之前採用攔截prompt()進行漏洞修復。
- 通過 WebViewClient 的shouldOverrideUrlLoading ()方法回調攔截 url 。這種方式的優點:不存在方式1的漏洞;缺點:JS獲取Android方法的返回值複雜。(ios主要用的是這個方式)
(1)Android通過 WebViewClient 的回調方法shouldOverrideUrlLoading ()攔截 url
(2)解析該 url 的協議
(3)如果檢測到是預先約定好的協議,就調用相應方法
- 通過 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回調攔截JS對話框alert()、confirm()、prompt() 消息
這種方式的優點:不存在方式1的漏洞;缺點:JS獲取Android方法的返回值複雜。
26、Universal-ImageLoader,Picasso,Fresco,Glide對比
Fresco 是 Facebook 推出的開源圖片緩存工具,主要特點包括:兩個內存緩存加上 Native 緩存構成了三級緩存。
優點:
- 圖片存儲在安卓系統的匿名共享內存, 而不是虛擬機的堆內存中, 圖片的中間緩衝數據也存放在本地堆內存, 所以, 應用程序有更多的內存使用, 不會因為圖片載入而導致oom, 同時也減少垃圾回收器頻繁調用回收 Bitmap 導致的界面卡頓, 性能更高。
- 漸進式載入 JPEG 圖片, 支持圖片從模糊到清晰載入。
- 圖片可以以任意的中心點顯示在 ImageView, 而不僅僅是圖片的中心。
- JPEG 圖片改變大小也是在 native 進行的, 不是在虛擬機的堆內存, 同樣減少 OOM。
- 很好的支持 GIF 圖片的顯示。
缺點:
- 框架較大, 影響 Apk 體積
- 使用較繁瑣
Universal-ImageLoader:(估計由於HttpClient被Google放棄,作者就放棄維護這個框架)
優點:
1.支持下載進度監聽
2.可以在 View 滾動中暫停圖片載入,通過 PauseOnScrollListener 介面可以在 View 滾動中暫停圖片載入。
3.默認實現多種內存緩存演算法 這幾個圖片緩存都可以配置緩存演算法,不過 ImageLoader 默認實現了較多緩存演算法,如 Size 最大先刪除、使用最少先刪除、最近最少使用、先進先刪除、時間最長先刪除等。
4.支持本地緩存文件名規則定義
Picasso 優點
- 自帶統計監控功能。支持圖片緩存使用的監控,包括緩存命中率、已使用內存大小、節省的流量等。
2.支持優先順序處理。每次任務調度前會選擇優先順序高的任務,比如 App 頁面中 Banner 的優先順序高於 Icon 時就很適用。
3.支持延遲到圖片尺寸計算完成載入
4.支持飛行模式、並發線程數根據網路類型而變。 手機切換到飛行模式或網路類型變換時會自動調整線程池最大並發數,比如 wifi 最大並發為 4,4g 為 3,3g 為 2。 這裡 Picasso 根據網路類型來決定最大並發數,而不是 CPU 核數。
5.「無」本地緩存。無」本地緩存,不是說沒有本地緩存,而是 Picasso 自己沒有實現,交給了 Square 的另外一個網路庫 okhttp 去實現,這樣的好處是可以通過請求 Response Header 中的 Cache-Control 及 Expired 控制圖片的過期時間。
Glide 優點
- 不僅僅可以進行圖片緩存還可以緩存媒體文件。Glide 不僅是一個圖片緩存,它支持 Gif、WebP、縮略圖。甚至是 Video,所以更該當做一個媒體緩存。
- 支持優先順序處理。
- 與 Activity/Fragment 生命周期一致,支持 trimMemory。Glide 對每個 context 都保持一個 RequestManager,通過 FragmentTransaction 保持與 Activity/Fragment 生命周期一致,並且有對應的 trimMemory 介面實現可供調用。
- 支持 okhttp、Volley。Glide 默認通過 UrlConnection 獲取數據,可以配合 okhttp 或是 Volley 使用。實際 ImageLoader、Picasso 也都支持 okhttp、Volley。
- 內存友好。Glide 的內存緩存有個 active 的設計,從內存緩存中取數據時,不像一般的實現用 get,而是用 remove,再將這個緩存數據放到一個 value 為軟引用的 activeResources map 中,並計數引用數,在圖片載入完成後進行判斷,如果引用計數為空則回收掉。內存緩存更小圖片,Glide 以 url、view_width、view_height、屏幕的解析度等做為聯合 key,將處理後的圖片緩存在內存緩存中,而不是原始圖片以節省大小與 Activity/Fragment 生命周期一致,支持 trimMemory。圖片默認使用默認 RGB_565 而不是 ARGB_888,雖然清晰度差些,但圖片更小,也可配置到 ARGB_888。
6.Glide 可以通過 signature 或不使用本地緩存支持 url 過期.
27、Xutils, OKhttp, Volley, Retrofit對比
Xutils這個框架非常全面,可以進行網路請求,可以進行圖片載入處理,可以數據儲存,還可以對view進行註解,使用這個框架非常方便,但是缺點也是非常明顯的,使用這個項目,會導致項目對這個框架依賴非常的嚴重,一旦這個框架出現問題,那麼對項目來說影響非常大的。
OKhttp:Android開發中是可以直接使用現成的api進行網路請求的。就是使用HttpClient,HttpUrlConnection進行操作。okhttp針對Java和Android程序,封裝的一個高性能的http請求庫,支持同步,非同步,而且okhttp又封裝了線程池,封裝了數據轉換,封裝了參數的使用,錯誤處理等。API使用起來更加的方便。但是我們在項目中使用的時候仍然需要自己在做一層封裝,這樣才能使用的更加的順手。
Volley:Volley是Google官方出的一套小而巧的非同步請求庫,該框架封裝的擴展性很強,支持HttpClient、HttpUrlConnection, 甚至支持OkHttp,而且Volley裡面也封裝了ImageLoader,所以如果你願意你甚至不需要使用圖片載入框架,不過這塊功能沒有一些專門的圖片載入框架強大,對於簡單的需求可以使用,稍複雜點的需求還是需要用到專門的圖片載入框架。Volley也有缺陷,比如不支持post大數據,所以不適合上傳文件。不過Volley設計的初衷本身也就是為頻繁的、數據量小的網路請求而生。
Retrofit:Retrofit是Square公司出品的默認基於OkHttp封裝的一套RESTful網路請求框架,RESTful是目前流行的一套api設計的風格, 並不是標準。Retrofit的封裝可以說是很強大,裡面涉及到一堆的設計模式,可以通過註解直接配置請求,可以使用不同的http客戶端,雖然默認是用http ,可以使用不同Json Converter 來序列化數據,同時提供對RxJava的支持,使用Retrofit + OkHttp + RxJava + Dagger2 可以說是目前比較潮的一套框架,但是需要有比較高的門檻。
Volley VS OkHttp
Volley的優勢在於封裝的更好,而使用OkHttp你需要有足夠的能力再進行一次封裝。而OkHttp的優勢在於性能更高,因為 OkHttp基於NIO和Okio ,所以性能上要比 Volley更快。IO 和 NIO這兩個都是Java中的概念,如果我從硬碟讀取數據,第一種方式就是程序一直等,數據讀完後才能繼續操作這種是最簡單的也叫阻塞式IO,還有一種是你讀你的,程序接著往下執行,等數據處理完你再來通知我,然後再處理回調。而第二種就是 NIO 的方式,非阻塞式, 所以NIO當然要比IO的性能要好了,而 Okio是 Square 公司基於IO和NIO基礎上做的一個更簡單、高效處理數據流的一個庫。理論上如果Volley和OkHttp對比的話,更傾向於使用 Volley,因為Volley內部同樣支持使用OkHttp,這點OkHttp的性能優勢就沒了, 而且 Volley 本身封裝的也更易用,擴展性更好些。
OkHttp VS Retrofit
毫無疑問,Retrofit 默認是基於 OkHttp 而做的封裝,這點來說沒有可比性,肯定首選 Retrofit。
Volley VS Retrofit
這兩個庫都做了不錯的封裝,但Retrofit解耦的更徹底,尤其Retrofit2.0出來,Jake對之前1.0設計不合理的地方做了大量重構, 職責更細分,而且Retrofit默認使用OkHttp,性能上也要比Volley佔優勢,再有如果你的項目如果採用了RxJava ,那更該使用 Retrofit 。所以這兩個庫相比,Retrofit更有優勢,在能掌握兩個框架的前提下該優先使用 Retrofit。但是Retrofit門檻要比Volley稍高些,要理解他的原理,各種用法,想徹底搞明白還是需要花些功夫的,如果你對它一知半解,那還是建議在商業項目使用Volley吧。
Java部分
1、線程中sleep和wait的區別
(1)這兩個方法來自不同的類,sleep是來自Thread,wait是來自Object;
(2)sleep方法沒有釋放鎖,而wait方法釋放了鎖。
(3)wait,notify,notifyAll只能在同步控制方法或者同步控制塊裡面使用,而sleep可以在任何地方使用。
2、Thread中的start()和run()方法有什麼區別
start()方法是用來啟動新創建的線程,而start()內部調用了run()方法,這和直接調用run()方法是不一樣的,如果直接調用run()方法,則和普通的方法沒有什麼區別。
3、String,StringBuffer,StringBuilder區別
1、三者在執行速度上:StringBuilder > StringBuffer > String (由於String是常量,不可改變,拼接時會重新創建新的對象)。
2、StringBuffer是線程安全的,StringBuilder是線程不安全的。(由於StringBuffer有緩衝區).
4、Java中重載和重寫的區別:
1、重載:一個類中可以有多個相同方法名的,但是參數類型和個數都不一樣。這是重載。
2、重寫:子類繼承父類,則子類可以通過實現父類中的方法,從而新的方法把父類舊的方法覆蓋。
5、Http https區別,並簡述https的實現原理
1、https協議需要到ca申請證書,一般免費證書較少,因而需要一定費用。
2、http是超文本傳輸協議,信息是明文傳輸,https則是具有安全性的ssl加密傳輸協議。
3、http和https使用的是完全不同的連接方式,用的埠也不一樣,前者是80,後者是443。
4、http的連接很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網路協議,比http協議安全。
https實現原理:
(1)客戶使用https的URL訪問Web伺服器,要求與Web伺服器建立SSL連接。
(2)Web伺服器收到客戶端請求後,會將網站的證書信息(證書中包含公鑰)傳送一份給客戶端。
(3)客戶端的瀏覽器與Web伺服器開始協商SSL連接的安全等級,也就是信息加密的等級。
(4)客戶端的瀏覽器根據雙方同意的安全等級,建立會話密鑰,然後利用網站的公鑰將會話密鑰加密,並傳送給網站。
(5)Web伺服器利用自己的私鑰解密出會話密鑰。
(6)Web伺服器利用會話密鑰加密與客戶端之間的通信。
6、TCP和UDP的區別
tcp是面向連接的,由於tcp連接需要三次握手,所以能夠最低限度的降低風險,保證連接的可靠性。
udp 不是面向連接的,udp建立連接前不需要與對象建立連接,無論是發送還是接收,都沒有發送確認信號。所以說udp是不可靠的。
由於udp不需要進行確認連接,使得UDP的開銷更小,傳輸速率更高,所以實時行更好。
7、Socket建立網路連接的步驟
建立Socket連接至少需要一對套接字,其中一個運行與客戶端–ClientSocket,一個運行於服務端–ServiceSocket
1、伺服器監聽:伺服器端套接字並不定位具體的客戶端套接字,而是處於等待連接的狀態,實時監控網路狀態,等待客戶端的連接請求。
2、客戶端請求:指客戶端的套接字提出連接請求,要連接的目標是伺服器端的套接字。注意:客戶端的套接字必須描述他要連接的伺服器的套接字,
指出伺服器套接字的地址和埠號,然後就像伺服器端套接字提出連接請求。
3、連接確認:當伺服器端套接字監聽到客戶端套接字的連接請求時,就響應客戶端套接字的請求,建立一個新的線程,把伺服器端套接字的描述
發給客戶端,一旦客戶端確認了此描述,雙方就正式建立連接。而服務端套接字則繼續處於監聽狀態,繼續接收其他客戶端套接字的連接請求。
推薦閱讀:
※HR遇到了真正的面試高手,句句經典!
※財務經理經典面試問題
※面試時,你最不想聽到HR說什麼?
※流量轟炸的起源之一·面試梗