Android:面試題

final關(guān)鍵字

可以用來修飾類、方法和變量(包括成員變量和局部變量)

1.修飾類

當(dāng)用final修飾一個類時,表明這個類不能被繼承

2.修飾方法

“使用final方法的原因有兩個。第一個原因是把方法鎖定,以防任何繼承類修改它的含義(重寫)

因此,如果只有在想明確禁止 該方法在子類中被覆蓋的情況下才將方法設(shè)置為final的。

注:類的private方法會隱式地被指定為final方法。

3.修飾變量

對于一個final變量,如果是基本數(shù)據(jù)類型的變量,則其數(shù)值一旦在初始化之后便不能更改;如果是引用類型的變量,則在對其初始化之后便不能再讓其指向另一個對象。

linklist 和 arraylist

ArrayList是基于索引的數(shù)據(jù)接口,它的底層是數(shù)組。它可以以O(shè)(1)時間復(fù)雜度對元素進行隨機訪問。與此對應(yīng),LinkedList是以元素列表的形式存儲它的數(shù)據(jù),每一個元素都和它的前一個和后一個元素鏈接在一起,在這種情況下,查找某個元素的時間復(fù)雜度是O(n)。
相對于ArrayList,LinkedList的插入,添加,刪除操作速度更快,因為當(dāng)元素被添加到集合任意位置的時候,不需要像數(shù)組那樣重新計算大小或者是更新索引。
LinkedList比ArrayList更占內(nèi)存,因為LinkedList為每一個節(jié)點存儲了兩個引用,一個指向前一個元素,一個指向下一個元素。

一次完整的HTTP網(wǎng)絡(luò)請求過程詳解

域名解析

TCP三次握手
(客戶端發(fā)出同步請求報文。
服務(wù)端接收到請求報文之后發(fā)出同步確認報文。
客服端收到同步確認報文之后發(fā)現(xiàn)確認報文,連接建立。)

建立TCP發(fā)起HTTP請求

服務(wù)器響應(yīng)HTTP請求

瀏覽器解析html代碼

同時請求html代碼中的資源(如js、css、圖片等)

總結(jié)Https的通信流程 Client發(fā)起請求

Server端響應(yīng)請求,并在之后將證書發(fā)送至Client

Client使用認證機構(gòu)的共鑰認證證書,并從證書中取出Server端公鑰。

Client使用共鑰加密一個隨機秘鑰,并傳到Server

Server使用私鑰解密出隨機秘鑰

通信雙方使用隨機秘鑰最為對稱秘鑰進行加密解密。

常見線程池

Android中的線程形態(tài)有傳統(tǒng)的Thread,AsyncTask,HandlerThread和IntentService。

AsyncTask

AsyncTask封裝了Thread和Handler,必須在主線程進行調(diào)用,它可以在子線程中執(zhí)行任務(wù),然后將執(zhí)行的結(jié)果傳遞給主線程并更新UI。但AsyncTask并不適合執(zhí)行特別耗時的任務(wù)。

HandlerThread

HandlerThread繼承了Thread,能夠使用Handler,實現(xiàn)簡單,節(jié)省系統(tǒng)資源開銷。 實現(xiàn)如下:

HandlerThread thread = new HandlerThread("MyHandlerThread");
thread.start();
mHandler = new Handler(thread.getLooper());
mHandler.post(new Runnable(){...});

IntentService

IntentService是特殊的Service,繼承了Service,因為IntentService是一個抽象類,所以必須創(chuàng)建IntentService的子類才能使用。
同時,IntentService是服務(wù),所以在執(zhí)行時,優(yōu)先級較高。
IntentService封裝了HandlerThread和Handler

線程池分為四種不同的功能

分別是FixedThreadPool、CachedThreadPool、ScheduledThreadPool、SingkeThreadExecutor。

FixedThreadPool
一個固定大小的線程池,可以用于已知并發(fā)壓力的情況下,對線程數(shù)做限制。

CachedThreadPool
一個可以無限擴大的線程池,比較適合處理執(zhí)行時間比較小的任務(wù)。

ScheduledThreadPool
可以延時啟動,定時啟動的線程池,適用于需要多個后臺線程執(zhí)行周期任務(wù)的場景。

SingkeThreadExecutor 一個單線程的線程池,可以用于需要保證順序執(zhí)行的場景,并且只有一個線程在執(zhí)行。

網(wǎng)絡(luò)

網(wǎng)絡(luò)由下往上分為:

物理層、數(shù)據(jù)鏈路層、網(wǎng)絡(luò)層、傳輸層、會話層、表示層和應(yīng)用層。

HTTP本身是一個協(xié)議,但其最終還是基于TCP的
IP 協(xié)議對應(yīng)于網(wǎng)絡(luò)層,TCP協(xié)議對應(yīng)于傳輸層,HTTP協(xié)議對應(yīng)于應(yīng)用層,三者從本質(zhì)上來說沒有可比性,socket則是對TCP/IP協(xié)議的封裝和應(yīng)用。

可以說,TPC/IP協(xié)議是傳輸層協(xié)議,主要解決數(shù)據(jù)如何在網(wǎng)絡(luò)中傳輸,而HTTP是應(yīng)用層協(xié)議,主要解決如何包裝數(shù)據(jù)

TCP是面向鏈接的,雖然說網(wǎng)絡(luò)的不安全不穩(wěn)定特性決定了多少次握手都不能保證連接的可靠性,

但TCP的三次握手在最低限度上(實際上也很大程度上保證了)保證了連接的可靠性;

而UDP不是面向連接的,UDP是無連接的、不可靠的一種數(shù)據(jù)傳輸協(xié)議,UDP傳送數(shù)據(jù)前并不與對方建立連接,對接收到的數(shù)據(jù)也不發(fā)送確認信號,

發(fā)送端不知道數(shù)據(jù)是否會正確接收,當(dāng)然也不用重發(fā),所以說
也正由于上面的特點,使得UDP的開銷更小數(shù)據(jù)傳輸速率更高,因為不必進行收發(fā)數(shù)據(jù)的確認,所以UDP的實時性更好。

TCP連接的三次握手:

