Android 面試(2020 )

final關(guān)鍵字

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

1.修飾類

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

2.修飾方法

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

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

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

3.修飾變量

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

linklist 和 arraylist

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

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

域名解析

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

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

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

瀏覽器解析html代碼

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

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

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

Client使用認(rèn)證機(jī)構(gòu)的共鑰認(rèn)證證書(shū),并從證書(shū)中取出Server端公鑰。

Client使用共鑰加密一個(gè)隨機(jī)秘鑰,并傳到Server

Server使用私鑰解密出隨機(jī)秘鑰

通信雙方使用隨機(jī)秘鑰最為對(duì)稱秘鑰進(jìn)行加密解密。

常見(jiàn)線程池

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

AsyncTask

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

HandlerThread

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

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

IntentService

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

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

分別是FixedThreadPool、CachedThreadPool、ScheduledThreadPool、SingkeThreadExecutor。

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

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

ScheduledThreadPool
可以延時(shí)啟動(dòng),定時(shí)啟動(dòng)的線程池,適用于需要多個(gè)后臺(tái)線程執(zhí)行周期任務(wù)的場(chǎng)景。

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

網(wǎng)絡(luò)

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

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

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

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

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

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

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

發(fā)送端不知道數(shù)據(jù)是否會(huì)正確接收,當(dāng)然也不用重發(fā),所以說(shuō)
也正由于上面的特點(diǎn),使得UDP的開(kāi)銷更小數(shù)據(jù)傳輸速率更高,因?yàn)椴槐剡M(jìn)行收發(fā)數(shù)據(jù)的確認(rèn),所以UDP的實(shí)時(shí)性更好。

TCP連接的三次握手:

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

類加載過(guò)程,雙親委派模型

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

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

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

join的用法

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

volatile和synchronized的區(qū)別

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

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

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

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

final、finally、finalize區(qū)別

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

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

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

Exception和Error的區(qū)別

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

Android性能優(yōu)化

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

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

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

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

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

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

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

啟動(dòng)優(yōu)化

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

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

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

Binder機(jī)制

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

圖片加載如何避免OOM

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

Android5.0~10.0之間大的變化

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

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

Android 7.0新特性
多窗口支持
V2簽名
增強(qiáng)的Java8語(yǔ)言模式
夜間模式

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

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

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

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

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

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

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

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

A onPause
B onCreate
B onStart
B onResume
A onStop

單個(gè) Activity + Fragment 的啟動(dòng)?

onCreate onStart onResume onPause onStop onDestry

onAttech onCreate onCreateView onActivityCreated onStart onResume onPause onStop onDestryView onDestry onDetach

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

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

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

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

int 與 integer 區(qū)別

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

String、StringBuffer、StringBuilder區(qū)別

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

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

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

進(jìn)程和線程的區(qū)別

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

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

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

一個(gè)線程只能屬于一個(gè)進(jìn)程,線程可直接使用同進(jìn)程的資源,線程依賴于進(jìn)程而存在。

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

可繼承 不可重寫(xiě) 而是被隱藏

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

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

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

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

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

Object類的equal和hashCode方法重寫(xiě),為什么?

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

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

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

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

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

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

TreeSet :
TreeSet類實(shí)現(xiàn)了SortedSet接口,能夠?qū)现械膶?duì)象進(jìn)行排序。

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

ArrayList() :
代表長(zhǎng)度可以改變得數(shù)組??梢詫?duì)元素進(jìn)行隨機(jī)的訪問(wèn),向ArrayList()中插入與刪除元素的速度慢。

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

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

HashMap:
Map基于散列表的實(shí)現(xiàn)。插入和查詢鍵值對(duì)的開(kāi)銷是固定的。可以通過(guò)構(gòu)造器設(shè)置容量capacity和負(fù)載因子load factor,以調(diào)整容器的性能。

LinkedHashMap:
類似于HashMap,但是迭代遍歷它時(shí),取得鍵值對(duì)的順序是其插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一點(diǎn)。而在迭代訪問(wèn)時(shí)發(fā)而更快,因?yàn)樗褂面湵砭S護(hù)內(nèi)部次序。

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

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

