【建議收藏】2020年中高級Android大廠面試祕籍,爲你保駕護航金三銀四,直通大廠(Android高級篇下)

前言

成爲一名優秀的Android開發,須要一份完備的知識體系,在這裏,讓咱們一塊兒成長爲本身所想的那樣~。

🔥 A awesome android expert interview questions and answers(continuous updating ...)javascript

從幾十份頂級面試倉庫和300多篇高質量面經中總結出一份全面成體系化的Android高級面試題集。html

歡迎來到2020年中高級Android大廠面試祕籍,爲你保駕護航金三銀四,直通大廠的Android高級篇下。前端

3、Android優秀三方庫源碼

一、你項目中用到哪些開源庫?說說其實現原理?

1、網絡底層框架:OkHttp實現原理

這個庫是作什麼用的?

網絡底層庫,它是基於http協議封裝的一套請求客戶端,雖然它也能夠開線程,但根本上它更偏向真正的請求,跟HttpClient, HttpUrlConnection的職責是同樣的。其中封裝了網絡請求get、post等底層操做的實現。java

爲何要在項目中使用這個庫?
  • OkHttp 提供了對最新的 HTTP 協議版本 HTTP/2 和 SPDY 的支持,這使得對同一個主機發出的全部請求均可以共享相同的套接字鏈接。
  • 若是 HTTP/2 和 SPDY 不可用,OkHttp 會使用鏈接池來複用鏈接以提升效率。
  • OkHttp 提供了對 GZIP 的默認支持來下降傳輸內容的大小。
  • OkHttp 也提供了對 HTTP 響應的緩存機制,能夠避免沒必要要的網絡請求。
  • 當網絡出現問題時,OkHttp 會自動重試一個主機的多個 IP 地址。
這個庫都有哪些用法?對應什麼樣的使用場景?

get、post請求、上傳文件、上傳表單等等。react

這個庫的優缺點是什麼,跟同類型庫的比較?
  • 優勢:在上面
  • 缺點:使用的時候仍然須要本身再作一層封裝。
這個庫的核心實現原理是什麼?若是讓你實現這個庫的某些核心功能,你會考慮怎麼去實現?

OkHttp內部的請求流程:使用OkHttp會在請求的時候初始化一個Call的實例,而後執行它的execute()方法或enqueue()方法,內部最後都會執行到getResponseWithInterceptorChain()方法,這個方法裏面經過攔截器組成的責任鏈,依次通過用戶自定義普通攔截器、重試攔截器、橋接攔截器、緩存攔截器、鏈接攔截器和用戶自定義網絡攔截器以及訪問服務器攔截器等攔截處理過程,來獲取到一個響應並交給用戶。其中,除了OKHttp的內部請求流程這點以外,緩存和鏈接這兩部份內容也是兩個很重要的點,掌握了這3點就說明你理解了OkHttp。android

各個攔截器的做用:
  • interceptors:用戶自定義攔截器
  • retryAndFollowUpInterceptor:負責失敗重試以及重定向
  • BridgeInterceptor:請求時,對必要的Header進行一些添加,接收響應時,移除必要的Header
  • CacheInterceptor:負責讀取緩存直接返回(根據請求的信息和緩存的響應的信息來判斷是否存在緩存可用)、更新緩存
  • ConnectInterceptor:負責和服務器創建鏈接

ConnectionPool:git

一、判斷鏈接是否可用,不可用則從ConnectionPool獲取鏈接,ConnectionPool無鏈接,建立新鏈接,握手,放入ConnectionPool。github

二、它是一個Deque,add添加Connection,使用線程池負責定時清理緩存。web

三、使用鏈接複用省去了進行 TCP 和 TLS 握手的一個過程。面試

  • networkInterceptors:用戶定義網絡攔截器
  • CallServerInterceptor:負責向服務器發送請求數據、從服務器讀取響應數據
你從這個庫中學到什麼有價值的或者說可借鑑的設計思想?

使用責任鏈模式實現攔截器的分層設計,每個攔截器對應一個功能,充分實現了功能解耦,易維護。

手寫攔截器?
OKhttp針對網絡層有哪些優化?
網絡請求緩存處理,okhttp如何處理網絡緩存的?
HttpUrlConnection 和 okhttp關係?
Volley與OkHttp的對比:

Volley:支持HTTPS。緩存、異步請求,不支持同步請求。協議類型是Http/1.0, Http/1.1,網絡傳輸使用的是 HttpUrlConnection/HttpClient,數據讀寫使用的IO。 OkHttp:支持HTTPS。緩存、異步請求、同步請求。協議類型是Http/1.0, Http/1.1, SPDY, Http/2.0, WebSocket,網絡傳輸使用的是封裝的Socket,數據讀寫使用的NIO(Okio)。 SPDY協議相似於HTTP,但旨在縮短網頁的加載時間和提升安全性。SPDY協議經過壓縮、多路複用和優先級來縮短加載時間。

Okhttp的子系統層級結構圖以下所示:

image

網絡配置層:利用Builder模式配置各類參數,例如:超時時間、攔截器等,這些參數都會由Okhttp分發給各個須要的子系統。 重定向層:負責重定向。 Header拼接層:負責把用戶構造的請求轉換爲發送給服務器的請求,把服務器返回的響應轉換爲對用戶友好的響應。 HTTP緩存層:負責讀取緩存以及更新緩存。 鏈接層:鏈接層是一個比較複雜的層級,它實現了網絡協議、內部的攔截器、安全性認證,鏈接與鏈接池等功能,但這一層尚未發起真正的鏈接,它只是作了鏈接器一些參數的處理。 數據響應層:負責從服務器讀取響應的數據。 在整個Okhttp的系統中,咱們還要理解如下幾個關鍵角色:

OkHttpClient:通訊的客戶端,用來統一管理髮起請求與解析響應。 Call:Call是一個接口,它是HTTP請求的抽象描述,具體實現類是RealCall,它由CallFactory建立。 Request:請求,封裝請求的具體信息,例如:url、header等。 RequestBody:請求體,用來提交流、表單等請求信息。 Response:HTTP請求的響應,獲取響應信息,例如:響應header等。 ResponseBody:HTTP請求的響應體,被讀取一次之後就會關閉,因此咱們重複調用responseBody.string()獲取請求結果是會報錯的。 Interceptor:Interceptor是請求攔截器,負責攔截並處理請求,它將網絡請求、緩存、透明壓縮等功能都統一塊兒來,每一個功能都是一個Interceptor,全部的Interceptor最 終鏈接成一個Interceptor.Chain。典型的責任鏈模式實現。 StreamAllocation:用來控制Connections與Streas的資源分配與釋放。 RouteSelector:選擇路線與自動重連。 RouteDatabase:記錄鏈接失敗的Route黑名單。

本身去設計網絡請求框架,怎麼作?
從網絡加載一個10M的圖片,說下注意事項?
http怎麼知道文件過大是否傳輸完畢的響應?
談談你對WebSocket的理解?
WebSocket與socket的區別?

2、網絡封裝框架:Retrofit實現原理

這個庫是作什麼用的?

Retrofit 是一個 RESTful 的 HTTP 網絡請求框架的封裝。Retrofit 2.0 開始內置 OkHttp,前者專一於接口的封裝,後者專一於網絡請求的高效。

爲何要在項目中使用這個庫?

一、功能強大:

  • 支持同步、異步
  • 支持多種數據的解析 & 序列化格式
  • 支持RxJava

二、簡潔易用:

  • 經過註解配置網絡請求參數
  • 採用大量設計模式簡化使用

三、可擴展性好:

  • 功能模塊高度封裝
  • 解耦完全,如自定義Converters
這個庫都有哪些用法?對應什麼樣的使用場景?

任何網絡場景都應該優先選擇,特別是後臺API遵循Restful API設計風格 & 項目中使用到RxJava。

這個庫的優缺點是什麼,跟同類型庫的比較?
  • 優勢:在上面
  • 缺點:擴展性差,高度封裝所帶來的必而後果,若是服務器不能給出統一的API形式,會很難處理。
這個庫的核心實現原理是什麼?若是讓你實現這個庫的某些核心功能,你會考慮怎麼去實現?

Retrofit主要是在create方法中採用動態代理模式(經過訪問代理對象的方式來間接訪問目標對象)實現接口方法,這個過程構建了一個ServiceMethod對象,根據方法註解獲取請求方式,參數類型和參數註解拼接請求的連接,當一切都準備好以後會把數據添加到Retrofit的RequestBuilder中。而後當咱們主動發起網絡請求的時候會調用okhttp發起網絡請求,okhttp的配置包括請求方式,URL等在Retrofit的RequestBuilder的build()方法中實現,併發起真正的網絡請求。

你從這個庫中學到什麼有價值的或者說可借鑑的設計思想?

內部使用了優秀的架構設計和大量的設計模式,在我分析過Retrofit最新版的源碼和大量優秀的Retrofit源碼分析文章後,我發現,要想真正理解Retrofit內部的核心源碼流程和設計思想,首先,須要對它使用到的九大設計模式有必定的瞭解,下面我簡單說一說:

一、建立Retrofit實例:

  • 使用建造者模式經過內部Builder類創建了一個Retroift實例。
  • 網絡請求工廠使用了工廠方法模式。

二、建立網絡請求接口的實例:

  • 首先,使用外觀模式統一調用建立網絡請求接口實例和網絡請求參數配置的方法。
  • 而後,使用動態代理動態地去建立網絡請求接口實例。
  • 接着,使用了建造者模式 & 單例模式建立了serviceMethod對象。
  • 再者,使用了策略模式對serviceMethod對象進行網絡請求參數配置,即經過解析網絡請求接口方法的參數、返回值和註解類型,從Retrofit對象中獲取對應的網絡的url地址、網絡請求執行器、網絡請求適配器和數據轉換器。
  • 最後,使用了裝飾者模式ExecuteCallBack爲serviceMethod對象加入線程切換的操做,便於接受數據後經過Handler從子線程切換到主線程從而對返回數據結果進行處理。

三、發送網絡請求:

  • 在異步請求時,經過靜態delegate代理對網絡請求接口的方法中的每一個參數使用對應的ParameterHanlder進行解析。

四、解析數據

五、切換線程:

  • 使用了適配器模式經過檢測不一樣的Platform使用不一樣的回調執行器,而後使用回調執行器切換線程,這裏一樣是使用了裝飾模式。

六、處理結果

Android:主流網絡請求開源庫的對比(Android-Async-Http、Volley、OkHttp、Retrofit)

www.jianshu.com/p/050c6db5a…

3、響應式編程框架:RxJava實現原理

RxJava 變換操做符 map flatMap concatMap buffer?
  • map:【數據類型轉換】將被觀察者發送的事件轉換爲另外一種類型的事件。
  • flatMap:【化解循環嵌套和接口嵌套】將被觀察者發送的事件序列進行拆分 & 轉換 後合併成一個新的事件序列,最後再進行發送。
  • concatMap:【有序】與 flatMap 的 區別在於,拆分 & 從新合併生成的事件序列 的順序與被觀察者舊序列生產的順序一致。
  • buffer:按期從被觀察者發送的事件中獲取必定數量的事件並放到緩存區中,而後把這些數據集合打包發射。
RxJava中map和flatmap操做符的區別及底層實現
手寫rxjava遍歷數組。
你認爲Rxjava的線程池與大家本身實現任務管理框架有什麼區別?

4、圖片加載框架:Glide實現原理

這個庫是作什麼用的?

Glide是Android中的一個圖片加載庫,用於實現圖片加載。

爲何要在項目中使用這個庫?

一、多樣化媒體加載:不只能夠進行圖片緩存,還支持Gif、WebP、縮略圖,甚至是Video。

二、經過設置綁定生命週期:能夠使加載圖片的生命週期動態管理起來。

三、高效的緩存策略:支持內存、Disk緩存,而且Picasso只會緩存原始尺寸的圖片,內Glide緩存的是多種規格,也就是Glide會根據你ImageView的大小來緩存相應大小的圖片尺寸。

四、內存開銷小:默認的Bitmap格式是RGB_565格式,而Picasso默認的是ARGB_8888格式,內存開銷小一半。

這個庫都有哪些用法?對應什麼樣的使用場景?

一、圖片加載:Glide.with(this).load(imageUrl).override(800, 800).placeholder().error().animate().into()。

二、多樣式媒體加載:asBitamp、asGif。

三、生命週期集成。

四、能夠配置磁盤緩存策略ALL、NONE、SOURCE、RESULT。

這個庫的優缺點是什麼,跟同類型庫的比較?

庫比較大,源碼實現複雜。

這個庫的核心實現原理是什麼?若是讓你實現這個庫的某些核心功能,你會考慮怎麼去實現?
  • Glide&with:

一、初始化各式各樣的配置信息(包括緩存,請求線程池,大小,圖片格式等等)以及glide對象。

二、將glide請求和application/SupportFragment/Fragment的生命週期綁定在一塊。

  • Glide&load:

設置請求url,並記錄url已設置的狀態。

三、Glide&into:

一、首先根據轉碼類transcodeClass類型返回不一樣的ImageViewTarget:BitmapImageViewTarget、DrawableImageViewTarget。

二、遞歸創建縮略圖請求,沒有縮略圖請求,則直接進行正常請求。

