基於開源項目搭建屬於本身的技術堆棧

在技術面試的時候確定都會問到使用了哪些第三方框架,爲何使用它而不用其餘的。身邊朋友就有這樣的親身經歷:java

面試官:大家項目中加載圖片都是用的什麼框架?
面試者:Glide啊(心裏竊喜)
面試官:爲何使用Glide而不用其餘的?
面試者:(沉默10s),Glide好啊,我比較喜歡。(心裏不安)
面試官:......(能不能好好聊天了)android

這篇博文主要就是針對日常使用到的框架作一個整理和分析其優劣。git

爲了從總體上進行把握,先來看看一個完整的APP總體架構github

1. APP的總體架構

從較高的層次將,一個APP的總體架構能夠分爲兩層,即應用層和基礎框架層。面試

  • 應用層專一於行業領域的實現,例如金融、支付、地圖導航、社交等,它直接面向用戶,是用戶對產品的第一層感知。算法

  • 基礎框架層專一於技術領域的實現,提供APP公有的特性,避免重複製造輪子,它是用戶對產品的第二層感知,例如性能、穩定性等。數據庫

一個理想的APP架構,應該擁有以下特色apache

  • 支持跨平臺開發
  • 具備清晰的層次劃分,同一層模塊間充分解耦,模塊內部符合面向對象設計六大原則
  • 在功能、性能、穩定性等方面達到綜合最優

基於以上設計原則,咱們能夠看出APP架構圖,最上層是應用層,應用層如下都屬於基礎框架層,基礎框架層包括:組件層、基礎層和跨平臺層。json

這裏寫圖片描述
這裏寫圖片描述

咱們要討論的重點是基礎層,下面開始一步一步地闡述如何基於開源函數庫搭建屬於本身的一個基礎技術堆棧。數組

2. 技術選型的考量點

首先要明確的是,咱們選擇開源函數庫或者第三方SDK、通常須要綜合考慮一下幾個方面

  • 特性:提供的特性是否知足項目的需求
  • 可用性,是否提供了簡潔便利的API,方便開發者集成使用。
  • 性能:性能不能太差,不然項目後面性能優化會過不去,可能回出現須要替換函數庫的狀況。
  • 文檔:文檔應該比較齊全,且可讀性高。
  • 技術支持:遇到問題或者發現BUG,是否可以及時獲得官方的技術支持是很重要的
  • 大小:引入函數庫會增長APK的大小,須要慎重抉擇
  • 方法數:若是函數庫方法數太多,積累起來會致使你的APP遇到64K問題,應該儘可能避免

3. 日誌記錄能力

日誌記錄不管在服務端開發仍是移動端開發,都是一個基礎且重要的能力,開發人員在代碼調試以及錯誤定位過程當中,大多說都要依賴日誌信息,一個簡潔靈活的日誌記錄模塊是至關重要的。
Logger 是基於系統Log類基礎上進行的封裝,但新增了以下超讚的特性。

  • 在Logcat中完美的格式化輸出,不再用擔憂和手機其餘APP或者系統的日誌信息相混淆了
  • 包含線程、類、方法信息,能夠清楚地看到日誌記錄的調用堆棧
  • 支持跳轉到源碼處
  • 支持格式化輸出JSON、XML格式信息

    Logcat截圖

    這裏寫圖片描述
    這裏寫圖片描述

固然Logger也不是完備的,它雖然支持格式化輸出JSON、XML,但並不支持諸如List、Set、Map和數組等常見Java集合類的格式化輸出。如何解決呢?能夠看下LogUtils 這個開源庫,它實現了Logger缺失的上述特性。

再者,Logger只支持輸出日誌到Logcat,但項目開發中每每還存在將日誌保存到磁盤上的需求,如何將二者結合起來呢?這是就遇到了timber

timber是JakeWharton開源的一個日誌記錄庫,它的特色是可擴展的框架,開發者能夠方便快捷的集成不一樣類型的日誌記錄方式,例如,打印日誌到Logcat、打印日誌到文件、打印日誌到網絡等,timber經過一行代碼就能夠同時調用多種方式。