HashMap和HashTable的區(qū)別

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

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

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

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

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

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

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

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

semaphore.acquire()請(qǐng)求一個(gè)信號(hào)量,這時(shí)候的信號(hào)量個(gè)數(shù)-1(一旦沒(méi)有可使用的信號(hào)量,也即信號(hào)量個(gè)數(shù)變?yōu)樨?fù)數(shù)時(shí),再次請(qǐng)求的時(shí)候就會(huì)阻塞,直到其他線程釋放了信號(hào)量)

semaphore.release()釋放一個(gè)信號(hào)量,此時(shí)信號(hào)量個(gè)數(shù)+1

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

等待對(duì)象的同步鎖,需要獲得該對(duì)象的同步鎖才可以調(diào)用這個(gè)方法,否則編譯可以通過(guò),但運(yùn)行時(shí)會(huì)收到一個(gè)異常:IllegalMonitorStateException。

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

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

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

如何保證線程安全?

1.synchronized;

2.Object方法中的wait,notify;

3.ThreadLocal機(jī)制 來(lái)實(shí)現(xiàn)的。

Java中堆和棧有什么不同?

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

相同點(diǎn):

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

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

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

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

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

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

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

泛型

通配符的出現(xiàn)是為了指定泛型中的類型范圍。
<?>被稱作無(wú)限定的通配符:<?>提供了只讀的功能
<? 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 是因?yàn)?List<String>和 List<Integer>在 jvm 中的 Class 都是 List.class。泛型信息被擦除了
利用反射,我們繞過(guò)編譯器去調(diào)用
List<Integer>[] li2 = new ArrayList<Integer>[];
List<Boolean> li3 = new ArrayList<Boolean>[];
這兩行代碼是無(wú)法在編譯器中編譯通過(guò)的。原因還是類型擦除帶來(lái)的影響。
List<?>[] li3 = new ArrayList<?>[10];
li3[1] = new ArrayList<String>();
List<?> v = li3[1];
借助于無(wú)限定通配符卻可以

SparseArray和ListArray的區(qū)別

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

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

Java容器特點(diǎn)

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

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

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

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

怎么中斷一個(gè)線程?
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!");
    }
}

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

AsyncTask原理

AsyncTask封裝了Thread和Handler

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

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

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

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

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

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

它沒(méi)有進(jìn)行測(cè)量和繪制,只在調(diào)flate或是setVisibility時(shí)才會(huì)加載進(jìn)布局

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

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

HashTable里使用的是synchronized關(guān)鍵字,這其實(shí)是對(duì)對(duì)象加鎖,鎖住的都是對(duì)象整體,當(dāng)Hashtable的大小增加到一定的時(shí)候,性能會(huì)急劇下降,因?yàn)榈鷷r(shí)需要被鎖定很長(zhǎng)的時(shí)間

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

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

synchronized 和 volatile 、ReentrantLock 、CAS 的區(qū)別
JVM 類加載機(jī)制、垃圾回收算法對(duì)比、Java 虛擬機(jī)結(jié)構(gòu)等

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

Java 線程有哪些狀態(tài),有哪些鎖,各種鎖的區(qū)別

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

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

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

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

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

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

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

APP 啟動(dòng)流程;

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

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

假設(shè)我們?cè)赩iew B的onTouchEvent中返回true

A -> dispatchTouchEvent
A -> onInterceptTouchEvent
B -> dispatchTouchEvent
B -> onInterceptTouchEvent
C -> dispatchTouchEvent
C -> onInterceptTouchEvent
C -> onTouchEvent ACTION_DOWN
B -> onTouchEvent ACTION_DOWN

假設(shè)我們?cè)赩iew B的onInterceptTouchEvent中返回true

A -> dispatchTouchEvent
A -> onInterceptTouchEvent
B -> dispatchTouchEvent
B -> onInterceptTouchEvent
B -> onTouchEvent ACTION_DOWN
A -> onTouchEvent ACTION_DOWN

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

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

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