三、若是沒指定寬高,會根據ImageView的寬高計算出圖片寬高,最終執行到onSizeReay()方法中的engine.load()方法。

四、engine是一個負責加載和管理緩存資源的類

  • 常規三級緩存的流程:強引用->軟引用->硬盤緩存

當咱們的APP中想要加載某張圖片時,先去LruCache中尋找圖片,若是LruCache中有,則直接取出來使用,若是LruCache中沒有,則去SoftReference中尋找(軟引用適合當cache,當內存吃緊的時候纔會被回收。而weakReference在每次system.gc()就會被回收)(當LruCache存儲緊張時,會把最近最少使用的數據放到SoftReference中),若是SoftReference中有,則從SoftReference中取出圖片使用,同時將圖片從新放回到LruCache中,若是SoftReference中也沒有圖片,則去硬盤緩存中中尋找,若是有則取出來使用,同時將圖片添加到LruCache中,若是沒有,則鏈接網絡從網上下載圖片。圖片下載完成後,將圖片保存到硬盤緩存中,而後放到LruCache中。

  • Glide的三層緩存機制:

Glide緩存機制大體分爲三層:內存緩存、弱引用緩存、磁盤緩存。

取的順序是:內存、弱引用、磁盤。

存的順序是:弱引用、內存、磁盤。

三層存儲的機制在Engine中實現的。先說下Engine是什麼?Engine這一層負責加載時作管理內存緩存的邏輯。持有MemoryCache、Map<Key, WeakReference<EngineResource<?>>>。經過load()來加載圖片,加載先後會作內存存儲的邏輯。若是內存緩存中沒有,那麼纔會使用EngineJob這一層來進行異步獲取硬盤資源或網絡資源。EngineJob相似一個異步線程或observable。Engine是一個全局惟一的,經過Glide.getEngine()來獲取。

須要一個圖片資源,若是Lrucache中有相應的資源圖片,那麼就返回,同時從Lrucache中清除,放到activeResources中。activeResources map是盛放正在使用的資源,以弱引用的形式存在。同時資源內部有被引用的記錄。若是資源沒有引用記錄了,那麼再放回Lrucache中,同時從activeResources中清除。若是Lrucache中沒有,就從activeResources中找,找到後相應資源引用加1。若是Lrucache和activeResources中沒有,那麼進行資源異步請求(網絡/diskLrucache),請求成功後,資源放到diskLrucache和activeResources中。

Glide源碼機制的核心思想:

使用一個弱引用map activeResources來盛放項目中正在使用的資源。Lrucache中不含有正在使用的資源。資源內部有個計數器來顯示本身是否是還有被引用的狀況,把正在使用的資源和沒有被使用的資源分開有什麼好處呢??由於當Lrucache須要移除一個緩存時,會調用resource.recycle()方法。注意到該方法上面註釋寫着只有沒有任何consumer引用該資源的時候才能夠調用這個方法。那麼爲何調用resource.recycle()方法須要保證該資源沒有任何consumer引用呢?glide中resource定義的recycle()要作的事情是把這個不用的資源(假設是bitmap或drawable)放到bitmapPool中。bitmapPool是一個bitmap回收再利用的庫,在作transform的時候會從這個bitmapPool中拿一個bitmap進行再利用。這樣就避免了從新建立bitmap,減小了內存的開支。而既然bitmapPool中的bitmap會被重複利用,那麼確定要保證回收該資源的時候(即調用資源的recycle()時),要保證該資源真的沒有外界引用了。這也是爲何glide花費那麼多邏輯來保證Lrucache中的資源沒有外界引用的緣由。

你從這個庫中學到什麼有價值的或者說可借鑑的設計思想?

Glide的高效的三層緩存機制,如上。

Glide如何肯定圖片加載完畢?
Glide使用什麼緩存?
Glide內存緩存如何控制大小?
計算一張圖片的大小

圖片佔用內存的計算公式:圖片高度 * 圖片寬度 * 一個像素佔用的內存大小。因此,計算圖片佔用內存大小的時候,要考慮圖片所在的目錄跟設備密度,這兩個因素其實影響的是圖片的寬高,android會對圖片進行拉昇跟壓縮。

加載bitmap過程(怎樣保證不產生內存溢出)

因爲Android對圖片使用內存有限制,如果加載幾兆的大圖片便內存溢出。Bitmap會將圖片的全部像素(即長x寬)加載到內存中,若是圖片分辨率過大,會直接致使內存OOM,只有在BitmapFactory加載圖片時使用BitmapFactory.Options對相關參數進行配置來減小加載的像素。

BitmapFactory.Options相關參數詳解:

(1).Options.inPreferredConfig值來下降內存消耗。

好比:默認值ARGB_8888改成RGB_565,節約一半內存。

(2).設置Options.inSampleSize 縮放比例,對大圖片進行壓縮 。

(3).設置Options.inPurgeable和inInputShareable:讓系統能及時回收內存。

A:inPurgeable:設置爲True時,表示系統內存不足時能夠被回收,設置爲False時,表示不能被回收。

B:inInputShareable:設置是否深拷貝,與inPurgeable結合使用,inPurgeable爲false時,該參數無心義。
複製代碼

(4).使用decodeStream代替decodeResource等其餘方法。

Android中軟引用與弱引用的應用場景。

Java 引用類型分類:

image

在 Android 應用的開發中,爲了防止內存溢出,在處理一些佔用內存大並且生命週期較長的對象時候,能夠儘可能應用軟引用和弱引用技術。

  • 一、軟/弱引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是軟引用所引用的對象被垃圾回收器回收,Java 虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。利用這個隊列能夠得知被回收的軟/弱引用的對象列表,從而爲緩衝器清除已失效的軟 / 弱引用。
  • 二、若是隻是想避免 OOM 異常的發生,則能夠使用軟引用。若是對於應用的性能更在乎,想盡快回收一些佔用內存比較大的對象,則能夠使用弱引用。
  • 三、能夠根據對象是否常用來判斷選擇軟引用仍是弱引用。若是該對象可能會常用的,就儘可能用軟引用。若是該對象不被使用的可能性更大些,就能夠用弱引用。
Android裏的內存緩存和磁盤緩存是怎麼實現的。

內存緩存基於LruCache實現,磁盤緩存基於DiskLruCache實現。這兩個類都基於Lru算法和LinkedHashMap來實現。

LRU算法能夠用一句話來描述,以下所示:

LRU是Least Recently Used的縮寫,最近最少使用算法,從它的名字就能夠看出,它的核心原則是若是一個數據在最近一段時間沒有使用到,那麼它在未來被訪問到的可能性也很小,則這類數據項會被優先淘汰掉。

LruCache原理

以前,咱們會使用內存緩存技術實現,也就是軟引用或弱引用,在Android 2.3(APILevel 9)開始,垃圾回收器會更傾向於回收持有軟引用或弱引用的對象,這讓軟引用和弱引用變得再也不可靠。

其實LRU緩存的實現相似於一個特殊的棧,把訪問過的元素放置到棧頂(若棧中存在,則更新至棧頂;若棧中不存在則直接入棧),而後若是棧中元素數量超過限定值,則刪除棧底元素(即最近最少使用的元素)。

它的內部存在一個 LinkedHashMap 和 maxSize,把最近使用的對象用強引用存儲在 LinkedHashMap 中,給出來 put 和 get 方法,每次 put 圖片時計算緩存中全部圖片的總大小,跟 maxSize 進行比較,大於 maxSize,就將最久添加的圖片移除,反之小於 maxSize 就添加進來。

LruCache的原理就是利用LinkedHashMap持有對象的強引用,按照Lru算法進行對象淘汰。具體說來假設咱們從表尾訪問數據,在表頭刪除數據,當訪問的數據項在鏈表中存在時,則將該數據項移動到表尾,不然在表尾新建一個數據項。當鏈表容量超過必定閾值,則移除表頭的數據。

詳細來講就是LruCache中維護了一個集合LinkedHashMap,該LinkedHashMap是以訪問順序排序的。當調用put()方法時,就會在結合中添加元素,並調用trimToSize()判斷緩存是否已滿,若是滿了就用LinkedHashMap的迭代器刪除隊頭元素,即近期最少訪問的元素。當調用get()方法訪問緩存對象時,就會調用LinkedHashMap的get()方法得到對應集合元素,同時會更新該元素到隊尾。

LruCache put方法核心邏輯

在添加過緩存對象後,調用trimToSize()方法,來判斷緩存是否已滿,若是滿了就要刪除近期最少使用的對象。trimToSize()方法不斷地刪除LinkedHashMap中隊頭的元素,即近期最少訪問的,直到緩存大小小於最大值(maxSize)。

LruCache get方法核心邏輯

當調用LruCache的get()方法獲取集合中的緩存對象時,就表明訪問了一次該元素,將會更新隊列,保持整個隊列是按照訪問順序排序的。

爲何會選擇LinkedHashMap呢?

這跟LinkedHashMap的特性有關,LinkedHashMap的構造函數裏有個布爾參數accessOrder,當它爲true時,LinkedHashMap會以訪問順序爲序排列元素,不然以插入順序爲序排序元素。

LinkedHashMap原理

LinkedHashMap 幾乎和 HashMap 同樣:從技術上來講,不一樣的是它定義了一個 Entry<K,V> header,這個 header 不是放在 Table 裏,它是額外獨立出來的。LinkedHashMap 經過繼承 hashMap 中的 Entry<K,V>,並添加兩個屬性 Entry<K,V> before,after,和 header 結合起來組成一個雙向鏈表,來實現按插入順序或訪問順序排序。

DisLruCache原理

DiskLruCache與LruCache原理類似,只是多了一個journal文件來作磁盤文件的管理,以下所示:

libcore.io.DiskLruCache
1
1
1

DIRTY 1517126350519
CLEAN 1517126350519 5325928
REMOVE 1517126350519
複製代碼

注:這裏的緩存目錄是應用的緩存目錄/data/data/pckagename/cache,未root的手機能夠經過如下命令進入到該目錄中或者將該目錄總體拷貝出來:

//進入/data/data/pckagename/cache目錄
adb shell
run-as com.your.packagename 
cp /data/data/com.your.packagename/

//將/data/data/pckagename目錄拷貝出來
adb backup -noapk com.your.packagename
複製代碼

咱們來分析下這個文件的內容:

第一行:libcore.io.DiskLruCache,固定字符串。 第二行:1,DiskLruCache源碼版本號。 第三行:1,App的版本號,經過open()方法傳入進去的。 第四行:1,每一個key對應幾個文件,通常爲1. 第五行:空行 第六行及後續行:緩存操做記錄。 第六行及後續行表示緩存操做記錄,關於操做記錄,咱們須要瞭解如下三點:

DIRTY 表示一個entry正在被寫入。寫入分兩種狀況,若是成功會緊接着寫入一行CLEAN的記錄;若是失敗,會增長一行REMOVE記錄。注意單獨只有DIRTY狀態的記錄是非法的。 當手動調用remove(key)方法的時候也會寫入一條REMOVE記錄。 READ就是說明有一次讀取的記錄。 CLEAN的後面還記錄了文件的長度,注意可能會一個key對應多個文件,那麼就會有多個數字。

Bitmap 壓縮策略

加載 Bitmap 的方式:

BitmapFactory 四類方法:

  • decodeFile( 文件系統 )
  • decodeResourece( 資源 )
  • decodeStream( 輸入流 )
  • decodeByteArray( 字節數 )

BitmapFactory.options 參數:

  • inSampleSize 採樣率,對圖片高和寬進行縮放,以最小比進行縮放(通常取值爲 2 的指數)。一般是根據圖片寬高實際的大小/須要的寬高大小,分別計算出寬和高的縮放比。但應該取其中最小的縮放比,避免縮放圖片過小,到達指定控件中不能鋪滿,須要拉伸從而致使模糊。
  • inJustDecodeBounds 獲取圖片的寬高信息,交給 inSampleSize 參數選擇縮放比。經過 inJustDecodeBounds = true,而後加載圖片就能夠實現只解析圖片的寬高信息,並不會真正的加載圖片,因此這個操做是輕量級的。當獲取了寬高信息,計算出縮放比後,而後在將 inJustDecodeBounds = false,再從新加載圖片,就能夠加載縮放後的圖片。

高效加載 Bitmap 的流程:

  • 一、將 BitmapFactory.Options 的 inJustDecodeBounds 參數設爲 true 並加載圖片
  • 二、從 BitmapFactory.Options 中取出圖片原始的寬高信息,對應於 outWidth 和 outHeight 參數
  • 三、根據採樣率規則並結合目標 view 的大小計算出採樣率 inSampleSize
  • 四、將 BitmapFactory.Options 的 inJustDecodeBounds 設置爲 false 從新加載圖片
Bitmap的處理:

當使用ImageView的時候,可能圖片的像素大於ImageView,此時就能夠經過BitmapFactory.Option來對圖片進行壓縮,inSampleSize表示縮小2^(inSampleSize-1)倍。

BitMap的緩存:

1.使用LruCache進行內存緩存。

2.使用DiskLruCache進行硬盤緩存。

實現一個ImageLoader的流程

同步異步加載、圖片壓縮、內存硬盤緩存、網絡拉取

  • 1.同步加載只建立一個線程而後按照順序進行圖片加載
  • 2.異步加載使用線程池,讓存在的加載任務都處於不一樣線程
  • 3.爲了避免開啓過多的異步任務,只在列表靜止的時候開啓圖片加載