1、客戶端發(fā)送報文到服務(wù)器 進入并進入SYN_SEND狀態(tài) 等待服務(wù)器確認
2、服務(wù)器確認收到報文,同時自己發(fā)送報文,此時服務(wù)器進入SYN_RECV狀態(tài)
3、客戶端收到服務(wù)器的報文,向服務(wù)器發(fā)送確認包,此包發(fā)送完畢,客戶端和服務(wù)器進入ESTABLISHED狀態(tài),完成三次握手

類加載過程,雙親委派模型

Java中類加載分為3個步驟:加載、鏈接、初始化。
加載。加載是將字節(jié)碼數(shù)據(jù)從不同的數(shù)據(jù)源讀取到JVM內(nèi)存,并映射為JVM認可的數(shù)據(jù)結(jié)構(gòu),也就是Class對象的過程。數(shù)據(jù)源可以是Jar文件、Class文件等等。如果數(shù)據(jù)的格式并不是ClassFile的結(jié)構(gòu),則會報ClassFormatError。
鏈接。鏈接是類加載的核心部分,這一步分為3個步驟:驗證、準備、解析。
驗證。驗證是保證JVM安全的重要步驟。JVM需要校驗字節(jié)信息是否符合規(guī)范,避免惡意信息和不規(guī)范數(shù)據(jù)危害JVM運行安全。如果驗證出錯,則會報VerifyError。
準備。這一步會創(chuàng)建靜態(tài)變量,并為靜態(tài)變量開辟內(nèi)存空間。
解析。這一步會將符號引用替換為直接引用。
初始化。初始化會為靜態(tài)變量賦值,并執(zhí)行靜態(tài)代碼塊中的邏輯。
雙親委派模型。
類加載器大致分為3類:啟動類加載器、擴展類加載器、應(yīng)用程序類加載器。
啟動類加載器主要加載 jre/lib下的jar文件。
擴展類加載器主要加載 jre/lib/ext 下的jar文件。
應(yīng)用程序類加載器主要加載 classpath下的文件。
所謂的雙親委派模型就是當(dāng)加載一個類時,會優(yōu)先使用父類加載器加載,當(dāng)父類加載器無法加載時才會使用子類加載器去加載。這么做的目的是為了避免類的重復(fù)加載。

sleep和wait、yield的區(qū)別

sleep方法是Thread類中的靜態(tài)方法,wait是Object類中的方法
sleep并不會釋放同步鎖,而wait會釋放同步鎖
sleep可以在任何地方使用,而wait只能在同步方法或者同步代碼塊中使用
sleep中必須傳入時間,而wait可以傳,也可以不傳,不傳時間的話只有notify或者notifyAll才能喚醒,傳時間的話在時間之后會自動喚醒
它僅僅釋放線程所占有的CPU資源,從而讓其他線程有機會運行,但是并不能保證某個特定的線程能夠獲得CPU資源

join的用法

join方法通常是保證線程間順序調(diào)度的一個方法,它是Thread類中的方法。比方說在線程A中執(zhí)行線程B.join(),這時線程A會進入等待狀態(tài),直到線程B執(zhí)行完畢之后才會喚醒,繼續(xù)執(zhí)行A線程中的后續(xù)方法。
join方法可以傳時間參數(shù),也可以不傳參數(shù),不傳參數(shù)實際上調(diào)用的是join(0)。它的原理其實是使用了wait方法

volatile和synchronized的區(qū)別

volatile本質(zhì)是在告訴jvm當(dāng)前變量在寄存器(工作內(nèi)存)中的值是不確定的,需要從主存中讀??; synchronized則是鎖定當(dāng)前變量,只有當(dāng)前線程可以訪問該變量,其他線程被阻塞住。
volatile僅能使用在變量級別;synchronized則可以使用在變量、方法、和類級別的
volatile僅能實現(xiàn)變量的修改可見性,不能保證原子性;而synchronized則可以保證變量的修改可見性和原子性
volatile不會造成線程的阻塞;synchronized可能會造成線程的阻塞。
volatile標記的變量不會被編譯器優(yōu)化;synchronized標記的變量可以被編譯器優(yōu)化

Thread和Runnable的聯(lián)系和區(qū)別

繼承Thread類,重寫Thread的run()方法
實現(xiàn)Runnable接口,重寫Runnable的run()方法,并將其作為參數(shù)實例化Thread

兩者的聯(lián)系
1、Thread類實現(xiàn)了Runnable接口
2、都需要重寫里面run()方法
兩者的區(qū)別
1、實現(xiàn)Runnable的類更具有健壯性,避免了單繼承的局限
2、Runnable更容易實現(xiàn)資源共享,能多個線程同時處理一個資源

final、finally、finalize區(qū)別

final可以修飾類、變量和方法。修飾類代表這個類不可被繼承。修飾變量代表此變量不可被改變。修飾方法表示此方法不可被重寫(override)。
finally是保證重點代碼一定會執(zhí)行的一種機制。通常是使用try-finally或者try-catch-finally來進行文件流的關(guān)閉等操作。
finalize是Object類中的一個方法,它的設(shè)計目的是保證對象在垃圾收集前完成特定資源的回收。finalize機制現(xiàn)在已經(jīng)不推薦使用,并且在JDK 9已經(jīng)被標記為deprecated。

Java中引用類型的區(qū)別,具體的使用場景

Java中引用類型分為四類:強引用、軟引用、弱引用、虛引用。
強引用:強引用指的是通過new對象創(chuàng)建的引用,垃圾回收器即使是內(nèi)存不足也不會回收強引用指向的對象。
軟引用:軟引用是通過SoftRefrence實現(xiàn)的,它的生命周期比強引用短,在內(nèi)存不足,拋出OOM之前,垃圾回收器會回收軟引用引用的對象。軟引用常見的使用場景是存儲一些內(nèi)存敏感的緩存,當(dāng)內(nèi)存不足時會被回收。
弱引用:弱引用是通過WeakRefrence實現(xiàn)的,它的生命周期比軟引用還短,GC只要掃描到弱引用的對象就會回收。弱引用常見的使用場景也是存儲一些內(nèi)存敏感的緩存。
虛引用:虛引用是通過FanttomRefrence實現(xiàn)的,它的生命周期最短,隨時可能被回收。如果一個對象只被虛引用引用,我們無法通過虛引用來訪問這個對象的任何屬性和方法。它的作用僅僅是保證對象在finalize后,做某些事情。虛引用常見的使用場景是跟蹤對象被垃圾回收的活動,當(dāng)一個虛引用關(guān)聯(lián)的對象被垃圾回收器回收之前會收到一條系統(tǒng)通知。