timber的思想很簡單,就是維護一個森林對象,它由不一樣類型的日誌樹組合而成,例如,Logcat記錄樹、文件記錄樹、網絡記錄樹等,森林對象提供對外的接口進行日誌打印。每種類型的樹均可以經過種植操做把本身添加到森林對象中,或者經過移除操做從森林對象中刪除,從而實現該類型日誌記錄的開啓和關閉。

最終咱們的日誌記錄模塊將由timber+Logger+LogUtils組成,固然輪子找到了,輪子的兼容合併就得靠咱們本身實現了,同時咱們還得增長打印到文件的日誌樹和打印到網絡的日誌樹實現。

4. JSON解析能力

移動互聯網產品與服務器端通訊的數據格式,若是沒有特殊需求的話,通常都使用JSON格式。Android系統也原生的提供了JSON解析的API,可是它的速度很是慢,並且沒有提供簡潔方便的接口來提升開發者的效率和下降出錯的可能。因此咱們就開始找第三方開源庫來實現JSON解析,比較優秀的包括以下幾種。

4.1 gson

gosn是Google出品的JSON解析函數庫,能夠將JSON字符串反序列化對應的Java對象,或者反過來將Java對象序列化爲對應的JSON字符串,免去了開發者手動經過JSONObject和JSONArray將JSON字段逐個進行解析的煩惱,也減小了出錯的可能性,加強了代碼的質量。使用gson解析時,對應的Java實體類無需使用註解進行標記,支持任意複雜Java對象包括沒有源代碼的對象。

4.2 jackson

jcakson是Java語言的一個流行的JSON函數庫,在Android開發中使用時,主要包含三部分。

  • jackson-core:JSON流處理核心庫
  • jackson-databind:數據綁定函數庫,實現Java對象和JSON字符串流的相互轉換。
  • jackson-annotations:databind使用的註解函數庫

因爲jackson是針對Java語言通用的JSON函數庫,並無爲Android優化定製過,所以函數保重包含不少非必要的API,相比其餘的JSON函數庫,用於Android平臺會更顯著的增大最終生成的APK的體積。

4.3 Fastjson

Fastjson是阿里巴巴出品的一個Java語言編寫的高性能且功能完善的JSON函數庫。它採用一種「假定有序快速匹配」的算法,把JSON Parse的性能提高到極致,號稱是目前Java語言中最快的JSON庫。Fastjson接口簡單易用,已經被普遍使用在緩存序列化、協議交互、Web輸出、Android客戶端等多種應用場景。

因爲是Java語言通用的,所以,之前在Android上使用時,Fastjson不可避免的引入了不少對於Android而言冗餘的功能,從而增長了包大小,不少人使用的就是標準版的fastjson,但事實上,fastjson還存在一個專門爲Android定製的版本---fastjson.android 。和標準版本相比,Android版本去掉了一些Android虛擬機dalvik不支持的功能,使得jar更小。

4.4 LoganSquare

LoganSquare是近兩年崛起的快速解析和序列化JSON的Android函數庫,其底層基於jackson的streaming API,使用APT(Android Annotation Tool)實現編譯時註解,從而提升JSON解析和序列化的性能。官網上能夠看到LoganSquare和gson、jackson databind的性能對比。

這裏寫圖片描述
這裏寫圖片描述

從性能方面看,LoganSquare是完勝gson和jackson的。若是和fastjson相比較,二者應該是不相上下的。

再來看下jar包的大小

  • gson:232KB
  • jackson:259+47+1229 = 1.5M
  • Fastjson:417KB
  • Fastjson.android:256KB
  • LoganSquare:48+259 = 307KB

從性能和包大小綜合考慮,最終咱們會選擇Fastjson.android做爲基礎技術堆棧中的JSON解析和序列化庫。

5. 數據庫操做能力

不管是iOS仍是Android,底層數據庫都是基於開源的SQLite實現,而後在系統層封裝成用於應用層的API。雖然直接使用系統的數據庫API性能很高,可是這些API接口並非很方便開發者使用,一不當心就會引入Bug,並且代碼的視覺效果也不佳。爲了解決這個問題,對象關係映射(ORM)框架出現了,比較好的有ActiveAndroid,ormlite和greenDAO。

5.1 ActiveAndroid

ActiveAndroid是一種Active Record風格的ORM框架,Active Record(活動目錄)是Yii,Rails等框架中對ORM實現的典型命名方式。它極大的簡化數據庫的使用,使用面向對象的方式管理數據庫,告別手寫SQL的歷史。每個數據庫表均可以被映射爲一個類,開發者只需使用相似save()或者delete()這樣的函數便可。