具體爲:

  • 一、ImageLoader做爲一個單例,提供了加載圖片到指定控件的方法:直接從內存緩存中獲取對象,若是沒有則用一個ThreadPoolExecutor去執行Runnable任務來加載圖片。ThreadPoolExecutor的建立須要指定核心線程數CPU數+1,最大線程數CPU數*2+1,線程閒置超市時長10s,這幾個關鍵數據,還能夠加入ThreadFactory參數來建立定製化的線程。
  • 二、ImageLoader的具體實現loadBitmap:先從內存緩存LruCache中加載,若是爲空再從磁盤緩存中加載,加載成功後記得存入內存緩存,若是爲空則從網絡中直接下載輸出流到磁盤緩存,而後再從磁盤中加載,若是爲空而且磁盤緩存沒有被建立的話,直接經過BitmapFactory的decodeStream獲取網絡請求的輸入流獲取Bitmap對象。
  • 三、v4包的LruCache能夠兼容到2.2版本,LruCache採用LinkedHashMap存儲緩存對象。建立對象只須要提供緩存容量並重寫sizeOf方法:做用是計算緩存對象的大小。有時須要重寫entryRemoved方法,用於回收一些資源。
  • 四、DiskLruCache經過open方法建立,設置緩存路徑,緩存容量。緩存添加經過Editor對象建立輸出流,下載資源到輸出流完成後,commit,若是失敗則abort撤回。而後刷新磁盤緩存。緩存查找經過Snapshot對象獲取輸入流,獲取FileDescriptor,經過FileDescriptor解析出Bitmap對象。
  • 五、列表中須要加載圖片的時候,當列表在滑動中不進行圖片加載,當滑動中止後再去加載圖片。
Bitmap在decode的時候申請的內存如何複用,釋放時機
圖片庫對比

stackoverflow.com/questions/2…

www.trinea.cn/android/and…

Fresco與Glide的對比:

Glide:相對輕量級,用法簡單優雅,支持Gif動態圖,適合用在那些對圖片依賴不大的App中。 Fresco:採用匿名共享內存來保存圖片,也就是Native堆,有效的的避免了OOM,功能強大,可是庫體積過大,適合用在對圖片依賴比較大的App中。

Fresco的總體架構以下圖所示:

image

DraweeView:繼承於ImageView,只是簡單的讀取xml文件的一些屬性值和作一些初始化的工做,圖層管理交由Hierarchy負責,圖層數據獲取交由負責。 DraweeHierarchy:由多層Drawable組成,每層Drawable提供某種功能(例如:縮放、圓角)。 DraweeController:控制數據的獲取與圖片加載,向pipeline發出請求,並接收相應事件,並根據不一樣事件控制Hierarchy,從DraweeView接收用戶的事件,而後執行取消網絡請求、回收資源等操做。 DraweeHolder:統籌管理Hierarchy與DraweeHolder。 ImagePipeline:Fresco的核心模塊,用來以各類方式(內存、磁盤、網絡等)獲取圖像。 Producer/Consumer:Producer也有不少種,它用來完成網絡數據獲取,緩存數據獲取、圖片解碼等多種工做,它產生的結果由Consumer進行消費。 IO/Data:這一層即是數據層了,負責實現內存緩存、磁盤緩存、網絡緩存和其餘IO相關的功能。 縱觀整個Fresco的架構,DraweeView是門面,和用戶進行交互,DraweeHierarchy是視圖層級,管理圖層,DraweeController是控制器,管理數據。它們構成了整個Fresco框架的三駕馬車。固然還有咱們 幕後英雄Producer,全部的髒活累活都是它乾的,最佳勞模👍

理解了Fresco總體的架構,咱們還有了解在這套礦建裏發揮重要做用的幾個關鍵角色,以下所示:

Supplier:提供一種特定類型的對象,Fresco裏有不少以Supplier結尾的類都實現了這個接口。 SimpleDraweeView:這個咱們就很熟悉了,它接收一個URL,而後調用Controller去加載圖片。該類繼承於GenericDraweeView,GenericDraweeView又繼承於DraweeView,DraweeView是Fresco的頂層View類。 PipelineDraweeController:負責圖片數據的獲取與加載,它繼承於AbstractDraweeController,由PipelineDraweeControllerBuilder構建而來。AbstractDraweeController實現了DraweeController接口,DraweeController 是Fresco的數據大管家,因此的圖片數據的處理都是由它來完成的。 GenericDraweeHierarchy:負責SimpleDraweeView上的圖層管理,由多層Drawable組成,每層Drawable提供某種功能(例如:縮放、圓角),該類由GenericDraweeHierarchyBuilder進行構建,該構建器 將placeholderImage、retryImage、failureImage、progressBarImage、background、overlays與pressedStateOverlay等 xml文件或者Java代碼裏設置的屬性信息都傳入GenericDraweeHierarchy中,由GenericDraweeHierarchy進行處理。 DraweeHolder:該類是一個Holder類,和SimpleDraweeView關聯在一塊兒,DraweeView是經過DraweeHolder來統一管理的。而DraweeHolder又是用來統一管理相關的Hierarchy與Controller DataSource:相似於Java裏的Futures,表明數據的來源,和Futures不一樣,它能夠有多個result。 DataSubscriber:接收DataSource返回的結果。 ImagePipeline:用來調取獲取圖片的接口。 Producer:加載與處理圖片,它有多種實現,例如:NetworkFetcherProducer,LocalAssetFetcherProducer,LocalFileFetchProducer。從這些類的名字咱們就能夠知道它們是幹什麼的。 Producer由ProducerFactory這個工廠類構建的,並且全部的Producer都是像Java的IO流那樣,能夠一層嵌套一層,最終只獲得一個結果,這是一個很精巧的設計👍 Consumer:用來接收Producer產生的結果,它與Producer組成了生產者與消費者模式。 注:Fresco源碼裏的類的名字都比較長,可是都是按照必定的命令規律來的,例如:以Supplier結尾的類都實現了Supplier接口,它能夠提供某一個類型的對象(factory, generator, builder, closure等)。 以Builder結尾的固然就是以構造者模式建立對象的類。

Bitmap如何處理大圖,如一張30M的大圖,如何預防OOM?

blog.csdn.net/guolin_blog…

blog.csdn.net/lmj62356579…

使用BitmapRegionDecoder動態加載圖片的顯示區域。

Bitmap對象的理解。
對inBitmap的理解。
本身去實現圖片庫,怎麼作?(對擴展開發,對修改封閉,同時又保持獨立性,參考Android源碼設計模式解析實戰的圖片加載庫案例便可)
寫個圖片瀏覽器,說出你的思路?

5、事件總線框架:EventBus實現原理

6、內存泄漏檢測框架:LeakCanary實現原理

這個庫是作什麼用?

內存泄露檢測框架。

爲何要在項目中使用這個庫?
  • 針對Android Activity組件徹底自動化的內存泄漏檢查,在最新的版本中,還加入了android.app.fragment的組件自動化的內存泄漏檢測。
  • 易用集成,使用成本低。
  • 友好的界面展現和通知。
這個庫都有哪些用法?對應什麼樣的使用場景?

直接從application中拿到全局的 refWatcher 對象,在Fragment或其餘組件的銷燬回調中使用refWatcher.watch(this)檢測是否發生內存泄漏。

這個庫的優缺點是什麼,跟同類型庫的比較?

檢測結果並非特別的準確,由於內存的釋放和對象的生命週期有關也和GC的調度有關。

這個庫的核心實現原理是什麼?若是讓你實現這個庫的某些核心功能,你會考慮怎麼去實現?

主要分爲以下7個步驟:

  • 一、RefWatcher.watch()建立了一個KeyedWeakReference用於去觀察對象。
  • 二、而後,在後臺線程中,它會檢測引用是否被清除了,而且是否沒有觸發GC。
  • 三、若是引用仍然沒有被清除,那麼它將會把堆棧信息保存在文件系統中的.hprof文件裏。
  • 四、HeapAnalyzerService被開啓在一個獨立的進程中,而且HeapAnalyzer使用了HAHA開源庫解析了指定時刻的堆棧快照文件heap dump。
  • 五、從heap dump中,HeapAnalyzer根據一個獨特的引用key找到了KeyedWeakReference,而且定位了泄露的引用。
  • 六、HeapAnalyzer爲了肯定是否有泄露,計算了到GC Roots的最短強引用路徑,而後創建了致使泄露的鏈式引用。
  • 七、這個結果被傳回到app進程中的DisplayLeakService,而後一個泄露通知便展示出來了。

簡單來講就是:

在一個Activity執行完onDestroy()以後,將它放入WeakReference中,而後將這個WeakReference類型的Activity對象與ReferenceQueque關聯。這時再從ReferenceQueque中查看是否有該對象,若是沒有,執行gc,再次查看,仍是沒有的話則判斷髮生內存泄露了。最後用HAHA這個開源庫去分析dump以後的heap內存(主要就是建立一個HprofParser解析器去解析出對應的引用內存快照文件snapshot)。

流程圖:

image

源碼分析中一些核心分析點:

AndroidExcludedRefs:它是一個enum類,它聲明瞭Android SDK和廠商定製的SDK中存在的內存泄露的case,根據AndroidExcludedRefs這個類的類名就可看出這些case都會被Leakcanary的監測過濾掉。

buildAndInstall()(即install方法)這個方法應該僅僅只調用一次。

debuggerControl : 判斷是否處於調試模式,調試模式中不會進行內存泄漏檢測。爲何呢?由於在調試過程當中可能會保留上一個引用從而致使錯誤信息上報。

watchExecutor : 線程控制器,在 onDestroy() 以後而且主線程空閒時執行內存泄漏檢測。

gcTrigger : 用於 GC,watchExecutor 首次檢測到可能的內存泄漏,會主動進行 GC,GC 以後會再檢測一次,仍然泄漏的斷定爲內存泄漏,最後根據heapDump信息生成相應的泄漏引用鏈。

gcTrigger的runGc()方法:這裏並無使用System.gc()方法進行回收,由於system.gc()並不會每次都執行。而是從AOSP中拷貝一段GC回收的代碼,從而相比System.gc()更可以保證進行垃圾回收的工做。

Runtime.getRuntime().gc();
複製代碼

子線程延時1000ms;

System.runFinalization();

install方法內部最終仍是調用了application的registerActivityLifecycleCallbacks()方法,這樣就可以監聽activity對應的生命週期事件了。

在RefWatcher#watch()中使用隨機的UUID保證了每一個檢測對象對應的key 的惟一性。

在KeyedWeakReference內部,使用了key和name標識了一個被檢測的WeakReference對象。在其構造方法中將弱引用和引用隊列 ReferenceQueue 關聯起來,若是弱引用reference持有的對象被GC回收,JVM就會把這個弱引用加入到與之關聯的引用隊列referenceQueue中。即 KeyedWeakReference 持有的 Activity 對象若是被GC回收,該對象就會加入到引用隊列 referenceQueue 中。

使用Android SDK的API Debug.dumpHprofData() 來生成 hprof 文件。

在HeapAnalyzerService(類型爲IntentService的ForegroundService)的runAnalysis()方法中,爲了不減慢app進程或佔用內存,這裏將HeapAnalyzerService設置在了一個獨立的進程中。

你從這個庫中學到什麼有價值的或者說可借鑑的設計思想?
leakCannary中如何判斷一個對象是否被回收?如何觸發手動gc?c層實現?
BlockCanary原理:

該組件利用了主線程的消息隊列處理機制,應用發生卡頓,必定是在dispatchMessage中執行了耗時操做。咱們經過給主線程的Looper設置一個Printer,打點統計dispatchMessage方法執行的時間,若是超出閥值,表示發生卡頓,則dump出各類信息,提供開發者分析性能瓶頸。

7、依賴注入框架:ButterKnife實現原理

ButterKnife對性能的影響很小,由於沒有使用使用反射,而是使用的Annotation Processing Tool(APT),註解處理器,javac中用於編譯時掃描和解析Java註解的工具。在編譯階段執行的,它的原理就是讀入Java源代碼,解析註解,而後生成新的Java代碼。新生成的Java代碼最後被編譯成Java字節碼,註解解析器不能改變讀入的Java類,好比不能加入或刪除Java方法。

AOP IOC 的好處以及在 Android 開發中的應用

8、依賴全局管理框架:Dagger2實現原理

9、數據庫框架:GreenDao實現原理

數據庫框架對比?
數據庫的優化
數據庫數據遷移問題
數據庫索引的數據結構
平衡二叉樹
  • 一、非葉子節點只能容許最多兩個子節點存在。
  • 二、每個非葉子節點數據分佈規則爲左邊的子節點小當前節點的值,右邊的子節點大於當前節點的值(這裏值是基於本身的算法規則而定的,好比hash值)。
  • 三、樹的左右兩邊的層級數相差不會大於1。

使用平衡二叉樹能保證數據的左右兩邊的節點層級相差不會大於1.,經過這樣避免樹形結構因爲刪除增長變成線性鏈表影響查詢效率,保證數據平衡的狀況下查找數據的速度近於二分法查找。

image

目前大部分數據庫系統及文件系統都採用B-Tree或其變種B+Tree做爲索引結構。

