亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Android&Java面試題大全—金九銀十面試必備

renweihub / 2299人閱讀

摘要:需要校驗(yàn)字節(jié)信息是否符合規(guī)范,避免惡意信息和不規(guī)范數(shù)據(jù)危害運(yùn)行安全。具有相同哈希值的鍵值對會組成鏈表。通過在協(xié)議下添加了一層協(xié)議對數(shù)據(jù)進(jìn)行加密從而保證了安全。常見的非對稱加密包括等。

類加載過程

Java 中類加載分為 3 個步驟:加載、鏈接、初始化。

加載。 加載是將字節(jié)碼數(shù)據(jù)從不同的數(shù)據(jù)源讀取到JVM內(nèi)存,并映射為 JVM 認(rèn)可的數(shù)據(jù)結(jié)構(gòu),也就是 Class 對象的過程。數(shù)據(jù)源可以是 Jar 文件、Class 文件等等。如果數(shù)據(jù)的格式并不是 ClassFile 的結(jié)構(gòu),則會報 ClassFormatError。

鏈接。 鏈接是類加載的核心部分,這一步分為 3 個步驟:驗(yàn)證、準(zhǔn)備、解析。
驗(yàn)證。 驗(yàn)證是保證JVM安全的重要步驟。JVM需要校驗(yàn)字節(jié)信息是否符合規(guī)范,避免惡意信息和不規(guī)范數(shù)據(jù)危害JVM運(yùn)行安全。如果驗(yàn)證出錯,則會報VerifyError。
準(zhǔn)備。 這一步會創(chuàng)建靜態(tài)變量,并為靜態(tài)變量開辟內(nèi)存空間。
解析。 這一步會將符號引用替換為直接引用。

初始化。 初始化會為靜態(tài)變量賦值,并執(zhí)行靜態(tài)代碼塊中的邏輯。

雙親委派模型

類加載器大致分為3類:啟動類加載器、擴(kuò)展類加載器、應(yīng)用程序類加載器。

啟動類加載器主要加載 jre/lib下的jar文件。

擴(kuò)展類加載器主要加載 jre/lib/ext 下的jar文件。

應(yīng)用程序類加載器主要加載 classpath 下的文件。

所謂的雙親委派模型就是當(dāng)加載一個類時,會優(yōu)先使用父類加載器加載,當(dāng)父類加載器無法加載時才會使用子類加載器去加載。這么做的目的是為了避免類的重復(fù)加載。

Java 中的集合類 HashMap 的原理

HashMap 的內(nèi)部可以看做數(shù)組+鏈表的復(fù)合結(jié)構(gòu)。數(shù)組被分為一個個的桶(bucket)。哈希值決定了鍵值對在數(shù)組中的尋址。具有相同哈希值的鍵值對會組成鏈表。需要注意的是當(dāng)鏈表長度超過閾值(默認(rèn)是8)的時候會觸發(fā)樹化,鏈表會變成樹形結(jié)構(gòu)。

把握HashMap的原理需要關(guān)注4個方法:hash、put、get、resize。

hash方法。 將 key 的 hashCode 值的高位數(shù)據(jù)移位到低位進(jìn)行異或運(yùn)算。這么做的原因是有些 key 的 hashCode 值的差異集中在高位,而哈希尋址是忽略容量以上高位的,這種做法可以有效避免哈希沖突。

put 方法。 put 方法主要有以下幾個步驟:

通過 hash 方法獲取 hash 值,根據(jù) hash 值尋址。

如果未發(fā)生碰撞,直接放到桶中。

如果發(fā)生碰撞,則以鏈表形式放在桶后。

當(dāng)鏈表長度大于閾值后會觸發(fā)樹化,將鏈表轉(zhuǎn)換為紅黑樹。

如果數(shù)組長度達(dá)到閾值,會調(diào)用 resize 方法擴(kuò)展容量。

get方法。 get 方法主要有以下幾個步驟:

通過 hash 方法獲取 hash 值,根據(jù) hash 值尋址。

如果與尋址到桶的 key 相等,直接返回對應(yīng)的 value。

如果發(fā)生沖突,分兩種情況。如果是樹,則調(diào)用 getTreeNode 獲取 value;如果是鏈表則通過循環(huán)遍歷查找對應(yīng)的 value。

resize 方法。 resize 做了兩件事:

將原數(shù)組擴(kuò)展為原來的 2 倍

重新計算 index 索引值,將原節(jié)點(diǎn)重新放到新的數(shù)組中。這一步可以將原先沖突的節(jié)點(diǎn)分散到新的桶中。

sleep 和 wait 的區(qū)別

sleep 方法是 Thread 類中的靜態(tài)方法,wait 是 Object 類中的方法

sleep 并不會釋放同步鎖,而 wait 會釋放同步鎖

sleep 可以在任何地方使用,而 wait 只能在同步方法或者同步代碼塊中使用

sleep 中必須傳入時間,而 wait 可以傳,也可以不傳,不傳時間的話只有 notify 或者 notifyAll - 才能喚醒,傳時間的話在時間之后會自動喚醒

volatile和synchronize的區(qū)別 final、finally、finalize區(qū)別

final 可以修飾類、變量和方法。修飾類代表這個類不可被繼承。修飾變量代表此變量不可被改變。修飾方法表示此方法不可被重寫 (override)。

finally 是保證重點(diǎn)代碼一定會執(zhí)行的一種機(jī)制。通常是使用 try-finally 或者 try-catch-finally 來進(jìn)行文件流的關(guān)閉等操作。

finalize 是 Object 類中的一個方法,它的設(shè)計目的是保證對象在垃圾收集前完成特定資源的回收。finalize 機(jī)制現(xiàn)在已經(jīng)不推薦使用,并且在 JDK 9已經(jīng)被標(biāo)記為 deprecated。

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

Java中引用類型分為四類:強(qiáng)引用、軟引用、弱引用、虛引用。

強(qiáng)引用: 強(qiáng)引用指的是通過 new 對象創(chuàng)建的引用,垃圾回收器即使是內(nèi)存不足也不會回收強(qiáng)引用指向的對象。