不過ActiveAndroid已經基本上處於維護階段了,最新的一個Release版本是在2012年發佈的。

5.2 ormlite

ormlite是Java平臺的一個ORM框架,支持JDBC鏈接、Spring和Android平臺。在Android中使用時,它包含兩部分。

  • ormlite-core:核心模塊,不管在哪一個平臺使用,都必須基於這個核心庫,是實現ORM映射的關鍵模塊。
  • ormlite-android:基於ormlite-core封裝的針對Android平臺的適配器模塊,Android開發中主要跟這個模塊打交道。

與ActiveAndroid相似,ormlite也已經不是一個活躍的開源庫,最近一次Release版本是在2013年發佈的。

5.3 greenDAO

greenDAO是一個輕量級且快速的ORM框架,專門爲Android高度優化和定製,它可以支持每秒數千條記錄的CRUD操做。官網上給出一張性能對比圖

這裏寫圖片描述
這裏寫圖片描述

縱軸表示每秒執行的操做數。並且greenDAO處在高度活躍中,最新Release版本是在2017年3月份發佈的

5.4 Realm

Realm是一個全新的移動數據庫引擎,它既不是基於iOS平臺的Core Data,也不是基於SQLite,它擁有本身的數據庫存儲引擎,並實現了高效快速的數據庫構建操做,相比Core Data和SQLite,Realm操做要快不少,跟ORM框架相比就更不用說了。

Realm的好處以下:

  • 跨平臺:Android和iOS已是事實上的兩大移動互聯網操做系統,絕大多數應用都會支持這兩個平臺。使用Realm,Android和iOS開發者無需考慮內部數據的架構,調用Realm提供的API便可輕鬆完成數據的交換。
  • 用法簡單:相比Core Data和SQLite所需的入門知識,Realm能夠極大下降開發者的學習成本,快速實現數據庫存儲功能。
  • 可視化操做:Realm爲開發者提供了一個輕量級的數據庫可視化操做工具,開發者能夠輕鬆查看數據庫中的內容,並實現簡單地插入和刪除等操做。

咱們看下上述四種數據庫包大小。

  • activeandroid:40KB
  • greendao:100KB
  • ormlite-android:57KB
  • realm-android:4.2M

能夠看出,前三個仍是正常範圍,但Realm的大小通常項目可能沒法接受。這是由於不一樣CPU架構平臺的 .so 文件增長了整個包的大小,因爲arm平臺的so在其餘平臺上面可以以兼容模式運行的,雖然會損失性能,可是能夠極大地減小函數庫佔用的空間。所以,能夠選擇只保留armeabi-v7a和x86兩個平臺的 .so 文件,直接刪除無用的 .so 文件,或者經過工程的build.gradle文件中增長 ndk abi 過濾,語句以下:

android {
    ...
    defaultConfig {
        ...
        ndk {
            abiFilters "armeabi-v7a", "x86"
        } 
    }
}複製代碼

所以,綜合性能考慮,包大小以及開源庫的可持續發展等因素,咱們最終選擇greenDAO。

6. 網絡通訊能力

如今的APP幾乎都須要從服務器獲取數據,不可避免的須要具有網絡通訊的能力,不然就是一個死界面。

6.1 android-async-http

Android最經典的網絡異步通訊函數庫,它對Apache的HttpClient API的封裝使得開發者能夠簡潔優雅地實現網絡請求和響應,而且同時支持同步和異步請求。主要特性以下:

  • 支持異步HTTP請求,並在匿名回調函數中處理響應
  • 在子線程中發起HTTP請求
  • 內部採用線程池來處理併發請求
  • 經過RequestParams類實現GET/POST參數構造
  • 無需第三方庫支持便可實現Multipart文件上傳
  • 庫的大小隻有60KB
  • 支持多種移動網絡環境下自動智能的請求重試機制
  • HTTP響應中實現自動的gzip解碼,實現快速請求響應
  • 內置多種形式的響應解析,有原生的字節流、String、JSON對象,甚至能夠將response寫入到文件中。
  • 可選的永久cookie保存,內部實現使用的是Android的SharedPreferences。

