android bitmap转图片_这是一份面向Android开发者的复习指南
來自:簡書,作者:九心
鏈接:https://www.jianshu.com/p/b3c1b9c6dd40
前言
相信很多同學都會有這樣的感受,前三天剛剛復習的知識點,今天問的時候怎么就講不出個所以然了呢?
本文的目的就是致力于幫助大家盡可能的建立Android知識體系,希望大家會喜歡~
必讀
知識結構
覆蓋的知識點有Android、Java、Kotlin、Jvm、網絡和設計模式。
面向人群
正在求職的中高級Android開發
食用指南
和大部分人一樣,我在復習完第一遍Android知識的情況下,看到相關的知識回答的仍然不能夠令自己滿意。
在第二遍系統復習的時候,我著重記住每個知識點的關鍵字,根據這些關鍵字拼湊出大概的知識點,最后看到每個知識點的時候,就知道大概會問哪些內容,達到這種境界以后,你就可以從容的面對每次面試了。
簡單的做法就是為每個知識點建立腦圖,盡可能把自己想到的關鍵點羅列出來,也就是下面每個章節前面的腦圖。
除此以外,我還為大家提供了可能會問到的面試題。
一、Android基
1. Activity
# Activity的四大啟動模式,以及應用場景?
Activity的四大啟動模式:
standard:標準模式,每次都會在活動棧中生成一個新的Activity實例。通常我們使用的活動都是標準模式。
singleTop:棧頂復用,如果Activity實例已經存在棧頂,那么就不會在活動棧中創建新的實例。比較常見的場景就是給通知跳轉的Activity設置,因為你肯定不想前臺Activity已經是該Activity的情況下,點擊通知,又給你再創建一個同樣的Activity。
singleTask:棧內復用,如果Activity實例在當前棧中已經存在,就會將當前Activity實例上面的其他Activity實例都移除棧。常見于跳轉到主界面。
singleInstance:單實例模式,創建一個新的任務棧,這個活動實例獨自處在這個活動棧中。
2. 屏幕適配
# 平時如何有使用屏幕適配嗎?原理是什么呢?
平時的屏幕適配一般采用的頭條的屏幕適配方案。簡單來說,以屏幕的一邊作為適配,通常是寬。
原理:設備像素px和設備獨立像素dp之間的關系
px = dp * density
假設UI給的設計圖屏幕寬度基于360dp,那么設備寬的像素點已知,即px,dp也已知,360dp,所以density = px / dp,之后根據這個修改系統中跟density相關的知識點即可。
3. Android消息機制
# Android消息機制介紹?
Android消息機制中的四大概念:
ThreadLocal:當前線程存儲的數據僅能從當前線程取出。
MessageQueue:具有時間優先級的消息隊列。
Looper:輪詢消息隊列,看是否有新的消息到來。
Handler:具體處理邏輯的地方。
過程:
準備工作:創建Handler,如果是在子線程中創建,還需要調用Looper#prepare(),在Handler的構造函數中,會綁定其中的Looper和MessageQueue。
發送消息:創建消息,使用Handler發送。
進入MessageQueue:因為Handler中綁定著消息隊列,所以Message很自然的被放進消息隊列。
Looper輪詢消息隊列:Looper是一個死循環,一直觀察有沒有新的消息到來,之后從Message取出綁定的Handler,最后調用Handler中的處理邏輯,這一切都發生在Looper循環的線程,這也是Handler能夠在指定線程處理任務的原因。
# Looper在主線程中死循環為什么沒有導致界面的卡死?
導致卡死的是在Ui線程中執行耗時操作導致界面出現掉幀,甚至ANR,Looper.loop()這個操作本身不會導致這個情況。
有人可能會說,我在點擊事件中設置死循環會導致界面卡死,同樣都是死循環,不都一樣的嗎?Looper會在沒有消息的時候阻塞當前線程,釋放CPU資源,等到有消息到來的時候,再喚醒主線程。
App進程中是需要死循環的,如果循環結束的話,App進程就結束了。
建議閱讀:
《Android中為什么主線程不會因為Looper.loop()里的死循環卡死?》
https://www.zhihu.com/question/34652589
# IdleHandler介紹?
介紹:
IdleHandler是在Hanlder空閑時處理空閑任務的一種機制。
執行場景:
MessageQueue沒有消息,隊列為空的時候。
MessageQueue屬于延遲消息,當前沒有消息執行的時候。
會不會發生死循環:
答案是否定的,MessageQueue使用計數的方法保證一次調用MessageQueue#next方法只會使用一次的IdleHandler集合。
4. View事件分發機制和View繪制原理
剛哥的《Android開發藝術探索》已經很全面了,建議閱讀。
5. Bitmap
# Bitmap的內存計算方式?
在已知圖片的長和寬的像素的情況下,影響內存大小的因素會有資源文件位置和像素點大小。
像素點大小:
常見的像素點有:
ARGB_8888:4個字節
ARGB_4444、ARGB_565:2個字節
資源文件位置:
不同dpi對應存放的文件夾
比如一個一張圖片的像素為180*180px,dpi(設備獨立像素密度)為320,如果它僅僅存放在drawable-hdpi,則有:
橫向像素點?=?180?*?320/240?+?0.5f?=?240?px縱向像素點?=?180?*?320/240?+?0.5f?=?240?px
如果它僅僅存放在drawable-xxhdpi,則有:
180?*?所以,對于一張180*180px的圖片,設備dpi為320,資源圖片僅僅存在drawable-hdpi,像素點大小為ARGB_4444,最后生成的文件內存大小為:
橫向像素點?=?180?*?320/240?+?0.5f?=?240?px縱向像素點?=?180?*?320/240?+?0.5f?=?240?px
內存大小?=?240?*?240?*?2?=?115200byte?約等于?112.5kb
建議閱讀:
《Android Bitmap的內存大小是如何計算的?》
https://ivonhoe.github.io/2017/03/22/Bitmap&Memory/
# Bitmap的高效加載?
Bitmap的高效加載在Glide中也用到了,思路:
獲取需要的長和寬,一般獲取控件的長和寬。
設置BitmapFactory.Options中的inJustDecodeBounds為true,可以幫助我們在不加載進內存的方式獲得Bitmap的長和寬。
對需要的長和寬和Bitmap的長和寬進行對比,從而獲得壓縮比例,放入BitmapFactory.Options中的inSampleSize屬性。
設置BitmapFactory.Options中的inJustDecodeBounds為false,將圖片加載進內存,進而設置到控件中。
二、Android進階
Android進階中重點考察Android Framework、性能優化和第三方框架。
1. Binder
# Binder的介紹?與其他IPC方式的優缺點?
Binder是Android中特有的IPC方式,引用《Android開發藝術探索》中的話(略有改動):
從IPC角度來說,Binder是Android中的一種跨進程通信方式;Binder還可以理解為虛擬的物理設備,它的設備驅動是/dev/binder;從Android Framework來講,Binder是Service Manager連接各種Manager和對應的ManagerService的橋梁。從面向對象和CS模型來講,Client通過Binder和遠程的Server進行通訊。
基于Binder,Android還實現了其他的IPC方式,比如AIDL、Messenger和ContentProvider。
與其他IPC比較:
效率高:除了內存共享外,其他IPC都需要進行兩次數據拷貝,而因為Binder使用內存映射的關系,僅需要一次數據拷貝。
安全性好:接收方可以從數據包中獲取發送發的進程Id和用戶Id,方便驗證發送方的身份,其他IPC想要實驗只能夠主動存入,但是這有可能在發送的過程中被修改。
# Binder的通信過程?Binder的原理?
圖片:
其實這個過程也可以從AIDL生成的代碼中看出。
原理:
Binder的結構:
Client:服務的請求方。
Server:服務的提供方。
Service Manager:為Server提供Binder的注冊服務,為Client提供Binder的查詢服務,Server、Client和Service Manager的通訊都是通過Binder。
Binder驅動:負責Binder通信機制的建立,提供一系列底層支持。
從上圖中,Binder通信的過程是這樣的:
Server在Service Manager中注冊:Server進程在創建的時候,也會創建對應的Binder實體,如果要提供服務給Client,就必須為Binder實體注冊一個名字。
Client通過Service Manager獲取服務:Client知道服務中Binder實體的名字后,通過名字從Service Manager獲取Binder實體的引用。
Client使用服務與Server進行通信:Client通過調用Binder實體與Server進行通信。
更詳細一點?
Binder通信的實質是利用內存映射,將用戶進程的內存地址和內核的內存地址映射為同一塊物理地址,也就是說他們使用的同一塊物理空間,每次創建Binder的時候大概分配128的空間。數據進行傳輸的時候,從這個內存空間分配一點,用完了再釋放即可。
2. 序列化
# Android有哪些序列化方式?
為了解決Android中內存序列化速度過慢的問題,Android使用了Parcelable。
| 易用性 | 簡單 | 不是很簡單 |
| 效率 | 低 | 高 |
| 場景 | IO、網絡和數據庫 | 內存中 |
3. Framework
Zygote孕育進程過程?
# Activity的啟動過程?
建議閱讀:
《3分鐘看懂Activity啟動流程》
https://www.jianshu.com/p/9ecea420eb52
# App的啟動過程?
介紹一下App進程和System Server進程如何聯系:
App進程
ActivityThread:依賴于Ui線程,實際處理與AMS中交互的工作。
ActivityManagerService:負責Activity、Service等的生命周期工作。
ApplicationThread:System Server進程中ApplicatonThreadProxy的服務端,幫助System Server進程跟App進程交流。
System Server:Android核心的進程,掌管著Android系統中各種重要的服務。
具體過程:
用戶點擊App圖標,Lanuacher進程通過Binder聯系到System Server進程發起startActivity。
System Server通過Socket聯系到Zygote,fork出一個新的App進程。
創建出一個新的App進程以后,Zygote啟動App進程的ActivityThread#main()方法。
在ActivtiyThread中,調用AMS進行ApplicationThread的綁定。
AMS發送創建Application的消息給ApplicationThread,進而轉交給ActivityThread中的H,它是一個Handler,接著進行Application的創建工作。
AMS以同樣的方式創建Activity,接著就是大家熟悉的創建Activity的工作了。
# Apk的安裝過程?
建議閱讀:
《Android Apk安裝過程分析》
https://www.jianshu.com/p/953475cea991
# Activity啟動過程跟Window的關系?
建議閱讀:
《簡析Window、Activity、DecorView以及ViewRoot之間的錯綜關系》
https://juejin.im/post/5dac6aa2518825630e5d17da
# ?Activity、Window、ViewRoot和DecorView之間的關系?
建議閱讀:
《總結UI原理和高級的UI優化方式》
https://www.jianshu.com/p/8766babc40e0
4. Context
# 關于Context的理解?
建議閱讀:
《Android Context 上下文 你必須知道的一切》
https://blog.csdn.net/lmj623565791/article/details/40481055
5. 斷點續傳
# 多線程斷點續傳?
基礎知識:
Http基礎:在Http請求中,可以加入請求頭Range,下載指定區間的文件數。
RandomAccessFile:支持隨機訪問,可以從指定位置進行數據的讀寫。
有了這個基礎以后,思路就清晰了:
通過HttpUrlConnection獲取文件長度。
自己分配好線程進行制定區間的文件數據的下載。
獲取到數據流以后,使用RandomAccessFile進行指定位置的讀寫。
6. 性能優化
# 平時做了哪些性能優化?
建議閱讀:
《Android 性能優化最佳實踐》
https://juejin.im/post/5b50b017f265da0f7b2f649c
7. 第三方庫
一定要在熟練使用后再去查看原理。
# Glide
Glide考察的頻率挺高的,常見的問題有:
Glide和其他圖片加載框架的比較?
如何設計一個圖片加載框架?
Glide緩存實現機制?
Glide如何處理生命周期?
...
建議閱讀:
《Glide最全解析》
https://blog.csdn.net/sinyu890807/category_9268670.html
《面試官:簡歷上最好不要寫Glide,不是問源碼那么簡單》
https://juejin.im/post/5dbeda27e51d452a161e00c8
# OkHttp
OkHttp常見知識點:
責任鏈模式
interceptors和networkInterceptors的區別?
建議看一遍源碼,過程并不復雜。
# Retrofit
Retrofit常見問題:
設計模式和封層解耦的理念
動態代理
建議看一遍源碼,過程并不復雜。
# RxJava
RxJava難在各種操作符,我們了解一下大致的設計思想即可。
建議尋找一些RxJava的文章。
# Android Jetpack(非必須)
我主要閱讀了Android Jetpack中以下庫的源碼:
Lifecycle:觀察者模式,組件生命周期中發送事件。
DataBinding:核心就是利用LiveData或者Observablexxx實現的觀察者模式,對16進制的狀態位更新,之后根據這個狀態位去更新對應的內容。
LiveData:觀察者模式,事件的生產消費模型。
ViewModel:借用Activty異常銷毀時存儲隱藏Fragment的機制存儲ViewModel,保證數據的生命周期盡可能的延長。
Paging:設計思想。
以后有時間再給大家做源碼分析。
建議閱讀:
《Android Jetpack源碼分析系列》
https://blog.csdn.net/mq2553299/column/info/24151
三、Java基礎
Java基礎中考察頻率比較高的是Object、String、面向對象、集合、泛型和反射。
1. Object
# equals和==的區別?equals和hashcode的關系?
==:基本類型比較值,引用類型比較地址。
equals:默認情況下,equals作為對象中的方法,比較的是地址,不過可以根據業務,修改equals方法。
equals和hashcode之間的關系:
默認情況下,equals相等,hashcode必相等,hashcode相等,equals不是必相等。hashcode基于內存地址計算得出,可能會相等,雖然幾率微乎其微。
2. String
# String、StringBuffer和StringBuilder的區別?
String:String屬于不可變對象,每次修改都會生成新的對象。
StringBuilder:可變對象,非多線程安全。
StringBuffer:可變對象,多線程安全。
大部分情況下,效率是:StringBuilder>StringBuffer>String。
3. 面向對象的特性
# Java中抽象類和接口的特點?
共同點:
抽象類和接口都不能生成具體的實例。
都是作為上層使用。
不同點:
抽象類可以有屬性和成員方法,接口不可以。
一個類只能繼承一個類,但是可以實現多個接口。
抽象類中的變量是普通變量,接口中的變量是靜態變量。
抽象類表達的是is-a的關系,接口表達的是like-a的關系。
# 關于多態的理解?
多態是面向對象的三大特性:繼承、封裝和多態之一。
多態的定義:允許不同類對同一消息做出響應。
多態存在的條件:
要有繼承。
要有復寫。
父類引用指向子類對象。
Java中多態的實現方式:接口實現,繼承父類進行方法重寫,同一個類中的方法重載。
4. 集合
# HashMap的特點是什么?HashMap的原理?
HashMap的特點:
基于Map接口,存放鍵值對。
允許key/value為空。
非多線程安全。
不保證有序,也不保證使用的過程中順序不會改變。
簡單來講,核心是數組+鏈表/紅黑樹,HashMap的原理就是存鍵值對的時候:
通過鍵的Hash值確定數組的位置。
找到以后,如果該位置無節點,直接存放。
該位置有節點即位置發生沖突,遍歷該節點以及后續的節點,比較key值,相等則覆蓋。
沒有就新增節點,默認使用鏈表,相連節點數超過8的時候,在jdk 1.8中會變成紅黑樹。
如果Hashmap中的數組使用情況超過一定比例,就會擴容,默認擴容兩倍。
當然這是存入的過程,其他過程可以自行查閱。這里需要注意的是:
key的hash值計算過程是高16位不變,低16位和高16位取抑或,讓更多位參與進來,可以有效的減少碰撞的發生。
初始數組容量為16,默認不超過的比例為0.75。
5. 泛型
# 說一下對泛型的理解?
泛型的本質是參數化類型,在不創建新的類型的情況下,通過泛型指定不同的類型來控制形參具體限制的類型。也就是說在泛型的使用中,操作的數據類型被指定為一個參數,這種參數可以被用在類、接口和方法中,分別被稱為泛型類、泛型接口和泛型方法。
泛型是Java中的一種語法糖,能夠在代碼編寫的時候起到類型檢測的作用,但是虛擬機是不支持這些語法的。
泛型的優點:
類型安全,避免類型的強轉。
提高了代碼的可讀性,不必要等到運行的時候才去強制轉換。
# 什么是類型擦除?
不管泛型的類型傳入哪一種類型實參,對于Java來說,都會被當成同一類處理,在內存中也只占用一塊空間。通俗一點來說,就是泛型只作用于代碼編譯階段,在編譯過程中,對于正確檢驗泛型結果后,會將泛型的信息擦除,也就是說,成功編譯過后的class文件是不包含任何泛型信息的。
6. 反射
# 動態代理和靜態代理
靜態代理很簡單,運用的就是代理模式:
聲明一個接口,再分別實現一個真實的主題類和代理主題類,通過讓代理類持有真實主題類,從而控制用戶對真實主題的訪問。
動態代理指的是在運行時動態生成代理類,即代理類的字節碼在運行時生成并載入當前的ClassLoader。
動態代理的原理是使用反射,思路和上面的一致。
使用動態代理的好處:
不需要為RealSubject寫一個形式完全一樣的代理類。
使用一些動態代理的方法可以在運行時制定代理類的邏輯,從而提升系統的靈活性。
四、Java并發
Java并發中考察頻率較高的有線程、線程池、鎖、線程間的等待和喚醒、線程特性和阻塞隊列等。
1. 線程
# 線程的狀態有哪些?
附上一張狀態轉換的圖:
# 線程中wait和sleep的區別?
wait方法既釋放cpu,又釋放鎖。
sleep方法只釋放cpu,但是不釋放鎖。
# 線程和進程的區別?
線程是CPU調度的最小單位,一個進程中可以包含多個線程,在Android中,一個進程通常是一個App,App中會有一個主線程,主線程可以用來操作界面元素,如果有耗時的操作,必須開啟子線程執行,不然會出現ANR,除此以外,進程間的數據是獨立的,線程間的數據可以共享。
2. 線程池
線程池的地位十分重要,基本上涉及到跨線程的框架都使用到了線程池,比如說OkHttp、RxJava、LiveData以及協程等。
# 與新建一個線程相比,線程池的特點?
節省開銷:線程池中的線程可以重復利用。
速度快:任務來了就能開始,省去創建線程的時間。
線程可控:線程數量可空和任務可控。
功能強大:可以定時和重復執行任務。
# 線程池中的幾個參數是什么意思,線程池的種類有哪些?
線程池的構造函數如下:
public?ThreadPoolExecutor(int?corePoolSize,int?maximumPoolSize,long?keepAliveTime,??????????????????????????TimeUnit?unit,
??????????????????????????BlockingQueue?workQueue)?{
參數解釋如下:
corePoolSize:核心線程數量,不會釋放。
maximumPoolSize:允許使用的最大線程池數量,非核心線程數量,閑置時會釋放。
keepAliveTime:閑置線程允許的最大閑置時間。
unit:閑置時間的單位。
workQueue:阻塞隊列,不同的阻塞隊列有不同的特性。
線程池分為四個類型:
CachedThreadPool:閑置線程超時會釋放,沒有閑置線程的情況下,每次都會創建新的線程。
FixedThreadPool:線程池只能存放指定數量的線程池,線程不會釋放,可重復利用。
SingleThreadExecutor:單線程的線程池。
ScheduledThreadPool:可定時和重復執行的線程池。
# 線程池的工作流程?
圖片來自《線程池是怎樣工作的》
https://www.jianshu.com/p/9de89960ec01
簡而言之:
任務來了,優先考慮核心線程。
核心線程滿了,進入阻塞隊列。
阻塞隊列滿了,考慮非核心線程(圖上好像少了這個過程)。
非核心線程滿了,再觸發拒絕任務。
3. 鎖
# 死鎖觸發的四大條件?
互斥鎖
請求與保持
不可剝奪
循環的請求與等待
# synchronized關鍵字的使用?synchronized的參數放入對象和Class有什么區別?
synchronized關鍵字的用法:
修飾方法
修飾代碼塊:需要自己提供鎖對象,鎖對象包括對象本身、對象的Class和其他對象。
放入對象和Class的區別是:
鎖住的對象不同:成員方法鎖住的實例對象,靜態方法鎖住的是Class。
訪問控制不同:如果鎖住的是實例,只會針對同一個對象方法進行同步訪問,多線程訪問同一個對象的synchronized代碼塊是串行的,訪問不同對象是并行的。如果鎖住的是類,多線程訪問的不管是同一對象還是不同對象的synchronized代碼塊是都是串行的。
# synchronized的原理?
任何一個對象都有一個monitor與之相關聯,JVM基于進入和退出mointor對象來實現代碼塊同步和方法同步,兩者實現細節不同:
代碼塊同步:在編譯字節碼的時候,代碼塊起始的地方插入monitorenter
指令,異常和代碼塊結束處插入monitorexit指令,線程在執行monitorenter指令的時候嘗試獲取monitor對象的所有權,獲取不到的情況下就是阻塞方法同步:synchronized方法在method_info結構有AAC_synchronized標記,線程在執行的時候獲取對應的鎖,從而實現同步方法
# synchronized和Lock的區別?
主要區別:
synchronized是Java中的關鍵字,是Java的內置實現;Lock是Java中的接口。
synchronized遇到異常會釋放鎖;Lock需要在發生異常的時候調用成員方法Lock#unlock()方法。
synchronized是不可以中斷的,Lock可中斷。
synchronized不能去嘗試獲得鎖,沒有獲得鎖就會被阻塞;Lock可以去嘗試獲得鎖,如果未獲得可以嘗試處理其他邏輯。
synchronized多線程效率不如Lock,不過Java在1.6以后已經對synchronized進行大量的優化,所以性能上來講,其實差不了多少。
# 悲觀鎖和樂觀鎖的舉例?以及它們的相關實現?
悲觀鎖和樂觀鎖的概念:
悲觀鎖:悲觀鎖會認為,修改共享數據的時候其他線程也會修改數據,因此只在不會受到其他線程干擾的情況下執行。這樣會導致其他有需要鎖的線程掛起,等到持有鎖的線程釋放鎖
樂觀鎖:每次不加鎖,每次直接修改共享數據假設其他線程不會修改,如果發生沖突就直接重試,直到成功為止
舉例:
悲觀鎖:典型的悲觀鎖是獨占鎖,有synchronized、ReentrantLock。
樂觀鎖:典型的樂觀鎖是CAS,實現CAS的atomic為代表的一系列類
# CAS是什么?底層原理?
CAS全稱Compare And Set,核心的三個元素是:內存位置、預期原值和新值,執行CAS的時候,會將內存位置的值與預期原值進行比較,如果一致,就將原值更新為新值,否則就不更新。
底層原理:是借助CPU底層指令cmpxchg實現原子操作。
4. 線程間通信
# notify和notifyAll方法的區別?
notify隨機喚醒一個線程,notifyAll喚醒所有等待的線程,讓他們競爭鎖。
# wait/notify和Condition類實現的等待通知有什么區別?
synchronized與wait/notify結合的等待通知只有一個條件,而Condition類可以實現多個條件等待。
5. 多線程間的特性
# 多線程間的有序性、可見性和原子性是什么意思?
原子性:執行一個或者多個操作的時候,要么全部執行,要么都不執行,并且中間過程中不會被打斷。Java中的原子性可以通過獨占鎖和CAS去保證
可見性:指多線程訪問同一個變量的時候,一個線程修改了變量的值,其他線程能夠立刻看得到修改的值。鎖和volatile能夠保證可見性
有序性:程序執行的順序按照代碼先后的順序執行。鎖和volatile能夠保證有序性
# happens-before原則有哪些?
Java內存模型具有一些先天的有序性,它通常叫做happens-before原則。
如果兩個操作的先后順序不能通過happens-before原則推倒出來,那就不能保證它們的先后執行順序,虛擬機就可以隨意打亂執行指令。happens-before原則有:
程序次序規則:單線程程序的執行結果得和看上去代碼執行的結果要一致。
鎖定規則:一個鎖的lock操作一定發生在上一個unlock操作之后。
volatile規則:對volatile變量的寫操作一定先行于后面對這個變量的對操作。
傳遞規則:A發生在B前面,B發生在C前面,那么A一定發生在C前面。
線程啟動規則:線程的start方法先行發生于線程中的每個動作。
線程中斷規則:對線程的interrupt操作先行發生于中斷線程的檢測代碼。
線程終結原則:線程中所有的操作都先行發生于線程的終止檢測。
對象終止原則:一個對象的初始化先行發生于他的finalize()方法的執行。
前四條規則比較重要。
# volatile的原理?
可見性
如果對聲明了volatile的變量進行寫操作的時候,JVM會向處理器發送一條Lock前綴的指令,將這個變量所在緩存行的數據寫入到系統內存。
多處理器的環境下,其他處理器的緩存還是舊的,為了保證各個處理器一致,會通過嗅探在總線上傳播的數據來檢測自己的數據是否過期,如果過期,會強制重新將系統內存的數據讀取到處理器緩存。
有序性
Lock前綴的指令相當于一個內存柵欄,它確保指令排序的時候,不會把后面的指令拍到內存柵欄的前面,也不會把前面的指令排到內存柵欄的后面。
6. 阻塞隊列
# 通常的阻塞隊列有哪幾種,特點是什么?
ArrayBlockQueue:基于數組實現的有界的FIFO(先進先出)阻塞隊列。
LinkedBlockQueue:基于鏈表實現的無界的FIFO(先進先出)阻塞隊列。
SynchronousQueue:內部沒有任何緩存的阻塞隊列。
PriorityBlockingQueue:具有優先級的無限阻塞隊列。
# ConcurrentHashMap的原理
數據結構的實現跟HashMap一樣,不做介紹。
JDK 1.8之前采用的是分段鎖,核心類是一個Segment,Segment繼承了ReentrantLock,每個Segment對象管理若干個桶,多個線程訪問同一個元素的時候只能去競爭獲取鎖。
JDK 1.8采用了CAS + synchronized,插入鍵值對的時候如果當前桶中沒有Node節點,使用CAS方式進行更新,如果有Node節點,則使用synchronized的方式進行更新。
五、網絡
掌握網絡知識其實是需要一個系統的過程,在時間充裕的情況下,建議還是系統化的學習。
高頻網絡知識有TCP、HTTP和HTTPS。
1. HTTP和HTTPS
# HTTP是哪一層的協議,常見的HTTP狀態碼有哪些,分別代表什么意思?
HTTP協議是應用層的協議。
常見的HTTP狀態碼有:
| 1xx | 請求已經接收,繼續處理 |
| 2xx | 服務器已經正確處理請求,比如200 |
| 3xx | 重定向,需要做進一步的處理才能完成請求 |
| 4xx | 服務器無法理解的請求,比如404,訪問的資源不存在 |
| 5xx | 服務器收到請求以后,處理錯誤 |
# HTTP 1.1 和HTTP 2有什么區別?
HTTP 2.0基于HTTP 1.1,與HTTP 2.0增加了:
二進制格式:HTTP 1.1使用純文本進行通信,HTTP 2.0使用二進制進行傳輸。
Head壓縮:對已經發送的Header使用鍵值建立索引表,相同的Header使用索引表示。
服務器推送:服務器可以進行主動推送
多路復用:一個TCP連接可以劃分成多個流,每個流都會分配Id,客戶端可以借助流和服務端建立全雙工進行通信,并且流具有優先級。
# HTTP和HTTPS有什么區別?
簡單來說,HTTP和HTTPS的關系是這樣的
HTTPS = HTTP + SSL/TLS
區別如下:
HTTP作用于應用層,使用80端口,起始地址是http://,明文傳輸,消息容易被攔截,串改。
HTTPS作用域傳輸層,使用443端口,起始地址是https://,需要下載CA證書,傳輸的過程需要加密,安全性高。
# SSL/TLS的握手過程?
這里借用《趣談網絡協議》的圖片:
# HTTPS傳輸過程中是如何處理進行加密的?為什么有對稱加密的情況下仍然需要進行非對稱加密?
過程和上圖類似,依次獲取證書,公鑰,最后生成對稱加密的鑰匙進行對稱加密。
對稱加密可以保證加密效率,但是不能解決密鑰傳輸問題;非對稱加密可以解決傳輸問題,但是效率不高。
2. TCP相關
# TCP的三次握手過程,為什么需要三次,而不是兩次或者四次?
只發送兩次,服務端是不知道自己發送的消息能不能被客戶端接收到。
因為TCP握手是三次,所以此時雙方都已經知道自己發送的消息能夠被對方收到,所以,第四次的發送就顯得多余了。
# TCP的四次揮手過程?
大致意思就是:
Client:我要斷開連接了
Server:我收到你的消息了
Server:我也要斷開連接了
Client:收到你要斷開連接的消息了
之后Client等待兩個MSL(數據包在網絡上生存的最長時間),如果服務端沒有回消息就徹底斷開了。
# TCP和UDP有什么區別?
TCP:基于字節流、面向連接、可靠、能夠進行全雙工通信,除此以外,還能進行流量控制和擁塞控制,不過效率略低
UDP:基于報文、面向無連接、不可靠,但是傳輸效率高。
總的來說,TCP適用于傳輸效率要求低,準確性要求高或要求有連接。而UDP適用于對準確性要求較低,傳輸效率要求較高的場景,比如語音通話、直播等。
# TCP為什么是一種可靠的協議?如何做到流量控制和擁塞控制?
TCP可靠:是因為可以做到數據包發送的有序、無差錯和無重復。
流量控制:是通過滑動窗口實現的,因為發送發和接收方消息發送速度和接收速度不一定對等,所以需要一個滑動窗口來平衡處理效率,并且保證沒有差錯和有序的接收數據包。
擁塞控制:慢開始和擁塞避免、快重傳和快恢復算法。這寫算法主要是為了適應網絡中的帶寬而作出的調整。
六、設計模式
經常考察的設計模式不多,活學活用即可。
1. 六大原則
設計模式的六大原則是:
單一職責:合理分配類和函數的職責
開閉原則:開放擴展,關閉修改
里式替換:繼承
依賴倒置:面向接口
接口隔離:控制接口的粒度
迪米特:一個類應該對其他的類了解最少
2. 單例模式
單例模式被問到的幾率很大,通常會問如下幾種問題。
# 單例的常用寫法有哪幾種?
懶漢模式
public?該模式的主要問題是每次獲取實例都需要同步,造成不必要的同步開銷。
DCL模式
public?高并發環境下可能會發生問題。
靜態內部類單例
public?枚舉單例
public?優點:線程安全和反序列化不會生成新的實例
# DCL模式會有什么問題?
對象生成實例的過程中,大概會經過以下過程:
為對象分配內存空間。
初始化對象中的成員變量。
將對象指向分配的內存空間(此時對象就不為null)。
由于Jvm會優化指令順序,也就是說2和3的順序是不能保證的。在多線程的情況下,當一個線程完成了1、3過程后,當前線程的時間片已用完,這個時候會切換到另一個線程,另一個線程調用這個單例,會使用這個還沒初始化完成的實例。
解決方法是使用volatile關鍵字:
public?3. 需要關注的設計模式
重點了解以下的幾種常用的設計模式:
工廠模式和抽象工廠模式:注意他們的區別。
責任鏈模式:View的事件分發和OkHttp的調用過程都使用到了責任鏈模式。
觀察者模式:重要性不言而喻。
代理模式:建議了解一下動態代理。
4. MVC\MVP\MVVM
MVC、MVP和MVVM應該是設計模式中考察頻率最高的知識點了,嚴格意義上來說,它們不能算是設計模式,而是框架。
# MVC、MVP和MVVM是什么?
MVC:Model-View-Controller,是一種分層解偶的框架,Model層提供本地數據和網絡請求,View層處理視圖,Controller處理邏輯,存在問題是Controller層和View層的劃分不明顯,Model層和View層的存在耦合。
MVP:Model-View-Presenter,是對MVC的升級,Model層和View層與MVC的意思一致,但Model層和View層不再存在耦合,而是通過Presenter層這個橋梁進行交流。
MVVM:Model-View-ViewModel,不同于上面的兩個框架,ViewModel持有數據狀態,當數據狀態改變的時候,會自動通知View層進行更新。
# MVC和MVP的區別是什么?
MVP是MVC的進一步解耦,簡單來講,在MVC中,View層既可以和Controller層交互,又可以和Model層交互;而在MVP中,View層只能和Presenter層交互,Model層也只能和Presenter層交互,減少了View層和Model層的耦合,更容易定位錯誤的來源。
# MVVM和MVP的最大區別在哪?
MVP中的每個方法都需要你去主動調用,它其實是被動的,而MVVM中有數據驅動這個概念,當你的持有的數據狀態發生變更的時候,你的View你可以監聽到這個變化,從而主動去更新,這其實是主動的。
# ViewModel如何知道View層的生命周期?
事實上,如果你僅僅使用ViewModel,它是感知不了生命周期,它需要結合LiveData去感知生命周期,如果僅僅使用DataBinding去實現MVVM,它對數據源使用了弱引用,所以一定程度上可以避免內存泄漏的發生。
七、算法題
沒什么好說的,Leetcode + 《劍指Offer》,著重記住一些解決問題的思路。
除此以外,你還得記住一些常用的算法:排序、反轉鏈表、樹的遍歷和手寫LruCache,這些都寫不出來,就尷尬了。
如果你不想閱讀書籍,可以參考一下這個Github,親眼見證了從3k Star到34k Star,跪了:
【fucking-algorithm】:
https://github.com/labuladong/fucking-algorithm
簡歷中最重要的是項目經歷。
可能有的同學會說,我天天在公司擰螺絲,根本沒什么東西可寫。
所以我們在平時的工作中,不應該僅僅滿足于寫一些業務代碼,而應該常常思考:
在結合的業務的情況下,我可以再做一點什么?
對于已經寫完的代碼,我還可以做哪一些優化?
九、寫在最后
經常聽到一些同學調侃,Boss不聘、前程堪憂、拉不上鉤,確實,今年的大環境比較嚴峻,但是一些高級崗位仍然稀缺。
談一下我自己,小廠背景、18年畢業、普通學校,所以,大廠都沒給過面試機會,好在前兩周內推成功了,我也抓住了這次機會,成功獲得了大廠的Offer。
所以我想表達什么?打鐵還需自身硬,一定是得建立完比較完整的知識體系的前提下,當機會來臨的時候,才能夠穩穩地把握住,希望和大家共勉~
如果大家還有什么問題,歡迎在下方留言和我討論。
分享不易,你的點贊是我分享的動力。
注:因為篇幅太長,文章有所刪減。
主要參考:
https://github.com/LRH1993
本來每一章節開始都是腦圖的,后來我看是在看不清就刪除了,抱著試一試的心態發現作者更新了百度云原圖下載,就又傳了一遍,就放最后吧,算是給堅持到最后的朋友放置的福利,我試了下,湊合能看清。
如果還是想要更高清的自己下載哈:
鏈接:https://pan.baidu.com/s/1bUQccZiuLv8EVKImMDziZQ?
密碼:wyc8
●編號684,輸入編號直達本文
●輸入m獲取到文章目錄
推薦↓↓↓Java編程
更多推薦《25個技術類公眾微信》
涵蓋:程序人生、算法與數據結構、黑客技術與網絡安全、大數據技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、數據庫、運維等。
總結
以上是生活随笔為你收集整理的android bitmap转图片_这是一份面向Android开发者的复习指南的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: H5开发流程
- 下一篇: 【Redis】懒惰删除