軟引用: 軟引用是通過 SoftRefrence 實(shí)現(xiàn)的,它的生命周期比強(qiáng)引用短,在內(nèi)存不足,拋出 OOM 之前,垃圾回收器會回收軟引用引用的對象。軟引用常見的使用場景是存儲一些內(nèi)存敏感的緩存,當(dāng)內(nèi)存不足時會被回收。

弱引用: 弱引用是通過 WeakRefrence 實(shí)現(xiàn)的,它的生命周期比軟引用還短,GC 只要掃描到弱引用的對象就會回收。弱引用常見的使用場景也是存儲一些內(nèi)存敏感的緩存。

虛引用: 虛引用是通過 FanttomRefrence 實(shí)現(xiàn)的,它的生命周期最短,隨時可能被回收。如果一個對象只被虛引用引用,我們無法通過虛引用來訪問這個對象的任何屬性和方法。它的作用僅僅是保證對象在 finalize 后,做某些事情。虛引用常見的使用場景是跟蹤對象被垃圾回收的活動,當(dāng)一個虛引用關(guān)聯(lián)的對象被垃圾回收器回收之前會收到一條系統(tǒng)通知。

Exception 和 Error的區(qū)別

Exception 和 Error 都繼承于 Throwable,在 Java 中,只有 Throwable 類型的對象才能被 throw 或者 catch,它是異常處理機(jī)制的基本組成類型.

Exception 和 Error 體現(xiàn)了 Java 對不同異常情況的分類。Exception 是程序正常運(yùn)行中,可以預(yù)料的意外情況,可能并且應(yīng)該被捕獲,進(jìn)行相應(yīng)的處理。

Error 是指在正常情況下,不大可能出現(xiàn)的情況,絕大部分 Error 都會使程序處于非正常、不可恢復(fù)的狀態(tài)。既然是非正常,所以不便于也不需要捕獲,常見的 OutOfMemoryError 就是 Error 的子類。

Exception 又分為 checked Exception 和 unchecked Exception。

checked Exception 在代碼里必須顯式的進(jìn)行捕獲,這是編譯器檢查的一部分。

unchecked Exception 也就是運(yùn)行時異常,類似空指針異常、數(shù)組越界等,通常是可以避免的邏輯錯誤,具體根據(jù)需求來判斷是否需要捕獲,并不會在編譯器強(qiáng)制要求。

--------------------網(wǎng)絡(luò)相關(guān)面試題------------------- http 與 https 的區(qū)別?https 是如何工作的?

http 是超文本傳輸協(xié)議,而 https 可以簡單理解為安全的 http 協(xié)議。https 通過在 http 協(xié)議下添加了一層 ssl 協(xié)議對數(shù)據(jù)進(jìn)行加密從而保證了安全。https 的作用主要有兩點(diǎn):建立安全的信息傳輸通道,保證數(shù)據(jù)傳輸安全;確認(rèn)網(wǎng)站的真實(shí)性。

http 與 https 的區(qū)別主要如下:

https 需要到 CA 申請證書,很少免費(fèi),因而需要一定的費(fèi)用

http 是明文傳輸,安全性低;而 https 在 http 的基礎(chǔ)上通過 ssl 加密,安全性高

二者的默認(rèn)端口不一樣,http 使用的默認(rèn)端口是80;https使用的默認(rèn)端口是 443

https 的工作流程

提到 https 的話首先要說到加密算法,加密算法分為兩類:對稱加密和非對稱加密。

對稱加密: 加密和解密用的都是相同的秘鑰,優(yōu)點(diǎn)是速度快,缺點(diǎn)是安全性低。常見的對稱加密算法有 DES、AES 等等。

非對稱加密: 非對稱加密有一個秘鑰對,分為公鑰和私鑰。一般來說,私鑰自己持有,公鑰可以公開給對方,優(yōu)點(diǎn)是安全性比對稱加密高,缺點(diǎn)是數(shù)據(jù)傳輸效率比對稱加密低。采用公鑰加密的信息只有對應(yīng)的私鑰可以解密。常見的非對稱加密包括RSA等。
在正式的使用場景中一般都是對稱加密和非對稱加密結(jié)合使用,使用非對稱加密完成秘鑰的傳遞,然后使用對稱秘鑰進(jìn)行數(shù)據(jù)加密和解密。二者結(jié)合既保證了安全性,又提高了數(shù)據(jù)傳輸效率。

https 的具體流程如下:

1.客戶端(通常是瀏覽器)先向服務(wù)器發(fā)出加密通信的請求

支持的協(xié)議版本,比如 TLS 1.0版

一個客戶端生成的隨機(jī)數(shù) random1,稍后用于生成"對話密鑰"

支持的加密方法,比如 RSA 公鑰加密

支持的壓縮方法
2.服務(wù)器收到請求,然后響應(yīng)

確認(rèn)使用的加密通信協(xié)議版本,比如 TLS 1.0版本。如果瀏覽器與服務(wù)器支持的版本不一致,服務(wù)器關(guān)閉加密通信

一個服務(wù)器生成的隨機(jī)數(shù) random2,稍后用于生成"對話密鑰"

確認(rèn)使用的加密方法,比如 RSA 公鑰加密

服務(wù)器證書
3.客戶端收到證書之后會首先會進(jìn)行驗(yàn)證

首先驗(yàn)證證書的安全性

驗(yàn)證通過之后,客戶端會生成一個隨機(jī)數(shù) pre-master secret,然后使用證書中的公鑰進(jìn)行加密,然后傳遞給服務(wù)器端
4.服務(wù)器收到使用公鑰加密的內(nèi)容,在服務(wù)器端使用私鑰解密之后獲得隨機(jī)數(shù) pre-master secret,然后根據(jù) radom1、radom2、pre-master secret 通過一定的算法得出一個對稱加密的秘鑰,作為后面交互過程中使用對稱秘鑰。同時客戶端也會使用 radom1、radom2、pre-master secret,和同樣的算法生成對稱秘鑰。
5.然后再后續(xù)的交互中就使用上一步生成的對稱秘鑰對傳輸?shù)膬?nèi)容進(jìn)行加密和解密。

