国产亚洲欧美人成在线,免费视频爱爱太爽了无码,日本免费一区二区三区高清视频 ,国产真实伦对白精彩视频

歡迎您光臨深圳塔燈網(wǎng)絡(luò)科技有限公司!
電話圖標(biāo) 余先生:13699882642

網(wǎng)站百科

為您解碼網(wǎng)站建設(shè)的點點滴滴

從零開始仿寫一個抖音App——app架構(gòu)更新與網(wǎng)絡(luò)層定制

發(fā)表日期:2018-10 文章編輯:小燈 瀏覽次數(shù):2955

本文首發(fā)于 ——何時夕,搬運轉(zhuǎn)載請注明出處,否則將追究版權(quán)責(zé)任。交流qq群:859640274

連載文章

  • 1.從零開始仿寫一個抖音app——開始
  • 4.從零開始仿寫一個抖音App——日志和埋點以及后端初步架構(gòu)
  • 6.從零開始仿寫一個抖音App——音視頻開篇
  • 7.從零開始仿寫一個抖音App——基于FFmpeg的極簡視頻播放器

本項目的 github 地址:MyTikTok

國慶快結(jié)束了,國慶中有六天都在寫文章看代碼還有比我苦逼的嗎(買個慘,哈哈)。這幾天為項目新增了五個模塊,順便看了看 kotlin 的語法并在項目中簡單的實踐了一下。本文中會講解其中的兩個模塊,剩下的一些會在不久后發(fā)布的下一篇文章中進行講解。

  • 1.討論——總結(jié)前兩周評論中有意義的討論并給予我的解答
  • 2.app架構(gòu)更新——隨著開發(fā)的進行,發(fā)現(xiàn)第二篇文章中的架構(gòu)有一些問題,所以在這里更新一下
  • 3.網(wǎng)絡(luò)層定制——基于 retrofit 和 okhttp3 定制一個網(wǎng)絡(luò)請求層,中間會附加一些原理講解

一、討論

討論1:zsh 對 bash 的支持并不是完全的,如果運行純 bash 有時候會出問題建議不要在服務(wù)器上用。

  • 1.這個讀者的建議非常好,上篇文章中我寫了一個 unbunt 環(huán)境的初始化腳本,看來這個腳本只能自己在 linux 下開發(fā)的時候使用了

討論2:我以為 aop 是通過 aspectjrt 來實現(xiàn)的原來是和 Butterknife 類似來實現(xiàn)的

  • 1.在我認知里面的 aop 可以簡單的歸納成:通過注解的信息在某些方法的前后添加代碼。
  • 2.所以 aspectj 也是可以實現(xiàn)我在前篇文章中說的 aop 日志的。
  • 3.如果讀者了解 aspectj 的原理的話就會發(fā)現(xiàn):他也是通過 gradle 插件來將代碼插到注解的方法前后的,只不過這一部分不需要開發(fā)者來是實現(xiàn)。
  • 4.而項目中自己實現(xiàn)一個這樣的東西一個是為了可定制性,另一個就是為了能了解一些技術(shù)的原理而不是單單只會用。

討論3:建議以已完成某個功能模塊或者某篇文章為版本,創(chuàng)建不同的tag,這樣利于食用。(github 上面的 issue)

  • 1.這個讀者的建議也非常好,我已經(jīng)在每次更新文章的 commit 上面加上了 tag ,大家可以結(jié)合這個來看代碼。

二、app架構(gòu)更新

我想看過本系列第二篇文章的同學(xué)都看過本項目的模塊架構(gòu)圖。距離寫下本項目的第一行代碼到現(xiàn)在已經(jīng)差不多三個月過去了,這個過程中項目中增加了很多模塊,我對大的項目的把握程度也加深了許多,所以這一節(jié)我更新一下 app 的架構(gòu)。

圖1:app 架構(gòu)圖.png

