一区二区三区欧美日韩-一区二区三区欧美-一区二区三区免费在线视频-一区二区三区免费在线观看-久久精品店-久久精品第一页

歡迎您光臨深圳塔燈網絡科技有限公司!
電話圖標 余先生:13699882642

網站百科

為您解碼網站建設的點點滴滴

從零開始仿寫一個抖音App——app架構更新與網絡層定制

發表日期:2018-10 文章編輯:小燈 瀏覽次數:2957

本文首發于 ——何時夕,搬運轉載請注明出處,否則將追究版權責任。交流qq群:859640274

連載文章

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

本項目的 github 地址:MyTikTok

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

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

一、討論

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

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

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

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

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

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

二、app架構更新

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

圖1:app 架構圖.png

我接下來就按照圖1開始講解,標了紅色的小模塊表示已經進行過開發的模塊

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

三、網絡層定制

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

1.網絡層請求流程

圖2:網絡層定制圖.png

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

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

2.網絡層定制代碼

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

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

(1)retrofit2.Call的裝飾

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

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

(2)OkhttpClient定制

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

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

(3)Converter定制

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

(4)CallAdapter定制

  • 1.大家可以回看 (1)retrofit2.Call的裝飾 這一節,我們添加了一個 CustomAdapterFactory。
  • 2.因為 CustomAdapterFactory 比 RxJava2CallAdapterFactory 先添加,所以其優先級比較高。再看圖8的40行,這里獲取了一個 delegate,其實就是 RxJava2CallAdapterFactory。所以我們可以在 RxJava2CallAdapter 返回的 Observable 上面添加一些統一的操作符。
  • 3.具體的代碼在圖8的49行,然后轉到圖9的42行。可以看見我就只添加了一些簡單的操作符:計數請求成功和失敗次數、配合 ThrottlingInterceptor 進行頻繁請求過濾。

(5)網絡層定制代碼總結

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

四、總結

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

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


本頁內容由塔燈網絡科技有限公司通過網絡收集編輯所得,所有資料僅供用戶學習參考,本站不擁有所有權,如您認為本網頁中由涉嫌抄襲的內容,請及時與我們聯系,并提供相關證據,工作人員會在5工作日內聯系您,一經查實,本站立刻刪除侵權內容。本文鏈接:http://www.junxiaosheng.cn/16145.html
相關APP開發
 八年  行業經驗

多一份參考,總有益處

聯系深圳網站公司塔燈網絡,免費獲得網站建設方案及報價

咨詢相關問題或預約面談,可以通過以下方式與我們聯系

業務熱線:余經理:13699882642

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

主站蜘蛛池模板: 国产乱码伦人偷精品视频| 秋霞电影院兔费理论观频84mb| 午夜片无码区在线观看| 国产真实女人一级毛片| 18黄女脱内衣| 十分钟免费看完整视频| 娇妻归来在线观看免费完整版电影| 国产精品久久久久AV麻豆| 精品免费视在线视频观看| 在线中文高清资源免费观看| 嫩草影院一区| 国产色偷偷男人的天堂| 影音先锋 av天堂| 色综合久久天天影视网| 久久re热在线视频精69| 成人免费毛片观看| 永久免费精品影视网站| 色婷婷粉嫩AV精品综合在线| 久久久无码精品亚洲欧美| 高清bblxx手机在线观看| 亚洲精品高清AV在线播放| 久久本道久久综合伊人| WRITEAS检查身体| 亚洲精品国偷拍自产在线 | 中文字幕一区在线观看视频| 日韩插啊免费视频在线观看| 久久国语精品| 国产精品国产三级国产an | 日韩欧美国产免费看清风阁| 久久视频在线视频| 国产亚洲精品AAAAAAA片| yin荡体育课羞耻play双性| 尤物国产在线精品三区| 迅雷哥在线观看高清| 日本免费一区二区三区四区五六区| 久久久无码精品一区二区三区 | 樱桃BT在线观看| 小夫妻天天恶战| 日本少妇内射视频播放舔| 免费看男人J放进女人J无遮掩| 黄色三级网址|