TCP三次握手流程 -----------------Android面試題------------------- 進(jìn)程間通信的方式有哪幾種

AIDL 、廣播、文件、socket、管道

廣播靜態(tài)注冊和動態(tài)注冊的區(qū)別

動態(tài)注冊廣播不是常駐型廣播,也就是說廣播跟隨 Activity 的生命周期。注意在 Activity 結(jié)束前,移除廣播接收器。 靜態(tài)注冊是常駐型,也就是說當(dāng)應(yīng)用程序關(guān)閉后,如果有信息廣播來,程序也會被系統(tǒng)調(diào)用自動運(yùn)行。

當(dāng)廣播為有序廣播時:優(yōu)先級高的先接收(不分靜態(tài)和動態(tài))。同優(yōu)先級的廣播接收器,動態(tài)優(yōu)先于靜態(tài)

同優(yōu)先級的同類廣播接收器,靜態(tài):先掃描的優(yōu)先于后掃描的,動態(tài):先注冊的優(yōu)先于后注冊的。

當(dāng)廣播為默認(rèn)廣播時:無視優(yōu)先級,動態(tài)廣播接收器優(yōu)先于靜態(tài)廣播接收器。同優(yōu)先級的同類廣播接收器,靜態(tài):先掃描的優(yōu)先于后掃描的,動態(tài):先注冊的優(yōu)先于后冊的。

Android 性能優(yōu)化工具使用(這個問題建議配合Android中的性能優(yōu)化)

Android 中常用的性能優(yōu)化工具包括這些:Android Studio 自帶的 Android Profiler、LeakCanary、BlockCanary

Android 自帶的 Android Profiler 其實(shí)就很好用,Android Profiler 可以檢測三個方面的性能問題:CPU、MEMORY、NETWORK。

LeakCanary 是一個第三方的檢測內(nèi)存泄漏的庫,我們的項目集成之后 LeakCanary 會自動檢測應(yīng)用運(yùn)行期間的內(nèi)存泄漏,并將之輸出給我們。

BlockCanary 也是一個第三方檢測UI卡頓的庫,項目集成后Block也會自動檢測應(yīng)用運(yùn)行期間的UI卡頓,并將之輸出給我們。

Android中的類加載器

PathClassLoader,只能加載系統(tǒng)中已經(jīng)安裝過的 apk
DexClassLoader,可以加載 jar/apk/dex,可以從 SD卡中加載未安裝的 apk

Android中的動畫有哪幾類,它們的特點(diǎn)和區(qū)別是什么

Android中動畫大致分為3類:幀動畫、補(bǔ)間動畫(View Animation)、屬性動畫(Object Animation)。

幀動畫:通過xml配置一組圖片,動態(tài)播放。很少會使用。

補(bǔ)間動畫(View Animation):大致分為旋轉(zhuǎn)、透明、縮放、位移四類操作。很少會使用。

屬性動畫(Object Animation):屬性動畫是現(xiàn)在使用的最多的一種動畫,它比補(bǔ)間動畫更加強(qiáng)大。屬性動畫大致分為兩種使用類型,分別是 ViewPropertyAnimator 和 ObjectAnimator。前者適合一些通用的動畫,比如旋轉(zhuǎn)、位移、縮放和透明,使用方式也很簡單通過 View.animate() 即可得到 ViewPropertyAnimator,之后進(jìn)行相應(yīng)的動畫操作即可。后者適合用于為我們的自定義控件添加動畫,當(dāng)然首先我們應(yīng)該在自定義 View 中添加相應(yīng)的 getXXX() 和 setXXX() 相應(yīng)屬性的 getter 和 setter 方法,這里需要注意的是在 setter 方法內(nèi)改變了自定義 View 中的屬性后要調(diào)用 invalidate() 來刷新View的繪制。之后調(diào)用 ObjectAnimator.of 屬性類型()返回一個 ObjectAnimator,調(diào)用 start() 方法啟動動畫即可。

補(bǔ)間動畫與屬性動畫的區(qū)別:

補(bǔ)間動畫是父容器不斷的繪制 view,看起來像移動了效果,其實(shí) view 沒有變化,還在原地。

是通過不斷改變 view 內(nèi)部的屬性值,真正的改變 view。

Handler 機(jī)制

說到 Handler,就不得不提與之密切相關(guān)的這幾個類:Message、MessageQueue,Looper。

Message。 Message 中有兩個成員變量值得關(guān)注:target 和 callback。

target 其實(shí)就是發(fā)送消息的 Handler 對象

callback 是當(dāng)調(diào)用 handler.post(runnable) 時傳入的 Runnable 類型的任務(wù)。post 事件的本質(zhì)也是創(chuàng)建了一個 Message,將我們傳入的這個 runnable 賦值給創(chuàng)建的Message的 callback 這個成員變量。

MessageQueue。 消息隊列很明顯是存放消息的隊列,值得關(guān)注的是 MessageQueue 中的 next() 方法,它會返回下一個待處理的消息。

Looper。 Looper 消息輪詢器其實(shí)是連接 Handler 和消息隊列的核心。首先我們都知道,如果想要在一個線程中創(chuàng)建一個 Handler,首先要通過 Looper.prepare() 創(chuàng)建 Looper,之后還得調(diào)用 Looper.loop()開啟輪詢。我們著重看一下這兩個方法。

prepare()。 這個方法做了兩件事:首先通過ThreadLocal.get()獲取當(dāng)前線程中的Looper,如果不為空,則會拋出一個RunTimeException,意思是一個線程不能創(chuàng)建2個Looper。如果為null則執(zhí)行下一步。第二步是創(chuàng)建了一個Looper,并通過 ThreadLocal.set(looper)。將我們創(chuàng)建的Looper與當(dāng)前線程綁定。這里需要提一下的是消息隊列的創(chuàng)建其實(shí)就發(fā)生在Looper的構(gòu)造方法中。