B-Tree

B樹和平衡二叉樹稍有不一樣的是B樹屬於多叉樹又名平衡多路查找樹(查找路徑不僅兩個)。

  • 一、排序方式:全部節點關鍵字是按遞增次序排列,並遵循左小右大原則。
  • 二、子節點數:非葉節點的子節點數>1,且<=M ,且M>=2,空樹除外(注:M階表明一個樹節點最多有多少個查找路徑,M=M路,當M=2則是2叉樹,M=3則是3叉)。
  • 三、關鍵字數:枝節點的關鍵字數量大於等於ceil(m/2)-1個且小於等於M-1個(注:ceil()是個朝正無窮方向取整的函數 如ceil(1.1)結果爲2)。
  • 四、全部葉子節點均在同一層、葉子節點除了包含了關鍵字和關鍵字記錄的指針外也有指向其子節點的指針只不過其指針地址都爲null對應下圖最後一層節點的空格子。

image

B樹相對於平衡二叉樹的不一樣是,每一個節點包含的關鍵字增多了,把樹的節點關鍵字增多後樹的層級比原來的二叉樹少了,減小數據查找的次數和複雜度。

B+Tree
規則:
  • 一、B+跟B樹不一樣B+樹的非葉子節點不保存關鍵字記錄的指針,只進行數據索引。
  • 二、B+樹葉子節點保存了父節點的全部關鍵字記錄的指針,全部數據地址必需要到葉子節點才能獲取到。因此每次數據查詢的次數都同樣。
  • 三、B+樹葉子節點的關鍵字從小到大有序排列,左邊結尾數據都會保存右邊節點開始數據的指針。
  • 四、非葉子節點的子節點數=關鍵字數(來源百度百科)(根據各類資料 這裏有兩種算法的實現方式,另外一種爲非葉節點的關鍵字數=子節點數-1(來源維基百科),雖然他們數據排列結構不同,但其原理仍是同樣的Mysql 的B+樹是用第一種方式實現)。

image

特色:

一、B+樹的層級更少:相較於B樹B+每一個非葉子節點存儲的關鍵字數更多,樹的層級更少因此查詢數據更快。

二、B+樹查詢速度更穩定:B+全部關鍵字數據地址都存在葉子節點上,因此每次查找的次數都相同因此查詢速度要比B樹更穩定。

三、B+樹自然具有排序功能:B+樹全部的葉子節點數據構成了一個有序鏈表,在查詢大小區間的數據時候更方便,數據緊密性很高,緩存的命中率也會比B樹高。

四、B+樹全節點遍歷更快:B+樹遍歷整棵樹只須要遍歷全部的葉子節點便可,而不須要像B樹同樣須要對每一層進行遍歷,這有利於數據庫作全表掃描。

B樹相對於B+樹的優勢是,若是常常訪問的數據離根節點很近,而B樹的非葉子節點自己存有關鍵字其數據的地址,因此這種數據檢索的時候會要比B+樹快。

B*Tree

B*樹是B+樹的變種,相對於B+樹他們的不一樣之處以下:

  • 一、首先是關鍵字個數限制問題,B+樹初始化的關鍵字初始化個數是cei(m/2),b樹的初始化個數爲(cei(2/3m))。

  • 二、B+樹節點滿時就會分裂,而B*樹節點滿時會檢查兄弟節點是否滿(由於每一個節點都有指向兄弟的指針),若是兄弟節點未滿則向兄弟節點轉移關鍵字,若是兄弟節點已滿,則從當前節點和兄弟節點各拿出1/3的數據建立一個新的節點出來。

在B+樹的基礎上因其初始化的容量變大,使得節點空間使用率更高,而又存有兄弟節點的指針,能夠向兄弟節點轉移關鍵字的特性使得B*樹分解次數變得更少。

image

結論:
  • 一、相同思想和策略:從平衡二叉樹、B樹、B+樹、B*樹整體來看它們貫徹的思想是相同的,都是採用二分法和數據平衡策略來提高查找數據的速度。
  • 二、不一樣的方式的磁盤空間利用:不一樣點是他們一個一個在演變的過程當中經過IO從磁盤讀取數據的原理進行一步步的演變,每一次演變都是爲了讓節點的空間更合理的運用起來,從而使樹的層級減小達到快速查找數據的目的;

還不理解請查看:平衡二叉樹、B樹、B+樹、B*樹 理解其中一種你就都明白了

4、熱修復、插件化、模塊化、組件化、Gradle、編譯插樁技術

一、熱修復和插件化

Android中ClassLoader的種類&特色

  • BootClassLoader(Java的BootStrap ClassLoader): 用於加載Android Framework層class文件。
  • PathClassLoader(Java的App ClassLoader): 用於加載已經安裝到系統中的apk中的class文件。
  • DexClassLoader(Java的Custom ClassLoader): 用於加載指定目錄中的class文件。
  • BaseDexClassLoader: 是PathClassLoader和DexClassLoader的父類。

熱修補技術是怎樣實現的,和插件化有什麼區別?

插件化:動態加載主要解決3個技術問題:

  • 一、使用ClassLoader加載類。
  • 二、資源訪問。
  • 三、生命週期管理。

插件化是體如今功能拆分方面的,它將某個功能獨立提取出來,獨立開發,獨立測試,再插入到主應用中。以此來減小主應用的規模。

熱修復:

緣由:由於一個dvm中存儲方法id用的是short類型,致使dex中方法不能超過65536個。

代碼熱修復原理:
  • 將編譯好的class文件拆分打包成兩個dex,繞過dex方法數量的限制以及安裝時的檢查,在運行時再動態加載第二個dex文件中。
  • 熱修復是體如今bug修復方面的,它實現的是不須要從新發版和從新安裝,就能夠去修復已知的bug。
  • 利用PathClassLoader和DexClassLoader去加載與bug類同名的類,替換掉bug類,進而達到修復bug的目的,原理是在app打包的時候阻止類打上CLASS_ISPREVERIFIED標誌,而後在熱修復的時候動態改變BaseDexClassLoader對象間接引用的dexElements,替換掉舊的類。

相同點:

都使用ClassLoader來實現加載新的功能類,均可以使用PathClassLoader與DexClassLoader。

不一樣點:

熱修復由於是爲了修復Bug的,因此要將新的類替代同名的Bug類,要搶先加載新的類而不是Bug類,因此多作兩件事:在原先的app打包的時候,阻止相關類去打上CLASS_ISPREVERIFIED標誌,還有在熱修復時動態改變BaseDexClassLoader對象間接引用的dexElements,這樣才能搶先代替Bug類,完成系統不加載舊的Bug類.。 而插件化只是增長新的功能類或者是資源文件,因此不涉及搶先加載新的類這樣的使命,就避過了阻止相關類去打上CLASS_ISPREVERIFIED標誌和還有在熱修復時動態改變BaseDexClassLoader對象間接引用的dexElements.

因此插件化比熱修復簡單,熱修復是在插件化的基礎上在進行替換舊的Bug類。

熱修復原理:

資源修復:

不少熱修復框架的資源修復參考了Instant Run的資源修復的原理。

傳統編譯部署流程以下:

Instant Run編譯部署流程以下:

  • Hot Swap:修改一個現有方法中的代碼時會採用Hot Swap。
  • Warm Swap:修改或刪除一個現有的資源文件時會採用Warm Swap。
  • Cold Swap:有不少狀況,如添加、刪除或修改一個字段和方法、添加一個類等。

Instant Run中的資源熱修復流程:

  • 一、建立新的AssetManager,經過反射調用addAssetPath方法加載外部的資源,這樣新建立的AssetManager就含有了外部資源。
  • 二、將AssetManager類型的mAssets字段的引用所有替換爲新建立的AssetManager。
代碼修復:

一、類加載方案:

65536限制:

65536的主要緣由是DVM Bytecode的限制,DVM指令集的方法調用指令invoke-kind索引爲16bits,最多能引用65535個方法。

LinearAlloc限制:

  • DVM中的LinearAlloc是一個固定的緩存區,當方法數超過了緩存區的大小時會報錯。

Dex分包方案主要作的是在打包時將應用代碼分紅多個Dex,將應用啓動時必須用到的類和這些類的直接引用類放到Dex中,其餘代碼放到次Dex中。當應用啓動時先加載主Dex,等到應用啓動後再動態地加載次Dex,從而緩解了主Dex的65536限制和LinearAlloc限制。

加載流程:

  • 根據dex文件的查找流程,咱們將有Bug的類Key.class進行修改,再將Key.class打包成包含dex的補丁包Patch.jar,放在Element數組dexElements的第一個元素,這樣會首先找到Patch.dex中的Key.class去替換以前存在Bug的Key.class,排在數組後面的dex文件中存在Bug的Key.class根據ClassLoader的雙親委託模式就不會被加載。

類加載方案須要重啓App後讓ClassLoader從新加載新的類,爲何須要重啓呢?

  • 這是由於類是沒法被卸載的,要想從新加載新的類就須要重啓App,所以採用類加載方案的熱修復框架是不能即時生效的。

各個熱修復框架的實現細節差別:

  • QQ空間的超級補丁和Nuwa是按照上面說的將補丁包放在Element數組的第一個元素獲得優先加載。
  • 微信的Tinker將新舊APK作了diff,獲得path.dex,再將patch.dex與手機中APK的classes.dex作合併,生成新的classes.dex,而後在運行時經過反射將classes.dex放在Elements數組的第一個元素。
  • 餓了麼的Amigo則是將補丁包中每一個dex對應的Elements取出來,以後組成新的Element數組,在運行時經過反射用新的Elements數組替換掉現有的Elements數組。

二、底層替換方案:

當咱們要反射Key的show方法,會調用Key.class.getDeclaredMethod("show").invoke(Key.class.newInstance());,最終會在native層將傳入的javaMethod在ART虛擬機中對應一個ArtMethod指針,ArtMethod結構體中包含了Java方法的全部信息,包括執行入口、訪問權限、所屬類和代碼執行地址等。

替換ArtMethod結構體中的字段或者替換整個ArtMethod結構體,這就是底層替換方案。

AndFix採用的是替換ArtMethod結構體中的字段,這樣會有兼容性問題,由於廠商可能會修改ArtMethod結構體,致使方法替換失敗。

Sophix採用的是替換整個ArtMethod結構體,這樣不會存在兼容問題。

底層替換方案直接替換了方法,能夠當即生效不須要重啓。採用底層替換方案主要是阿里係爲主,包括AndFix、Dexposed、阿里百川、Sophix。

三、Instant Run方案:

什麼是ASM?

ASM是一個java字節碼操控框架,它可以動態生成類或者加強現有類的功能。ASM能夠直接產生class文件,也能夠在類被加載到虛擬機以前動態改變類的行爲。

Instant Run在第一次構建APK時,使用ASM在每個方法中注入了相似的代碼邏輯:當change不爲null時,則調用它的accessdispatch方法,參數爲具體的方法名和方法參數。當MainActivity的onCreate方法作了修改,就會生成替換類MainActivityoverride,這個類實現了IncrementalChange接口,同時也會生成一個AppPatchesLoaderImpl類,這個類的getPatchedClasses方法會返回被修改的類的列表(裏面包含了MainActivity),根據列表會將MainActivity的change設置爲MainActivityoverride。最後這個change就不會爲null,則會執行MainActivityoverride的accessdispatch方法,最終會執行onCreate方法,從而實現了onCreate方法的修改。

借鑑Instant Run原理的熱修復框架有Robust和Aceso。

動態連接庫修復:

從新加載so。

加載so主要用到了System類的load和loadLibrary方法,最終都會調用到nativeLoad方法。其會調用JavaVMExt的LoadNativeLibrary函數來加載so。

so修復主要有兩個方案:

  • 一、將so補丁插入到NativeLibraryElement數組的前部,讓so補丁的路徑先被返回和加載。
  • 二、調用System的load方法來接管so的加載入口。

爲何選用插件化?

在Android傳統開發中,一旦應用的代碼被打包成APK並被上傳到各個應用市場,咱們就不能修改應用的源碼了,只能經過服務器來控制應用中預留的分支代碼。可是不少時候咱們沒法預知需求和忽然發生的狀況,也就不能提早在應用代碼中預留分支代碼,這時就須要採用動態加載技術,即在程序運行時,動態加載一些程序中本來不存在的可執行文件並運行這些文件裏的代碼邏輯。其中可執行文件包括動態連接庫so和dex相關文件(dex以及包含dex的jar/apk文件)。隨着應用開發技術和業務的逐步發展,動態加載技術派生出兩個技術:熱修復和插件化。其中熱修復技術主要用來修復Bug,而插件化技術則主要用於解決應用愈來愈龐大以及功能模塊的解耦。詳細點說,就是爲了解決如下幾種狀況:

  • 一、業務複雜、模塊耦合:隨着業務愈來愈複雜,應用程序的工程和功能模塊數量會愈來愈多,一個應用可能由幾十甚至幾百人來協同開發,其中的一個工程可能就由一個小組來進行開發維護,若是功能模塊間的耦合度較高,修改一個模塊會影響其它功能模塊,勢必會極大地增長溝通成本。
  • 二、應用間的接入:當一個應用須要接入其它應用時,如淘寶,爲了將流量引流到其它的淘寶應用如:飛豬旅遊、口碑外賣、聚划算等等應用,如使用常規技術有兩個問題:可能要維護多個版本的問題或單個應用體積將會很是龐大的問題。
  • 三、65536限制,內存佔用大。