自定義View

View的工作流程主要是指measure、layout、draw這三大流程,即測(cè)量、布局和繪制,其中measure確定View的測(cè)量寬/高,layout確定View的最終寬/高和四個(gè)頂點(diǎn)的位置,而draw則將View繪制到屏幕上
繪制背景 background.draw(canvas)
繪制自己(onDraw)
繪制 children(dispatchDraw)
繪制裝飾(onDrawScollBars
https://juejin.im/post/5bab9f85e51d450e452abdaf

invalidate()和postInvalidate()的區(qū)別 ?

invalidate()與postInvalidate()都用于刷新View,主要區(qū)別是invalidate()在主線程中調(diào)用,若在子線程中使用需要配合handler;而postInvalidate()可在子線程中直接調(diào)用。
SurfaceView和View的區(qū)別?
surfaceView是在一個(gè)新起的單獨(dú)線程中可以重新繪制畫(huà)面而View必須在UI的主線程中更新畫(huà)面

Scroller是怎么實(shí)現(xiàn)View的彈性滑動(dòng)?

在MotionEvent.ACTION_UP事件觸發(fā)時(shí)調(diào)用startScroll()方法,該方法并沒(méi)有進(jìn)行實(shí)際的滑動(dòng)操作,而是記錄滑動(dòng)相關(guān)量(滑動(dòng)距離、滑動(dòng)時(shí)間)
接著調(diào)用invalidate/postInvalidate()方法,請(qǐng)求View重繪,導(dǎo)致View.draw方法被執(zhí)行
當(dāng)View重繪后會(huì)在draw方法中調(diào)用computeScroll方法,而computeScroll又會(huì)去向Scroller獲取當(dāng)前的scrollX和scrollY;然后通過(guò)scrollTo方法實(shí)現(xiàn)滑動(dòng);接著又調(diào)用postInvalidate方法來(lái)進(jìn)行第二次重繪,和之前流程一樣,如此反復(fù)導(dǎo)致View不斷進(jìn)行小幅度的滑動(dòng),而多次的小幅度滑動(dòng)就組成了彈性滑動(dòng),直到整個(gè)滑動(dòng)過(guò)成結(jié)束

單例為什么要雙重檢查null
service

https://juejin.im/entry/59ffba72f265da432b4a345d

startService / stopService

生命周期順序:onCreate->onStartCommand->onDestroy

bindService / unbindService

生命周期順序:onCreate->onBind->onUnBind->onDestroy
在什么情況下使用 startService 或 bindService 或 同時(shí)使用

startService 和 bindService?使用場(chǎng)景

①如果你只是想要啟動(dòng)一個(gè)后臺(tái)服務(wù)長(zhǎng)期進(jìn)行某項(xiàng)任務(wù)那么使用 startService 便可以了。

②如果你想要與正在運(yùn)行的 Service 取得聯(lián)系,那么有兩種方法,一種是使用 broadcast ,另外是使用 bindService ,前者的缺點(diǎn)是如果交流較為頻繁,容易造成性能上的問(wèn)題,并且 BroadcastReceiver 本身執(zhí)行代碼的時(shí)間是很短的(也許執(zhí)行到一半,后面的代碼便不會(huì)執(zhí)行),而后者則沒(méi)有這些問(wèn)題,因此我們肯定選擇使用 bindService(這個(gè)時(shí)候你便同時(shí)在使用 startService 和 bindService 了,這在 Activity 中更新 Service 的某些運(yùn)行狀態(tài)是相當(dāng)有用的)。

Service與Thread的區(qū)別:

Thread:Thread 是程序執(zhí)行的最小單元,可以用 Thread 來(lái)執(zhí)行一些異步的操作。

Service:Service 是android的一種機(jī)制,當(dāng)它運(yùn)行的時(shí)候如果是Local Service,那么對(duì)應(yīng)的 Service 是運(yùn)行在主進(jìn)程的 main 線程上的。如果是Remote Service,那么對(duì)應(yīng)的 Service 則是運(yùn)行在獨(dú)立進(jìn)程的 main 線程上。

Thread 的運(yùn)行是獨(dú)立的,也就是說(shuō)當(dāng)一個(gè) Activity 被 finish 之后,如果沒(méi)有主動(dòng)停止 Thread 或者 Thread 里的 run 方法沒(méi)有執(zhí)行完畢的話,Thread 也會(huì)一直執(zhí)行。因此這里會(huì)出現(xiàn)一個(gè)問(wèn)題:當(dāng) Activity 被 finish 之后,不再持有該 Thread 的引用,也就是不能再控制該Thread。另一方面,沒(méi)有辦法在不同的 Activity 中對(duì)同一 Thread 進(jìn)行控制。
例如:如果 一個(gè)Thread 需要每隔一段時(shí)間連接服務(wù)器校驗(yàn)數(shù)據(jù),該Thread需要在后臺(tái)一直運(yùn)行。這時(shí)候如果創(chuàng)建該Thread的Activity被結(jié)束了而該Thread沒(méi)有停止,那么將沒(méi)有辦法再控制該Thread,除非kill掉該程序的進(jìn)程。
這時(shí)候如果創(chuàng)建并啟動(dòng)一個(gè) Service ,在 Service 里面創(chuàng)建、運(yùn)行并控制該 Thread,這樣便解決了該問(wèn)題(因?yàn)槿魏?Activity 都可以控制同一個(gè)Service,而系統(tǒng)也只會(huì)創(chuàng)建一個(gè)對(duì)應(yīng) Service 的實(shí)例)。
因此可以把 Service 想象成一種消息服務(wù),可以在任何有 Context 的地方調(diào)用 Context.startService、Context.stopService、Context.bindService、Context.unbindService來(lái)控制它,也可以在 Service 里注冊(cè) BroadcastReceiver,通過(guò)發(fā)送 broadcast 來(lái)達(dá)到控制的目的,這些都是 Thread 做不到的。