loop()。 這個方法開啟了整個事件機(jī)制的輪詢。它的本質(zhì)是開啟了一個死循環(huán),不斷的通過 MessageQueue的next()方法獲取消息。拿到消息后會調(diào)用 msg.target.dispatchMessage()來做處理。其實(shí)我們在說到 Message 的時候提到過,msg.target 其實(shí)就是發(fā)送這個消息的 handler。這句代碼的本質(zhì)就是調(diào)用 handler的dispatchMessage()。

Handler。 上面做了這么多鋪墊,終于到了最重要的部分。Handler 的分析著重在兩個部分:發(fā)送消息和處理消息。

發(fā)送消息。其實(shí)發(fā)送消息除了 sendMessage 之外還有 sendMessageDelayed 和 post 以及 postDelayed 等等不同的方式。但它們的本質(zhì)都是調(diào)用了 sendMessageAtTime。在 sendMessageAtTime 這個方法中調(diào)用了 enqueueMessage。在 enqueueMessage 這個方法中做了兩件事:通過 msg.target = this 實(shí)現(xiàn)了消息與當(dāng)前 handler 的綁定。然后通過 queue.enqueueMessage 實(shí)現(xiàn)了消息入隊。

處理消息。 消息處理的核心其實(shí)就是dispatchMessage()這個方法。這個方法里面的邏輯很簡單,先判斷 msg.callback 是否為 null,如果不為空則執(zhí)行這個 runnable。如果為空則會執(zhí)行我們的handleMessage方法。

Android 性能優(yōu)化

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

內(nèi)存優(yōu)化: 下一個問題就是。

布局優(yōu)化: 布局優(yōu)化的本質(zhì)就是減少 View 的層級。常見的布局優(yōu)化方案如下

在 LinearLayout 和 RelativeLayout 都可以完成布局的情況下優(yōu)先選擇 RelativeLayout,可以減少 View 的層級

將常用的布局組件抽取出來使用 < include >標(biāo)簽

通過 < ViewStub >標(biāo)簽來加載不常用的布局

使用 < Merge >標(biāo)簽來減少布局的嵌套層次

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

盡量減少網(wǎng)絡(luò)請求,能夠合并的就盡量合并

避免 DNS 解析,根據(jù)域名查詢可能會耗費(fèi)上百毫秒的時間,也可能存在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 體積,但實(shí)際效果微乎其微

減少應(yīng)用中不必要的資源文件,比如圖片,在不影響 APP 效果的情況下盡量壓縮圖片,有一定的效果

在使用了 SO 庫的時候優(yōu)先保留 v7 版本的 SO 庫,刪掉其他版本的SO庫。原因是在 2018 年,v7 版本的 SO 庫可以滿足市面上絕大多數(shù)的要求,可能八九年前的手機(jī)滿足不了,但我們也沒必要去適配老掉牙的手機(jī)。實(shí)際開發(fā)中減少 apk 體積的效果是十分顯著的,如果你使用了很多 SO 庫,比方說一個版本的SO庫一共 10M,那么只保留 v7 版本,刪掉 armeabi 和 v8 版本的 SO 庫,一共可以減少 20M 的體積。

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

Android的內(nèi)存優(yōu)化在我看來分為兩點(diǎn):避免內(nèi)存泄漏、擴(kuò)大內(nèi)存,其實(shí)就是開源節(jié)流。
其實(shí)內(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)存泄漏。原因就是因?yàn)檫@個靜態(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 其實(shí)是一個單例模式,他會一直持有 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)存泄漏。其實(shí)避免WebView導(dǎo)致內(nèi)存泄漏的最好方法就是讓W(xué)ebView所在的Activity處于另一個進(jìn)程中,當(dāng)這個 Activity 結(jié)束時殺死當(dāng)前 WebView 所處的進(jìn)程即可,我記得阿里釘釘?shù)?WebView 就是另外開啟的一個進(jìn)程,應(yīng)該也是采用這種方法避免內(nèi)存泄漏。

擴(kuò)大內(nèi)存

為什么要擴(kuò)大我們的內(nèi)存呢?有時候我們實(shí)際開發(fā)中不可避免的要使用很多第三方商業(yè)的 SDK,這些 SDK 其實(shí)有好有壞,大廠的 SDK 可能內(nèi)存泄漏會少一些,但一些小廠的 SDK 質(zhì)量也就不太靠譜一些。那應(yīng)對這種我們無法改變的情況,最好的辦法就是擴(kuò)大內(nèi)存。
擴(kuò)大內(nèi)存通常有兩種方法:一個是在清單文件中的 Application 下添加largeHeap="true"這個屬性,另一個就是同一個應(yīng)用開啟多個進(jìn)程來擴(kuò)大一個應(yīng)用的總內(nèi)存空間。第二種方法其實(shí)就很常見了,比方說我使用過個推的 S DK,個推的 Service 其實(shí)就是處在另外一個多帶帶的進(jìn)程中。
Android 中的內(nèi)存優(yōu)化總的來說就是開源和節(jié)流,開源就是擴(kuò)大內(nèi)存,節(jié)流就是避免內(nèi)存泄漏。

Binder 機(jī)制

在Linux中,為了避免一個進(jìn)程對其他進(jìn)程的干擾,進(jìn)程之間是相互獨(dú)立的。在一個進(jìn)程中其實(shí)還分為用戶空間和內(nèi)核空間。這里的隔離分為兩個部分,進(jìn)程間的隔離和進(jìn)程內(nèi)的隔離。
既然進(jìn)程間存在隔離,那其實(shí)也是存在著交互。進(jìn)程間通信就是 IPC,用戶空間和內(nèi)核空間的通信就是系統(tǒng)調(diào)用。
Linux 為了保證獨(dú)立性和安全性,進(jìn)程之間不能直接相互訪問,Android 是基于 Linux 的,所以也是需要解決進(jìn)程間通信的問題。
其實(shí) Linux 進(jìn)程間通信有很多方式,比如管道、socket 等等。為什么 Android 進(jìn)程間通信采用了Binder而不是 Linux
已有的方式,主要是有這么兩點(diǎn)考慮:性能和安全

性能。 在移動設(shè)備上對性能要求是比較嚴(yán)苛的。Linux傳統(tǒng)的進(jìn)程間通信比如管道、socket等等進(jìn)程間通信是需要復(fù)制兩次數(shù)據(jù),而Binder則只需要一次。所以Binder在性能上是優(yōu)于傳統(tǒng)進(jìn)程通信的。