插件化的思想:

安裝的應用能夠理解爲插件,這些插件能夠自由地進行插拔。

插件化的定義:

插件通常是指通過處理的APK,so和dex等文件,插件能夠被宿主進行加載,有的插件也能夠做爲APK獨立運行。

將一個應用按照插件的方式進行改造的過程就叫做插件化。

插件化的優點:

  • 低耦合
  • 應用間的接入和維護更便捷,每一個應用團隊只須要負責本身的那一部分。
  • 應用及主dex的體積也會相應變小,間接地避免了65536限制。
  • 第一次加載到內存的只有淘寶客戶端,當使用到其它插件時纔會加載相應插件到內存,以減小內存佔用。

插件化框架對比:

  • 最先的插件化框架:2012年大衆點評的屠毅敏就推出了AndroidDynamicLoader框架。
  • 目前主流的插件化方案有滴滴任玉剛的VirtualApk、360的DroidPlugin、RePlugin、Wequick的Small框架。
  • 若是加載的插件不須要和宿主有任何耦合,也無須和宿主進行通訊,好比加載第三方App,那麼推薦使用RePlugin,其餘狀況推薦使用VirtualApk。因爲VirtualApk在加載耦合插件方面是插件化框架的首選,具備廣泛的適用性,所以有必要對它的源碼進行了解。

插件化原理:

Activity插件化:

主要實現方式有三種:

  • 反射:對性能有影響,主流的插件化框架沒有采用此方式。
  • 接口:dynamic-load-apk採用。
  • Hook:主流。

Hook實現方式有兩種:Hook IActivityManager和Hook Instrumentation。主要方案就是先用一個在AndroidManifest.xml中註冊的Activity來進行佔坑,用來經過AMS的校驗,接着在合適的時機用插件Activity替換佔坑的Activity。

Hook IActivityManager:

一、佔坑、經過校驗:

在Android 7.0和8.0的源碼中IActivityManager藉助了Singleton類實現單例,並且該單例是靜態的,所以IActivityManager是一個比較好的Hook點。

接着,定義替換IActivityManager的代理類IActivityManagerProxy,因爲Hook點IActivityManager是一個接口,建議這裏採用動態代理。

  • 攔截startActivity方法,獲取參數args中保存的Intent對象,它是本來要啓動插件TargetActivity的Intent。
  • 新建一個subIntent用來啓動StubActivity,並將前面獲得的TargetActivity的Intent保存到subIntent中,便於之後還原TargetActivity。
  • 最後,將subIntent賦值給參數args,這樣啓動的目標就變爲了StubActivity,用來經過AMS的校驗。

而後,用代理類IActivityManagerProxy來替換IActivityManager。

  • 當版本大於等於26時,使用反射獲取ActivityManager的IActivityManagerSingleton字段,小於時則獲取ActivityManagerNative中的gDefault字段。
  • 而後,經過反射獲取對應的Singleton實例,從上面獲得的2個字段中拿到對應的IActivityManager。
  • 最後,使用Proxy.newProxyInstance()方法動態建立代理類IActivityManagerProxy,用IActivityManagerProxy來替換IActivityManager。

二、還原插件Activity:

  • 前面用佔坑Activity經過了AMS的校驗,可是咱們要啓動的是插件TargetActivity,還須要用插件TargetActivity來替換佔坑的SubActivity,替換時機爲圖中步驟2以後。
  • 在ActivityThread的H類中重寫的handleMessage方法會對LAUNCH_ACTIVITY類型的消息進行處理,最終會調用Activity的onCreate方法。在Handler的dispatchMessage處理消息的這個方法中,看到若是Handelr的Callback類型的mCallBack不爲null,就會執行mCallback的handleMessage方法,所以mCallback能夠做爲Hook點。咱們能夠用自定義的Callback來替換mCallback。

自定義的Callback實現了Handler.Callback,並重寫了handleMessage方法,當收到消息的類型爲LAUNCH_ACTIVITY時,將啓動SubActivity的Intent替換爲啓動TargetActivity的Intent。而後使用反射將Handler的mCallback替換爲自定義的CallBack便可。使用時則在application的attachBaseContext方法中進行hook便可。

三、插件Activity的生命週期:

  • AMS和ActivityThread之間的通訊採用了token來對Activity進行標識,而且此後的Activity的生命週期處理也是根據token來對Activity進行標識的,由於咱們在Activity啓動時用插件TargetActivity替換佔坑SubActivity,這一過程在performLaunchActivity以前,所以performLaunchActivity的r.token就是TargetActivity。因此TargetActivity具備生命週期。

Hook Instrumentation:

Hook Instrumentation實現一樣也須要用到佔坑Activity,與Hook IActivity實現不一樣的是,用佔坑Activity替換插件Activity以及還原插件Activity的地方不一樣。

分析:在Activity經過AMS校驗前,會調用Activity的startActivityForResult方法,其中調用了Instrumentation的execStartActivity方法來激活Activity的生命週期。而且在ActivityThread的performLaunchActivity中使用了mInstrumentation的newActivity方法,其內部會用類加載器來建立Activity的實例。

方案:在Instrumentation的execStartActivity方法中用佔坑SubActivity來經過AMS的驗證,在Instrumentation的newActivity方法中還原TargetActivity,這兩部操做都和Instrumentation有關,所以咱們能夠用自定義的Instumentation來替換掉mInstrumentation。具體爲:

  • 首先檢查TargetActivity是否已經註冊,若是沒有則將TargetActivity的ClassName保存起來用於後面還原。接着把要啓動的TargetActivity替換爲StubActivity,最後經過反射調用execStartActivity方法,這樣就能夠用StubActivity經過AMS的驗證。
  • 在newActivity方法中建立了此前保存的TargetActivity,完成了還原TargetActivity。最後使用反射用InstrumentationProxy替換mInstumentation。
資源插件化:

資源的插件化和熱修復的資源修復都藉助了AssetManager。

資源的插件化方案主要有兩種:

  • 一、合併資源方案,將插件的資源所有添加到宿主的Resources中,這種方案插件能夠訪問宿主的資源。
  • 二、構建插件資源方案,每一個插件都構造出獨立的Resources,這種方案插件不能夠訪問宿主資源。
so的插件化:

so的插件化方案和so熱修復的第一種方案相似,就是將so插件插入到NativelibraryElement數組中,而且將存儲so插件的文件添加到nativeLibraryDirectories集合中就能夠了。

插件的加載機制方案:
  • 一、Hook ClassLoader。
  • 二、委託給系統的ClassLoader幫忙加載。

二、模塊化和組件化

模塊化的好處

www.jianshu.com/p/376ea8a19…

分析現有的組件化方案:

不少大廠的組件化方案是以 多工程 + 多 Module 的結構(微信, 美團等超級 App 更是以 多工程 + 多 Module + 多 P 工程(以頁面爲單元的代碼隔離方式) 的三級工程結構), 使用 Git Submodule 建立多個子倉庫管理各個模塊的代碼, 並將各個模塊的代碼打包成 AAR 上傳至私有 Maven 倉庫使用遠程版本號依賴的方式進行模塊間代碼的隔離。

組件化開發的好處:

  • 避免重複造輪子,能夠節省開發和維護的成本。
  • 能夠經過組件和模塊爲業務基準合理地安排人力,提升開發效率。
  • 不一樣的項目能夠共用一個組件或模塊,確保總體技術方案的統一性。
  • 爲將來插件化共用同一套底層模型作準備。

跨組件通訊:

跨組件通訊場景:

  • 第一種是組件之間的頁面跳轉 (Activity 到 Activity, Fragment 到 Fragment, Activity 到 Fragment, Fragment 到 Activity) 以及跳轉時的數據傳遞 (基礎數據類型和可序列化的自定義類類型)。
  • 第二種是組件之間的自定義類和自定義方法的調用(組件向外提供服務)。

跨組件通訊方案分析:

  • 第一種組件之間的頁面跳轉不須要過多描述了, 算是 ARouter 中最基礎的功能, API 也比較簡單, 跳轉時想傳遞不一樣類型的數據也提供有相應的 API。
  • 第二種組件之間的自定義類和自定義方法的調用要稍微複雜點, 須要 ARouter 配合架構中的 公共服務(CommonService) 實現:
提供服務的業務模塊:

在公共服務(CommonService) 中聲明 Service 接口 (含有須要被調用的自定義方法), 而後在本身的模塊中實現這個 Service 接口, 再經過 ARouter API 暴露實現類。

使用服務的業務模塊:

經過 ARouter 的 API 拿到這個 Service 接口(多態持有, 實際持有實現類), 便可調用 Service 接口中聲明的自定義方法, 這樣就能夠達到模塊之間的交互。 此外,能夠使用 AndroidEventBus 其獨有的 Tag, 能夠在開發時更容易定位發送事件和接受事件的代碼, 若是以組件名來做爲 Tag 的前綴進行分組, 也能夠更好的統一管理和查看每一個組件的事件, 固然也不建議你們過多使用 EventBus。

如何管理過多的路由表?

RouterHub 存在於基礎庫, 能夠被看做是全部組件都須要遵照的通信協議, 裏面不只能夠放路由地址常量, 還能夠放跨組件傳遞數據時命名的各類 Key 值, 再配以適當註釋, 任何組件開發人員不須要事先溝通只要依賴了這個協議, 就知道了各自該怎樣協同工做, 既提升了效率又下降了出錯風險, 約定的東西天然要比口頭上說的強。

Tips: 若是您以爲把每一個路由地址都寫在基礎庫的 RouterHub 中, 太麻煩了, 也能夠在每一個組件內部創建一個私有 RouterHub, 將不須要跨組件的路由地址放入私有 RouterHub 中管理, 只將須要跨組件的路由地址放入基礎庫的公有 RouterHub 中管理, 若是您不須要集中管理全部路由地址的話, 這也是比較推薦的一種方式。

ARouter路由原理:

ARouter維護了一個路由表Warehouse,其中保存着所有的模塊跳轉關係,ARouter路由跳轉實際上仍是調用了startActivity的跳轉,使用了原生的Framework機制,只是經過apt註解的形式製造出跳轉規則,並人爲地攔截跳轉和設置跳轉條件。

多模塊開發的時候不一樣的負責人可能會引入重複資源,相同的字符串,相同的icon等可是文件名並不同,怎樣去重?

三、gradle

gradle熟悉麼,自動打包知道麼?

如何加快 Gradle 的編譯速度?

Gradle的Flavor可否配置sourceset?

Gradle生命週期

四、編譯插樁

談談你對AOP技術的理解?

說說你瞭解的編譯插樁技術?

5、架構設計

MVC MVP MVVM原理和區別?

架構設計的目的

經過設計是模塊程序化,從而作到高內聚低耦合,讓開發者能更專一於功能實現自己,提供程序開發效率、更容易進行測試、維護和定位問題等等。並且,不一樣的規模的項目應該選用不一樣的架構設計。

MVC

MVC是模型(model)-視圖(view)-控制器(controller)的縮寫,其中M層處理數據,業務邏輯等;V層處理界面的顯示結果;C層起到橋樑的做用,來控制V層和M層通訊以此來達到分離視圖顯示和業務邏輯層。在Android中的MVC劃分是這樣的:

  • 視圖層(View):通常採用XML文件進行界面的描述,也能夠在界面中使用動態佈局的方式。
  • 控制層(Controller):由Activity承擔。
  • 模型層(Model):數據庫的操做、對網絡等的操做,複雜業務計算等等。
MVC缺點

在Android開發中,Activity並非一個標準的MVC模式中的Controller,它的首要職責是加載應用的佈局和初始化用戶界面,並接受和處理來自用戶的操做請求,進而做出響應。隨着界面及其邏輯的複雜度不斷提高,Activity類的職責不斷增長,以至變得龐大臃腫。

MVP

MVP框架由3部分組成:View負責顯示,Presenter負責邏輯處理,Model提供數據。

  • View:負責繪製UI元素、與用戶進行交互(在Android中體現爲Activity)。
  • Model:負責存儲、檢索、操縱數據(有時也實現一個Model interface用來下降耦合)。
  • Presenter:做爲View與Model交互的中間紐帶,處理與用戶交互的邏輯。
  • View interface:須要View實現的接口,View經過View interface與Presenter進行交互,下降耦合,方便使用MOCK對Presenter進行單元測試。

MVP的Presenter是框架的控制者,承擔了大量的邏輯操做,而MVC的Controller更多時候承擔一種轉發的做用。所以在App中引入MVP的緣由,是爲了將此前在Activty中包含的大量邏輯操做放到控制層中,避免Activity的臃腫。

MVP與MVC的主要區別:
  • 一、(最主要區別)View與Model並不直接交互,而是經過與Presenter交互來與Model間接交互。而在MVC中View能夠與Model直接交互。
  • 二、Presenter與View的交互是經過接口來進行的,更有利於添加單元測試。