Exception和Error的區(qū)別

Exception和Error都繼承于Throwable,在Java中,只有Throwable類型的對象才能被throw或者catch,它是異常處理機制的基本組成類型。
Exception和Error體現(xiàn)了Java對不同異常情況的分類。Exception是程序正常運行中,可以預(yù)料的意外情況,可能并且應(yīng)該被捕獲,進行相應(yīng)的處理。
Error是指在正常情況下,不大可能出現(xiàn)的情況,絕大部分Error都會使程序處于非正常、不可恢復(fù)的狀態(tài)。既然是非正常,所以不便于也不需要捕獲,常見的OutOfMemoryError就是Error的子類。
Exception又分為checked Exception和unchecked Exception。checked Exception在代碼里必須顯式的進行捕獲,這是編譯器檢查的一部分。unchecked Exception也就是運行時異常,類似空指針異常、數(shù)組越界等,通常是可以避免的邏輯錯誤,具體根據(jù)需求來判斷是否需要捕獲,并不會在編譯器強制要求。

Android性能優(yōu)化

Android中的性能優(yōu)化在我看來分為以下幾個方面:內(nèi)存優(yōu)化、布局優(yōu)化、網(wǎng)絡(luò)優(yōu)化、啟動優(yōu)化、安裝包優(yōu)化。

內(nèi)存優(yōu)化:下一個問題就是。
布局優(yōu)化:布局優(yōu)化的本質(zhì)就是減少View的層級。常見的布局優(yōu)化方案如下

在LinearLayout和RelativeLayout都可以完成布局的情況下優(yōu)先選擇RelativeLayout,可以減少View的層級
將常用的布局組件抽取出來使用 < include > 標簽
通過 < ViewStub > 標簽來加載不常用的布局
使用 < Merge > 標簽來減少布局的嵌套層次

網(wǎng)絡(luò)優(yōu)化:常見的網(wǎng)絡(luò)優(yōu)化方案如下

盡量減少網(wǎng)絡(luò)請求,能夠合并的就盡量合并
避免DNS解析,根據(jù)域名查詢可能會耗費上百毫秒的時間,也可能存在DNS劫持的風(fēng)險??梢愿鶕?jù)業(yè)務(wù)需求采用增加動態(tài)更新IP的方式,或者在IP方式訪問失敗時切換到域名訪問方式。
大量數(shù)據(jù)的加載采用分頁的方式
網(wǎng)絡(luò)數(shù)據(jù)傳輸采用GZIP壓縮
加入網(wǎng)絡(luò)數(shù)據(jù)的緩存,避免頻繁請求網(wǎng)絡(luò)
上傳圖片時,在必要的時候壓縮圖片

安裝包優(yōu)化:安裝包優(yōu)化的核心就是減少apk的體積,常見的方案如下

使用混淆,可以在一定程度上減少apk體積,但實際效果微乎其微
減少應(yīng)用中不必要的資源文件,比如圖片,在不影響APP效果的情況下盡量壓縮圖片,有一定的效果
在使用了SO庫的時候優(yōu)先保留v7版本的SO庫,刪掉其他版本的SO庫。原因是在2018年,v7版本的SO庫可以滿足市面上絕大多數(shù)的要求,可能八九年前的手機滿足不了,但我們也沒必要去適配老掉牙的手機。實際開發(fā)中減少apk體積的效果是十分顯著的,如果你使用了很多SO庫,比方說一個版本的SO庫一共10M,那么只保留v7版本,刪掉armeabi和v8版本的SO庫,一共可以減少20M的體積。

啟動優(yōu)化

異步加載了。但是并不是所有的都可以進行異步處理。這里分情況給出一些建議:
1、比如像友盟,bugly這樣的業(yè)務(wù)非必要的可以的異步加載。
2、比如地圖,推送等,非第一時間需要的可以在主線程做延時啟動。當(dāng)程序已經(jīng)啟動起來之后,在進行初始化。
3、對于圖片,網(wǎng)絡(luò)請求框架必須在主線程里初始化了。

Android內(nèi)存優(yōu)化