我接下來就按照圖1開始講解,標(biāo)了紅色的小模塊表示已經(jīng)進行過開發(fā)的模塊

  • 1.首先從最底層開始,這里是一些二方庫(自己開發(fā)的sdk)、三方庫(開源的sdk)。其他的所有模塊都能依賴這里的庫,當(dāng)然都是單向依賴(A 依賴 B,但是 B 不能依賴 A)
  • 2.再向上一層,這里有兩個大模塊,generate-codeinternal-base
    • 1.generate-code:這里放著生成代碼的幾個模塊,比如用 apt 生成代碼的 annotation-progress,又如用 gradle 的 transform 配合 javassist 生成代碼的 invoker。
    • 2.internal-base:這里放著 app 中的所有的底層模塊,例如負責(zé)網(wǎng)絡(luò)請求的 http 模塊,例如負責(zé)圖片加載的 image 模塊,例如復(fù)制數(shù)據(jù)庫的 database 模塊等等。
    • 3.在這里 generate-code 與 internal-base 這兩個大模塊之間可以互相依賴(注意這里表示的不是類似 http 與 image 之間可以互相依賴,因為這樣會產(chǎn)生循環(huán)依賴的錯誤)
    • 4.這兩個大模塊都可以被更上層的大模塊所依賴,注意這里是單向依賴,是必須遵守的約定,因為沒有代碼層面的約束
  • 3.再向上看,左邊是一個 external-base 大模塊和一個 core 小模塊組成的
    • 1.external-base:這個大模塊里面目前還沒有添加小模塊,但是未來應(yīng)該會添加進去,這里面裝著的是外部侵入的代碼的封裝,比如 bugly 除了需要添加庫的依賴還需要為其加一些另外的代碼,又比如一些 android 廠商的 push 方案集成之后需要的適配代碼
    • 2.core:這個是一個小模塊,將其單獨放在外面是因為其起一個承上啟下的作用
      • 1.這里面裝著更上層模塊的公共代碼,比如 app 進入時的初始化代碼。
      • 2.解決一些底層小模塊之間需要互相引用的問題,比如 http 需要和 image 之間互相引用,此時會造成循環(huán)引用的錯誤,此時就將這些代碼放到 core 中進行處理。暫定,在寫下面的時候我發(fā)現(xiàn)這個特性可能會造成本模塊依賴過多的問題,后面應(yīng)該還會繼續(xù)拆分這個小模塊
      • 3.溝通底層和上層的模塊
    • 3.這里的兩個模塊可以被同層右邊的 app-plugins 大模塊所依賴,這里也是單向依賴
  • 4.再看同層的右邊,這一個大模塊名為 app-plugins,里面的每一個小模塊都能被編譯成一個 app。然后其可以被最頂層的 app-variants 所依賴,最終構(gòu)建出不同功能的 app。
  • 5.最頂層就是 app-variants,這個大模塊只能依賴 app-plugins,里面幾乎不會有什么代碼,有的就是一個個 gradle 配置,最終會生成不同功能的 app。

三、網(wǎng)絡(luò)層定制

現(xiàn)在 okhttp + retrofit,也許是一個新項目的標(biāo)配了,但是很多人都只是在使用這兩個庫的最基本的功能,殊不知這兩個庫可以通過定制來實現(xiàn)更多的功能。這一節(jié)我就來講講如何基于這兩個庫來定制一個大項目的網(wǎng)絡(luò)請求層。中間會穿插著一些原理的講解。

1.網(wǎng)絡(luò)層請求流程

圖2:網(wǎng)絡(luò)層定制圖.png

接下來我會按照圖2開始講解 okhttp + retrofit 整個請求流程,待讀者對整個流程有所了解之后再講定制的代碼,這樣會事半功倍。

  • 1.圖中紅色的框是開始部分,我們就從這里開始。這里默認大家都會使用這兩個框架,多余的東西就不再贅述了。
  • 2.首先我們在需要請求一個接口的時候會使用 Retrofit 對象調(diào)用其 create 方法創(chuàng)建一個 XXXService。我們看下圖3的代碼:
    • 1.可以看見這里就是簡單的用了一下動態(tài)代理的方式將 XXXService 的每個接口交給特定的 ServiceMethod 來實現(xiàn)。
    • 2.這里的 ServiceMethod 怎么來的呢?看36行的 loadServiceMethod 方法,這首先為了性能會去 serviceMethodCache 中看看是否有 XXXService 某個接口對應(yīng)的 ServiceMethod,如果沒有的話就用 Builder 模式創(chuàng)建一個。