MVP的優勢
  • 一、模型與視圖徹底分離,咱們能夠修改視圖而不影響模型。
  • 二、能夠更高效地使用模型,由於全部的交互都發生在一個地方——Presenter內部。
  • 三、咱們能夠將一個Presenter用於多個視圖,而不須要改變Presenter的邏輯。這個特性很是的有用,由於視圖的變化老是比模型的變化頻繁。
  • 四、若是咱們把邏輯放在Presenter中,那麼咱們就能夠脫離用戶接口來測試這些邏輯(單元測試)。

UI層通常包括Activity,Fragment,Adapter等直接和UI相關的類,UI層的Activity在啓動以後實例化相應的Presenter,App的控制權後移,由UI轉移到Presenter,二者之間的通訊經過BroadCast、Handler、事件總線機制或者接口完成,只傳遞事件和結果。

MVP的執行流程:首先V層通知P層用戶發起了一個網絡請求,P層會決定使用負責網絡相關的M層去發起請求網絡,最後,P層將完成的結果更新到V層。

MVP的變種:Passive View

View直接依賴Presenter,可是Presenter間接依賴View,它直接依賴的是View實現的接口。相對於View的被動,那Presenter就是主動的一方。對於Presenter的主動,有以下的理解:

  • Presenter是整個MVP體系的控制中心,而不是單純的處理View請求的人。
  • View僅僅是用戶交互請求的彙報者,對於響應用戶交互相關的邏輯和流程,View不參與決策,真正的決策者是Presenter。
  • View向Presenter發送用戶交互請求應該採用這樣的口吻:「我如今將用戶交互請求發送給你,你看着辦,須要個人時候我會協助你」。
  • 對於綁定到View上的數據,不該該是View從Presenter上「拉」回來的,應該是Presenter主動「推」給View的。(這裏借鑑了IOC作法)
  • View儘量不維護數據狀態,由於其自己僅僅實現單純的、獨立的UI操做;Presenter纔是整個體系的協調者,它根據處理用於交互的邏輯給View和Model安排工做。
MVP架構存在的問題與解決辦法
  • 一、加入模板方法

將邏輯操做從V層轉移到P層後,可能有一些Activity仍是比較膨脹,此時,能夠經過繼承BaseActivity的方式加入模板方法。注意,最好不要超過3層繼承。

  • 二、Model內部分層

模型層(Model)中的總體代碼量是最大的,此時能夠進行模塊的劃分和接口隔離。

  • 三、使用中介者和代理

在UI層和Presenter之間設置中介者Mediator,將例如數據校驗、組裝在內的輕量級邏輯操做放在Mediator中;在Presenter和Model之間使用代理Proxy;經過上述二者分擔一部分Presenter的邏輯操做,但總體框架的控制權仍是在Presenter手中。

MVVM

MVVM能夠算是MVP的升級版,其中的VM是ViewModel的縮寫,ViewModel能夠理解成是View的數據模型和Presenter的合體,ViewModel和View之間的交互經過Data Binding完成,而Data Binding能夠實現雙向的交互,這就使得視圖和控制層之間的耦合程度進一步下降,關注點分離更爲完全,同時減輕了Activity的壓力。

MVC->MVP->MVVM演進過程

MVC -> MVP -> MVVM 這幾個軟件設計模式是一步步演化發展的,MVVM 是從 MVP 的進一步發展與規範,MVP 隔離了MVC中的 M 與 V 的直接聯繫後,靠 Presenter 來中轉,因此使用 MVP 時 P 是直接調用 View 的接口來實現對視圖的操做的,這個 View 接口的東西通常來講是 showData、showLoading等等。M 與 V已經隔離了,方便測試了,但代碼還不夠優雅簡潔,因此 MVVM 就彌補了這些缺陷。在 MVVM 中就出現的 Data Binding 這個概念,意思就是 View 接口的 showData 這些實現方法能夠不寫了,經過 Binding 來實現。

三種模式的相同點

M層和V層的實現是同樣的。

三種模式的不一樣點

三者的差別在於如何粘合View和Model,實現用戶的交互操做以及變動通知。

  • Controller:接收View的命令,對Model進行操做,一個Controller能夠對應多個View。
  • Presenter:Presenter與Controller同樣,接收View的命令,對Model進行操做;與Controller不一樣的是Presenter會副作用於View,Model的變動通知首先被Presenter得到,而後Presenter再去更新View。一般一個Presenter只對應於一個View。據Presenter和View對邏輯代碼分擔的程度不一樣,這種模式又有兩種狀況:普通的MVP模式和Passive View模式。
  • ViewModel:注意這裏的「Model」指的是View的Model,跟MVVM中的一個Model不是一回事。所謂View的Model就是包含View的一些數據屬性和操做的這麼一個東東,這種模式的關鍵技術就是數據綁定(data binding),View的變化會直接影響ViewModel,ViewModel的變化或者內容也會直接體如今View上。這種模式其實是框架替應用開發者作了一些工做,開發者只須要較少的代碼就能實現比較複雜的交互。
補充:基於AOP的架構設計

AOP(Aspect-Oriented Programming, 面向切面編程),誕生於上個世紀90年代,是對OOP(Object-Oriented Programming, 面向對象編程)的補充和完善。OOP引入封裝、繼承和多態性等概念來創建一種從上道下的對象層次結構,用以模擬公共行爲的一個集合。當咱們須要爲分散的對象引入公共行爲的時候,即定義從左到右的關係時,OOP則顯得無能爲力。例如日誌功能。日誌代碼每每水平地散佈在全部對象層次中,而與它所散佈到的對象的核心功能毫無關係。對於其餘類型的代碼,如安全性、異常處理和透明的持續性也是如此。這種散佈在各處的無關的代碼被稱爲橫切(Cross-Cutting)代碼,在OOP設計中,它致使了大量代碼的重複,而不利於各個模塊的重用。而AOP技術則偏偏相反,它利用一種稱爲「橫切」的技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行爲封裝到一個可重用模塊,並將其名爲「Aspect」,即方面。所謂「方面」,簡單地說,就是將那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來,便於減小系統的重複代碼,下降模塊間的耦合度,並有利於將來的可操做性和可維護性。

在Android App中的橫切關注點有Http, SharedPreferences, Log, Json, Xml, File, Device, System, 格式轉換等。Android App的需求差異很大,不一樣的需求橫切關注點必然是不同的。通常的App工程中應該有一個Util Package來存放相關的切面操做,在項目多了以後能夠將其中使用較多的Util封裝爲一個Jar包/aar文件/遠程依賴的方式供工程調用。

在使用MVP和AOP對App進行縱向和橫向的切割以後,可以使得App總體的結構更清晰合理,避免局部的代碼臃腫,方便開發、測試以及後續的維護。這樣縱,橫兩次對於App代碼的分割已經能使得程序不會過多堆積在一個Java文件裏,但靠一次開發過程就寫出高質量的代碼是很困難的,趁着項目的間歇期,對代碼進行重構頗有必要。

最後的建議

若是「從零開始」,用什麼設計架構的問題屬於想得太多作得太少的問題。 從零開始意味着一個項目的主要技術難點是基本功能實現。當每個功能都須要考慮如何作到的時候,我以爲通常人都沒辦法考慮如何作好。 由於,全部的優化都是站在最上層進行統籌規劃。在這以前,你必須對下層的每個模塊都很是熟悉,進而提煉可複用的代碼、規劃邏輯流程。

MVC的狀況下怎麼把Activity的C和V抽離?

MVP 架構中 Presenter 定義爲接口有什麼好處;

MVP如何管理Presenter的生命週期,什麼時候取消網絡請求?

aop思想

Fragment若是在Adapter中使用應該如何解耦?

項目框架裏有沒有Base類,BaseActivity和BaseFragment這種封裝致使的問題,以及解決方法?

設計一個音樂播放界面,你會如何實現,用到那些類,如何設計,如何定義接口,如何與後臺交互,如何緩存與下載,如何優化(15分鐘時間)

從0設計一款App總體架構,如何去作?

說一款你認爲當前比較火的應用並設計(好比:直播APP,P2P金融,小視頻等)

實現一個庫,完成日誌的實時上報和延遲上報兩種功能,該從哪些方面考慮?

你最優秀的工程設計項目,是怎麼設計和實現的;擴展,如何作成一個平臺級產品?

6、其它高頻面試題

一、如何保證一個後臺服務不被殺死?(相同問題:如何保證service在後臺不被kill?)比較省電的方式是什麼?

保活方案

一、AIDL方式單進程、雙進程方式保活Service。(基於onStartCommand() return START_STICKY)

START_STICKY 在運行onStartCommand後service進程被kill後,那將保留在開始狀態,可是不保留那些傳入的intent。不久後service就會再次嘗試從新建立,由於保留在開始狀態,在建立 service後將保證調用onstartCommand。若是沒有傳遞任何開始命令給service,那將獲取到null的intent。

除了華爲此方案無效以及未更改底層的廠商不起做用外(START_STICKY字段就能夠保持Service不被殺)。此方案能夠與其餘方案混合使用

二、下降oom_adj的值(提高service進程優先級):

Android中的進程是託管的,當系統進程空間緊張的時候,會依照優先級自動進行進程的回收。Android將進程分爲6個等級,它們按優先級順序由高到低依次是:

  • 1.前臺進程 (Foreground process)
  • 2.可見進程 (Visible process)
  • 3.服務進程 (Service process)
  • 4.後臺進程 (Background process)
  • 5.空進程 (Empty process)

當service運行在低內存的環境時,將會kill掉一些存在的進程。所以進程的優先級將會很重要,能夠使用startForeground 將service放到前臺狀態。這樣在低內存時被kill的概率會低一些。

  • 常駐通知欄(可經過啓動另一個服務關閉Notification,不對oom_adj值有影響)。

  • 使用」1像素「的Activity覆蓋在getWindow()的view上。

此方案無效果

  • 循環播放無聲音頻(黑科技,7.0下殺不掉)。

成功對華爲手機保活。小米8下也成功突破20分鐘

  • 三、監聽鎖屏廣播:使Activity始終保持前臺。
  • 四、使用自定義鎖屏界面:覆蓋了系統鎖屏界面。
  • 五、經過android:process屬性來爲Service建立一個進程。
  • 六、跳轉到系統白名單界面讓用戶本身添加app進入白名單。

復活方案

一、onDestroy方法裏重啓service

service + broadcast 方式,就是當service走onDestory的時候,發送一個自定義的廣播,當收到廣播的時候,從新啓動service。

二、JobScheduler:原理相似定時器,5.0,5.1,6.0做用很大,7.0時候有必定影響(能夠在電源管理中給APP受權)。

只對5.0,5.一、6.0起做用。

三、推送互相喚醒復活:極光、友盟、以及各大廠商的推送。

四、同派系APP廣播互相喚醒:好比今日頭條系、阿里系。

此外還能夠監聽系統廣播判斷Service狀態,經過系統的一些廣播,好比:手機重啓、界面喚醒、應用狀態改變等等監聽並捕獲到,而後判斷咱們的Service是否還存活。

結論:高版本狀況下能夠使用彈出通知欄、雙進程、無聲音樂提升後臺服務的保活機率。

二、Android動畫框架實現原理。

Animation 框架定義了透明度,旋轉,縮放和位移幾種常見的動畫,並且控制的是整個View。實現原理:

每次繪製視圖時,View 所在的 ViewGroup 中的 drawChild 函數獲取該View 的 Animation 的 Transformation 值,而後調用canvas.concat(transformToApply.getMatrix()),經過矩陣運算完成動畫幀,若是動畫沒有完成,繼續調用 invalidate() 函數,啓動下次繪製來驅動動畫,動畫過程當中的幀之間間隙時間是繪製函數所消耗的時間,可能會致使動畫消耗比較多的CPU資源,最重要的是,動畫改變的只是顯示,並不能響應事件。

三、Activity-Window-View三者的差異?

Activity像一個工匠(控制單元),Window像窗戶(承載模型),View像窗花(顯示視圖) LayoutInflater像剪刀,Xml配置像窗花圖紙。

在Activity中調用attach,建立了一個Window, 建立的window是其子類PhoneWindow,在attach中建立PhoneWindow。 在Activity中調用setContentView(R.layout.xxx), 其中其實是調用的getWindow().setContentView(), 內部調用了PhoneWindow中的setContentView方法。

建立ParentView:

做爲ViewGroup的子類,實際是建立的DecorView(做爲FramLayout的子類), 將指定的R.layout.xxx進行填充, 經過佈局填充器進行填充【其中的parent指的就是DecorView】, 調用ViewGroup的removeAllView(),先將全部的view移除掉,添加新的view:addView()。

參考文章

四、低版本SDK如何實現高版本api?

  • 一、在使用了高版本API的方法前面加一個 @TargetApi(API號)。
  • 二、在代碼上用版本判斷來控制不一樣版本使用不一樣的代碼。

五、說說你對Context的理解?

六、Android的生命週期和啓動模式

由A啓動B Activity,A爲棧內複用模式,B爲標準模式,而後再次啓動A或者殺死B,說說A,B的生命週期變化,爲何?

Activity的啓動模式有哪些?棧裏是A-B-C,先想直接到A,BC都清理掉,有幾種方法能夠作到?這幾種方法產生的結果是有幾個A的實例?

七、ListView和RecyclerView系列

RecyclerView和ListView有什麼區別?局部刷新?前者使用時多重type場景下怎麼避免滑動卡頓。懶加載怎麼實現,怎麼優化滑動體驗。