安全。 傳統(tǒng)的 Linux 進(jìn)程通信是不包含通信雙方的身份驗(yàn)證的,這樣會導(dǎo)致一些安全性問題。而Binder機(jī)制自帶身份驗(yàn)證,從而有效的提高了安全性。

Binder 是基于 CS 架構(gòu)的,有四個主要組成部分。

Client。 客戶端進(jìn)程。

Server。 服務(wù)端進(jìn)程。

ServiceManager。 提供注冊、查詢和返回代理服務(wù)對象的功能。

Binder 驅(qū)動。 主要負(fù)責(zé)建立進(jìn)程間的 Binder 連接,進(jìn)程間的數(shù)據(jù)交互等等底層操作。

Binder 機(jī)制主要的流程是這樣的:

服務(wù)端通過Binder驅(qū)動在 ServiceManager 中注冊我們的服務(wù)。

客戶端通過Binder驅(qū)動查詢在 ServiceManager 中注冊的服務(wù)。

ServiceManager 通過 inder 驅(qū)動返回服務(wù)端的代理對象。

客戶端拿到服務(wù)端的代理對象后即可進(jìn)行進(jìn)程間通信。

LruCache的原理

LruCache 的核心原理就是對 LinkedHashMap 的有效利用,它的內(nèi)部存在一個 LinkedHashMap 成員變量。值得我們關(guān)注的有四個方法:構(gòu)造方法、get、put、trimToSize。

構(gòu)造方法: 在 LruCache 的構(gòu)造方法中做了兩件事,設(shè)置了 maxSize、創(chuàng)建了一個 LinkedHashMap。這里值得注意的是 LruCache 將 LinkedHashMap的accessOrder 設(shè)置為了 true,accessOrder 就是遍歷這個LinkedHashMap 的輸出順序。true 代表按照訪問順序輸出,false代表按添加順序輸出,因?yàn)橥ǔ6际前凑仗砑禹樞蜉敵?,所?accessOrder 這個屬性默認(rèn)是 false,但我們的 LruCache 需要按訪問順序輸出,所以顯式的將 accessOrder 設(shè)置為 true。

get方法: 本質(zhì)上是調(diào)用 LinkedHashMap 的 get 方法,由于我們將 accessOrder 設(shè)置為了 true,所以每調(diào)用一次get方法,就會將我們訪問的當(dāng)前元素放置到這個LinkedHashMap的尾部。

put方法: 本質(zhì)上也是調(diào)用了 LinkedHashMap 的 put 方法,由于 LinkedHashMap 的特性,每調(diào)用一次 put 方法,也會將新加入的元素放置到 LinkedHashMap 的尾部。添加之后會調(diào)用 trimToSize 方法來保證添加后的內(nèi)存不超過 maxSize。

trimToSize方法: trimToSize 方法的內(nèi)部其實(shí)是開啟了一個 while(true)的死循環(huán),不斷的從 LinkedHashMap 的首部刪除元素,直到刪除之后的內(nèi)存小于 maxSize 之后使用 break 跳出循環(huán)。

其實(shí)到這里我們可以總結(jié)一下,為什么這個算法叫 最近最少使用 算法呢?原理很簡單,我們的每次 put 或者get都可以看做一次訪問,由于 LinkedHashMap 的特性,會將每次訪問到的元素放置到尾部。當(dāng)我們的內(nèi)存達(dá)到閾值后,會觸發(fā) trimToSize 方法來刪除 LinkedHashMap 首部的元素,直到當(dāng)前內(nèi)存小于 maxSize。為什么刪除首部的元素,原因很明顯:我們最近經(jīng)常訪問的元素都會放置到尾部,那首部的元素肯定就是 最近最少使用 的元素了,因此當(dāng)內(nèi)存不足時應(yīng)當(dāng)優(yōu)先刪除這些元素。

設(shè)計一個圖片的異步加載框架

設(shè)計一個圖片加載框架,肯定要用到圖片加載的三級緩存的思想。三級緩存分為內(nèi)存緩存、本地緩存和網(wǎng)絡(luò)緩存。
內(nèi)存緩存 :將Bitmap緩存到內(nèi)存中,運(yùn)行速度快,但是內(nèi)存容量小。
本地緩存 :將圖片緩存到文件中,速度較慢,但容量較大。
網(wǎng)絡(luò)緩存 :從網(wǎng)絡(luò)獲取圖片,速度受網(wǎng)絡(luò)影響。
如果我們設(shè)計一個圖片加載框架,流程一定是這樣的:

拿到圖片url后首先從內(nèi)存中查找BItmap,如果找到直接加載。

內(nèi)存中沒有找到,會從本地緩存中查找,如果本地緩存可以找到,則直接加載。

內(nèi)存和本地都沒有找到,這時會從網(wǎng)絡(luò)下載圖片,下載到后會加載圖片,并且將下載到的圖片放到內(nèi)存緩存和本地緩存中。

上面是一些基本的概念,如果是具體的代碼實(shí)現(xiàn)的話,大概需要這么幾個方面的文件:

首先需要確定我們的內(nèi)存緩存,這里一般用的都是 LruCache。

確定本地緩存,通常用的是 DiskLruCache,這里需要注意的是圖片緩存的文件名一般是 url 被 MD5 加密后的字符串,為了避免文件名直接暴露圖片的 url。

內(nèi)存緩存和本地緩存確定之后,需要我們創(chuàng)建一個新的類 MemeryAndDiskCache,當(dāng)然,名字隨便起,這個類包含了之前提到的 LruCache 和 DiskLruCache。在 MemeryAndDiskCache 這個類中我們定義兩個方法,一個是 getBitmap,另一個是 putBitmap,對應(yīng)著圖片的獲取和緩存,內(nèi)部的邏輯也很簡單。getBitmap中按內(nèi)存、本地的優(yōu)先級去取 BItmap,putBitmap 中先緩存內(nèi)存,之后緩存到本地。

