雖然本人不搞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、連線確認:當伺服器端套接字監聽到客戶端套接字的連線請求時,就響應客戶端套接字的請求,建立一個新的執行緒,把伺服器端套接字的描述
發給客戶端,一旦客戶端確認了此描述,雙方就正式建立連線。而服務端套接字則繼續處於監聽狀態,繼續接收其他客戶端套接字的連線請求。