ListView、RecyclerView區別?

1、使用方面:

ListView的基礎使用:

  • 繼承重寫 BaseAdapter 類
  • 自定義 ViewHolder 和 convertView 一塊兒完成複用優化工做

RecyclerView 基礎使用關鍵點一樣有兩點:

  • 繼承重寫 RecyclerView.Adapter 和 RecyclerView.ViewHolder
  • 設置佈局管理器,控制佈局效果

RecyclerView 相比 ListView 在基礎使用上的區別主要有以下幾點:

  • ViewHolder 的編寫規範化了
  • RecyclerView 複用 Item 的工做 Google 全幫你搞定,再也不須要像 ListView 那樣本身調用 setTag
  • RecyclerView 須要多出一步 LayoutManager 的設置工做

2、佈局方面:

RecyclerView 支持 線性佈局、網格佈局、瀑布流佈局 三種,並且同時還可以控制橫向仍是縱向滾動。

3、API提供方面:

ListView 提供了 setEmptyView ,addFooterView 、 addHeaderView.

RecyclerView 供了 notifyItemChanged 用於更新單個 Item View 的刷新,咱們能夠省去本身寫局部更新的工做。

4、動畫效果:

RecyclerView 在作局部刷新的時候有一個漸變的動畫效果。繼承 RecyclerView.ItemAnimator 類,並實現相應的方法,再調用 RecyclerView的 setItemAnimator(RecyclerView.ItemAnimator animator) 方法設置完便可實現自定義的動畫效果。

5、監聽 Item 的事件:

ListView 提供了單擊、長按、選中某個 Item 的監聽設置。

RecyclerView與ListView緩存機制的不一樣

想改變listview的高度,怎麼作?

listview跟recyclerview上拉加載的時候分別應該如何處理?

如何本身實現RecyclerView的側滑刪除?

RecyclerView的ItemTouchHelper的實現原理

八、如何實現一個推送,消息推送原理?推送到達率的問題?

一:客戶端不斷的查詢服務器,檢索新內容,也就是所謂的pull 或者輪詢方式。

二:客戶端和服務器之間維持一個TCP/IP長鏈接,服務器向客戶端push。

blog.csdn.net/clh604/arti…

www.jianshu.com/p/45202dcd5…

九、動態權限系列。

動態權限適配方案,權限組的概念

Runtime permission,如何把一個預置的app默認給它權限?不要受權。

十、自定義View系列。

Canvas的底層機制,繪製框架,硬件加速是什麼原理,canvas lock的緩衝區是怎麼回事?

雙指縮放拖動大圖

TabLayout中如何讓當前標籤永遠位於屏幕中間

TabLayout如何設置指示器的寬度包裹內容?

自定義View如何考慮機型適配?

  • 合理使用warp_content,match_parent。
  • 儘量地使用RelativeLayout。
  • 針對不一樣的機型,使用不一樣的佈局文件放在對應的目錄下,android會自動匹配。
  • 儘可能使用點9圖片。
  • 使用與密度無關的像素單位dp,sp。
  • 引入android的百分比佈局。
  • 切圖的時候切大分辨率的圖,應用到佈局當中,在小分辨率的手機上也會有很好的顯示效果。

十一、對谷歌新推出的Room架構。

十二、沒有給權限如何定位,特定機型定位失敗,如何解決?

1三、Debug跟Release的APK的區別?

1四、android文件存儲,各版本存儲位置的權限控制的演進,外部存儲,內部存儲

1五、有什麼提升編譯速度的方法?

1六、Scroller原理。

Scroller執行流程裏面的三個核心方法

mScroller.startScroll();
mScroller.computeScrollOffset();
view.computeScroll();
複製代碼

一、在mScroller.startScroll()中爲滑動作了一些初始化準備,好比:起始座標,滑動的距離和方向以及持續時間(有默認值),動畫開始時間等。

二、mScroller.computeScrollOffset()方法主要是根據當前已經消逝的時間來計算當前的座標點。由於在mScroller.startScroll()中設置了動畫時間,那麼在computeScrollOffset()方法中依據已經消逝的時間就很容易獲得當前時刻應該所處的位置並將其保存在變量mCurrX和mCurrY中。除此以外該方法還可判斷動畫是否已經結束。

1七、Hybrid系列。

webwiew瞭解?怎麼實現和javascript的通訊?相互雙方的通訊。@JavascriptInterface在?版本有bug,除了這個還有其餘調用android方法的方案嗎?

Android中Java和JavaScript交互
webView.addJavaScriptInterface(new Object(){xxx}, "xxx");
1
答案:能夠使用WebView控件執行JavaScript腳本,而且能夠在JavaScript中執行Java代碼。要想讓WebView控件執行JavaScript,須要調用WebSettings.setJavaScriptEnabled方法,代碼以下:

WebView webView = (WebView)findViewById(R.id.webview);
WebSettings webSettings = webView.getSettings();
//設置WebView支持JavaScript
webSettings.setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient());

JavaScript調用Java方法須要使用WebView.addJavascriptInterface方法設置JavaScript調用的Java方法,代碼以下:

webView.addJavascriptInterface(new Object()
{
    //JavaScript調用的方法
    public String process(String value)
    {
        //處理代碼
        return result;
    }
}, "demo");       //demo是Java對象映射到JavaScript中的對象名

能夠使用下面的JavaScript代碼調用process方法,代碼以下:

<script language="javascript">
    function search()
    {
        //調用searchWord方法
        result.innerHTML = "<font color='red'>" + window.demo.process('data') + "</font>";
    }
複製代碼

1八、若是在當前線程內使用Handler postdelayed 兩個消息,一個延遲5s,一個延遲10s,而後使當前線程sleep 5秒,以上消息的執行時間會如何變化?

答:照常執行

擴展:sleep時間<=5 對兩個消息無影響,5< sleep時間 <=10 對第一個消息有影響,第一個消息會延遲到sleep後執行,sleep時間>10 對兩個時間都有影響,都會延遲到sleep後執行。

1九、Android中進程內存的分配,能不能本身分配定額內存?

20、下拉狀態欄是否是影響activity的生命週期,若是在onStop的時候作了網絡請求,onResume的時候怎麼恢復

2一、Android長鏈接,怎麼處理心跳機制。

長鏈接:長鏈接是創建鏈接以後, 不主動斷開. 雙方互相發送數據, 發完了也不主動斷開鏈接, 以後有須要發送的數據就繼續經過這個鏈接發送.

心跳包:其實主要是爲了防止NAT超時,客戶端隔一段時間就主動發一個數據,探測鏈接是否斷開。

服務器處理心跳包:假如客戶端心跳間隔是固定的, 那麼服務器在鏈接閒置超過這個時間還沒收到心跳時, 能夠認爲對方掉線, 關閉鏈接. 若是客戶端心跳會動態改變, 應當設置一個最大值, 超過這個最大值才認爲對方掉線. 還有一種狀況就是服務器經過TCP鏈接主動給客戶端發消息出現寫超時, 能夠直接認爲對方掉線.

2二、CrashHandler實現原理?

獲取app crash的信息保存在本地而後在下一次打開app的時候發送到服務器。
複製代碼

2三、SurfaceView和View的最本質的區別?

SurfaceView是在一個新起的單獨線程中能夠從新繪製畫面,而view必須在UI的主線程中更新畫面。

在UI的主線程中更新畫面可能會引起問題,好比你更新的時間過長,那麼你的主UI線程就會被你正在畫的函數阻塞。那麼將沒法響應按鍵、觸屏等消息。當使用SurfaceView因爲是在新的線程中更新畫面因此不會阻塞你的UI主線程。但這也帶來了另一個問題,就是事件同步。好比你觸屏了一下,你須要在SurfaceView中的thread處理,通常就須要有一個event queue的設計來保存touchevent,這會稍稍複雜一點,由於涉及到線程安全。

2四、Android程序運行時權限與文件系統權限

一、Linux 文件系統權限。不一樣的用戶對文件有不一樣的讀寫執行權限。在android系統中,system和應用程序是分開的,system裏的數據是不可更改的。

二、Android中有3種權限,進程權限UserID,簽名,應用申明權限。每次安裝時,系統根據包名爲應用分配惟一的userID,不一樣的userID運行在不一樣的進程裏,進程間的內存是獨立的,不能夠相互訪問,除非經過特定的Binder機制。

Android提供了以下的一種機制,能夠使兩個apk打破前面講的這種壁壘。

在AndroidManifest.xml中利用sharedUserId屬性給不一樣的package分配相同的userID,經過這樣作,兩個package能夠被當作同一個程序,系統會分配給兩個程序相同的UserID。固然,基於安全考慮,兩個package須要有相同的簽名,不然沒有驗證也就沒有意義了。

2五、曲面屏的適配。

2六、TextView調用setText方法的內部執行流程。

2七、怎麼控制另一個進程的View顯示(RemoteView)?

2八、如何實現右滑finish activity?

2九、如何在整個系統層面實現界面的圓角效果。(即全部的APP打開界面都會是圓角)

30、非UI線程能夠更新UI嗎?

能夠,當訪問UI時,ViewRootImpl會調用checkThread方法去檢查當前訪問UI的線程是哪一個,若是不是UI線程則會拋出異常。執行onCreate方法的那個時候ViewRootImpl還沒建立,沒法去檢查當前線程.ViewRootImpl的建立在onResume方法回調以後。

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}
複製代碼

非UI線程是能夠刷新UI的,前提是它要擁有本身的ViewRoot,即更新UI的線程和建立ViewRoot的線程是同一個,或者在執行checkThread()前更新UI。

3一、如何解決git衝突?

3二、單元測試有沒有作過,說說熟悉的單元測試框架?

首先,Android測試主要分爲三個方面:

  • 單元測試(Junit四、Mockito、PowerMockito、Robolectric)
  • UI測試(Espresso、UI Automator)
  • 壓力測試(Monkey)

WanAndroid項目和XXX項目中使用用到了單元測試和部分自動化UI測試,其中單元測試使用的是Junit4+Mockito+PowerMockito+Robolectric。下面我分別簡單介紹下這些測試框架:

一、Junit4:

使用@Test註解指定一個方法爲一個測試方法,除此以外,還有以下經常使用註解@BeforeClass->@Before->@Test->@After->@AfterClass以及@Ignore。