Android的內(nèi)存優(yōu)化在我看來分為兩點:避免內(nèi)存泄漏、擴大內(nèi)存,其實就是開源節(jié)流。
其實內(nèi)存泄漏的本質(zhì)就是較長生命周期的對象引用了較短生命周期的對象。
常見的內(nèi)存泄漏:
單例模式導(dǎo)致的內(nèi)存泄漏。最常見的例子就是創(chuàng)建這個單例對象需要傳入一個Context,這時候傳入了一個Activity類型的Context,由于單例對象的靜態(tài)屬性,導(dǎo)致它的生命周期是從單例類加載到應(yīng)用程序結(jié)束為止,所以即使已經(jīng)finish掉了傳入的Activity,由于我們的單例對象依然持有Activity的引用,所以導(dǎo)致了內(nèi)存泄漏。解決辦法也很簡單,不要使用Activity類型的Context,使用Application類型的Context可以避免內(nèi)存泄漏。
靜態(tài)變量導(dǎo)致的內(nèi)存泄漏。靜態(tài)變量是放在方法區(qū)中的,它的生命周期是從類加載到程序結(jié)束,可以看到靜態(tài)變量生命周期是非常久的。最常見的因靜態(tài)變量導(dǎo)致內(nèi)存泄漏的例子是我們在Activity中創(chuàng)建了一個靜態(tài)變量,而這個靜態(tài)變量的創(chuàng)建需要傳入Activity的引用this。在這種情況下即使Activity調(diào)用了finish也會導(dǎo)致內(nèi)存泄漏。原因就是因為這個靜態(tài)變量的生命周期幾乎和整個應(yīng)用程序的生命周期一致,它一直持有Activity的引用,從而導(dǎo)致了內(nèi)存泄漏。
非靜態(tài)內(nèi)部類導(dǎo)致的內(nèi)存泄漏。非靜態(tài)內(nèi)部類導(dǎo)致內(nèi)存泄漏的原因是非靜態(tài)內(nèi)部類持有外部類的引用,最常見的例子就是在Activity中使用Handler和Thread了。使用非靜態(tài)內(nèi)部類創(chuàng)建的Handler和Thread在執(zhí)行延時操作的時候會一直持有當(dāng)前Activity的引用,如果在執(zhí)行延時操作的時候就結(jié)束Activity,這樣就會導(dǎo)致內(nèi)存泄漏。解決辦法有兩種:第一種是使用靜態(tài)內(nèi)部類,在靜態(tài)內(nèi)部類中使用弱引用調(diào)用Activity。第二種方法是在Activity的onDestroy中調(diào)用handler.removeCallbacksAndMessages來取消延時事件。
使用資源未及時關(guān)閉導(dǎo)致的內(nèi)存泄漏。常見的例子有:操作各種數(shù)據(jù)流未及時關(guān)閉,操作Bitmap未及時recycle等等。
使用第三方庫未能及時解綁。有的三方庫提供了注冊和解綁的功能,最常見的就是EventBus了,我們都知道使用EventBus要在onCreate中注冊,在onDestroy中解綁。如果沒有解綁的話,EventBus其實是一個單例模式,他會一直持有Activity的引用,導(dǎo)致內(nèi)存泄漏。同樣常見的還有RxJava,在使用Timer操作符做了一些延時操作后也要注意在onDestroy方法中調(diào)用disposable.dispose()來取消操作。
屬性動畫導(dǎo)致的內(nèi)存泄漏。常見的例子就是在屬性動畫執(zhí)行的過程中退出了Activity,這時View對象依然持有Activity的引用從而導(dǎo)致了內(nèi)存泄漏。解決辦法就是在onDestroy中調(diào)用動畫的cancel方法取消屬性動畫。
WebView導(dǎo)致的內(nèi)存泄漏。WebView比較特殊,即使是調(diào)用了它的destroy方法,依然會導(dǎo)致內(nèi)存泄漏。其實避免WebView導(dǎo)致內(nèi)存泄漏的最好方法就是讓W(xué)ebView所在的Activity處于另一個進程中,當(dāng)這個Activity結(jié)束時殺死當(dāng)前WebView所處的進程即可,我記得阿里釘釘?shù)腤ebView就是另外開啟的一個進程,應(yīng)該也是采用這種方法避免內(nèi)存泄漏。
擴大內(nèi)存,為什么要擴大我們的內(nèi)存呢?有時候我們實際開發(fā)中不可避免的要使用很多第三方商業(yè)的SDK,這些SDK其實有好有壞,大廠的SDK可能內(nèi)存泄漏會少一些,但一些小廠的SDK質(zhì)量也就不太靠譜一些。那應(yīng)對這種我們無法改變的情況,最好的辦法就是擴大內(nèi)存。
擴大內(nèi)存通常有兩種方法:一個是在清單文件中的Application下添加largeHeap="true"這個屬性,另一個就是同一個應(yīng)用開啟多個進程來擴大一個應(yīng)用的總內(nèi)存空間。第二種方法其實就很常見了,比方說我使用過個推的SDK,個推的Service其實就是處在另外一個單獨的進程中。
Android中的內(nèi)存優(yōu)化總的來說就是開源和節(jié)流,開源就是擴大內(nèi)存,節(jié)流就是避免內(nèi)存泄漏。

Binder機制

在Linux中,為了避免一個進程對其他進程的干擾,進程之間是相互獨立的。在一個進程中其實還分為用戶空間和內(nèi)核空間。這里的隔離分為兩個部分,進程間的隔離和進程內(nèi)的隔離。
既然進程間存在隔離,那其實也是存在著交互。進程間通信就是IPC,用戶空間和內(nèi)核空間的通信就是系統(tǒng)調(diào)用。
Linux為了保證獨立性和安全性,進程之間不能直接相互訪問,Android是基于Linux的,所以也是需要解決進程間通信的問題。
其實Linux進程間通信有很多方式,比如管道、socket等等。為什么Android進程間通信采用了Binder而不是Linux已有的方式,主要是有這么兩點考慮:性能和安全
性能。在移動設(shè)備上對性能要求是比較嚴苛的。Linux傳統(tǒng)的進程間通信比如管道、socket等等進程間通信是需要復(fù)制兩次數(shù)據(jù),而Binder則只需要一次。所以Binder在性能上是優(yōu)于傳統(tǒng)進程通信的。
安全。傳統(tǒng)的Linux進程通信是不包含通信雙方的身份驗證的,這樣會導(dǎo)致一些安全性問題。而Binder機制自帶身份驗證,從而有效的提高了安全性。
Binder是基于CS架構(gòu)的,有四個主要組成部分。
Client??蛻舳诉M程。
Server。服務(wù)端進程。
ServiceManager。提供注冊、查詢和返回代理服務(wù)對象的功能。
Binder驅(qū)動。主要負責(zé)建立進程間的Binder連接,進程間的數(shù)據(jù)交互等等底層操作。
Binder機制主要的流程是這樣的:
服務(wù)端通過Binder驅(qū)動在ServiceManager中注冊我們的服務(wù)。
客戶端通過Binder驅(qū)動查詢在ServiceManager中注冊的服務(wù)。
ServiceManager通過Binder驅(qū)動返回服務(wù)端的代理對象。
客戶端拿到服務(wù)端的代理對象后即可進行進程間通信。

圖片加載如何避免OOM

我們知道內(nèi)存中的Bitmap大小的計算公式是:長所占像素 * 寬所占像素 * 每個像素所占內(nèi)存。想避免OOM有兩種方法:等比例縮小長寬、減少每個像素所占的內(nèi)存。
等比縮小長寬。我們知道Bitmap的創(chuàng)建是通過BitmapFactory的工廠方法,decodeFile()、decodeStream()、decodeByteArray()、decodeResource()。這些方法中都有一個Options類型的參數(shù),這個Options是BitmapFactory的內(nèi)部類,存儲著BItmap的一些信息。Options中有一個屬性:inSampleSize。我們通過修改inSampleSize可以縮小圖片的長寬,從而減少BItmap所占內(nèi)存。需要注意的是這個inSampleSize大小需要是2的冪次方,如果小于1,代碼會強制讓inSampleSize為1。
減少像素所占內(nèi)存。Options中有一個屬性inPreferredConfig,默認是ARGB_8888,代表每個像素所占尺寸。我們可以通過將之修改為RGB_565或者ARGB_4444來減少一半內(nèi)存。

