Android面試刨根問底之常用源碼分析總結篇(二):OkHttp, Retrofit,自定義View

本着針對面試負責任的態度,記錄面試過程中各個知識點,而不是入門系列,如果有不懂的自行學習。

目前總結了以下幾個方面:

  • Android優化
  • HashMap分析
  • Handler源碼分析
  • OkHttp分析
  • Retrofit分析
  • 自定義View

由於篇幅原因拆分成兩個部分分享,上一篇講解了前面三個方面,今天講剩下的三個部分,之後的還會持續更新。

OkHttp分析

Okhttp是對Socket的封裝。有三個主要的類,Request,Response,Call默認使用new OkHttpClient() 創建初client對象。

如果需要初始化網絡請求的參數,如timeout,interceptor等,可以創建Builder,通過builder.build() 創建初client對象。

使用new Request.Builder().url().builder()創建初requst對象。

通過client.newCall()創建出call對象,同步使用call.excute(), 異步使用call,enqueue(). 這裏Call是個接口,具體的實現在RealCall這個實現類裏面。

Okhttp的高效體現在,okhttp內有個Dispatcher類,是okhttp內部維護的一個線程池,對最大連接數,host最大訪問量做了初始定義。維護3個隊列及1個線程池

readyAsyncCalls

待訪問請求隊列,裏面存儲準備執行的請求。

runningAsyncCalls

異步請求隊列,裏面存儲正在執行,包含已經取消但是還沒有結束的請求。

runningSyncCalls

同步請求隊列,正在執行的請求,包含已經取消但是還沒有結束的請求。

ExecutorService

線程池,最小0,最大Max的線程池

在執行call.excute()的時候,調用到realcall類裏的excute方法,這個是同步方法,在方法的第一行就加了鎖,判斷executed標記,如果是true就拋出異常,保證一個請求只被執行一次。false的話繼續向下執行。調用client.dispatcher.excute()進入到dispatcher類中,向runningSyncCalls隊列中添加當前這個請求。執行結束會調用finished方法

如果是異步操作,會創建一個RealCall.AsyncCall對象,AsyncCall繼承的NamedRunnable接口,NamedRunnable是個runnable。進入到Dispatcher的enqueue()方法中,首先判斷線程池中線程的數據,host的訪問量,如果都沒有達到那麼加入到runningAsyncCalls中,並執行。否則加入到readyAsyncCalls隊列中。
finished方法,如果是異步操作,promoteCall方法,promoteCalls()中用迭代器遍歷readyAsyncCalls 然後加入到runningAsyncCalls

RealConnection

真正的連接操作類,對soket封裝,http1/http2的選擇,ssl協議等等信息。一個recalconnection就是一次鏈接

ConnectionPool

鏈接池,管理http1/http2的連接,同一個address共享一個connection,實現鏈接的複用。

StreamAlloction

保存了鏈接信息,address,HttpCodec,realconnection,connectionpool等信息

在realCall中 getResponseWithInterceptorChain()創建並開啓攔截器鏈

Okhttp中的攔截器,默認的分爲5種

  • RetryAndFollowUpInterceptor
    做網絡失敗重連,但是並不是所有的請求都需要重連,根據響應碼。MAX_FOLLOW_UPS=20最大重連次數
    在intercept方法中創建了StreamAllocation對象,並調用chain.proceed方法,執行下一個攔截器,對request進行處理,並返回response。
  • BirdgeInterceptor
    初始化信息,添加請求頭等,例如gzip,keep-alive,返回的response進行解壓
  • CacheInterceptor
    內部有Cache類,處理緩存操作,intercache內部類,disklrucache算法等
    重點是不緩存非get的請求。
    CacheStrategy緩存策略類,通過工廠模式獲取
  • ConnectionInterceptor(建議重點閱讀源碼)
    建立鏈接,使用之前創建好的StreamAllocation,初始化httpcodec,realConnection。內部使用了類似gc標記清理算法,對無用的connection進行標記,StramAlloction漸漸變成0,線程池檢測並回收,保證多個健康的keep-alive鏈接
  • CallServerInterceptor
    發起真正的網絡請求,解析返回的數據
    http寫入網絡IO流,從網絡IO流中讀取返回給客戶端的數據。
  • Network Interceptors
    Application interceptors & Network Interceptors區別查看相關資料
    okhttp wiki