EventBus 事件線程切換原理

http://www.itdecent.cn/p/3eb7050ca248

雙重檢驗(yàn)鎖
public class Singleton {
    private static Singleton instance;
    private Singleton (){}
 
    public static Singleton getInstance() {
     if (instance == null) {
         instance = new Singleton();
     }
     return instance;
    }
}

這段代碼簡(jiǎn)單明了,而且使用了懶加載模式,但是卻存在致命的問(wèn)題。當(dāng)有多個(gè)線程并行調(diào)用 getInstance() 的時(shí)候,就會(huì)創(chuàng)建多個(gè)實(shí)例。也就是說(shuō)在多線程下不能正常工作。為了解決上面的問(wèn)題,最簡(jiǎn)單的方法是將整個(gè) getInstance() 方法設(shè)為同步(synchronized)。雖然做到了線程安全,并且解決了多實(shí)例的問(wèn)題,但是它并不高效。因?yàn)樵谌魏螘r(shí)候只能有一個(gè)線程調(diào)用 getInstance() 方法。但是同步操作只需要在第一次調(diào)用時(shí)才被需要,即第一次創(chuàng)建單例實(shí)例對(duì)象時(shí)。這就引出了雙重檢驗(yàn)鎖。


public static Singleton getSingleton() {
    if (instance == null) {                         //Single Checked
        synchronized (Singleton.class) {
            if (instance == null) {                 //Double Checked
                instance = new Singleton();
            }
        }
    }
    return instance ;
}

第一個(gè)if 控制阻塞條件 第二個(gè)控制實(shí)例創(chuàng)建條件

這段代碼看起來(lái)很完美,很可惜,它是有問(wèn)題。主要在于instance = new Singleton()這句,這并非是一個(gè)原子操作,事實(shí)上在 JVM 中這句話大概做了下面 3 件事情。

給 instance 分配內(nèi)存
調(diào)用 Singleton 的構(gòu)造函數(shù)來(lái)初始化成員變量
將instance對(duì)象指向分配的內(nèi)存空間(執(zhí)行完這步 instance 就為非 null 了)
我們只需要將 instance 變量聲明成 volatile 就可以了。