圖3:Retrofit#create.png
  • 3.回看圖2,創(chuàng)建好了 XXXService 的實現(xiàn)類之后,我們一般會結(jié)合 Rxjava 調(diào)用某個接口,讓其返回一個 Observable 對象。由前面的介紹,我們知道這里 Observable 其實是調(diào)用 ServiceMethod.adapt(OkhttpCall) 返回的(可以看圖3的21行),我們進入這個方法。
    • 1.這個方法里會將調(diào)用交給 CallAdapter.adapt(OkhttpCall)
    • 2.有些同學(xué)可能知道這個 CallAdapter 是在初始化 Retrofit 的時候被Retrofit.Builder() 添加的 CallAdapterFactory 創(chuàng)建的。其有幾個具體實現(xiàn)如圖2。
    • 3.那么這里要選哪一個呢?選擇 CallAdapter 的具體邏輯在 ServiceMethod.build 里面他會調(diào)用 ServiceMethod.createCallAdapter 這里最終會交給 Retrofit.callAdapter 來尋找合適的 CallAdapter。
    • 4.那么3中的具體查找邏輯是什么呢?這里我總結(jié)一下:
      • 1.會對 CallAdapterFactory 進行循環(huán)查找,一旦返回一個 CallAdapter 不為 null 那么就使用這個。
      • 2.具體是否為 null 的邏輯交給具體的 CallAdapterFactory 去實現(xiàn)。
      • 3.因為是順序查找,所以如果列表中有多個匹配項,這里只取最開始的一個。
  • 4.到這里我們先不看圖2,一般來說匹配上的 CallAdapterFactory 會是 RxJava2CallAdapterFactory。我們先研究一下他是怎么產(chǎn)生一個 Observable 的。
    • 1.先看一下圖4,我們直接看20行,這里解釋了為什么一般會匹配到 RxJava2CallAdapterFactory 因為我們的 XXXService 定義接口的時候一般選擇的返回值 都是 Observable 或者有關(guān) Rxjava 的返回值。然后我們直接看55行,這里返回了一個 RxJava2CallAdapter,這個就是生成 Observable 的對象。
    • 2.接著我們看圖5,還記得上面的3.1中我們說的嗎?Observable 就是 CallAdapter.adapt(OkhttpCall) 產(chǎn)生的。這里就是具體實現(xiàn)。
      • 1.可以看見18行根據(jù)接口調(diào)用是同步還是異步會生成兩種不同的 Observable。
      • 2.然后后面都是根據(jù)一些 flag,為 Observable 添加一些操作符。
圖4:RxJava2CallAdapterFactory#get.png 圖5:RxJava2CallAdapter#adapt.png
  • 5.再回到圖2,現(xiàn)在我們已經(jīng)有 Observable 了。這里我們先跳過圖2中的幾個步驟,直接來到黃色的框,從這里開始我們可以讓得到的 Observable 開始運行。對 Rxjava 熟悉的同學(xué)應(yīng)該知道,一個 Observable 會從操作符流的最頂部開始運行。所以這里會從我們前面講到的 RxJava2CallAdapter.adapt 中定義的第一個 Observable 的 subscribe 開始運行。我們就默認這次接口調(diào)用是同步的這樣簡單點,所以會先進入 CallExecuteObservable 中。
    • 1.先看圖6,第1行構(gòu)造這個對象的時候會傳入一個 Call 對象,其實現(xiàn)有很多我們在這里可以默認其為 OkhttpCall。
    • 2.圖6的第5行,是 Observable 開始運行的時候最先調(diào)用的方法(有興趣的同學(xué)可以看看Rxjava 的源碼解析)。這里我們可以看見13行,其將調(diào)用交給了 Okhttp.execute。
    • 3.我們可以看向圖7的20行,這里調(diào)用了 createRawCall 創(chuàng)建了一個 okhttp3.Call 其具體實現(xiàn)是 RealCall(我們直接使用 okhttp 的時候也是通過這個請求網(wǎng)絡(luò))。
    • 4.在回到圖2中,如圖2所示當(dāng)調(diào)用 RealCall.execute 的時候,就會進入 okhttp 的請求鏈。okhttp 使用了責(zé)任鏈模式,將請求穿過圖2中的一個個攔截器,每個攔截器都負責(zé)一個功能。開發(fā)者可以在攔截器鏈的最開始插入自己的攔截器,以實現(xiàn)一些定制操作。
    • 5.再回到圖7,okhttp 將數(shù)據(jù)請求完畢之后會返回一個 okhttp3.Response,這時候會在32行調(diào)用43行的 parseResponse 來將解析這個 Response。
    • 6.圖7中后面有些代碼看不見了,其實最終 Response 的解析會交給 ServiceMethod.toResponse。而其又會交給 Converter.coverter。這接口的實現(xiàn)類也很多,最常見的應(yīng)該就是 GsonConverterFactory 提供的 GsonResponseBodyConverter 了。如圖2,我們一般也是在創(chuàng)建 Retrofit 的時候添加一些 Converter 以供這里使用。同樣類似 CallAdapter,Converter 的選取也是一樣的策略
    • 7.經(jīng)過以上調(diào)用,我們就有了一個retrofit2.Respons,其內(nèi)部有一個解析了 body 之后的對象。