可是在6.0以後,系統對開發者隱藏了HttpClient函數庫,這顯著增大了使用android-async-http的代價。 若是鐵了心想繼續使用HttpClient,官方推薦的作法是在編譯期引入org.apache.http.legacy 這個庫,庫目錄在Android SDK目錄下的platforms\android-23\optional中找到,它的做用是確保在編譯時不會出現找不到HttpClient相關API的錯誤,在應用運行時能夠不依賴這個庫,由於6.0以上的Android系統尚未真正移除HttpClient的代碼,只不過API設置爲對開發者不可見。咱們查看android-async-http源碼發現,須要使用下面這個函數庫來替換以前的Apache的HttpClient。

dependencies {
    compile 'cz.msebera.android:httpclient:4.3.6'
}複製代碼

這樣顯著的增長了APP的包的大小,若是想繼續使用android-async-http,那麼你的APP須要額外增長1.1MB左右的大小。

6.2 OkHttp

OkHttp是一個高效的HTTP客戶端,具備以下特性。

  • 支持HTTP/2和SPDY,對同一臺主機的全部請求共享同一個socket。
  • 當SPDY不可用時,使用鏈接池減小請求的延遲。
  • 透明的GZIP壓縮減小下載數據大小
  • 緩存響應避免重複的網絡請求

OkHttp在網絡性能不好的狀況下可以很好地工做,它可以避免常見的網絡鏈接問題。若是你的HTTP服務有多個IP地址,OkHttp在第一次鏈接失敗是,會嘗試其餘可選的地址。這對於IPv4+IPv6以及託管在冗餘數據中心的服務來講是必要的。OkHttp使用現代的TLS特性(SNI,ALPN)初始化HTTP鏈接,當握手失敗時,會下降使用TSL1.0初始化鏈接。

OkHttp依賴於okio,okio做爲java.io和java.nio的補充,是square公司開發的一個函數庫。okio使得開發者能夠更好地訪問、存儲和處理數據。一開始是做爲OkHttp的一個組件存在的,固然咱們也能夠單獨使用它。

使用Okhttp須要引入Jar包,包的大小爲:326+66 = 392KB

6.3 Volley

Volley是Google在2013年發佈的用於Android平臺的網絡通訊庫,能使網絡通訊更快、更簡單、更健壯。官網配出一張弓箭發射圖來講明Volley特別使用於數據量小等通訊頻繁的場景。

這裏寫圖片描述
這裏寫圖片描述

具體的將,Volley是爲了簡化網絡任務而設計的,用於幫助開發者處理請求、加載、緩存、多線程、同步等任務。Volley設計了一個靈活的網絡棧適配器,在Android2.2及以前的版本中,Volley底層使用Apache HttpClient,在Android2.3及以上版本中,它使用HttpURLConnection來發起網絡請求,並且開發者也很容易將網絡棧切換成使用OkHttp。
Volley 官方源碼託管在Google Source上面,使用時只能直接以Jar包形式引入,若是想在Gradle中使用compile在線引入,能夠考慮使用mcxiaoke在Github上面的Volley Mirror,而後再build.gradle中使用以下語句便可。

compile 'com.mcxiaoke.volley:library:1.0.19'複製代碼

6.4 Retrofit

確切的說,Retrofit並非一個完整的網絡請求函數庫,而是將REST API轉換成Java接口的一個開源函數庫,它要求服務器API接口遵循REST規範。基於註解使得代碼變得很簡潔,Retrofit默認狀況下使用GSON做爲JSON解析器,使用OkHttp實現網絡請求,三者一般配合使用,固然咱們也能夠將這二者換成其餘的函數庫。

經過以上分析,HttpURLConnection、Apache HttpClient 和OkHttp封裝了底層的網絡請求,而android-async-http,Volley和Retrofit是基於前面三者的基礎上二次開發而成。

最後看下函數庫的大小

  • android-async-http:106KB+1.1MB = 1.2MB
  • OkHttp:326KB+66KB = 392KB
  • Volley:94KB
  • Retrofit:122KB+211KB = 333KB

7. 圖片緩存和顯示能力