禁止指令重排序優(yōu)化

單例模式:某個(gè)類只能有一個(gè)實(shí)例,提供一個(gè)全局的訪問(wèn)點(diǎn)。

簡(jiǎn)單工廠:一個(gè)工廠類根據(jù)傳入的參量決定創(chuàng)建出那一種產(chǎn)品類的實(shí)例。

工廠方法:定義一個(gè)創(chuàng)建對(duì)象的接口,讓子類決定實(shí)例化那個(gè)類。

抽象工廠:創(chuàng)建相關(guān)或依賴對(duì)象的家族,而無(wú)需明確指定具體類。

建造者模式:封裝一個(gè)復(fù)雜對(duì)象的構(gòu)建過(guò)程,并可以按步驟構(gòu)造。

原型模式:通過(guò)復(fù)制現(xiàn)有的實(shí)例來(lái)創(chuàng)建新的實(shí)例。

適配器模式:將一個(gè)類的方法接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口。

組合模式:將對(duì)象組合成樹(shù)形結(jié)構(gòu)以表示“”部分-整體“”的層次結(jié)構(gòu)。

裝飾模式:動(dòng)態(tài)的給對(duì)象添加新的功能。

代理模式:為其他對(duì)象提供一個(gè)代理以便控制這個(gè)對(duì)象的訪問(wèn)。

亨元(蠅量)模式:通過(guò)共享技術(shù)來(lái)有效的支持大量細(xì)粒度的對(duì)象。

外觀模式:對(duì)外提供一個(gè)統(tǒng)一的方法,來(lái)訪問(wèn)子系統(tǒng)中的一群接口。

橋接模式:將抽象部分和它的實(shí)現(xiàn)部分分離,使它們都可以獨(dú)立的變化。

模板模式:定義一個(gè)算法結(jié)構(gòu),而將一些步驟延遲到子類實(shí)現(xiàn)。

解釋器模式:給定一個(gè)語(yǔ)言,定義它的文法的一種表示,并定義一個(gè)解釋器。

策略模式:定義一系列算法,把他們封裝起來(lái),并且使它們可以相互替換。

狀態(tài)模式:允許一個(gè)對(duì)象在其對(duì)象內(nèi)部狀態(tài)改變時(shí)改變它的行為。

觀察者模式:對(duì)象間的一對(duì)多的依賴關(guān)系。

備忘錄模式:在不破壞封裝的前提下,保持對(duì)象的內(nèi)部狀態(tài)。

中介者模式:用一個(gè)中介對(duì)象來(lái)封裝一系列的對(duì)象交互。

命令模式:將命令請(qǐng)求封裝為一個(gè)對(duì)象,使得可以用不同的請(qǐng)求來(lái)進(jìn)行參數(shù)化。

訪問(wèn)者模式:在不改變數(shù)據(jù)結(jié)構(gòu)的前提下,增加作用于一組對(duì)象元素的新功能。

責(zé)任鏈模式:將請(qǐng)求的發(fā)送者和接收者解耦,使的多個(gè)對(duì)象都有處理這個(gè)請(qǐng)求的機(jī)會(huì)。

迭代器模式:一種遍歷訪問(wèn)聚合對(duì)象中各個(gè)元素的方法,不暴露該對(duì)象的內(nèi)部結(jié)構(gòu)。

Java中父類和子類的代碼執(zhí)行順序。

1.執(zhí)行父類靜態(tài)代碼塊和靜態(tài)變量(靜態(tài)代碼塊先于靜態(tài)變量)
2.執(zhí)行子類靜態(tài)代碼塊和靜態(tài)變量
3.執(zhí)行父類非靜態(tài)代碼塊和非靜態(tài)變量(靜態(tài)代碼塊先于靜態(tài)變量)
4.執(zhí)行父類構(gòu)造函數(shù)
5.執(zhí)行子類非靜態(tài)代碼塊和非靜態(tài)變量
6.執(zhí)行子類構(gòu)造函數(shù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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