圖6:CallExecuteObservable#subscribeActual.png 圖7:OkHttpCall#execute.png
  • 6.CallExecuteObservable 中調(diào)用完畢之后,調(diào)用流程一般會交給 BodyObservable,這里面很簡單,就是將 retrofit2.Respons 中的解析后的 body 交給下一個 Observable 操作符。就這樣順著操作符流最終我們在 XXXService 中定義的接口的返回值 Observable 的泛型對象就會被傳入到 subscribe 中供外部調(diào)用者使用。如圖2中的粉色框。

2.網(wǎng)絡(luò)層定制代碼

所謂定制就是在網(wǎng)絡(luò)請求流程的各個主要節(jié)點中添加自己的代碼實現(xiàn)以達到特殊的需求。經(jīng)過前面的講解,我想讀者應(yīng)該對整個網(wǎng)絡(luò)層的請求流程有了一個大致的了解。這時我們可以再看看圖2,可以看見其中有幾處我綠色的框,這幾個地方就是我們可以添加定制代碼的地方。接下來我就會按順序講解一下這幾處的定制代碼是如何實現(xiàn)的。

圖8:RetrofitFactory.png 圖9:DefaultRetrofitConfig.png

(1)retrofit2.Call的裝飾

我們按請求順序可以在圖2中首先看見的是 NewCall.execute 這個框,接下來我就來說說這個可以怎么定制。

  • 1.按照我們前面的講解,大家應(yīng)該知道,如果不做任何定制的話這里的 NewCall 就是 OkhttpCall,其會返回一個 retrofit.Response。最終會在開發(fā)者的 subscribe 里面返回一個解析了 body 之后的數(shù)據(jù)結(jié)構(gòu)(這里就稱為 ContentData)。有時候我們會在 subscribe 里面需要更多的信息,比如在數(shù)據(jù)轉(zhuǎn)化過程中丟失的 head 的信息
  • 2.此時我們就可以對 OkhttpCall 進行一個封裝,首先我們可以定義一個我們自己的 DataContainer<T> 對象,其用于封裝 ContentData,然后其還可以裝數(shù)據(jù)轉(zhuǎn)化中丟失的數(shù)據(jù)。如圖10。
圖10:DataContainer.png
  • 3.那么我們在定義 XXXService 的接口的返回值的時候就能這樣定義:Observable<DataContainer<ContentData>>。
  • 4.此時有人眼尖就會發(fā)現(xiàn),不對啊這個 DataContainer 是被 Gson 反序列化過來的,里面的 okhttp3.Response 對象服務(wù)器又不知道是什么這樣怎么序列化呢?
  • 5.答案就在圖8,圖9中。大家可以看圖8的第7行,這里我添加了一個自定義的 CallAdapterFactory。
  • 6.在看圖8的44、48、49行,根據(jù)前面我們描述的請求流程,44行的 CallAdapter 會用來生成 Observable。再看48行,這里的 call 就是 OkhttpCall 了。我們將其傳入 buildCall 中返回了一個 NewCall,這里就是關(guān)鍵。
  • 7.buildCall 的實現(xiàn)代碼在圖9,可以看38行。這里的實現(xiàn)非常簡單直接就是將 OkhttpCall 封裝 返回了一個 ContainerCall,如圖11。
圖11:DataContainerCall.png
  • 8.DataContainerCall 里面的代碼就不用我說了吧,就是給 DataContainer 傳入一個 okhttp3.Response 對象。
  • 9.大家是不是覺得就這樣一個小東西很簡單?其實我也覺得很簡單,但是只要你會用了這一個小東西,那么更多實用的功能都能被這樣實現(xiàn)。

(2)OkhttpClient定制

