2019年Android高級面試題與相關(guān)知識點總結(jié)

說下你所知道的設(shè)計模式與使用場景
a.建造者模式:
將一個復雜對象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。
使用場景比如最常見的AlertDialog,拿我們開發(fā)過程中舉例,比如Camera開發(fā)過程中,可能需要設(shè)置一個初始化的相機配置,設(shè)置攝像頭方向,閃光燈開閉,成像質(zhì)量等等,這種場景下就可以使用建造者模式
裝飾者模式:動態(tài)的給一個對象添加一些額外的職責,就增加功能來說,裝飾模式比生成子類更為靈活。裝飾者模式可以在不改變原有類結(jié)構(gòu)的情況下曾強類的功能,比如Java中的BufferedInputStream 包裝FileInputStream,舉個開發(fā)中的例子,比如在我們現(xiàn)有網(wǎng)絡(luò)框架上需要增加新的功能,那么再包裝一層即可,裝飾者模式解決了繼承存在的一些問題,比如多層繼承代碼的臃腫,使代碼邏輯更清晰
- 觀察者模式:
- 代理模式:
- 門面模式:
- 單例模式:
- 生產(chǎn)者消費者模式:
java語言的特點與OOP思想
這個通過對比來描述,比如面向?qū)ο蠛兔嫦蜻^程的對比,針對這兩種思想的對比,還可以舉個開發(fā)中的例子,比如播放器的實現(xiàn),面向過程的實現(xiàn)方式就是將播放視頻的這個功能分解成多個過程,比如,加載視頻地址,獲取視頻信息,初始化解碼器,選擇合適的解碼器進行解碼,讀取解碼后的幀進行視頻格式轉(zhuǎn)換和音頻重采樣,然后讀取幀進行播放,這是一個完整的過程,這個過程中不涉及類的概念,而面向?qū)ο笞畲蟮奶攸c就是類,封裝繼承和多態(tài)是核心,同樣的以播放器為例,一面向?qū)ο蟮姆绞絹韺崿F(xiàn),將會針對每一個功能封裝出一個對象,吧如說Muxer,獲取視頻信息,Decoder,解碼,格式轉(zhuǎn)換器,視頻播放器,音頻播放器等,每一個功能對應(yīng)一個對象,由這個對象來完成對應(yīng)的功能,并且遵循單一職責原則,一個對象只做它相關(guān)的事情
說下handler原理
Handler,Message,looper和MessageQueue構(gòu)成了安卓的消息機制,handler創(chuàng)建后可以通過sendMessage將消息加入消息隊列,然后looper不斷的將消息從MessageQueue中取出來,回調(diào)到Hander的handleMessage方法,從而實現(xiàn)線程的通信。
從兩種情況來說,第一在UI線程創(chuàng)建Handler,此時我們不需要手動開啟looper,因為在應(yīng)用啟動時,在ActivityThread的main方法中就創(chuàng)建了一個當前主線程的looper,并開啟了消息隊列,消息隊列是一個無限循環(huán),為什么無限循環(huán)不會ANR?因為可以說,應(yīng)用的整個生命周期就是運行在這個消息循環(huán)中的,安卓是由事件驅(qū)動的,Looper.loop不斷的接收處理事件,每一個點擊觸摸或者Activity每一個生命周期都是在Looper.loop的控制之下的,looper.loop一旦結(jié)束,應(yīng)用程序的生命周期也就結(jié)束了。我們可以想想什么情況下會發(fā)生ANR,第一,事件沒有得到處理,第二,事件正在處理,但是沒有及時完成,而對事件進行處理的就是looper,所以只能說事件的處理如果阻塞會導致ANR,而不能說looper的無限循環(huán)會ANR
另一種情況就是在子線程創(chuàng)建Handler,此時由于這個線程中沒有默認開啟的消息隊列,所以我們需要手動調(diào)用looper.prepare(),并通過looper.loop開啟消息
主線程Looper從消息隊列讀取消息,當讀完所有消息時,主線程阻塞。子線程往消息隊列發(fā)送消息,并且往管道文件寫數(shù)據(jù),主線程即被喚醒,從管道文件讀取數(shù)據(jù),主線程被喚醒只是為了讀取消息,當消息讀取完畢,再次睡眠。因此loop的循環(huán)并不會對CPU性能有過多的消耗。
WebView java js的通信
通過Android原生的方式進行通信
1.Java層調(diào)用js方法:
通過WebView的loadUrl():沒返回值,得通過js改變iframe.src把結(jié)果返回,這樣執(zhí)行效率較低
通過WebView的evaluateJavascript():sdk19(4.4)以上,在回調(diào)方法里有返回值,效率優(yōu)于前一種,因為該方法的執(zhí)行不會使頁面刷新,而方法(loadUrl )的執(zhí)行則會使頁面刷新。
建議兩種方法混合使用,即Android 4.4以下使用方法1,Android 4.4以上方法2
//假如js中定義了這個方法,要在java中調(diào)用它 <script> // Android需要調(diào)用的方法 function callJS(){ alert("Android調(diào)用了JS的callJS方法"); } </script>
//這樣調(diào)用,callJS就是方法名,javascript:是固定寫法 webView.loadUrl("javascript:callJS()"); // 只需要將第一種方法的loadUrl()換成下面該方法即可 webView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { //此處為 js 返回的結(jié)果 } }); }
2.Js調(diào)用Java代碼的方法有3種:
1.通過WebView的addJavascriptInterface()進行對象映射
這種方法在安卓4.2以下存在遠程代碼調(diào)用漏洞,漏洞產(chǎn)生原因是:當JS拿到Android這個對象后,就可以調(diào)用這個Android對象中所有的方法,包括系統(tǒng)類(java.lang.Runtime 類),從而進行任意代碼執(zhí)行。
具體獲取系統(tǒng)類的描述:(結(jié)合 Java 反射機制)
1.Android中的對象有一公共的方法:getClass() ;
2.該方法可以獲取到當前類 類型Class
3.該類有一關(guān)鍵的方法: Class.forName;
4.該方法可以加載一個類(可加載 java.lang.Runtime 類)
5.而該類是可以執(zhí)行本地命令的
//以下是攻擊的Js核心代碼: function execute(cmdArgs) { // 步驟1:遍歷 window 對象 // 目的是為了找到包含 getClass ()的對象 // 因為Android映射的JS對象也在window中,所以肯定會遍歷到 for (var obj in window) { if ("getClass" in window[obj]) { // 步驟2:利用反射調(diào)用forName()得到Runtime類對象 return window[obj].getClass().forName("java.lang.Runtime") .getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs); // 步驟3:以后,就可以調(diào)用靜態(tài)方法來執(zhí)行一些命令,比如訪問文件的命令 // 從執(zhí)行命令后返回的輸入流中得到字符串,有很嚴重暴露隱私的危險。 // 如執(zhí)行完訪問文件的命令之后,就可以得到文件名的信息了。 } } }
a.定義一個與JS對象映射關(guān)系的Android類:AndroidtoJs
// 繼承自O(shè)bject類 public class AndroidtoJs extends Object { // 定義JS需要調(diào)用的方法 // 被JS調(diào)用的方法必須加入@JavascriptInterface注解 @JavascriptInterface public void hello(String msg) { System.out.println("JS調(diào)用了Android的hello方法"); } }
b.在Android里通過WebView設(shè)置Android類與JS代碼的映射
WebSettings webSettings = mWebView.getSettings(); // 設(shè)置與Js交互的權(quán)限 webSettings.setJavaScriptEnabled(true); // 通過addJavascriptInterface()將Java對象映射到JS對象 //參數(shù)1:Javascript對象名 //參數(shù)2:Java對象名,在js中通過test調(diào)用hello方法 mWebView.addJavascriptInterface(new AndroidtoJs(), "test"); //AndroidtoJS類對象映射到j(luò)s的test對象
c.對應(yīng)的js代碼為
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Carson</title> <script> function callAndroid(){ // 由于對象映射,所以調(diào)用test對象等于調(diào)用Android映射的對象 test.hello("js調(diào)用了android中的hello方法"); } </script> </head> <body> //點擊按鈕則調(diào)用callAndroid函數(shù) <button type="button" id="button1" "callAndroid()"></button> </body> </html>
2.通過 WebViewClient 的shouldOverrideUrlLoading ()方法回調(diào)攔截 url,Android通過 WebViewClient 的回調(diào)方法shouldOverrideUrlLoading ()攔截 url解析該 url 的協(xié)議,如果檢測到是預(yù)先約定好的協(xié)議,就調(diào)用相應(yīng)方法
function callAndroid(){ /*約定的url協(xié)議為:js://webview?arg1=111&arg2=222*/ document.location = "js://webview?arg1=111&arg2=222"; } //點擊之后執(zhí)行了callAndroid()方法 <button type="button" id="button1" "callAndroid()">點擊調(diào)用Android代碼</button>
回調(diào)到shouldOverrideUrlLocading方法
public boolean shouldOverrideUrlLoading(WebView view, String url) { // 步驟2:根據(jù)協(xié)議的參數(shù),判斷是否是所需要的url // 一般根據(jù)scheme(協(xié)議格式) & authority(協(xié)議名)判斷(前兩個參數(shù)) //假定傳入進來的 url = "js://webview?arg1=111&arg2=222"(同時也是約定好的需要攔截的) Uri uri = Uri.parse(url); // 如果url的協(xié)議 = 預(yù)先約定的 js 協(xié)議 // 就解析往下解析參數(shù) if ( uri.getScheme().equals("js")) { // 如果 authority = 預(yù)先約定協(xié)議里的 webview,即代表都符合約定的協(xié)議 // 所以攔截url,下面JS開始調(diào)用Android需要的方法 if (uri.getAuthority().equals("webview")) { // 步驟3: // 執(zhí)行JS所需要調(diào)用的邏輯 System.out.println("js調(diào)用了Android的方法"); } return true; } return super.shouldOverrideUrlLoading(view, url); }
如果JS想要得到Android方法的返回值,只能通過 WebView 的 loadUrl ()去執(zhí)行 JS 方法把返回值傳遞回去,相關(guān)的代碼如下
// Android: MainActivity.java mWebView.loadUrl("javascript:returnResult(" + result + ")"); // JS: javascript.html function returnResult(result){ alert("result is" + result); }
3.通過 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回調(diào)攔截JS對話框alert()、confirm()、prompt()消息
1.常用的攔截是:攔截 JS的輸入框(即prompt()方法)
2.因為只有prompt()可以返回任意類型的值,操作最全面方便、更加靈活;而alert()對話框沒有返回值;confirm()對話框只能返回兩種狀態(tài)(確定 / 取消)兩個值
3.如果是攔截警告框(即alert()),則觸發(fā)回調(diào)onJsAlert();
4.如果是攔截確認框(即confirm()),則觸發(fā)回調(diào)onJsConfirm();
//點擊按鈕調(diào)用clickprompt方法 <button type="button" id="button1" "clickprompt()">點擊調(diào)用Android代碼</button>
function clickprompt(){ // 調(diào)用prompt() var result=prompt("js://demo?arg1=111&arg2=222"); alert("demo " + result); } mWebView.setWebChromeClient(new WebChromeClient() { // 攔截輸入框(原理同方式2) // 參數(shù)message:代表promt()的內(nèi)容(不是url) // 參數(shù)result:代表輸入框的返回值 @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { // 根據(jù)協(xié)議的參數(shù),判斷是否是所需要的url(原理同方式2) // 一般根據(jù)scheme(協(xié)議格式) & authority(協(xié)議名)判斷(前兩個參數(shù)) //假定傳入進來的 url = "js://webview?arg1=111&arg2=222"(同時也是約定好的需要攔截的) Uri uri = Uri.parse(message); // 如果url的協(xié)議 = 預(yù)先約定的 js 協(xié)議 // 就解析往下解析參數(shù) if ( uri.getScheme().equals("js")) { // 如果 authority = 預(yù)先約定協(xié)議里的 webview,即代表都符合約定的協(xié)議 // 所以攔截url,下面JS開始調(diào)用Android需要的方法 if (uri.getAuthority().equals("webview")) { // // 執(zhí)行JS所需要調(diào)用的邏輯 System.out.println("js調(diào)用了Android的方法"); // 可以在協(xié)議上帶有參數(shù)并傳遞到Android上 HashMap<String, String> params = new HashMap<>(); Set<String> collection = uri.getQueryParameterNames(); //參數(shù)result:代表消息框的返回值(輸入值) result.confirm("js調(diào)用了Android的方法成功啦"); } return true; } return super.onJsPrompt(view, url, message, defaultValue, result); }
說下Activity的啟動模式,生命周期,兩個Activity跳轉(zhuǎn)的生命周期,如果一個Activity跳轉(zhuǎn)另一個Activity再按下Home鍵在回到Activity的生命周期是什么樣的
啟動模式
Standard模式:Activity可以有多個實例,每次啟動Activity,無論任務(wù)棧中是否已經(jīng)有這個Activity的實例,系統(tǒng)都會創(chuàng)建一個新的Activity實例
SingleTop模式:當一個singleTop模式的Activity已經(jīng)位于任務(wù)棧的棧頂,再去啟動它時,不會再創(chuàng)建新的實例,如果不位于棧頂,就會創(chuàng)建新的實例
SingleTask模式:如果Activity已經(jīng)位于棧頂,系統(tǒng)不會創(chuàng)建新的Activity實例,和singleTop模式一樣。但Activity已經(jīng)存在但不位于棧頂時,系統(tǒng)就會把該Activity移到棧頂,并把它上面的activity出棧
SingleInstance模式:singleInstance模式也是單例的,但和singleTask不同,singleTask只是任務(wù)棧內(nèi)單例,系統(tǒng)里是可以有多個singleTask Activity實例的,而singleInstance Activity在整個系統(tǒng)里只有一個實例,啟動一singleInstanceActivity時,系統(tǒng)會創(chuàng)建一個新的任務(wù)棧,并且這個任務(wù)棧只有他一個Activity
生命周期
onCreate onStart onResume onPause onStop onDestroy
兩個Activity跳轉(zhuǎn)的生命周期
1.啟動A
onCreate - onStart - onResume
2.在A中啟動B
ActivityA onPause
ActivityB onCreate
ActivityB onStart
ActivityB onResume
ActivityA onStop
3.從B中返回A(按物理硬件返回鍵)
ActivityB onPause
ActivityA onRestart
ActivityA onStart
ActivityA onResume
ActivityB onStop
ActivityB onDestroy
4.繼續(xù)返回
ActivityA onPause
ActivityA onStop
ActivityA onDestroy
onRestart的調(diào)用場景
(1)按下home鍵之后,然后切換回來,會調(diào)用onRestart()。
(2)從本Activity跳轉(zhuǎn)到另一個Activity之后,按back鍵返回原來Activity,會調(diào)用onRestart();
(3)從本Activity切換到其他的應(yīng)用,然后再從其他應(yīng)用切換回來,會調(diào)用onRestart();
說下Activity的橫豎屏的切換的生命周期,用那個方法來保存數(shù)據(jù),兩者的區(qū)別。觸發(fā)在什么時候在那個方法里可以獲取數(shù)據(jù)等。
如何實現(xiàn)進程?;?br />
a: Service設(shè)置成START_STICKY kill 后會被重啟(等待5秒左右),重傳Intent,保持與重啟前一樣
b: 通過 startForeground將進程設(shè)置為前臺進程, 做前臺服務(wù),優(yōu)先級和前臺應(yīng)用一個級別,除非在系統(tǒng)內(nèi)存非常缺,否則此進程不會被 kill
c: 雙進程Service: 讓2個進程互相保護對方,其中一個Service被清理后,另外沒被清理的進程可以立即重啟進程
d: 用C編寫守護進程(即子進程) : Android系統(tǒng)中當前進程(Process)fork出來的子進程,被系統(tǒng)認為是兩個不同的進程。當父進程被殺死的時候,子進程仍然可以存活,并不受影響(Android5.0以上的版本不可行)聯(lián)系廠商,加入白名單
e.鎖屏狀態(tài)下,開啟一個一像素Activity
說下冷啟動與熱啟動是什么,區(qū)別,如何優(yōu)化,使用場景等。
app冷啟動: 當應(yīng)用啟動時,后臺沒有該應(yīng)用的進程,這時系統(tǒng)會重新創(chuàng)建一個新的進程分配給該應(yīng)用, 這個啟動方式就叫做冷啟動(后臺不存在該應(yīng)用進程)。冷啟動因為系統(tǒng)會重新創(chuàng)建一個新的進程分配給它,所以會先創(chuàng)建和初始化Application類,再創(chuàng)建和初始化MainActivity類(包括一系列的測量、布局、繪制),最后顯示在界面上。
app熱啟動: 當應(yīng)用已經(jīng)被打開, 但是被按下返回鍵、Home鍵等按鍵時回到桌面或者是其他程序的時候,再重新打開該app時, 這個方式叫做熱啟動(后臺已經(jīng)存在該應(yīng)用進程)。熱啟動因為會從已有的進程中來啟動,所以熱啟動就不會走Application這步了,而是直接走MainActivity(包括一系列的測量、布局、繪制),所以熱啟動的過程只需要創(chuàng)建和初始化一個MainActivity就行了,而不必創(chuàng)建和初始化Application
冷啟動的流程
當點擊app的啟動圖標時,安卓系統(tǒng)會從Zygote進程中fork創(chuàng)建出一個新的進程分配給該應(yīng)用,之后會依次創(chuàng)建和初始化Application類、創(chuàng)建MainActivity類、加載主題樣式Theme中的windowBackground等屬性設(shè)置給MainActivity以及配置Activity層級上的一些屬性、再inflate布局、當onCreate/onStart/onResume方法都走完了后最后才進行contentView的measure/layout/draw顯示在界面上
冷啟動的生命周期簡要流程:
Application構(gòu)造方法 –> attachBaseContext()–>onCreate –>Activity構(gòu)造方法 –> onCreate() –> 配置主體中的背景等操作 –>onStart() –> onResume() –> 測量、布局、繪制顯示
冷啟動的優(yōu)化主要是視覺上的優(yōu)化,解決白屏問題,提高用戶體驗,所以通過上面冷啟動的過程。能做的優(yōu)化如下:
減少onCreate()方法的工作量
不要讓Application參與業(yè)務(wù)的操作
不要在Application進行耗時操作
不要以靜態(tài)變量的方式在Application保存數(shù)據(jù)
減少布局的復雜度和層級
減少主線程耗時
為什么冷啟動會有白屏黑屏問題?原因在于加載主題樣式Theme中的windowBackground等屬性設(shè)置給MainActivity發(fā)生在inflate布局當onCreate/onStart/onResume方法之前,而windowBackground背景被設(shè)置成了白色或者黑色,所以我們進入app的第一個界面的時候會造成先白屏或黑屏一下再進入界面。解決思路如下
1.給他設(shè)置windowBackground背景跟啟動頁的背景相同,如果你的啟動頁是張圖片那么可以直接給windowBackground這個屬性設(shè)置該圖片那么就不會有一閃的效果了
<style name=``"Splash_Theme"` `parent=``"@android:style/Theme.NoTitleBar"``>` <item name=``"android:windowBackground"``>@drawable/splash_bg</item>` <item name=``"android:windowNoTitle"``>``true``</item>` </style>`
2.采用世面的處理方法,設(shè)置背景是透明的,給人一種延遲啟動的感覺。,將背景顏色設(shè)置為透明色,這樣當用戶點擊桌面APP圖片的時候,并不會"立即"進入APP,而且在桌面上停留一會,其實這時候APP已經(jīng)是啟動的了,只是我們心機的把Theme里的windowBackground的顏色設(shè)置成透明的,強行把鍋甩給了手機應(yīng)用廠商(手機反應(yīng)太慢了啦)
<style name=``"Splash_Theme"` `parent=``"@android:style/Theme.NoTitleBar"``>` <item name=``"android:windowIsTranslucent"``>``true``</item>` <item name=``"android:windowNoTitle"``>``true``</item>` </style>`
3.以上兩種方法是在視覺上顯得更快,但其實只是一種表象,讓應(yīng)用啟動的更快,有一種思路,將Application中的不必要的初始化動作實現(xiàn)懶加載,比如,在SpashActivity顯示后再發(fā)送消息到Application,去初始化,這樣可以將初始化的動作放在后邊,縮短應(yīng)用啟動到用戶看到界面的時
ANR的原因
1.耗時的網(wǎng)絡(luò)訪問
2.大量的數(shù)據(jù)讀寫
3.數(shù)據(jù)庫操作
4.硬件操作(比如camera)
5.調(diào)用thread的join()方法、sleep()方法、wait()方法或者等待線程鎖的時候
6.service binder的數(shù)量達到上限
7.system server中發(fā)生WatchDog ANR
8.service忙導致超時無響應(yīng)
9.其他線程持有鎖,導致主線程等待超時
10.其它線程終止或崩潰導致主線程一直等待
三級緩存原理
當Android端需要獲得數(shù)據(jù)時比如獲取網(wǎng)絡(luò)中的圖片,首先從內(nèi)存中查找(按鍵查找),內(nèi)存中沒有的再從磁盤文件或sqlite中去查找,若磁盤中也沒有才通過網(wǎng)絡(luò)獲取
jvm,jre以及jdk三者之間的關(guān)系?JDK(Java Development Kit)是針對Java開發(fā)員的產(chǎn)品,是整個Java的核心,包括了Java運行環(huán)境JRE、Java工具和Java基礎(chǔ)類庫。
Java Runtime Environment(JRE)是運行JAVA程序所必須的環(huán)境的集合,包含JVM標準實現(xiàn)及Java核心類庫。
JVM是Java Virtual Machine(Java虛擬機)的縮寫,是整個java實現(xiàn)跨平臺的最核心的部分,能夠運行以Java語言寫作的軟件程序。
談?wù)勀銓?JNIEnv 和 JavaVM 理解?
- JavaVm
JavaVM 是虛擬機在 JNI 層的代表,一個進程只有一個 JavaVM,所有的線程共用一個 JavaVM。
- JNIEnv
JNIEnv 表示 Java 調(diào)用 native 語言的環(huán)境,是一個封裝了幾乎全部 JNI 方法的指針。
JNIEnv 只在創(chuàng)建它的線程生效,不能跨線程傳遞,不同線程的 JNIEnv 彼此獨立。
native 環(huán)境中創(chuàng)建的線程,如果需要訪問 JNI,必須要調(diào)用 AttachCurrentThread 關(guān)聯(lián),并使用 DetachCurrentThread 解除鏈接。
Serializable與Parcable的區(qū)別?
1.Serializable (java 自帶)
方法:對象繼承 Serializable類即可實現(xiàn)序列化,就是這么簡單,也是它最吸引我們的地方
2.Parcelable(Android專用):Parcelable方式的實現(xiàn)原理是將一個完整的對象進行分解,用起來比較麻煩
1)在使用內(nèi)存的時候,Parcelable比Serializable性能高,所以推薦使用Parcelable。
2)Serializable在序列化的時候會產(chǎn)生大量的臨時變量,從而引起頻繁的GC。
3)Parcelable不能使用在要將數(shù)據(jù)存儲在磁盤上的情況,因為Parcelable不能很好的保證數(shù)據(jù)的持續(xù)性,在外界有變化的情況下。盡管Serializable效率低點,但此時還是建議使用Serializable 。
4)android上應(yīng)該盡量采用Parcelable,效率至上,效率遠高于Serializable
寶馬面試題
Handler消息機制。
自定義view步驟,如何自定義屬性。
布局的xml文件中 merge、include、viewstub關(guān)鍵字的使用。
MVP和MVC的區(qū)別。
DataBinding的使用。
谷歌最新技術(shù)的關(guān)注度?
自定義view中的onDraw()方法中,canvas和paint的api的功能。以及刷新功能的使用。
Synchronized關(guān)鍵字的各種使用。
如何繪制一個帶圓角的箭頭?你的實現(xiàn)思路是什么?
Java中Exception和Error的區(qū)別
1.Error類一般是指與虛擬機相關(guān)的問題,如系統(tǒng)崩潰,虛擬機錯誤,內(nèi)存空間不足,方法調(diào)用棧溢等
2.Exception類表示程序可以處理的異常,可以捕獲且可能恢復。遇到這類異常,應(yīng)該盡可能處理異常,使程序恢復運行,而不應(yīng)該隨意終止異常。Exception又分為可檢查異常和不可檢查異常,可檢查異常在源代碼里必須顯示的進行捕獲處理,這是編譯期檢查的一部分。不可檢查異常就是所謂的運行時異常,類似NullPointerException、ArrayIndexOutOfBoundsException之類,通常是可以編碼避免的邏輯錯誤,具體可以根據(jù)需要來判斷是否需要捕獲,并不會在編譯期強制要求
3..exception和error都是繼承了throwable類,在java中只有throwable類型的實例才可以被拋出(throw)或者捕獲(catch),它是異常處理機制的基本組成類型
4.exception和error體現(xiàn)了java平臺設(shè)計者對不同異常情況的分類。exception是程序正常運行中,可以預(yù)料的意外情況,并且應(yīng)該被捕獲,進行相應(yīng)的處理
Android單線程模型
Android單線程模型的核心原則就是:只能在UI線程(Main Thread)中對UI進行處理。當一個程序第一次啟動時,Android會同時啟動一個對應(yīng)的 主線程(Main Thread),主線程主要負責處理與UI相關(guān)的事件,如:用戶的按鍵事件,用戶接觸屏幕的事件以及屏幕繪圖事 件,并把相關(guān)的事件分發(fā)到對應(yīng)的組件進行處理。所以主線程通常又被叫做UI線 程。在開發(fā)Android應(yīng)用時必須遵守單線程模型的原則: Android UI操作并不是線程安全的并且這些操作必須在UI線程中執(zhí)行。
Android的單線程模型有兩條原則:
1.不要阻塞UI線程。
2.不要在UI線程之外訪問Android UI toolkit(主要是這兩個包中的組件:android.widget and android.view
RecyclerView在很多方面能取代ListView,Google為什么沒把ListView劃上一條過時的橫線?
ListView采用的是RecyclerBin的回收機制在一些輕量級的List顯示時效率更高。
App啟動流程
App啟動時,AMS會檢查這個應(yīng)用程序所需要的進程是否存在,不存在就會請求Zygote進程啟動需要的應(yīng)用程序進程,Zygote進程接收到AMS請求并通過fock自身創(chuàng)建應(yīng)用程序進程,這樣應(yīng)用程序進程就會獲取虛擬機的實例,還會創(chuàng)建Binder線程池(ProcessState.startThreadPool())和消息循環(huán)(ActivityThread looper.loop),然后App進程,通過Binder IPC向sytem_server進程發(fā)起attachApplication請求;system_server進程在收到請求后,進行一系列準備工作后,再通過Binder IPC向App進程發(fā)送scheduleLaunchActivity請求;App進程的binder線程(ApplicationThread)在收到請求后,通過handler向主線程發(fā)送LAUNCH_ACTIVITY消息;主線程在收到Message后,通過反射機制創(chuàng)建目標Activity,并回調(diào)Activity.onCreate()等方法。到此,App便正式啟動,開始進入Activity生命周期,執(zhí)行完onCreate/onStart/onResume方法,UI渲染結(jié)束后便可以看到App的主界面。
雙親委托模式
類加載器查找class所采用的是雙親委托模式,所謂雙親委托模式就是判斷該類是否已經(jīng)加載,如果沒有則不是自身去查找而是委托給父加載器進行查找,這樣依次進行遞歸,直到委托到最頂層的Bootstrap ClassLoader,如果Bootstrap ClassLoader找到了該Class,就會直接返回,如果沒找到,則繼續(xù)依次向下查找,如果還沒找到則最后交給自身去查找
雙親委托模式的好處
1.避免重復加載,如果已經(jīng)加載過一次Class,則不需要再次加載,而是直接讀取已經(jīng)加載的Class
2.更加安全,確保,java核心api中定義類型不會被隨意替換,比如,采用雙親委托模式可以使得系統(tǒng)在Java虛擬機啟動時舊加載了String類,也就無法用自定義的String類來替換系統(tǒng)的String類,這樣便可以防止核心API庫被隨意篡改。
什么情況下會觸發(fā)類的初始化
遇到new,getstatic,putstatic,invokestatic這4條指令;
使用java.lang.reflect包的方法對類進行反射調(diào)用;
初始化一個類的時候,如果發(fā)現(xiàn)其父類沒有進行過初始化,則先初始化其父類(注意!如果其父類是接口的話,則不要求初始化父類);
當虛擬機啟動時,用戶需要指定一個要執(zhí)行的主類(包含main方法的那個類),虛擬機會先初始化這個主類;
當使用jdk1.7的動態(tài)語言支持時,如果一個java.lang.invoke.MethodHandle實例最后的解析結(jié)果REF_getstatic,REF_putstatic,REF_invokeStatic的方法句柄,并且這個方法句柄所對應(yīng)的類沒有進行過初始化,則先觸發(fā)其類初始化;
類的加載過程
類加載過程主要包含加載、驗證、準備、解析、初始化、使用、卸載七個方面,下面一一闡述。
1.加載:獲取定義此類的二進制字節(jié)流,生成這個類的java.lang.Class對象
2.驗證:保證Class文件的字節(jié)流包含的信息符合JVM規(guī)范,不會給JVM造成危害
3.準備:準備階段為變量分配內(nèi)存并設(shè)置類變量的初始化
4.解析:解析過程是將常量池內(nèi)的符號引用替換成直接引用
5.初始化:不同于準備階段,本次初始化,是根據(jù)程序員通過程序制定的計劃去初始化類的變量和其他資源。這些資源有static{}塊,構(gòu)造函數(shù),父類的初始化等
6.使用:使用過程就是根據(jù)程序定義的行為執(zhí)行
7.卸載:卸載由GC完成。
8.三次握手時最后一次客戶端收到服務(wù)端SYN+ACK包時,發(fā)送確認信息給服務(wù)端,此時服務(wù)端沒有收到,那么兩端都處于什么狀態(tài)
當?shù)谌挝帐质r的處理操作,可以看出當失敗時服務(wù)器并不會重傳ack報文,而是直接發(fā)送RTS報文段,進入CLOSED狀態(tài)。這樣做的目的是為了防止SYN洪泛攻擊。
9.BLocked和Waiting狀態(tài)的區(qū)別
10.wait的實現(xiàn)機制
11.C++重載的原理
消息隊列中的消息處理完之后,Looper進入組賽狀態(tài),那么此時我想進行一次垃圾回收,能不能做到?
可以。通過IdleHandler實現(xiàn),IdleHandler即在looper里面的message處理完了的時候去調(diào)用
IdleHandler源碼
/** * Callback interface for discovering when a thread is going to block * waiting for more messages.消息隊列進入組賽狀態(tài)后會執(zhí)行 */ public static interface IdleHandler { /** * Called when the message queue has run out of messages and will now * wait for more. Return true to keep your idle handler active, false * to have it removed. This may be called if there are still messages * pending in the queue, but they are all scheduled to be dispatched * after the current time. *返回值為true,則保持此Idle一直在Handler中,一直運行,否則,執(zhí)行一次后就從Handler線程中remove掉,只執(zhí)行一次。 */ boolean queueIdle(); }
系統(tǒng)源碼中IdleHandler的使用
void scheduleGcIdler() { if (!mGcIdlerScheduled) { mGcIdlerScheduled = true; Looper.myQueue().addIdleHandler(mGcIdler); } mH.removeMessages(H.GC_WHEN_IDLE); }
這個方法的調(diào)用發(fā)生在ActivityThread的Handler handleMessage中,可以看到,gc是在主線程消息隊列阻塞狀態(tài)(等待新的消息或者消息執(zhí)行完成)時進行的,并且每次執(zhí)行g(shù)c有一個最小時間間隔5x1000
public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { ...... case GC_WHEN_IDLE: scheduleGcIdler(); break;
void scheduleGcIdler() { if (!mGcIdlerScheduled) { mGcIdlerScheduled = true; Looper.myQueue().addIdleHandler(mGcIdler); } mH.removeMessages(H.GC_WHEN_IDLE); } final class GcIdler implements MessageQueue.IdleHandler { @Override public final boolean queueIdle() { doGcIfNeeded(); return false; } } void doGcIfNeeded() { mGcIdlerScheduled = false; final long now = SystemClock.uptimeMillis(); //Slog.i(TAG, "**** WE MIGHT WANT TO GC: then=" + Binder.getLastGcTime() // + "m now=" + now); //private static final long MIN_TIME_BETWEEN_GCS = 5*1000; if ((BinderInternal.getLastGcTime()+MIN_TIME_BETWEEN_GCS) < now) { //Slog.i(TAG, "**** WE DO, WE DO WANT TO GC!"); BinderInternal.forceGc("bg"); } }
Handler消息的優(yōu)先級-同步屏障機制(sync barrier)
同步屏障可以通過MessageQueue.postSyncBarrier函數(shù)來設(shè)置。該方法發(fā)送了一個沒有target的Message到Queue中,在next方法中獲取消息時,如果發(fā)現(xiàn)沒有target的Message,則在一定的時間內(nèi)跳過同步消息,優(yōu)先執(zhí)行異步消息。再換句話說,同步屏障為Handler消息機制增加了一種簡單的優(yōu)先級機制,異步消息的優(yōu)先級要高于同步消息。在創(chuàng)建Handler時有一個async參數(shù),傳true表示此handler發(fā)送的時異步消息。ViewRootImpl.scheduleTraversals方法就使用了同步屏障,保證UI繪制優(yōu)先執(zhí)行。
Linux管道機制
阻塞后的Looper如何再次被激活
Java中的泛型是什么 ? 使用泛型的好處是什么?
在集合中存儲對象并在使用前進行類型轉(zhuǎn)換不方便。泛型防止了那種情況的發(fā)生。它提供了編譯期的類型安全,確保你只能把正確類型的對象放入 集合中,避免了在運行時出現(xiàn)ClassCastException。
Java的泛型是如何工作的 ? 什么是類型擦除 ?
Java源代碼里面類型提供實現(xiàn)泛型功能,而編譯后Class文件類型就變成原生類型(即類型被擦除掉),而在引用處插入強制類型轉(zhuǎn)換以實現(xiàn)JVM對泛型的支持。本質(zhì)是Java泛型只是Java提供的一個語法糖,底層
的JVM并不提供支持,Java中的泛型屬于偽泛型。
但是編譯后的字節(jié)碼通過反射后還是可以獲取到泛型的真實類型信息,因為泛型擦除并沒有把保存泛型元數(shù)據(jù)擦除掉。
泛型是通過類型擦除來實現(xiàn)的,編譯器在編譯時擦除了所有類型相關(guān)的信息,所以在運行時不存在任何類型相關(guān)的信息。例如 List<String>在運行時僅用一個List來表示。這樣做的目的,是確保能和Java 5之前的版本開發(fā)二進制類庫進行兼容。你無法在運行時訪問到類型參數(shù),因為編譯器已經(jīng)把泛型類型轉(zhuǎn)換成了原始類型。根據(jù)你對這個泛型問題的回答情況,你會 得到一些后續(xù)提問,比如為什么泛型是由類型擦除來實現(xiàn)的或者給你展示一些會導致編譯器出錯的錯誤泛型代碼。
什么是泛型中的限定通配符和非限定通配符 ?
這是另一個非常流行的Java泛型面試題。限定通配符對類型進行了限制。有兩種限定通配符,一種是<? extends T>它通過確保類型必須是T的子類來設(shè)定類型的上界,另一種是<? super T>它通過確保類型必須是T的父類來設(shè)定類型的下界。泛型類型必須用限定內(nèi)的類型來進行初始化,否則會導致編譯錯誤。另一方面<?>表 示了非限定通配符,因為<?>可以用任意類型來替代。
List<? extends T>和List <? super T>之間有什么區(qū)別 ?
這和上一個面試題有聯(lián)系,有時面試官會用這個問題來評估你對泛型的理解,而不是直接問你什么是限定通配符和非限定通配符。這兩個List的聲明都是 限定通配符的例子,List<? extends T>可以接受任何繼承自T的類型的List,而List<? super T>可以接受任何T的父類構(gòu)成的List。例如List<? extends Number>可以接受List<Integer>或List<Float>。在本段出現(xiàn)的連接中可以找到更多信息。
為什么Java泛型是偽泛型
Java 的泛型是偽泛型, 也就是騙騙編譯器的。運行期的泛型類型,被擦除了,因此,在運行期,ArrayList<String> 和 ArrayList<int> 是相同的類型。Java源代碼里面類型提供實現(xiàn)泛型功能,而編譯后Class文件類型就變成原生類型(即類型被擦除掉),而在引用處插入強制類型轉(zhuǎn)換以實現(xiàn)JVM對泛型的支持。本質(zhì)是Java泛型只是Java提供的一個語法糖,底層
的JVM并不提供支持,Java中的泛型屬于偽泛型。
相關(guān)文章
- 這篇文章主要介紹了華為Android三面成功通過,面試官都問了什么,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-04-26
- 這篇文章主要介紹了Android 一線大廠面試總結(jié),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-01-08
- 這篇文章主要介紹了2019年必備的Android面試題及參考答案,整理匯總了Android開發(fā)中常見的各種知識點、技術(shù)細節(jié)與注意事項,需要的朋友可以參考下2019-10-30
- 這篇文章主要介紹了2019 Android 面試真題集錦,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習2019-08-28
2019 金三銀四:阿里P9架構(gòu)的Android大廠面試題總結(jié)
面試是一道坎,很多人會恐懼面試,即使是工作很多年的老鳥,可能仍存在面試的焦慮。 今天介紹了阿里P9架構(gòu)的Android大廠面試題總結(jié)的相關(guān)資料,小編覺得挺不錯的,現(xiàn)在分2019-05-05- 這篇文章主要介紹了這20道題,聽說只有大廠的Android工程師能全對,趕快一起來測試一下自己把,看看是不是都掌握了2020-05-25