雖然本人不搞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、用戶端請求:指用戶端的套接字提出連接配接請求,要連接配接的目标是伺服器端的套接字。注意:用戶端的套接字必須描述他要連接配接的伺服器的套接字,
指出伺服器套接字的位址和端口号,然後就像伺服器端套接字提出連接配接請求。