Android5.0~10.0之間大的變化

MaterialDesign設(shè)計風(fēng)格
支持64位ART虛擬機(5.0推出的ART虛擬機,在5.0之前都是Dalvik。他們的區(qū)別是:Dalvik,每次運行,字節(jié)碼都需要通過即時編譯器轉(zhuǎn)換成機器碼(JIT)。ART,第一次安裝應(yīng)用的時候,字節(jié)碼就會預(yù)先編譯成機器碼(AOT))
通知詳情可以用戶自己設(shè)計

Android 6.0新特性
動態(tài)權(quán)限管理
支持快速充電的切換
支持文件夾拖拽應(yīng)用
相機新增專業(yè)模式

Android 7.0新特性
多窗口支持
V2簽名
增強的Java8語言模式
夜間模式

Android 8.0(O)新特性
優(yōu)化通知:通知渠道 (Notification Channel) 通知標志 休眠 通知超時 通知設(shè)置 通知清除
畫中畫模式:清單中Activity設(shè)置android:supportsPictureInPicture
后臺限制
自動填充框架
系統(tǒng)優(yōu)化等等優(yōu)化很多

Android 9.0(P)新特性
室內(nèi)WIFI定位
“劉?!逼聊恢С?br> 安全增強等等優(yōu)化很多

Android 10.0(Q)新特性
夜間模式:包括手機上的所有應(yīng)用都可以為其設(shè)置暗黑模式。
桌面模式:提供類似于PC的體驗,但是遠遠不能代替PC。
屏幕錄制:通過長按“電源”菜單中的"屏幕快照"來開啟。

Android中常用布局分為傳統(tǒng)布局和新型布局

傳統(tǒng)布局(編寫XML代碼、代碼生成):
框架布局(FrameLayout):
線性布局(LinearLayout):
絕對布局(AbsoluteLayout):
相對布局(RelativeLayout):
表格布局(TableLayout):
約束布局(ConstrainLayout)

Glide的設(shè)計微妙在于:

Glide的生命周期綁定:可以控制圖片的加載狀態(tài)與當(dāng)前頁面的生命周期同步,使整個加載過程隨著頁面的狀態(tài)而啟動/恢復(fù),停止,銷毀
Glide的緩存設(shè)計:通過(三級緩存,Lru算法,Bitmap復(fù)用)對Resource進行緩存設(shè)計
Glide的完整加載過程:采用Engine引擎類暴露了一系列方法供Request操作

A 啟動 B,A 和 B 的生命周期函數(shù)調(diào)用順序?

A onPause
B onCreate
B onStart
B onResume
A onStop

單個 Activity + Fragment 的啟動?

onCreate onStart onResume onPause onStop onDestry

onAttech onCreate onCreateView onActivityCreated onStart onResume onPause onStop onDestryView onDestry onDetach

Java基礎(chǔ)- ==和equals和hashCode的區(qū)別

對于==,如果作用于基本數(shù)據(jù)類型,則直接比較其存儲的“值”是否相等,如果作用于引用類型的變量,則比較的是所指向的對象的地址。
對于 equals 方法,注意:equals不能作用于基本數(shù)據(jù)類型,如果沒有對equals進行重寫,則比較的是 引用類所指向的地址。如果重寫了,比較的就是對象的內(nèi)容。
hashCode用來鑒定兩個對象是否相等,Object類中的hashCode方法返回對象在內(nèi)存中地址轉(zhuǎn)換成的一個int值,所以如果沒有重寫hashCode方法,任何對象的hashCode方法是不相等的。

int、char、long 各占多少字節(jié)數(shù)

byte 是 字節(jié)
bit 是 位
1 byte = 8 bit
char在java中是2個字節(jié),java采用unicode,2個字節(jié)來表示一個字符
short 2個字節(jié)
int 4個字節(jié)
long 8個字節(jié)
float 4個字節(jié)
double 8個字節(jié)

int 與 integer 區(qū)別

Integer是int的包裝類,int則是java的一種基本數(shù)據(jù)類型。
Integer變量必須實例化后才能使用,而int變量不需要。
Integer是對象的引用,當(dāng)new一個Integer時,實際上生成一個指針指向此對象,而int則是直接存儲數(shù)據(jù)值。
Integer默認值是null,int的默認值是0。

String、StringBuffer、StringBuilder區(qū)別

String:
字符串常量 不適用于經(jīng)常要改變值得情況,每次改變相當(dāng)于生成一個新的對象

StringBuffer:
字符串變量 (線程安全)

StringBuilder:
字符串變量(線程不安全) 確保單線程下可用,效率略高于StringBuffer

進程和線程的區(qū)別

進程是cpu資源分配的最小單位,
線程是cpu調(diào)度的最小單位。

進程之間不能共享資源,而線程共享所在進程的地址空間和其它資源。

一個進程內(nèi)可擁有多個線程,進程可開啟進程,也可開啟線程。

一個線程只能屬于一個進程,線程可直接使用同進程的資源,線程依賴于進程而存在。

靜態(tài)屬性和靜態(tài)方法是否可以被繼承?是否可以被重寫?以及原因?

可繼承 不可重寫 而是被隱藏

如果子類里面定義了靜態(tài)方法和屬性,那么這時候父類的靜態(tài)方法或?qū)傩苑Q之為"隱藏"。如果你想要調(diào)用父類的靜態(tài)方法和屬性,直接通過父類名.方法或變量名完成。

Java中實現(xiàn)多態(tài)的機制是什么?

方法的重寫Overriding和重載Overloading是Java多態(tài)性的不同表現(xiàn)

重寫
Overriding是父類與子類之間多態(tài)性的一種表現(xiàn)

重載
Overloading是一個類中多態(tài)性的一種表現(xiàn).

Object類的equal和hashCode方法重寫,為什么?

首先equals與hashcode間的關(guān)系是這樣的:

如果兩個對象相同(即用equals比較返回true),那么它們的hashCode值一定要相同;

如果兩個對象的hashCode相同,它們并不一定相同(即用equals比較返回false)。