在緩存策略類確定好之后,我們創(chuàng)建一個 ImageLoader 類,這個類必須包含兩個方法,一個是展示圖片 displayImage(url,imageView),另一個是從網(wǎng)絡(luò)獲取圖片downloadImage(url,imageView)。在展示圖片方法中首先要通過 ImageView.setTag(url),將 url 和 imageView 進(jìn)行綁定,這是為了避免在列表中加載網(wǎng)絡(luò)圖片時會由于ImageView的復(fù)用導(dǎo)致的圖片錯位的 bug。之后會從 MemeryAndDiskCache 中獲取緩存,如果存在,直接加載;如果不存在,則調(diào)用從網(wǎng)絡(luò)獲取圖片這個方法。從網(wǎng)絡(luò)獲取圖片方法很多,這里我一般都會使用 OkHttp+Retrofit。當(dāng)從網(wǎng)絡(luò)中獲取到圖片之后,首先判斷一下imageView.getTag()與圖片的 url 是否一致,如果一致則加載圖片,如果不一致則不加載圖片,通過這樣的方式避免了列表中異步加載圖片的錯位。同時在獲取到圖片之后會通過 MemeryAndDiskCache 來緩存圖片。

Android中的事件分發(fā)機(jī)制

在我們的手指觸摸到屏幕的時候,事件其實(shí)是通過 Activity -> ViewGroup -> View 這樣的流程到達(dá)最后響應(yīng)我們觸摸事件的 View。
說到事件分發(fā),必不可少的是這幾個方法:dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent。接下來就按照Activity -> ViewGroup -> View 的流程來大致說一下事件分發(fā)機(jī)制。
我們的手指觸摸到屏幕的時候,會觸發(fā)一個 Action_Down 類型的事件,當(dāng)前頁面的 Activity 會首先做出響應(yīng),也就是說會走到 Activity 的 dispatchTouchEvent() 方法內(nèi)。在這個方法內(nèi)部簡單來說是這么一個邏輯:

調(diào)用 getWindow.superDispatchTouchEvent()。

如果上一步返回 true,直接返回 true;否則就 return 自己的 onTouchEvent()。
這個邏輯很好理解,getWindow().superDispatchTouchEvent() 如果返回 true 代表當(dāng)前事件已經(jīng)被處理,無需調(diào)用自己的 onTouchEvent;否則代表事件并沒有被處理,需要 Activity 自己處理,也就是調(diào)用自己的 onTouchEvent。