按順序下來,第二個定制的地方就是 OkhttpCall 調(diào)用 okhttp.RealCall 的地方了。

  • 1.我們看圖8的21行,這里給 Retrofit 添加了一個 OkhttpClient。之后的請求都是通過它來發(fā)送的。
  • 2.這里插一下,大家可以看看3行,這里傳的是一個 RetrofitConfig,它其實是一個接口,像圖9的 DefaultRetrofitConfig 就是它的一個實現(xiàn)。當(dāng)然我們還可以有不同的實現(xiàn)以實現(xiàn)不同的定制方式。
  • 3.那么我們還是再看圖9的6行,可以看見這個方法的返回值 Builder 中添加了一系列 Intercept。由我們前面的講解可知,這些是攔截器,然后會按添加的順序攔截請求和響應(yīng)。
  • 4.這里可以看見我實現(xiàn)了各種不同的功能:打印網(wǎng)絡(luò)請求日志(這個在上一篇文章中沒實現(xiàn),現(xiàn)在實現(xiàn)了)、過濾過于頻繁的請求(防止ddos攻擊)、SSL認證(當(dāng)然現(xiàn)在沒有后端還沒實現(xiàn))、超時攔截、添加自定義的參數(shù)等等。
  • 5.這里的定制比較簡單,大家可以去看看各個攔截器中的實現(xiàn)。

(3)Converter定制

  • 1.其實這個也很簡單,大家可能都用過,就是圖8的5、6兩行,添加的數(shù)據(jù)轉(zhuǎn)換器。
  • 2.大家只要了解我前面講解的 Converter 的執(zhí)行策略就可以了。

(4)CallAdapter定制

  • 1.大家可以回看 (1)retrofit2.Call的裝飾 這一節(jié),我們添加了一個 CustomAdapterFactory。
  • 2.因為 CustomAdapterFactory 比 RxJava2CallAdapterFactory 先添加,所以其優(yōu)先級比較高。再看圖8的40行,這里獲取了一個 delegate,其實就是 RxJava2CallAdapterFactory。所以我們可以在 RxJava2CallAdapter 返回的 Observable 上面添加一些統(tǒng)一的操作符。
  • 3.具體的代碼在圖8的49行,然后轉(zhuǎn)到圖9的42行??梢钥匆娢揖椭惶砑恿艘恍┖唵蔚牟僮鞣河嫈?shù)請求成功和失敗次數(shù)、配合 ThrottlingInterceptor 進行頻繁請求過濾。

(5)網(wǎng)絡(luò)層定制代碼總結(jié)

上面就是在網(wǎng)絡(luò)請求的四個主要節(jié)點進行定制的方式。其實總結(jié)起來比較簡單:1是擴展 Retrofit 返回的結(jié)果、2是擴展 okhttp 請求和返回、3是解析 okhttp 返回給 Retrofit 的結(jié)果、4是增強對 Retrofit 返回結(jié)果的處理。

四、總結(jié)

不知不覺已經(jīng)寫了這么多了,本來以為還可以寫一節(jié) Fresco 的定制,現(xiàn)在看來只能放在下一篇文章了。在這里預(yù)告一下:從零開始仿寫一個抖音App這一系列的文章大概還有一到兩篇 android 層面的文章,并且會在接下來的一周左右放出。

這一階段結(jié)束之后我的文章和學(xué)習(xí)重心將會轉(zhuǎn)向音視頻這塊。這幾個月過來雖然有時候文章會 delay,但最終我也信守承諾沒有棄坑。最后希望大家能持續(xù)關(guān)注本系列,畢竟我都已經(jīng)這么努力了不是:)。


本頁內(nèi)容由塔燈網(wǎng)絡(luò)科技有限公司通過網(wǎng)絡(luò)收集編輯所得,所有資料僅供用戶學(xué)習(xí)參考,本站不擁有所有權(quán),如您認為本網(wǎng)頁中由涉嫌抄襲的內(nèi)容,請及時與我們聯(lián)系,并提供相關(guān)證據(jù),工作人員會在5工作日內(nèi)聯(lián)系您,一經(jīng)查實,本站立刻刪除侵權(quán)內(nèi)容。本文鏈接:http://jstctz.cn/16145.html
相關(guān)APP開發(fā)
 八年  行業(yè)經(jīng)驗

多一份參考,總有益處

聯(lián)系深圳網(wǎng)站公司塔燈網(wǎng)絡(luò),免費獲得網(wǎng)站建設(shè)方案及報價

咨詢相關(guān)問題或預(yù)約面談,可以通過以下方式與我們聯(lián)系

業(yè)務(wù)熱線:余經(jīng)理:13699882642

Copyright ? 2013-2018 Tadeng NetWork Technology Co., LTD. All Rights Reserved.