由于為了提高程序的效率才實現(xiàn)了hashcode方法,先進行hashcode的比較,如果不同,那沒就不必在進行equals的比較了,這樣就大大減少了equals比較的次數(shù),這對比需要比較的數(shù)量很大的效率提高是很明顯的

List,Set,Map的區(qū)別

Set
是最簡單的一種集合。集合中的對象不按特定的方式排序,并且沒有重復(fù)對象。 Set接口主要實現(xiàn)了兩個實現(xiàn)類:
HashSet:
HashSet類按照哈希算法來存取集合中的對象,存取速度比較快

TreeSet :
TreeSet類實現(xiàn)了SortedSet接口,能夠?qū)现械膶ο筮M行排序。

List
是其元素以線性方式存儲,集合中可以存放重復(fù)對象。

ArrayList() :
代表長度可以改變得數(shù)組??梢詫υ剡M行隨機的訪問,向ArrayList()中插入與刪除元素的速度慢。

LinkedList():
在實現(xiàn)中采用鏈表數(shù)據(jù)結(jié)構(gòu)。插入和刪除速度快,訪問速度慢。

Map
是一種把鍵對象和值對象映射的集合,它的每一個元素都包含一對鍵對象和值對象。 Map沒有繼承于Collection接口 從Map集合中檢索元素時,只要給出鍵對象,就會返回對應(yīng)的值對象。

HashMap:
Map基于散列表的實現(xiàn)。插入和查詢鍵值對的開銷是固定的??梢酝ㄟ^構(gòu)造器設(shè)置容量capacity和負載因子load factor,以調(diào)整容器的性能。

LinkedHashMap:
類似于HashMap,但是迭代遍歷它時,取得鍵值對的順序是其插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一點。而在迭代訪問時發(fā)而更快,因為它使用鏈表維護內(nèi)部次序。

TreeMap :
基于紅黑樹數(shù)據(jù)結(jié)構(gòu)的實現(xiàn)。查看鍵或鍵值對時,它們會被排序(次序由Comparabel或Comparator決定)。TreeMap的特點在 于,你得到的結(jié)果是經(jīng)過排序的。TreeMap是唯一的帶有subMap()方法的Map,它可以返回一個子樹。

WeakHashMao :
弱鍵(weak key)Map,Map中使用的對象也被允許釋放: 這是為解決特殊問題設(shè)計的。如果沒有map之外的引用指向某個“鍵”,則此“鍵”可以被垃圾收集器回收。

HashMap和HashTable的區(qū)別

HashMap
不是線程安全的,效率高一點、方法不是Synchronize的要提供外同步,有containsvalue和containsKey方法。

hashtable
是線程安全,不允許有null的鍵和值,效率稍低,方法是是Synchronize的。有contains方法方法。Hashtable繼承于Dictionary 類

HashSet與HashMap怎么判斷集合元素重復(fù)?

HashSet
不能添加重復(fù)的元素,當(dāng)調(diào)用add(Object)方法時候,

首先會調(diào)用Object的hashCode方法判hashCode是否已經(jīng)存在,如不存在則直接插入元素;如果已存在則調(diào)用Object對象的equals方法判斷是否返回true,如果為true則說明元素已經(jīng)存在,如為false則插入元素。

run()和start()方法區(qū)別

這個問題經(jīng)常被問到,但還是能從此區(qū)分出面試者對Java線程模型的理解程度。start()方法被用來啟動新創(chuàng)建的線程,而且start()內(nèi)部調(diào)用了run()方法,這和直接調(diào)用run()方法的效果不一樣。當(dāng)你調(diào)用run()方法的時候,只會是在原來的線程中調(diào)用,沒有新的線程啟動,start()方法才會啟動新線程。

如何控制某個方法允許并發(fā)訪問線程的個數(shù)?

semaphore.acquire()請求一個信號量,這時候的信號量個數(shù)-1(一旦沒有可使用的信號量,也即信號量個數(shù)變?yōu)樨摂?shù)時,再次請求的時候就會阻塞,直到其他線程釋放了信號量)

semaphore.release()釋放一個信號量,此時信號量個數(shù)+1

談?wù)剋ait/notify關(guān)鍵字的理解

等待對象的同步鎖,需要獲得該對象的同步鎖才可以調(diào)用這個方法,否則編譯可以通過,但運行時會收到一個異常:IllegalMonitorStateException。

調(diào)用任意對象的 wait()方法導(dǎo)致該線程阻塞,該線程不可繼續(xù)執(zhí)行,并且該對象上的鎖被釋放。

喚醒在等待該對象同步鎖的線程(只喚醒一個,如果有多個在等待),注意的是在調(diào)用此方法的時候,并不能確切的喚醒某一個等待狀態(tài)的線程,而是由JVM確定喚醒哪個線程,而且不是按優(yōu)先級。

調(diào)用任意對象的notify()方法則導(dǎo)致因調(diào)用該對象的wait()方法而阻塞的線程中隨機選擇的一個解除阻塞(但要等到獲得鎖后才真正可執(zhí)行)。

如何保證線程安全?

1.synchronized;

2.Object方法中的wait,notify;

3.ThreadLocal機制 來實現(xiàn)的。

Java中堆和棧有什么不同?

為什么把這個問題歸類在多線程和并發(fā)面試題里?因為棧是一塊和線程緊密相關(guān)的內(nèi)存區(qū)域。每個線程都有自己的棧內(nèi)存,用于存儲本地變量,方法參數(shù)和棧調(diào)用,一個線程中存儲的變量對其它線程是不可見的。而堆是所有線程共享的一片公用內(nèi)存區(qū)域。對象都在堆里創(chuàng)建,為了提升效率線程會從堆中弄一個緩存到自己的棧,如果多個線程使用該變量就可能引發(fā)問題,這時volatile變量就可以發(fā)揮作用了,它要求線程從主存中讀取變量的值。
1.堆內(nèi)存用來存放由new創(chuàng)建的對象和數(shù)組。
2.棧內(nèi)存用來存放方法或者局部變量等
3.堆是先進先出,后進后出
4.棧是后進先出,先進后出

相同點:

1.都是屬于Java內(nèi)存的一種
2.系統(tǒng)都會自動去回收它,但是對于堆內(nèi)存一般開發(fā)人員會自動回收它