getWindow()方法返回了一個 Window 類型的對象,這個我們都知道,在 Android 中,PhoneWindow 是Window 的唯一實(shí)現(xiàn)類。所以這句本質(zhì)上是調(diào)用了`PhoneWindow中的superDispatchTouchEvent()。
而在 PhoneWindow 的這個方法中實(shí)際調(diào)用了mDecor.superDispatchTouchEvent(event)。這個 mDecor 就是 DecorView,它是 FrameLayout 的一個子類,在 DecorView 中的 superDispatchTouchEvent() 中調(diào)用的是 super.dispatchTouchEvent()。到這里就很明顯了,DecorView 是一個 FrameLayout 的子類,F(xiàn)rameLayout 是一個 ViewGroup 的子類,本質(zhì)上調(diào)用的還是 ViewGroup的dispatchTouchEvent()。
分析到這里,我們的事件已經(jīng)從 Activity 傳遞到了 ViewGroup,接下來我們來分析下 ViewGroup 中的這幾個事件處理方法。
在 ViewGroup 中的 dispatchTouchEvent()中的邏輯大致如下:

通過 onInterceptTouchEvent() 判斷當(dāng)前 ViewGroup 是否攔截事件,默認(rèn)的 ViewGroup 都是不攔截的;

如果攔截,則 return 自己的 onTouchEvent();

如果不攔截,則根據(jù) child.dispatchTouchEvent()的返回值判斷。如果返回 true,則 return true;否則 return 自己的 onTouchEvent(),在這里實(shí)現(xiàn)了未處理事件的向上傳遞。

通常情況下 ViewGroup 的 onInterceptTouchEvent()都返回 false,也就是不攔截。這里需要注意的是事件序列,比如 Down 事件、Move 事件......Up事件,從 Down 到 Up 是一個完整的事件序列,對應(yīng)著手指從按下到抬起這一系列的事件,如果 ViewGroup 攔截了 Down 事件,那么后續(xù)事件都會交給這個 ViewGroup的onTouchEvent。如果 ViewGroup 攔截的不是 Down 事件,那么會給之前處理這個 Down 事件的 View 發(fā)送一個 Action_Cancel 類型的事件,通知子 View 這個后續(xù)的事件序列已經(jīng)被 ViewGroup 接管了,子 View 恢復(fù)之前的狀態(tài)即可。
這里舉一個常見的例子:在一個 Recyclerview 鐘有很多的 Button,我們首先按下了一個 button,然后滑動一段距離再松開,這時候 Recyclerview 會跟著滑動,并不會觸發(fā)這個 button 的點(diǎn)擊事件。這個例子中,當(dāng)我們按下 button 時,這個 button 接收到了 Action_Down 事件,正常情況下后續(xù)的事件序列應(yīng)該由這個 button處理。但我們滑動了一段距離,這時 Recyclerview 察覺到這是一個滑動操作,攔截了這個事件序列,走了自身的 onTouchEvent()方法,反映在屏幕上就是列表的滑動。而這時 button 仍然處于按下的狀態(tài),所以在攔截的時候需要發(fā)送一個 Action_Cancel 來通知 button 恢復(fù)之前狀態(tài)。
事件分發(fā)最終會走到 View 的 dispatchTouchEvent()中。在 View 的 dispatchTouchEvent() 中沒有 onInterceptTouchEvent(),這也很容易理解,View 不是 ViewGroup,不會包含其他子 View,所以也不存在攔截不攔截這一說。忽略一些細(xì)節(jié),View 的 dispatchTouchEvent()中直接 return 了自己的 onTouchEvent()。如果 onTouchEvent()返回 true 代表事件被處理,否則未處理的事件會向上傳遞,直到有 View 處理了事件或者一直沒有處理,最終到達(dá)了 Activity 的 onTouchEvent() 終止。
這里經(jīng)常有人問 onTouch 和 onTouchEvent 的區(qū)別。首先,這兩個方法都在 View 的 dispatchTouchEvent()中,是這么一個邏輯:

如果 touchListener 不為 null,并且這個 View 是 enable 的,而且 onTouch 返回的是 true,滿足這三個條件時會直接 return true,不會走 onTouchEvent()方法。

上面只要有一個條件不滿足,就會走到 onTouchEvent()方法中。所以 onTouch 的順序是在 onTouchEvent 之前的。

View的繪制流程

視圖繪制的起點(diǎn)在 ViewRootImpl 類的 performTraversals()方法,在這個方法內(nèi)其實(shí)是按照順序依次調(diào)用了 mView.measure()、mView.layout()、mView.draw()
View的繪制流程分為3步:測量、布局、繪制,分別對應(yīng)3個方法 measure、layout、draw。

測量階段。 measure 方法會被父 View 調(diào)用,在measure 方法中做一些優(yōu)化和準(zhǔn)備工作后會調(diào)用 onMeasure 方法進(jìn)行實(shí)際的自我測量。onMeasure方法在View和ViewGroup做的事情是不一樣的:

View。 View 中的 onMeasure 方法會計算自己的尺寸并通過 setMeasureDimension 保存。

ViewGroup。 ViewGroup 中的 onMeasure 方法會調(diào)用所有子 iew的measure 方法進(jìn)行自我測量并保存。然后通過子View的尺寸和位置計算出自己的尺寸并保存。

布局階段。 layout 方法會被父View調(diào)用,layout 方法會保存父 View 傳進(jìn)來的尺寸和位置,并調(diào)用 onLayout 進(jìn)行實(shí)際的內(nèi)部布局。onLayout 在 View 和 ViewGroup 中做的事情也是不一樣的:

View。 因?yàn)?View 是沒有子 View 的,所以View的onLayout里面什么都不做。

ViewGroup。 ViewGroup 中的 onLayout 方法會調(diào)用所有子 View 的 layout 方法,把尺寸和位置傳給他們,讓他們完成自我的內(nèi)部布局。

繪制階段。 draw 方法會做一些調(diào)度工作,然后會調(diào)用 onDraw 方法進(jìn)行 View 的自我繪制。draw 方法的調(diào)度流程大致是這樣的:

繪制背景。 對應(yīng) drawBackground(Canvas)方法。

繪制主體。 對應(yīng) onDraw(Canvas)方法。

繪制子View。 對應(yīng) dispatchDraw(Canvas)方法。

繪制滑動相關(guān)和前景。 對應(yīng) onDrawForeground(Canvas)。

Android與 js 是如何交互的

在 Android 中,Android 與js 的交互分為兩個方面:Android 調(diào)用 js 里的方法、js 調(diào)用 Android 中的方法。

Android調(diào)js。 Android 調(diào) js 有兩種方法:

WebView.loadUrl("javascript:js中的方法名")。 這種方法的優(yōu)點(diǎn)是很簡潔,缺點(diǎn)是沒有返回值,如果需要拿到j(luò)s方法的返回值則需要js調(diào)用Android中的方法來拿到這個返回值。

WebView.evaluateJavaScript("javascript:js中的方法名",ValueCallback)。 這種方法比 loadUrl 好的是可以通過 ValueCallback 這個回調(diào)拿到 js方法的返回值。缺點(diǎn)是這個方法 Android4.4 才有,兼容性較差。不過放在 2018 年來說,市面上絕大多數(shù) App 都要求最低版本是 4.4 了,所以我認(rèn)為這個兼容性問題不大。

js 調(diào) Android。 js 調(diào) Android有三種方法:

WebView.addJavascriptInterface()。 這是官方解決 js 調(diào)用 Android 方法的方案,需要注意的是要在供 js 調(diào)用的 Android 方法上加上 @JavascriptInterface 注解,以避免安全漏洞。這種方案的缺點(diǎn)是 Android4.2 以前會有安全漏洞,不過在 4.2 以后已經(jīng)修復(fù)了。同樣,在 2018 年來說,兼容性問題不大。

重寫 WebViewClient的shouldOverrideUrlLoading()方法來攔截url, 拿到 url 后進(jìn)行解析,如果符合雙方的規(guī)定,即可調(diào)用 Android 方法。優(yōu)點(diǎn)是避免了 Android4.2 以前的安全漏洞,缺點(diǎn)也很明顯,無法直接拿到調(diào)用 Android 方法的返回值,只能通過 Android 調(diào)用 js 方法來獲取返回值。

重寫 WebChromClient 的 onJsPrompt() 方法,同前一個方式一樣,拿到 url 之后先進(jìn)行解析,如果符合雙方規(guī)定,即可調(diào)用Android方法。最后如果需要返回值,通過 result.confirm("Android方法返回值") 即可將 Android 的返回值返回給 js。方法的優(yōu)點(diǎn)是沒有漏洞,也沒有兼容性限制,同時還可以方便的獲取 Android 方法的返回值。其實(shí)這里需要注意的是在 WebChromeClient 中除 了 onJsPrompt 之外還有 onJsAlert 和 onJsConfirm 方法。那么為什么不選擇另兩個方法呢?原因在于 onJsAlert 是沒有返回值的,而 onJsConfirm 只有 true 和 false 兩個返回值,同時在前端開發(fā)中 prompt 方法基本不會被調(diào)用,所以才會采用 onJsPrompt。

Activity 啟動過程 SparseArray 原理

SparseArray,通常來講是 Android 中用來替代 HashMap 的一個數(shù)據(jù)結(jié)構(gòu)。
準(zhǔn)確來講,是用來替換key為 Integer 類型,value為Object 類型的HashMap。需要注意的是 SparseArray 僅僅實(shí)現(xiàn)了 Cloneable 接口,所以不能用Map來聲明。
從內(nèi)部結(jié)構(gòu)來講,SparseArray 內(nèi)部由兩個數(shù)組組成,一個是 int[]類型的 mKeys,用來存放所有的鍵;另一個是 Object[]類型的 mValues,用來存放所有的值。
最常見的是拿 SparseArray 跟HashMap 來做對比,由于 SparseArray 內(nèi)部組成是兩個數(shù)組,所以占用內(nèi)存比 HashMap 要小。我們都知道,增刪改查等操作都首先需要找到相應(yīng)的鍵值對,而 SparseArray 內(nèi)部是通過二分查找來尋址的,效率很明顯要低于 HashMap 的常數(shù)級別的時間復(fù)雜度。提到二分查找,這里還需要提一下的是二分查找的前提是數(shù)組已經(jīng)是排好序的,沒錯,SparseArray 中就是按照key進(jìn)行升序排列的。
綜合起來來說,SparseArray 所占空間優(yōu)于 HashMap,而效率低于 HashMap,是典型的時間換空間,適合較小容量的存儲。
從源碼角度來說,我認(rèn)為需要注意的是 SparseArray的remove()、put()和 gc()方法。

remove()。 SparseArray 的 remove() 方法并不是直接刪除之后再壓縮數(shù)組,而是將要刪除的 value 設(shè)置為 DELETE 這個 SparseArray 的靜態(tài)屬性,這個 DELETE 其實(shí)就是一個 Object 對象,同時會將 SparseArray 中的 mGarbage 這個屬性設(shè)置為 true,這個屬性是便于在合適的時候調(diào)用自身的 gc()方法壓縮數(shù)組來避免浪費(fèi)空間。這樣可以提高效率,如果將來要添加的key等于刪除的key,那么會將要添加的 value 覆蓋 DELETE。

gc()。 SparseArray 中的 gc() 方法跟 JVM 的 GC 其實(shí)完全沒有任何關(guān)系。`gc() 方法的內(nèi)部實(shí)際上就是一個for循環(huán),將 value 不為 DELETE 的鍵值對往前移動覆蓋value 為DELETE的鍵值對來實(shí)現(xiàn)數(shù)組的壓縮,同時將 mGarbage 置為 false,避免內(nèi)存的浪費(fèi)。