圖片緩存函數庫有不少很是優秀的,開發人員能夠根據需求進行選擇。傳統的圖片緩存方案中設置有兩級緩存,分別是內存緩存和磁盤緩存。在Facebook推出的Fresco中,它增長了一級緩存,也就是Native緩存,這極大地下降了使用Fresco的APP出現OOM的機率。

7.1 BitmapFun

BitmapFun函數庫是Android官方教程中的一個圖片加載和緩存實例,對於簡單的圖片加載需求來講,使用BitmapFun就夠了,在早期用的多,如今漸漸退出了實際項目開發的舞臺。

7.2 Picasso

Picasso是著名的square公司衆多開源項目中的一個,它除了實現圖片的下載和二級緩存功能,還解決了常見的一些問題。

  • 在adapter中正常的處理ImageView回收和下載的取消
  • 使用盡可能小的內存實現複雜的圖像變換

在Picasso中,咱們使用一行代碼便可實現圖片下載並渲染到ImageView中。

Picasso.with(context).load(url).into(imageView);複製代碼

7.3 Glide

Glide是Google推薦的用於Android平臺上的圖片加載和緩存函數庫。這個庫被普遍應用在Google的開源項目中,Glide和Picasso有90%的類似度,只是在細節上仍是存在很多區別。Glide爲包含圖片的滾動列表作了儘量流暢的優化。除了靜態圖片,Glide也支持GIF格式圖片的顯示。Glide提供了靈活的API可讓開發者方便地替換下載圖片所用的網絡函數庫,默認狀況下,它使用HttpUrlConnection做爲網絡請求模塊,開發者也能夠根據本身項目的實際需求靈活使用Google的Volley或者Square的OkHttp等函數庫進行替換。

Glide的使用也可使用一行代碼來完成,語句以下

Glide.with(context).load(url).into(imageView);複製代碼

7.4 Fresco

Fresco是Facebook開源的功能強大的圖片加載和緩存函數庫,相比其餘圖片緩存庫,Fresco最顯著的特色是具備三級緩存:兩級內存緩存和一級磁盤緩存。主要特性以下:

  • 漸進式地加載JPEG圖片
  • 顯示GIF和WebP動畫
  • 可擴展,可自定義圖片加載和顯示
  • 在Android 4.X和一下的系統上,將圖片放在Android內存一個特殊的區域,從而使得應用運行更流暢,同時極大減低出現OutOfMemoryError的錯誤。

7.5 Android-Universal-Image-Loader

Android-Universal-Image-Loader簡稱UIL,是Android平臺老牌的圖片下載和緩存函數庫,功能強大靈活且高度可自定義,它提供一系列配置選項,並能很好地控制圖片加載和緩存的過程。使用者甚多,如今項目仍在使用。UIL也支持二級緩存,特性以下:

  • 同步或異步的多線程圖片加載
  • 高度可自定義:線程池、下載器、解碼器、內存和磁盤緩存、圖片顯示選項等。
  • 每張圖片的顯示支持多種自定義選項:默認存根圖片、解碼選項、Bitmap處理和顯示等。
  • 圖片可緩存在內存或者磁盤(設備的文件系統或者SD卡)上。
  • 可實時監聽圖片加載流程,包括下載進度。

最後看下幾個庫的包大小

  • BitmapFun:71KB
  • Picasso:120KB
  • Glide:475KB
  • Fresco:47KB+93KB+93KB+10KB+3MB+62KB+8KB+111KB = 3.4MB
  • Android-Universal-Image-Loader:162KB

圖片函數庫的選擇須要根據APP的具體狀況而定,對於嚴重依賴圖片緩存的APP,例如壁紙類,圖片社交類APP來講,能夠選擇最專業的Fresco。對於通常的APP,選擇Fresco會顯得比較重,畢竟Fresco 3.4MB的體量擺在這。

根據APP對圖片顯示和緩存的需求從低到高咱們能夠對以上函數庫作一個排序

BitmapFun < Picasso < Android-Universal-Image-Loader < Glide < Fresco複製代碼

值得一提的是,若是你的APP計劃使用React Native進行部分模塊功能的開發的話,那麼在基礎函數庫選擇方面須要考慮和React Native的依賴庫的複用,這樣能夠減小引入React Native 所增長的APP的大小,能夠複用的函數庫有:OkHttp,Fresco,jackson-core.

Thanks

《Android高級進階》顧浩鑫

相關文章
相關標籤/搜索