有三個線程T1,T2,T3,怎么確保它們按順序執(zhí)行?

在多線程中有多種方法讓線程按特定順序執(zhí)行,你可以用線程類的join()方法在一個線程中啟動另一個線程,另外一個線程完成該線程繼續(xù)執(zhí)行。為了確保三個線程的順序你應(yīng)該先啟動最后一個(T3調(diào)用T2,T2調(diào)用T1),這樣T1就會先完成而T3最后完成。

同步和異步的區(qū)別 阻塞和非阻塞的區(qū)別

同步/異步關(guān)注的是消息通信機制 (synchronous communication/ asynchronous communication) 。

所謂同步,就是在發(fā)出一個調(diào)用時,在沒有得到結(jié)果之前, 該調(diào)用就不返回。
異步則是相反,調(diào)用在發(fā)出之后,這個調(diào)用就直接返回了,所以沒有返回結(jié)果
阻塞/非阻塞關(guān)注的是程序在等待調(diào)用結(jié)果(消息,返回值)時的狀態(tài).

阻塞調(diào)用是指調(diào)用結(jié)果返回之前,當(dāng)前線程會被掛起。調(diào)用線程只有在得到結(jié)果之后才會返回。
非阻塞調(diào)用指在不能立刻得到結(jié)果之前,該調(diào)用不會阻塞當(dāng)前線程。

泛型

通配符的出現(xiàn)是為了指定泛型中的類型范圍。
<?>被稱作無限定的通配符:<?>提供了只讀的功能
<? extends T>被稱作有上限的通配符:代表類型 T 及 T 的子類
<? super T>被稱作有下限的通配符:表示包括T在內(nèi)的任何T的父類

泛型擦除

List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass());
打印的結(jié)果為 true 是因為 List<String>和 List<Integer>在 jvm 中的 Class 都是 List.class。泛型信息被擦除了
利用反射,我們繞過編譯器去調(diào)用
List<Integer>[] li2 = new ArrayList<Integer>[];
List<Boolean> li3 = new ArrayList<Boolean>[];
這兩行代碼是無法在編譯器中編譯通過的。原因還是類型擦除帶來的影響。
List<?>[] li3 = new ArrayList<?>[10];
li3[1] = new ArrayList<String>();
List<?> v = li3[1];
借助于無限定通配符卻可以

SparseArray和ListArray的區(qū)別

SparseArray是android里為<Interger,Object>這樣的Hashmap而專門寫的類,目的是提高內(nèi)存效率,其核心是折半查找函數(shù)

SparseArray有兩個優(yōu)點:1.避免了自動裝箱
2.數(shù)據(jù)結(jié)構(gòu)不會依賴于外部對象映射。我們知道HashMap 采用一種所謂的“Hash 算法”來決定每個元素的存儲位置,存放的都是數(shù)組元素的引用,通過每個對象的 hash值來映射對象。而SparseArray則是用數(shù)組數(shù)據(jù)結(jié)構(gòu)來保存映射,然后通過折半查找來找到對象。但其實一般來說,SparseArray執(zhí)行效率比HashMap要慢一點,因為查找需要折半查找,而添加刪除則需要在數(shù)組中執(zhí)行,而HashMap都是通過外部映射。但相對來說影響不大,最主要是SparseArray不需要開辟內(nèi)存空間來額外存儲外部映射,從而節(jié)省內(nèi)存。
List特點:元素有放入順序,元素可重復(fù)
Map特點:元素按鍵值對存儲,無放入順序
Set特點:元素?zé)o放入順序,元素不可重復(fù)(注意:元素雖然無放入順序,但是元素在set中的位置是有該元素的HashCode決定的,其位置其實是固定的)

Java容器特點

List接口有三個實現(xiàn)類:LinkedList,ArrayList,Vector
LinkedList:底層基于鏈表實現(xiàn),鏈表內(nèi)存是散亂的,每一個元素存儲本身內(nèi)存地址的同時還存儲下一個元素的地址。鏈表增刪快,查找慢
ArrayList和Vector的區(qū)別:ArrayList是非線程安全的,效率高;Vector是基于線程安全的,效率低
Set接口有兩個實現(xiàn)類:HashSet(底層由HashMap實現(xiàn)),LinkedHashSet
SortedSet接口有一個實現(xiàn)類:TreeSet(底層由平衡二叉樹實現(xiàn))
Query接口有一個實現(xiàn)類:LinkList
Map接口有三個實現(xiàn)類:HashMap,HashTable,LinkeHashMap
HashMap非線程安全,高效,支持null;HashTable線程安全,低效,不支持null
SortedMap有一個實現(xiàn)類:TreeMap
其實最主要的是,list是用來處理序列的,而set是用來處理集的。Map是知道的,存儲的是鍵值對
set 一般無序不重復(fù).map kv 結(jié)構(gòu) list 有序

Gradle插件怎么做
https怎么實現(xiàn)防抓包

服務(wù)器配置的證書被打包到了客戶端程序,客戶端校驗服務(wù)器返回的https證書的時候會先和本地證書做匹配。

做證書檢測,禁止中間人代理 ssl pining 證書驗證,通過引入自定義證書,然后給OkHttp設(shè)置sslSocketFactory可以有效的防止抓包,但是.cer放到assets下很容易被反編譯,可以通過jdk下的命令keytool -printcert -rfc -file srca.cer導(dǎo)出字符串,然后通過

怎么中斷一個線程?
public class InterruptedDemo {
    public static void main(String[] args) {
        Thread t1 = new Thread() {

            @Override
            public void run() {
                while(true) {
                    System.out.println("The thread is waiting for interrupted!");
                    //中斷處理邏輯
                    if(Thread.currentThread().isInterrupted()) {
                        System.out.println("The thread is interrupted!");
                        break;
                    }
                    //Thread.yield();
                }
            }
        };
        t1.start();
        t1.interrupt();//中斷線程
        //System.out.println("The Thread is interrupted!");
    }
}

因此,通過上面的代碼就可以看到,使用Thread.interrupt()方法處理現(xiàn)場中斷,需要使用Thread.isInterrupted()判斷線程是否被中斷,然后進入中斷處理邏輯代碼。

AsyncTask原理