put()。 put 方法是這么一個邏輯,如果通過二分查找 在 mKeys 數(shù)組中找到了 key,那么直接覆蓋 value 即可。如果沒有找到,會拿到與數(shù)組中與要添加的 key 最接近的 key 索引,如果這個索引對應(yīng)的 value 為 DELETE,則直接把新的 value 覆蓋 DELET 即可,在這里可以避免數(shù)組元素的移動,從而提高了效率。如果 value 不為 DELETE,會判斷 mGarbage,如果為 true,則會調(diào)用 gc()方法壓縮數(shù)組,之后會找到合適的索引,將索引之后的鍵值對后移,插入新的鍵值對,這個過程中可能會觸發(fā)數(shù)組的擴(kuò)容。

圖片加載如何避免 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 可以縮小圖片的長寬,從而減少 BItma p 所占內(nèi)存。需要注意的是這個 inSampleSize 大小需要是 2 的冪次方,如果小于 1,代碼會強(qiáng)制讓inSampleSize為1。

減少像素所占內(nèi)存。Options 中有一個屬性 inPreferredConfig,默認(rèn)是 ARGB_8888,代表每個像素所占尺寸。我們可以通過將之修改為 RGB_565 或者 ARGB_4444 來減少一半內(nèi)存。

大圖加載

加載高清大圖,比如清明上河圖,首先屏幕是顯示不下的,而且考慮到內(nèi)存情況,也不可能一次性全部加載到內(nèi)存。這時候就需要局部加載了,Android中有一個負(fù)責(zé)局部加載的類:BitmapRegionDecoder。使用方法很簡單,通過BitmapRegionDecoder.newInstance()創(chuàng)建對象,之后調(diào)用decodeRegion(Rect rect, BitmapFactory.Options options)即可。第一個參數(shù)rect是要顯示的區(qū)域,第二個參數(shù)是BitmapFactory中的內(nèi)部類Options。

OkHttp

OkHttp源碼分析

Retrofit

Retrofit源碼分析

RxJava

RxJava源碼分析

Glide

Glide源碼分析

EventBus

EventBus源碼分析

#### 閱讀更多

程序員如何寫一份更好的簡歷?

你這樣介紹項目,輕松搞定面試官|(zhì)offer加分必備

NDK項目實(shí)戰(zhàn)—高仿360手機(jī)助手之卸載監(jiān)聽

(Android)面試題級答案(精選版)

裸辭后,從Android轉(zhuǎn)戰(zhàn)Web前端的學(xué)習(xí)以及求職之路

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/71789.html

相關(guān)文章

  • 奮戰(zhàn)金九銀十?刷完這一套面試就夠了!自動化軟件測試工程師跳槽漲薪秘籍!

    摘要:九安卓中如何取出日志信息把安卓系統(tǒng)日志信息實(shí)時導(dǎo)入到本地運(yùn)行使用某個,實(shí)時獲取該的日志信息里面的返回信息接口自動化面試題一按你的理解,軟件接口是什么答就是指程序中具體負(fù)責(zé)在不同模塊之間傳輸或接受數(shù)據(jù)的并做處理的類或者函數(shù)。 ...

    Freelander 評論0 收藏0
  • 前端硬核面試 (備戰(zhàn) 2019 的金九銀十)

    摘要:文章內(nèi)容包括數(shù)據(jù)結(jié)構(gòu)與算法。因?yàn)槲恼聝?nèi)容太多,加上思否平臺對文章大小的限制,所以發(fā)不了該文章,只能保存在文件里面,已經(jīng)提交到上了。文章地址數(shù)據(jù)結(jié)構(gòu)與算法 文章內(nèi)容包括:HTML + CSS + JS + ES6 + Webpack + Vue + React + Node + HTTPS + 數(shù)據(jù)結(jié)構(gòu)與算法 + Git。因?yàn)槲恼聝?nèi)容太多,加上思否平臺對文章大小的限制,所以發(fā)不了該文章,...

    cpupro 評論0 收藏0

發(fā)表評論

0條評論

renweihub

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<