OkHttp中的設計模式

單例、Builder、策略、責任鏈、觀察者

思考:
策略與簡單工廠的區別

相關面試題:

  1. 責任鏈模式DEMO
  2. IO操作流程
  3. 三級緩存的流程說一遍
  4. 請求配置都有哪些方法。
  5. okhttp斷點續傳用什麼保存,怎麼實現斷點續傳流程

Retrofit分析

涉及到的設計模式

外觀模式,構建者模式,工廠模式,代理模式,適配器模式,策略模式,觀察者模式

概括

Retrofit就是一個網絡請求框架的封裝,底層的網絡請求默認使用的Okhttp,本身只是簡化了用戶網絡請求的參數配置等,還能與Rxjava相結合,使用起來更加簡潔方便。

  • App應用程序通過Retrofit請求網絡,實際上是使用Retrofit接口層封裝請求參數,之後由OkHttp完成後續的請求操作。
  • 在服務端返回數據之後,OkHttp將原始的結果交給Retrofit,Retrofit根據用戶的需求對結果進行解析。
  • 完成數據的轉化(converterFactory),適配(callAdapterFactory),通過設計模式進行各種擴展。

使用

    @GET("/user/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);

    //call封裝了整個okhttp的請求

    Retrofit retrofit = new Retrofit.Builder()
                        .baseUrl("https://api.github.com/")
                        .addConverterFactory(GsonConverteractory.create())
                        //.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                        .build();
    //converterFactory
    //後續交個okhttp

使用Retrofit的七步驟

  • 添加Retrofit依賴,網絡權限
  • 定義接收服務器返回數據的Bean
  • 創建網絡請求的接口,使用註解(動態代理,核心)
  • builder模式創建Retrofit實例,converter,calladapter...
  • 創建接口實例,調用具體的網絡請求
  • call同步/異步網絡請求
  • 處理服務器返回的數據

Retrofit網絡通信八步驟

  1. 創建Retrofit實例
  2. 定義網絡請求接口,併爲接口中的方法添加註解
  3. 通過動態代理生成網絡請求對象
  4. 通過網絡請求適配器將網絡請求對象進行平臺適配
  5. 通過網絡請求執行器,發送網絡請求(call)
  6. 通過數據解析器解析數據
  7. 通過回調執行器,切換線程
  8. 用戶在主線程處理返回結果

代理

爲其他對象提供一種代理,用以控制對這個對象的訪問

  1. 靜態

  2. 動態

    • 在程序運行時創建的代理方式
    • 無侵入增強
    • jdk動態代理 vs cglib

jdk動態代理

  • 只能爲接口動態
  • InvocationHandler必須要實現
  • invoke的參數中獲取參數
  • invoke的返回值返回給使用者
  • newProxyInstance中傳入InvocationHandler

總結:

  1. 運行期
  2. InvocationHandler接口和Proxy類
  3. 動態代理與靜態代理的不同

源碼

  1. serviceMethonCache //緩存,網絡請求對象
  2. Factory callFactory //默認ok
  3. HttpUrl baseUrl
  4. List<Converter.Factory> converterFactories
  5. List<CallAdapter.Factory> callAdapterFactories
  6. Executor callbackExecutor //執行回調
  7. boolean validateEagerly //是否立即解析接口中方法

builder

  1. platform

單例獲取不同平臺
Android平臺中MainthreadExecutor

callAdapterFactory

通過calladapter將原始Call進行封裝,找到對應的執行器。如rxjavaCallFactory對應的Observable,轉換形式Call<T> --> Observable<T>

converterFactory

數據解析Converter,將response通過converterFactory轉換成對應的數據形式,GsonConverterFactory,FastJsonConverterFactory。

Retrofit

Retrofit核心類,對外提供接口。通過retrofit.create()創建retrofit實例,外觀模式。在create()方法中,使用動態代理模式對請求的接口中方法進行封裝(ServiceMethod),初始化OkhttpCall。

ServiceMethod

核心處理類,解析方法和註解,toRequest()方法中生成HttpRequest。創建responseConverter(將response流轉換爲String或實體),創建callAdapter

OkhttpCall

是對okhttp3.Call的封裝調用

自定義View

自定義View三種方式,組合現有控件,繼承現有控件,繼承View

本文只針對繼承View的方式,另兩種自行學習。

1. 重寫方法

onMeasure、 onLayout、onDraw、onTouchEvent

onMeasure

可能多次觸發,在measure的過程中注意MeasureSpec,specMode、specSize
講到LinearLayout、RelativeLayout源碼

MeasureSpec

MeasureSpec,specMode、specSize

  1. EXACTLY

表示父佈局希望子佈局的大小應該是由specSize的值來決定的,系統默認會按照這個規則來設置子佈局的大小,開發人員當然也可以按照自己的意願設置成任意的大小。

  1. AT_MOST

表示子佈局最多隻能是specSize中指定的大小,開發人員應該儘可能小得去設置這個佈局,並且保證不會超過specSize。系統默認會按照這個規則來設置子佈局的大小,開發人員當然也可以按照自己的意願設置成任意的大小。

  1. UNSPECIFIED

表示開發人員可以將佈局按照自己的意願設置成任意的大小,沒有任何限制。這種情況比較少見,不太會用到。

childParams/parentMode EXACTLY AT_MOST UNSPECIFIED
dp/px EXACTLY(childsize) EXACTLY(childsize) EXACTLY(childsize)
match_parent EXACTLY(parentsize) AT_MOST(parentsize) UNSPECIFIED(0)
wrap_content AT_MOST(parentsize) AT_MOST(parentsize) UNSPECIFIED(0)

上圖表摘自https://blog.csdn.net/singwhatiwanna/article/details/38426471

onLayout

在ViewGroup中,只觸發一次,決定子View的位置

onDraw

繪製內容,Canvas.drawxxx(),paint

onTouchEvent

處理點擊事件

2. 自定義view與viewgroup的區別

  1. onDraw(Canvas canvas)

View類中用於重繪的方法,這個方法是所有View、ViewGroup及其派生類都具有的方法,也是Android UI繪製最重要的方法。開發者可重載該方法,並在重載的方法內部基於參數canvas繪製自己的各種圖形、圖像效果。

  1. onLayout()

重載該類可以在佈局發生改變時作定製處理,這在實現一些特效時非常有用。View中的onLayout不是必須重寫的,ViewGroup中的onLayout()是抽象的,自定義ViewGroup必須重寫。

  1. dispatchDraw()

ViewGroup類及其派生類具有的方法,控制子View繪製分發,重載該方法可改變子View的繪製,進而實現一些複雜的視效,典型的例子可參見Launcher模塊Workspace的dispatchDraw重載。

  1. drawChild()

ViewGroup類及其派生類具有的方法,直接控制繪製某局具體的子view,重載該方法可控制具體某個具體子View。

3. View方法執行過程

三次measure,兩次layout和一次draw
http://blog.csdn.net/u012422440/article/details/52972825

Android視圖樹的根節點是DecorView,而它是FrameLayout的子類,所以就會讓其子視圖繪製兩次,所以onMeasure函數會先被調用兩次。

  • onResume(Activity)
  • onPostResume(Activity)
  • onAttachedToWindow(View)
  • onMeasure(View)
  • onMeasure(View)
  • onLayout(View)
  • onSizeChanged(View)
  • onMeasure(View)
  • onLayout(View)
  • onDraw(View)
  • dispatchDraw()

4. invalidate()、postInvalidate()、requestLayout()

invalidate()

/**
     * Invalidate the whole view. If the view is visible,
     * {@link #onDraw(android.graphics.Canvas)} will be called at some point in
     * the future.
     * <p>
     * This must be called from a UI thread. To call from a non-UI thread, call
     */
    public void invalidate() {
        invalidate(true);
    }

invalidate方法會執行draw過程,重繪View樹。
當改變view的顯隱性、背景、狀態(focus/enable)等,這些都屬於appearance範疇,都會引起invalidate操作。需要更新界面顯示,就可以直接調用invalidate方法。

注意:

View(非容器類)調用invalidate方法只會重繪自身,ViewGroup調用則會重繪整個View樹。

postInvalidate()

/**
     * <p>Cause an invalidate to happen on a subsequent cycle through the event loop.
     * Use this to invalidate the View from a non-UI thread.</p>
     *
     * <p>This method can be invoked from outside of the UI thread
     * only when this View is attached to a window.</p>
     */
    public void postInvalidate() {
        postInvalidateDelayed(0);
    }

在子線程中被調用,刷新UI。

requestLayout()

 /**
     * Call this when something has changed which has invalidated the
     * layout of this view. This will schedule a layout pass of the view
     * tree. This should not be called while the view hierarchy is currently in a layout
     * pass ({@link #isInLayout()}. If layout is happening, the request may be honored at the
     * end of the current layout pass (and then layout will run again) or after the current
     * frame is drawn and the next layout occurs.
     *
     * <p>Subclasses which override this method should call the superclass method to
     * handle possible request-during-layout errors correctly.</p>
     */
    @CallSuper
    public void requestLayout() {
        }

當View的寬高,發生了變化,不再適合現在的區域,調用requestLayout方法重新對View佈局。
當View執行requestLayout方法,會向上遞歸到頂級父View中,再執行這個頂級父View的requestLayout,所以其他View的onMeasure,onLayout也可能會被調用。

面試大廠複習路線

多餘的話就不講了,接下來將分享面試的一個複習路線,如果你也在準備面試但是不知道怎麼高效複習,可以參考一下我的複習路線,有任何問題也歡迎一起互相交流,加油吧!

這裏給大家提供一個方向,進行體系化的學習:

1、看視頻進行系統學習

前幾年的Crud經歷,讓我明白自己真的算是菜雞中的戰鬥機,也正因爲Crud,導致自己技術比較零散,也不夠深入不夠系統,所以重新進行學習是很有必要的。我差的是系統知識,差的結構框架和思路,所以通過視頻來學習,效果更好,也更全面。關於視頻學習,個人可以推薦去B站進行學習,B站上有很多學習視頻,唯一的缺點就是免費的容易過時。

另外,我自己也珍藏了好幾套視頻,有需要的我也可以分享給你。

2、進行系統梳理知識,提升儲備

客戶端開發的知識點就那麼多,面試問來問去還是那麼點東西。所以面試沒有其他的訣竅,只看你對這些知識點準備的充分程度。so,出去面試時先看看自己複習到了哪個階段就好。

系統學習方向:

  • 架構師築基必備技能:深入Java泛型+註解深入淺出+併發編程+數據傳輸與序列化+Java虛擬機原理+反射與類加載+動態代理+高效IO

  • Android高級UI與FrameWork源碼:高級UI晉升+Framework內核解析+Android組件內核+數據持久化

  • 360°全方面性能調優:設計思想與代碼質量優化+程序性能優化+開發效率優化

  • 解讀開源框架設計思想:熱修復設計+插件化框架解讀+組件化框架設計+圖片加載框架+網絡訪問框架設計+RXJava響應式編程框架設計+IOC架構設計+Android架構組件Jetpack

  • NDK模塊開發:NDK基礎知識體系+底層圖片處理+音視頻開發

  • 微信小程序:小程序介紹+UI開發+API操作+微信對接

  • Hybrid 開發與Flutter:Html5項目實戰+Flutter進階

知識梳理完之後,就需要進行查漏補缺,所以針對這些知識點,我手頭上也準備了不少的電子書和筆記,這些筆記將各個知識點進行了完美的總結。

3、讀源碼,看實戰筆記,學習大神思路

“編程語言是程序員的表達的方式,而架構是程序員對世界的認知”。所以,程序員要想快速認知並學習架構,讀源碼是必不可少的。閱讀源碼,是解決問題 + 理解事物,更重要的:看到源碼背後的想法;程序員說:讀萬行源碼,行萬種實踐。

主要內含微信 MMKV 源碼、AsyncTask 源碼、Volley 源碼、Retrofit源碼、OkHttp 源碼等等。

4、面試前夕,刷題衝刺

面試的前一週時間內,就可以開始刷題衝刺了。請記住,刷題的時候,技術的優先,算法的看些基本的,比如排序等即可,而智力題,除非是校招,否則一般不怎麼會問。

關於面試刷題,我個人也準備了一套系統的面試題,幫助你舉一反三:

總結

改變人生,沒有什麼捷徑可言,這條路需要自己親自去走一走,只有深入思考,不斷反思總結,保持學習的熱情,一步一步構建自己完整的知識體系,纔是最終的制勝之道,也是程序員應該承擔的使命。

以上內容均免費分享給大家,需要完整版的朋友,點這裏可以看到全部內容。或者關注主頁掃描加 微信 獲取。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章