AsyncTask封裝了Thread和Handler

onPreExecute
在主線程執(zhí)行,在異步任務(wù)執(zhí)行之前被調(diào)用,一般用于一些準備工作
doInBackground
在線程池中執(zhí)行,此方法用于執(zhí)行異步任務(wù),params參數(shù)表示異步任務(wù)的輸入?yún)?shù),在此方法中可以通過publishProgress方法來更新任務(wù)的進度,publishProgress會調(diào)用onProgressUpdate方法,此外此方法需要返回計算結(jié)果給onPostExecute
onProgressUpdate
在主線程執(zhí)行,異步任務(wù)結(jié)束后調(diào)用,返回結(jié)果
onPostExecute
在主線程中執(zhí)行,在異步任務(wù)結(jié)束后,此方法被調(diào)用,其中result參數(shù)是后臺任務(wù)的返回值

OkHttp原理(說了那些攔截器)怎么實現(xiàn)多路復(fù)用的

https://juejin.im/post/5e69a4bf6fb9a07cd74f6ab8

Handler的機制,android內(nèi)部是怎么實現(xiàn)發(fā)送延時消息

用系統(tǒng)開機時間+延時時間得到一個時間T1,當(dāng)手機當(dāng)前時間到了T1的話,就會把消息發(fā)送出去。但有可能UI線程被阻塞了,所以到了T1時間,也不能確保100%這個Message被發(fā)出去的

ViewStub為什么能實現(xiàn)延遲加載

它沒有進行測量和繪制,只在調(diào)flate或是setVisibility時才會加載進布局

HashMap、HashTable、ConCurrentHashMap區(qū)別比較

(1)HashMap是非線程安全的,HashTable是線程安全的。
(2)HashMap的鍵和值都允許有null存在,而HashTable則都不行。
(3)因為線程安全、哈希效率的問題,HashMap效率比HashTable的要高。
它是線程安全的HashMap的實現(xiàn)。

HashTable里使用的是synchronized關(guān)鍵字,這其實是對對象加鎖,鎖住的都是對象整體,當(dāng)Hashtable的大小增加到一定的時候,性能會急劇下降,因為迭代時需要被鎖定很長的時間

ConcurrentHashMap對整個桶數(shù)組進行了分割分段(Segment),然后在每一個分段上都用lock鎖進行保護,相對于HashTable的syn關(guān)鍵字鎖的粒度更精細了一些,并發(fā)性能更好,而HashMap沒有鎖機制,不是線程安全的。

(2)HashMap的鍵值對允許有null,但是ConCurrentHashMap都不允許

synchronized 和 volatile 、ReentrantLock 、CAS 的區(qū)別
JVM 類加載機制、垃圾回收算法對比、Java 虛擬機結(jié)構(gòu)等
Java 線程有哪些狀態(tài),有哪些鎖,各種鎖的區(qū)別

https://blog.csdn.net/songzi1228/article/details/100535928

TCP 有哪些狀態(tài)。

https://zhuanlan.zhihu.com/p/81144898

瀏覽器輸入一個 URL,按下回車網(wǎng)絡(luò)傳輸?shù)牧鞒蹋?/h5>

URL 解析
DNS 查詢
TCP 連接
處理請求
接受響應(yīng)
渲染頁面

Router 原理,如何實現(xiàn)組件間通信,組件化平級調(diào)用數(shù)據(jù)方式
app打包

1、通過aapt打包res資源文件,生成R.java、resources.arsc和res文件(二進制 & 非二進制如res/raw和pic保持原樣)
2、處理.aidl文件,生成對應(yīng)的Java接口文件
3、通過Java Compiler編譯R.java、Java接口文件、Java源文件,生成.class文件
4、通過dex命令,將.class文件和第三方庫中的.class文件處理生成classes.dex
5、通過apkbuilder工具,將aapt生成的resources.arsc和res文件、assets文件和classes.dex一起打包生成apk
6、通過Jarsigner工具,對上面的apk進行debug或release簽名
7、通過zipalign工具,將簽名后的apk進行對齊處理。

APP 啟動流程;

https://juejin.im/post/5d9d948de51d45782c23fabc
Launcher進程請求AMS
AMS發(fā)送創(chuàng)建應(yīng)用進程請求
Zygote進程接受請求并孵化應(yīng)用進程
應(yīng)用進程啟動ActivityThread

Java中線程的狀態(tài)
  1. 初始(NEW):新創(chuàng)建了一個線程對象,但還沒有調(diào)用start()方法。
  2. 運行(RUNNABLE):Java線程中將就緒(ready)和運行中(running)兩種狀態(tài)籠統(tǒng)的稱為“運行”。
    線程對象創(chuàng)建后,其他線程(比如main線程)調(diào)用了該對象的start()方法。該狀態(tài)的線程位于可運行線程池中,等待被線程調(diào)度選中,獲取CPU的使用權(quán),此時處于就緒狀態(tài)(ready)。就緒狀態(tài)的線程在獲得CPU時間片后變?yōu)檫\行中狀態(tài)(running)。
  3. 阻塞(BLOCKED):表示線程阻塞于鎖。
  4. 等待(WAITING):進入該狀態(tài)的線程需要等待其他線程做出一些特定動作(通知或中斷)。
  5. 超時等待(TIMED_WAITING):該狀態(tài)不同于WAITING,它可以在指定的時間后自行返回。
  6. 終止(TERMINATED):表示該線程已經(jīng)執(zhí)行完畢。
Android事件分發(fā)機制A 嵌套 B ,B 嵌套 C,從 C 中心按下,一下滑出到 A,事件分發(fā)的過程

http://www.itdecent.cn/p/46bbe7a0f994

卡頓的底層原理是什么?如何理解16毫秒刷新一次?假如界面沒有更新操作,View會每16毫秒draw一次嗎?

卡頓是由于主線程有耗時操作,導(dǎo)致View繪制掉幀,屏幕每16毫秒會刷新一次,也就是每秒會刷新60次,人眼能感覺到卡頓的幀率是每秒24幀。所以解決卡頓的辦法就是:耗時操作放到子線程、View的層級不能太多、要合理使用include、ViewStub標簽等等這些,來保證每秒畫24幀以上。

最后編輯于
?著作權(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ù)。

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