Junit4的主要測試方法就是斷言,即assertEquals()方法。而後,你能夠經過實現TestRule接口的方式重寫apply()方法去自定義Junit Rule,這樣就能夠在執行測試方法的先後作一些通用的初始化或釋放資源等工做,接着在想要的測試類中使用@Rule註解聲明使用JsonChaoRule便可。(注意被@Rule註解的變量必須是final的。最後,咱們直接運行對應的單元測試方法或類,若是你想要一鍵運行項目中全部的單元測試類,直接點擊運行Gradle Projects下的app/Tasks/verification/test便可,它會在module下的build/reports/tests/下生成對應的index.html報告。

Junit4它的優勢是速度快,支持代碼覆蓋率如jacoco等代碼質量的檢測工具。缺點就是沒法單獨對Android UI,一些類進行操做,與原生Java有一些差別。

二、Mockito:

能夠使用mock()方法模擬各類各樣的對象,以替代真正的對象作出但願的響應。除此以外,它還有不少驗證方法調用的方式如Mockit.when(調用方法).thenReturn(驗證的返回值)、verfiy(模擬對象).驗證方法等等。

這裏有一點要補充下:簡單的測試會使總體的代碼更簡潔,更可讀、更可維護。若是你不能把測試寫的很簡單,那麼請在測試時重構你的代碼。

最後,對於Mockito來講,它的優勢是有各類各樣的方式去驗證"模仿對象"的互動或驗證發生的某些行爲。而它的缺點就是不支持mock匿名類、final類、static方法private方法。

三、PowerMockito:

所以,爲了解決Mockito的缺陷,PoweMockito出現了,它擴展了Mockito,支持mock匿名類、final類、static方法、private方法。只要使用它提供的api如PowerMockito.mockStatic()去mock含靜態方法或字段的類,PowerMockito.suppress(PowerMockito.method(類.class, 方法名)便可。

四、Robolectric

前面3種咱們說的都是Java相關的單元測試方法,若是想在Java單元測試裏面進行Android單元測試,還得使用Robolectric,它提供了一套能運行在JVM的Android代碼。它提供了一系列相似ShadowToast.getLatestToast()、ShadowApplication.getInstance()這種方式來獲取Android平臺對應的對象。能夠看到它的優勢就是支持大部分Android平臺依賴類的底層引用與模擬。缺點就是在異步測試的狀況下有些問題,這是能夠結合Mockito來將異步轉爲同步便可解決。

最後,自動化UI測試項目中我使用的是Expresso,它提供了一系列相似onView().check().perform()的方式來實現點擊、滑動、檢測頁面顯示等自動化的UI測試效果,這裏在個人WanAndroid項目下的BasePageTest基類裏面封裝了一系列通用的方法,有興趣能夠去看看。

3三、Jenkins持續集成。

3四、工做中有沒有用過或者寫過什麼工具?腳本,插件等等;好比:多人協同開發可能對一些相同資源都各自放了一份,有沒有方法自動檢測這種重複之類的。

3五、如何繞過9.0限制?

如何限制?

  • 一、阻止java反射和JNI。
  • 二、當獲取方法或Field時進行檢測。
  • 三、怎麼檢測?

區分出是系統調用仍是開發者調用:

根據堆棧,回溯Class,查看ClassLoader是不是BootStrapClassLoader。

區分後,再區分是不是hidden api:

Method,Field都有access_flag,有一些備用字段,hidden信息存儲其中。

如何繞過?

一、不用反射:

利用一個fakelib,例如寫一個android.app.ActivityThread#currentActivityThread空實現,直接調用;

二、假裝系統調用:

jni修改一個class的classloder爲BootStrapClassLoader,麻煩。

利用系統方法去反射:

利用原反射,即:getDeclaredMethod這個方法是系統的方法,經過getDeclaredmethod反射去執行hidden api。

三、修改Method,Field中存儲hidden信息的字段:

利用jni去修改。

3六、對文件描述符怎麼理解?

3七、如何實現進程安全寫文件?

其它面試題


其餘擴展面試題

1、Kotlin (⭐⭐)

一、Kotlin 特性,和 Java 相比有什麼不一樣的地方?

  • 能直接與Java相互調用,能與Java工程共存
  • 大大減小樣板代碼
  • 能夠將Kotlin代碼編譯爲無需虛擬機就可運行的原生二進制文件
  • 支持協程
  • 支持高階函數
  • 語言層面解決空指針問題
  • 對字符串格式化的處理($變量名)
  • 更像Python的語法
  • 對λ表達式支持更好

mp.weixin.qq.com/s/FqXLNz5p9…

二、Kotlin爲何能和Java混編?

三、什麼是協程?

2、大前端 (⭐⭐)

一、Hybrid通訊原理是什麼,有作研究嗎?

二、JS的交互理解嗎?平時工做用的多嗎,項目中是怎麼與Web交互的?

Android經過WebView調用JS代碼:

一、經過WebView的loadUrl():

  • 設置與Js交互的權限:

    webSettings.setJavaScriptEnabled(true)

  • 設置容許JS彈窗:

    webSettings.setJavaScriptCanOpenWindowsAutomatically(true)

  • 載入JS代碼:

    mWebView.loadUrl("file:///android_asset/javascript.html")

  • webview只是載體,內容的渲染須要使用webviewChromClient類去實現,經過設置WebChromeClient對象處理JavaScript的對話框。

特別注意:

JS代碼調用必定要在 onPageFinished() 回調以後才能調用,不然不會調用。

二、經過WebView的evaluateJavascript():

  • 該方法比第一種方法效率更高、使用更簡潔,由於該方法的執行不會使頁面刷新,而第一種方法(loadUrl )的執行則會。
  • Android 4.4 後纔可以使用。

只須要將第一種方法的loadUrl()換成evaluateJavascript()便可,經過onReceiveValue()回調接收返回值。

建議:兩種方法混合使用,即Android 4.4如下使用方法1,Android 4.4以上方法2。

JS經過WebView調用 Android 代碼:

一、經過 WebView的addJavascriptInterface()進行對象映射:

-定義一個與JS對象映射關係的Android類:AndroidtoJs:

  • 定義JS須要調用的方法,被JS調用的方法必須加入@JavascriptInterface註解。
  • 經過addJavascriptInterface()將Java對象映射到JS對象。

優勢:使用簡單,僅將Android對象和JS對象映射便可。

缺點:addJavascriptInterface 接口引發遠程代碼執行漏洞,漏洞產生緣由是:

當JS拿到Android這個對象後,就能夠調用這個Android對象中全部的方法,包括系統類(java.lang.Runtime 類),從而進行任意代碼執行。

二、經過 WebViewClient 的方法shouldOverrideUrlLoading ()回調攔截 url:

  • Android經過 WebViewClient 的回調方法shouldOverrideUrlLoading ()攔截 url。

  • 解析該 url 的協議。

  • 若是檢測到是預先約定好的協議,就調用相應方法。

    根據協議的參數,判斷是不是所須要的url。 通常根據scheme(協議格式) & authority(協議名)判斷(前兩個參數)。

優勢:不存在方式1的漏洞;

缺點:JS獲取Android方法的返回值複雜,若是JS想要獲得Android方法的返回值,只能經過 WebView 的 loadUrl ()去執行 JS 方法把返回值傳遞回去。

三、經過 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回調攔截JS對話框alert()、confirm()、prompt() 消息:

原理:

Android經過 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回調分別攔截JS對話框 (警告框、確認框、輸入框),獲得他們的消息內容,而後解析便可。

經常使用的攔截是:攔截 JS的輸入框(即prompt()方法),由於只有prompt()能夠返回任意類型的值,操做最全面方便、更加靈活;而alert()對話框沒有返回值;confirm()對話框只能返回兩種狀態(肯定 / 取消)兩個值。

image

Android:你要的WebView與 JS 交互方式 都在這裏了

三、react native有多少了解?講一下原理。

四、weex瞭解嗎?如何本身實現相似技術?

五、flutter瞭解嗎?內部是如何實現跨平臺的?如何實現多Native頁面接入?如何實現對現有工程的flutter遷移?

六、Dart語言有研究過嗎?

七、快應用瞭解嗎?跟其她方式相比有什麼優缺點?

八、說說你用過的混合開發技術有哪些?各有什麼優缺點?

3、腳本語言 (⭐⭐)

一、腳本語言會嗎?

二、Python會嗎?

Python基礎

人工智能瞭解

三、Gradle瞭解多少?groovy語法會嗎?

非技術面試題

1、高頻題集 (⭐⭐⭐)

一、你以爲安卓開發最關鍵的技術在哪裏?

技術是沒有止境的,因此確定會不斷有演進和難點。

一. 底層和框架如何更好地設計及優化以適應業務的高速增加。提及來很簡單,低耦合高擴展,作起來是須要長期經驗積累。

二. 我拋幾個細節難點:

  • 插件化如何使插件的 Manifest 生效
  • H5 容器如何更好地優化和兼容
  • App 端優化,這是個沒止境的話題,網絡、圖片、動畫、內存、電量等等隨着優化的加深,你會發現不能侷限在客戶端,服務端也須要深刻。
  • SPDY 的優勢併入 HTTP 2.0 大家有在測試或用嗎?
  • Fresco 出來前你是否是以爲圖片緩存已經到頭了?
  • Android App 爲何總體流暢性老是被詬病?……

三. 若是你以爲沒有難點或者難點在兼容、UI 之類問題上,那麼可能兩個緣由:

  • 公司業務發展過慢,對技術的需求不夠迫切
  • 我的長時間在業務開發上,這個對於走技術路線的人來講挺麻煩的,不主動去接觸學習的話,n 年之後也仍是這個樣子爲了更好的我的成長,這兩點都是須要注意和解決的問題。

二、你還要什麼瞭解和要問的嗎?

你在公司的一天是如何度過的?

可否給我簡單介紹下貴公司業務與戰略的將來發展?

貴公司最讓你自豪的企業文化是什麼?(適合大公司)

團隊、公司如今面臨的最大挑戰是什麼?

對於將來加入這個團隊,你對個人指望是什麼?

您以爲我哪方面知識須要深刻學習或者個人不足在那些方面,從此我該注意什麼*?

你還能夠問下項目團隊多少人,主要以什麼方向爲主,一年內的目標怎樣,團隊氣氛怎樣,等內容着手。

三、研究比較深刻的領域有哪些?

四、本身最擅長的技術點,最感興趣的技術領域和技術?

五、項目中用了哪些開源庫,如何避免由於引入開源庫而致使的安全性和穩定性問題?

六、說下你都看過那些技術書籍,你是如何自學的。你以爲本身的優點與弱點是什麼。

七、說下項目中遇到的棘手問題,包括技術,交際和溝通。

八、說下你近幾年的規劃?

九、對加班怎麼看(不要太浮誇,現實一點哦)?

十、介紹你作過的哪些項目。

十一、你並不是畢業於名牌院校?

十二、爲何要離職?

1三、當你的開發任務很緊張,你怎麼去作代碼優化的?

2、次高頻題集 (⭐⭐)

一、對業內信息的關注渠道有哪些?

二、最近都讀哪些書?

三、給你一個項目,你怎麼看待他的市場和技術的關係?

四、你以往的項目中,以你如今的眼光去評價項目的利弊?

五、對於非立項(KPI)項目,怎麼推動?

六、都使用過哪些自定義控件?

七、除了簡歷上的工做經歷,您還會去關注哪些領域?

八、評價下本身,評價下本身的技術水平,我的代碼量如何?

九、你朋友對你的評價?

十、本身的優勢和缺點是什麼?並舉例說明?

十一、你以爲你個性上最大的優勢是什麼?

十二、說說你最大的缺點?

1三、最能歸納你本身的三個詞是什麼?

1四、說說你的家庭?

1五、除了本公司外,還應聘了哪些公司?(相似問題:當前的offer情況)

1六、經過哪些渠道瞭解的招聘信息?

1七、你的業餘愛好是什麼?

1八、你作過的哪件事最令本身感到驕傲?

1九、談談你對跳槽的見解?

20、怎樣看待學歷和能力?

2一、您跟您的主管或直接上司有沒有針對以上離職緣由的這些問題溝經過?若是沒有請說明緣由。若是有請說一下過程和結果?

2二、您以爲你關注的這些領域跟您目前從事的職業有哪些利弊關係?若是有請說明利弊關係?

2三、您在選擇工做中更看重的是什麼?(多是成長空間、培訓機會、發揮平臺、薪酬等答案)

2四、您可不能夠說說您在薪酬方面的內心預期?

2五、有人說掙將來比掙錢更爲重要,您怎麼理解?

2六、假設,某一天,在工做辦公室走廊,您和一位同事正在抱怨上級陳某平時作事缺少公平性,恰巧被陳某聽到,您會怎麼辦?

2七、怎麼樣處理工做和生活的關係?怎麼處理在工做中遇到困難?請舉例說明

2八、在您的現實生活中,您最不喜歡和什麼樣的人共事?爲何?舉例說明。

2九、在您認識的人中,有沒有人不喜歡您?爲何不喜歡您?請舉例說明。

30、當老闆/上司/同事/客戶誤會你,你會怎麼辦?

3一、當你發現其餘部門的工做疏漏已經影響到您的工做績效時,您怎麼辦?

3二、您但願在什麼樣的領導下工做?

3三、咱們工做與生活歷程並非一路順風的,談談您的工做或生活中出現的挫折或低潮期,您如何克服?

3四、假如您的上司是一個很是嚴厲、領導手腕強硬,時常給您巨大壓力的人,您以爲這種領導方式對您有何利、弊?

3五、您的領導給您佈置了一項您之前從未觸及過的任務,您打算如何去完成它?(若是有相似的經歷說說完成的經歷。)

3六、談談您以往職業生涯中最有壓力的1、兩件事,並說說是如何克服的。

3七、談談您以往職業生涯中令您有成就感的1、兩件事,並說說它給您的啓示。

3八、請您舉一個例子,說明在完成一項重要任務時,您是怎樣和他人進行有效合做的。

3九、當你要犧牲本身的某些方面與他人共事時,你會怎麼辦?

40、有時團隊成員不能有效共事,當遇到這種問題時你是怎麼處理的?你又是如何改善這類狀況的?

4一、咱們有時不得不與本身不喜歡的人在一個團隊工做,若是遇到這樣的狀況你會怎麼辦?

4二、您對委任的任務完成不了時如何處理?

4三、說說您對下屬佈置的任務在時間方面是如何要求的?

4四、說說您在完成上司佈置的任務時,在時間方面是如何要求本身的?

4五、您以往在領導崗位中,一個月內分別有哪些主要的工做任務?

4六、當您發現您的部屬目前士氣較低沉,您通常從哪些方面去調動?

4七、說說您在以往領導崗位中出現管理失控的事例及過後的緣由分析。您的部屬在一個專業的問題上跟您發生爭議,您如何對待這種事件?

4八、你對某某某互聯網發生事情的見解?(直播答題等等)

4九、怎麼看待前端和後端?

結語

其實,在面試中,不少題目並無真正的答案,你能回答到什麼樣的深度,仍是得靠本身真正的去使用和研究。知識的深度和廣度都很重要,因此都須要花相應的時間去學習。這樣,就算被問到本身不熟悉的領域也能夠同面試官嘮嗑兩句,而後能夠在適當的時機引向本身有深刻研究的領域,讓面試官以爲你是這個領域的專家。

讚揚

若是這個庫對您有很大幫助,您願意支持這個項目的進一步開發和這個項目的持續維護。你能夠掃描下面的二維碼,讓我喝一杯咖啡或啤酒。很是感謝您的捐贈。謝謝!


Contanct Me

● 微信 && 微信羣:

歡迎關注個人微信:bcce5360。因爲微信羣人數太多沒法生成羣邀二維碼,因此麻煩你們想進微信羣的朋友們,加我微信拉你進羣(PS:微信羣的學習氛圍與各項福利將會超乎你的想象)

● QQ羣:

2千人QQ羣,Awesome-Android學習交流羣,QQ羣號:959936182, 歡迎你們加入~

About me

很感謝您閱讀這篇文章,但願您能將它分享給您的朋友或技術羣,這對我意義重大。

但願咱們能成爲朋友,在 Github掘金上一塊兒分享知識。

相關文章
相關標籤/搜索