cp from : https://academy.realm.io/cn/posts/oredev-ty-smith-building-android-sdks-fabric/java
Ty 是一個在 Twitter 的 Android 技術負責人,專職於 Fabric 開發工具團隊。他曾經負責架構了 Fabric 平臺和 Twitter 的 Android SDK,推進了 Digits 和 Twitter SDK 的開源事業,能夠說是他一手建立了更大的 Twitter 體系結構。他一直專一於 Android 超過7年了,也是 GDE(谷歌開發專家)組織的一員。他常常在 Android 開發者的國際會議和各大 Android 開發者大會上進行演講。在到 Twitter 以前,Ty 曾在 Evernote 的 Android 團隊工做,他帶領構建 SDK 和合做夥伴的集成工做,帶領開發短信平臺,並創建 Zagat for Android,Zagat 以後被谷歌收購。node
----------------------------------------------------------android
Twitter 的 Fabric 是知名的注重質量的 SDK,並已部署在數十億的設備。在此次 Øredev 演講中,來自 Twitter 的 Ty Smith,揭示了 Fabric 團隊建立他們 Fabric 的各類原則,特別是在 Android 方面。經過深刻參與技術決策團隊,Ty 瞭解到不少信息,他展現了團隊在建立這個 SDK 過程當中,學到的各類經驗心得,關於穩定性、性能、SDK 體積控制、以及對於一些特殊狀況的處理這些方面。不管你如今或未來想要建設一個 SDK,經過此次演講你應該能收益不少關於設計 SDK 的偉大想法。git
做爲開發者,咱們中的大多數人不得不使用 SDK 和 API,但常常地,咱們會遇到一些使人沮喪的或設計很差的東西。要使得開發偉大的應用程序更加輕鬆,做爲核心的軟件開發工具包還有很長的路要走。在 Fabric 上,咱們很關心開發者的經驗,咱們花了大量的時間,使咱們的 SDK 更容易和更有趣。個人名字是 Ty,在 Twitter 的 Android Fabric 工程師團隊。我想與大家分享一些咱們在開發 SDK 過程當中學到的經驗。github
那麼 Fabric 到底是什麼呢?它是一套 Twitter 開發的模塊化開發工具包,Twitter 共享了一個共同的移動和Web平臺,用於減小佔用空間和提供一致性的解決問題方案,旨在提高第三方移動 App 的質量。咱們去年發佈了 Fabric,這是徹底免費的。咱們相信這是一個偉大的方式來引導你的移動應用程序。就在上個月,咱們宣佈了多個外部合做夥伴,如今正在基於Fabric 進行開發,將來將有更多高質量的內容提供給你們。設計模式
咱們創建 Fabric SDK,咱們保持了幾個目標,幫助引導咱們進行開發。這些原則決定了咱們開發 API 和作決策的選擇。這些想法能夠融入你本身的SDK或甚至你的應用。咱們很高興可以看到你們離開這裏後,在將來開發的大家的 SDK 中採納和咱們一致的想法。api
在咱們進入 library 或 SDK 編碼以前,咱們有必要考慮幾個方面。緩存
首先要考慮的是要找出你在 library 裏的實際服務的對象是誰,是內部開發人員仍是公共開發人員?誰會使用它?它帶來的新價值是什麼?市場上已經有了一個解決辦法嗎?若是是這樣的話,你應該是去對其進行開發和貢獻而不是從新創造一個「輪子」。安全
考慮開源與閉源是一個大問題。開源一般會讓你更好地經過社區,得到更穩定的軟件,以及更熱心的內部工程師。然而,須要思考的事,你的 SDK 僅僅是集中於一個工程點呢?仍是說它是一個完整的產品,但有一個後臺服務呢?服務器
所以,仔細考慮你將採用哪種許可證(開源協議)。例如,若是你使用 GPL 許可,那麼將會使得用了你的 SDK 或 library 的人也必須得使用 GPL 開源協議。更靈活的許可證多是 Apache 2 或 MIT 許可。
特別是對於 Android,打包你的代碼並不必定是簡單的。你有三個問題須要���慮。首先是對於一個標準的庫項目,開發者將他們包含到他們的代碼中,而且由 IDE 幫忙鏈接它們,它是很是靈活的,可是若是他們須要分叉(fork),他們得如何保持更新?
在 Java 的世界 jar 包是另外一個好例子——很標準的二進制包裝。不幸的是,對於 Android,它們不能捆綁打包本身的資源文件,因此對於一些和視圖相關的 library 通常都不能採用 jar 方式打包。
最後,在 Android 世界還有一種打包方式即 aar,它是谷歌如今支持二進制打包方式。這是一個壓縮的容器,包含了編譯的源代碼以及資源文件,它能夠經過 Gradle Maven 依賴源從而很是方便快捷地分發給開發者。
最後的考慮是在哪裏託管你的打包結果。Maven Central,是標準的倉庫。然而,他們都須要開源許可,由於他們想保護他們的服務的用戶,他們不但願有人會隱式地拉下來一個二進制包,並選擇一個他們沒有獲得審查的服務條款。若是使用了另外的資源庫(若是你有一個專有的二進制文件),則開發人員必須手動添庫到編譯腳本中。
在創造 Fabric SDK 的工做是一個夢幻般的學習過程。咱們的目標就是涵蓋這五大方面:易用、穩定、輕巧、靈活,很好的支持。咱們相信偉大的 SDK 要實現這些得走很長的路。
其中的一個關鍵就是可用性。咱們認爲產品應該是易於使用的。
那麼所謂的易用究竟是什麼呢?咱們想創造一種最簡單的方式,讓人們在他們的應用中開始使用 Fabric. 若是它是易用的,它應該是不須要侵入太多你的代碼或者你須要作不少繁瑣的集成工做。只要在你的代碼中新增一行咱們的代碼,就可使用它了,相似這樣:
Fabric.with(this, new Crashlytics());
但易用的同時,有時還得可以定製,許多開發者可能但願更多的定製。要作到這一點,咱們使用的 Builder 生成器模式設置一些選項,好比設置一個監聽器好讓程序在應用程序崩潰以前通知你。
Crashlytics crashlytics = new Crashlytics.Builder() .delay(1) .listener(createCrashlyticsListener()) .pinningInfo(createPinningInfoProvider()) .build(); Fabric.with(this, crashlytics);
對於 Fabric SDK,咱們須要一個 API key 做爲鏈接咱們網絡服務器的驗證密鑰。這是咱們要開發人員處理的事情,但須要儘可能減小所需的工做量或者說繁瑣度。咱們的標準方法是:經過咱們的構建插件提供的方式,並將其注入到清單(manifest)文件中。這裏是一個例子,使用 metadata 在清單文件中插入數據:
<manifest package="con.example.SDK"> <application> <meta-data android:value="01235813213455" android:name="com.fabric.ApiKey" /> </application> </manifest>
當 Fabric 在初始化的時候,咱們能夠經過 package manager 獲取到咱們插入在清單文件中的 API key 而且繼續後續工做。另外,咱們能夠容許其餘的方法來管理這個 API key 的值,對於開源項目這麼作可能會更好(保護 key 的值,由於有時咱們對項目進行開源,但 key 不想開源)。例如,您能夠建立一個屬性文件,而後咱們將在運行時讀取該目錄文件內容。
除了我剛纔提到的實施細節,咱們喜歡在設計 API 時考慮這些特色:第一個是 直覺。若是一個接口調用的行爲剛好是開發人員預期的方式,而無需參考文檔。
咱們發現,在你的 SDK API 中使用 一致 的命名,也是有助於使用者理解。使用日常的表達語言來命名你的方法,以及相似的設計模式。而且遵循各個平臺約定俗成的命名規則,好比 iOS 和 Android 平臺,它們各有不一樣的命名規則。
最後,若是 API 很難被誤用,將能夠防止一些錯誤的發生。驗證輸入的參數,和書寫明確的文檔,將使得開發者在使用的時候,可以有信心和避免錯誤。也會帶來一個更愉快的體驗。
讓咱們看一個反直覺的例子:
URL url1 = new URL("http://foo.example.com"); URL url2 = new URL("http://example.com"); url1.equals(url2)
這一個感受,將影響很深,也是 API 中最難的部分。當使用一個偉大的 API 時,咱們能夠猜想它是如何表現的。在這個例子裏,咱們將指望 equals 執行某種標準化的字符串比較。
可是實際上反直覺的是,equals 表明若是這兩個 URL 解析到相同的IP地址,在 Java 中的實現,將返回 true,這裏的緣由是這個 API 的實現十分有趣:它發射同步的 DNS 請求。誰會想到?阻塞調用線程是一個意外行爲的例子,在 API 中應該是很是明確指出的。
舉一個例子,Fabric 和 Crashlytics 的初始化方式便都是一致的。在初始化 Fabric 或 Crashlytics,兩個不一樣的二進制依賴庫文件,正如咱們以前看到的咱們容許它們使用同一模式建造。用戶可使用無參數構造函數,或定義輔助方法來設置默認值,另外,這二者都提供了一個可用於重定義對象的生成器(builder)。
最後該講到如何防止誤用了。例如,從 Fabric Builder 的構造函數咱們能夠得知,Context 對象是必須的,而其它一些 setter 是可選的。一旦咱們在構建的階段中建立實例,這些可選參數也就一併被初始化。
這樣設計的話,開發人員使用 API 將不能不提供 context,但可使用其它 setters 在另外的閒暇時間。咱們相信這樣就很難被誤用了。
咱們如何才能設計出高品質的 API 呢?讓咱們來看看咱們的設計流程。設計 API 是很難的,它一般不僅是一個工程師獨自坐在一個黑暗的房間,決定該是什麼樣子,它須要整個團隊付出大量的工做。
咱們在 Fabric 的 API 設計上第一個重點就着眼於咱們將支持的幾個平臺。咱們建立一個設計文檔以前,任何實施工做都是這樣作的,進行討論在這些平臺上,不一樣的方法的優勢和缺點。
有一句話我很喜歡:一個 API 就像一個嬰兒。他們頗有趣,但他們須要18年的支持。任何 API 咱們都必需要長期地支持,因此咱們要讓你們感受到,咱們正走在正確的路上,才能才久堅持支持下去。
最後,即便咱們可讓 iOS 或者 Android 開發中愉快地使用咱們的 API 了,咱們還須要創建相關的平臺,首要的就是讓開發者們感到最舒服的。
如今咱們已經設計了一些很容易使用的東西,讓咱們來討論一下咱們如何能得到開發者的信任,相信這是很是重要的。所以,確保軟件開發工具包是可靠的,他們不影響應用程序自己的穩定性。你們都知道,相比開發應用程序,開發一個 SDK 須要更高的穩定性要求。讓咱們來看看若是產生了一個錯誤將會有什麼影響。
若是一個應用程序有一個關鍵的錯誤,阻礙了它的用戶使用,它可能僅僅須要發送一個新版應用程序給顧客進行更新便可。而若是是咱們 SDK 發現了一個漏洞,咱們很快修復它,它可能還須要一個月才能到達你的用戶,在此期間,你的用戶就會有很很差的體驗了。
顯然,若是一個 SDK 有一個嚴重的 bug,它的修復更新到達時間要長得多。這可能須要幾個月,用你的SDK應用程序的用戶才能獲得錯誤修正。應用程序開發人員可能須要數週才能注意或升級您的 SDK 版本,並進行修復、測試 bug。因此說確保一個 SDK 的穩定性是咱們的最高優先事項之一。
做爲開發人員咱們能夠作什麼,以確保儘量高的穩定性?有一些事情是咱們開發過程當中的關鍵。首先,代碼審查是很是重要的,必須得認真對待它們。而後,經過不斷地問本身「這個代碼有什麼問題嗎?」咱們能夠這樣試着去問本身,以達到儘量的防守。
若是可以自動得到一些基本的正確性保證,也能夠在早期幫助捕捉錯誤,因此單元測試是很是有用的。
另外一方面,人們常常忽略的是:在用戶使用初次使用進行測試時候,使它可以運行你的一些 SDK 代碼,這樣作他們能夠在你的 SDK 集成時進行捕捉 bug。
最後,持續整合(譯者注:維基百科詞條 - 持續整合 )和」吃你自家的狗糧」(譯者注:維基百科詞條 - Eating your own dog food )也均可以做爲你的保護層,可能有助於早期快速識別問題。
有一些技巧可讓你的 SDK 具有更好的可測試性。其中,爲了測試,有時咱們須要進行模擬,模擬(mock)類做爲真實類的仿製類,它沒有真實操做,而且容許被重寫調用和驗證方式。
經過避免靜態方法,您能夠容許在模擬實例上進行操做任何方法的調用。若是您將使用靜態方法,須要確保它能夠被隔離,而且您將提供全部的依賴關係,而且沒有基於任何狀態。
許多 mocking libraries 對於 final 的類也會產生許多問題,因此要考慮你的類擴展。在你的模擬類中不該該存在 public 屬性,因此須要被訪問的一切都應該經過一個訪問的方法來運行。
在你的 API 中使用接口。若是您的輸入點使用接口,設置類來測試將更容易。該接口容許開發人員進行重寫的行爲,好比契合模擬服務器或在內存中存儲,來替代真實場景真實存儲的開銷。
最後,須要考慮到測試人員不須要構造多個層次深度的模擬。這個鼓勵測試的原則應該被寫入你的指引文檔,並提供更穩定的測試框架。
有一些 class 很難被模擬,好比 final 類型的,它將建立它本身的依賴關係,而且是一個基於狀態的單例。這在 Java 中是很常見的,雖然它一般是一個反模式。這使得它在隔離測試中很是具備挑戰性。那麼,咱們能作什麼來解決它?其實只要有一些小修改,咱們可使這些難解的點變得更容易測試一些。
public class Tweeter { private Network network; private public Tweeter(Network network) { this.network = network; } public List<Tweet> getTweets() { return getNetwork.getTweets(); } }
與其把它做爲一個單例,爲何不把它作爲一個實例?開發者能夠本身去重用它,緩存,或者作其它的事情。刪除在 class 中的 final Mockito 或其餘框架就能夠模擬它了,同時也能讓 SDK 在初始化、構造時候管理它的依賴。依賴注入不只僅是一個框架,它仍是一個幫助組織代碼的設計模式,使得代碼更具備模塊性和可測試性。
開發者常常是容易不耐煩的,因此有一些錯誤越儘早拋出就越好。若是你一直在使用 Gradle 你應該會明白個人意思,一些錯誤若是在 build 期間不能經過老是好於 build 完成以後5分鐘纔出現錯誤。你應該 把一些能夠預期的異常拋出,以便於開發者可以儘快知道這些異常,好比在這種狀況下,開發者試圖設置一個 null 的 logger 到咱們的 builder 裏,咱們得立刻拋出一個異常,這樣他們就能夠很快知道並解決他們的錯誤。
然而,你得保證你的 SDK 在生產環境中毫不會出錯,讓你的代碼持續運行在他們的應用中,是你保證開發者們信心的惟一方法。他們的應用程序每每是他們生計的依賴,因此他們不會喜歡去賭着使用一個常常崩潰的庫。因此當他們在調試過程當中出現問題的時候,你能夠提供額外的信息,寫清楚這個 Exception,但要隱藏在生產過程當中可能出現的問題,這樣容許他們的應用程序的其他部分繼續運行。你的 SDK 的出現問題可能對你來講是一個大問題,但並非世界末日。
做爲一個開發者,你使用 SDK,你的應用程序應該增長價值,而最糟糕的事情就是你引進了某物反而使得本來多價值下降了甚至徹底破壞用戶體驗。開發者們,包括我本身,不須要任何人來幫助咱們寫一個糟糕的 App。
除了穩定,用戶不太可能下載大的應用程序,這意味着安裝包的大小是一個關鍵內容。下載軟件產生的流量須要用戶去付錢,因此即便你的應用是徹底免費的,用戶也得爲下載它付出流量。在許多新興市場,由於下載速度太慢,因此不少用戶不愛下載大型應用程序;在某些市場,用戶主動選擇更新的應用僅僅基於更新日誌和添加新的特性是否值得,由於他們須要支付每千字節流量費用。
讓咱們來討論一些 Fabric 用於保持輕量的技術吧。有一些偉大的第三方庫,能夠真正給予貢獻於你的應用程序,但當涉及到 size 規模和影響時,他們會他們顯得不自由。例如,圖像加載方面有各類不一樣體積大小的圖片加載庫。其中 Fresco,是比其餘任何一個第三方庫都還大量級的一個庫。然而,它對於舊設備有更好的支持,加載速度快並且內存友好,並支持漸進式JPEG。
做爲一個 SDK 你應該努力平衡你的尺寸與功能。所以,要注意引入第三方庫,以確保它們只知足所需的內容。
使用開源庫有很大的優點,由於這些庫每每通過不少的測試了,你們都使用得很好,而且他們有按期向他們提供更新的社區。這一般提供了一個更好的方案。在咱們的 Twitter SDK,咱們利用 Square 的 RetroFit 這個庫做爲一個依賴來簡化咱們的 API,並且也能使咱們提供更好的可擴展性,這是值得的。
task reportSdkFootprint << { def sdkProject = project(':clients:SdkProject') def nonSdkProject = project(':clients:NonSdkProject') def footprint = getSizeDifferent( new File("$sdkProject.buildDir}.../Sdk.apk"), new File("${nonSdkProject.buildDir}.../onSdk.apk")) println footprint }
這裏有一個很好的減小你的庫大小的方法,就是若是你天天都稱量本身的體重,你就可以減肥。咱們採起這種方法來關注和減少咱們的 SDK 大小。咱們積極地監控每一個 build,使咱們可以衡量的真實影響開發者的 APK 大小狀況。沒有硬性和快速的規則來解決大小增長,但一直關注它顯然是有幫助的。
不少人應該都會遇到過 Dalvik 65K 的限制吧?不過大家可能不熟悉這個錯誤的具體緣由,對於方法的調用,能夠經過在 Android 一個 DEX 文件,而它的引用的數量有限。問題的關鍵是,DEX 的工具,在編譯時,試圖把全部的方法引用到必定的空間中,但引用數目大於空間所能容納的數目,致使分配失敗。
如今你可使用 multidex 來解決這個問題,但這增長了在較舊的設備上應用程序的加載時間,它的初始化時間顯著增長,它並不適合一些較新的設備用戶之外的人。它甚至某些流行的三星設備上會致使出錯,並致使應用程序崩潰。這些特定的設備有數以百萬計的設備,在歐洲和亞洲都是很流行的,因此說這個解決方案真是無可奈何,能不觸及儘可能不要觸及。
但若是開發者遇到這個問題,在他們去使用 multidex 或相似的東西以前,他們常常決定審覈他們的第三方庫,選擇是否能夠減小應用程序方法數量的庫。因此咱們的目標和建議是儘量地使你的庫模塊化和精益。
咱們用一個偉大的庫被稱爲 dex-method-counts 在 Gradle 中,它包裝了一些Android 構建工具對每一個第三方庫的方法數量並給出了詳細的數據分析。這讓咱們快速洞察到咱們的庫大小和咱們的依賴的大小。
咱們想要模塊化開發人員須要的特定功能。我���這樣作是經過指定一個樹的傳遞依賴關係。因此,咱們有兩個例子展現如何初始化 Fabric:
容易整合的:
Fabric.with(this, new Twitter());
更多控制的:
Fabric.with(this, new TweetUi(), new TweetComposer());
第一個例子讓你立刻開始,其次是一個擁有更多定製的版本,在這裏你能夠選擇須要的特定組件,而後它們才生效。
讓咱們的 SDK 儘量小,咱們着眼於模塊化來設計咱們的架構。就像咱們討論過的,在 Android 上二進制文件的大小和方法計數是很是重要的,因此這使咱們可以儘量高效。由於若是咱們利用 AAR(標準的經過 Maven 提供標準的庫),咱們可使用分解依賴來知足咱們的需求。
在架構 Twitter SDK 堆棧,全部的一切都創建在 Fabric 上,它提供共享的 common 代碼給全部咱們的 SDKs. 咱們創建了 TwitterCore 層,它提供了登陸和 API 客戶端和其餘一些核心功能。
而後,咱們纔有基於特徵的 SDKs,像咱們的 Tweets,咱們的 composer,咱們的短信登陸基礎設施;這些特色均可能是你使用咱們 SDK 的理由。最後,咱們提供了一個接口,經過傳遞依賴關係將其包裝起來。這容許應用程序開發人員根據他們須要的層次結構選擇組件。
除了注意 SDK 自己的大小以外,咱們建議你在「壓線」以前進行壓縮。咱們能夠明顯看到,不一樣格式之間有壓縮大小差別,如 XML 和 JSON,一塊兒使用jzip壓縮格式,或二進制格式 Protobuf。有些人可能不熟悉 Protobuf,它是一個二進制的壓縮格式,在服務器和客戶端接口使用一致的預期協議,使得不須要將全部字段都進行傳輸。它能夠很是高效地進行序列化和反序列化。
有效地發射是避免消耗過多電源功率的關鍵。Android 有三種典型的能量狀態:全功率、低功耗、空閒狀態或稱待機狀態。
在高功耗的時候進行統一的網絡傳輸,比在空閒的時候斷斷續續進行屢次傳輸來得節約時間和節約電源。
對於在典型的3G設備上每個數據鏈接,網絡接收模塊會產生約20秒的活躍時間。這意味着若是你每分鐘有三個網絡鏈接,咱們將會保持這個網絡接收模塊不斷地處於激活。經過批處理這三個鏈接,咱們能夠在這裏減小20秒的啓用速率,以及至少40秒的待機或空閒時間。
不過對於這個方面,有一個例子,說的是天真統計分析 SDK,它們有時會不停地 ping 你的服務器,大約20秒一次,僅僅是爲了告知服務器你的應用當前處於前臺。這麼作的後果就是會形成網絡接收模塊一直處於激活狀態,而且把電源耗盡又沒有傳輸什麼實質上的數據內容。
保持 Fabric 的輕量部分方式是,咱們應該清楚地知道什麼時候在主線程作的工做什麼時候在後臺線程工做,咱們建議在你的應用程序啓動時候初始 Fabric,所以它的初始化將在主線程,咱們從咱們周圍不少顧客那裏聽到你們都是比較關注啓動時間的。
爲了緩解這一點,咱們作了很是有限的一些同步工做,而後立馬返回到開發者的 application,同時在後臺繼續作一些畢竟耗時的運行工做,以保持您的應用程序下次可以快速啓動。
有些事情須要用同步作的一個例子:若是你使用在使用咱們的 Crashlytics ,你得當即使用它的 crash handler,由於一旦崩潰異常發生在異步初始化 crash handler 完成以前,就會捕捉不到這個異常。
一個 SDK 的開發者沒有像應用開發者那麼多的選擇權。你不能選擇你的設備,API level,或客戶。你須要支持更大範圍的設備,應用程序開發者並不侷限在選擇你的SDK,因此提供最大程度的靈活性是很重要的。
靈活性的一個體現是,可讓開發者選擇不一樣的依賴管理器或者構件工具來引入或集成你的庫。
咱們提供一些主要的開發工具插件支持,包括 Gradle, Maven, 和 Ant. 咱們還爲一般的 IDE 提供 GUI 插件,以及一個幫助開發 Mac 和 iOS app 的應用。儘管咱們這是主要在講 Android 方面開發 SDK 的內容,但我仍是忍不住想告訴你們一個好消息就是咱們最近完成了使人興奮的 CocoaPods 支持,這將很是方便於 iOS 開發者使用咱們的 SDK.
靈活性的關鍵是瞭解您的 SDK 用戶的需求。而後作出須要支持的最低系統版本的決定。咱們很但願咱們的 SDK 可以儘量支持更多的系統設備。對於這一點,下降支持最低操做系統版本是頗有必要盡力去作的。
但另外一方面,兼容低版本也是要付出代價的。並無什麼直接的法則可以告訴咱們如何才能在繁瑣度和更好的兼容性上肯定平衡。支持舊的操做系統版本,一般意味着不利用更加好用的新接口,同時還要面對一些舊版本存在的問題。初次以後,你還要花費更多精力去測試你的代碼之正確性。
通常來講,SDK 會比應用軟件在支持的最低系統版本的選擇上,更近保守。例如,Crashlytics 提供了 Android 2.2 的支持,由於這是一個還基本實用的 Android 應用程序。而不是像一些應用程序,它們僅支持某一操做系統的版本,它們關注用戶數量而去選擇最佳系統版本。
另外一個重要的部分是可以檢測出你正在運行的 Android 版本,因此你才能知道,哪些是能夠調用的方法。一般,SDK 支持更老版本的 Android SDK。這對咱們來講是很是重要的,由於咱們想提供最大限度的設備支持。
在 Android 系統上,運行時兼容性是靈活性的重要組成部分。Android 開發者都知道,一個應用程序的元數據(metadata)在 manifest 文件中聲明,一個 apk 一個 manifest 文件。
此外,每個你包含進來的 aar 都會帶一個 Manifest 文件,只是在 build 的過程當中,Android 工具會自動幫它們合併到一個 Manifest 裏面。這意味着,若是一個 AAR 的 Manifest 會影響最終 Manifest 的生成,好比新增一個權限聲明,這對於開發者來講是很難注意到的。
權限是任何 Android 應用程序的關鍵。在 Crashlytics 咱們利用 wifi state permission 能夠更好地管理上傳崩潰日誌。在 Android 的棉花糖系統上,這是屬於正常水平的權限,這意味着它還須要在 manifest 文件中聲明並贊成。然而,咱們沒有在咱們的清單中聲明這個權限,因此,若是使用了咱們 SDK 的開發者想要上傳崩潰日誌,就必須得在他的清單文件中聲明上這條權限。若是開發者聲明瞭那條權限,咱們在下面的代碼中就可以知道擁有這個權限了。
protected boolean canCheckNetworkState(Context context) { String permission = Manifest.permission.ACCESS_NETWORK_STATE; int result = context.checkCallingOrSelfPermission(permission); return (result == PackageManager.PERMISSION_GRANTED); }
咱們只須要檢查上下文對象中的權限,以判斷是否已授予該權限。若是沒有得到該權限也沒辦法了,咱們不能爲了權限而可能影響到安全異常。一個 SDK 能夠在運行時檢查是否被容許使用某權限,若是可用再調用相應的 API.
不少時候咱們都須要回退機制,可是在這種狀況下,若是咱們不知道WiFi或互聯網的狀態,咱們必須假定它老是鏈接嘗試,讓超時發生。
有不少 Android 設備的存在,同時有不少各式各樣的特性或者功能可能其它機器設備並無,好比有的有 Kindle Fire 而有的沒有,有的設備甚至尚未攝像頭。
若是你正在構建一個基於相機的 SDK,你會在清單文件中���明使用相機。這就要求商店不要將應用程序展示給一個沒有攝像頭的設備。我鼓勵你,把這個功能列爲可選的,並容許你的庫在運行時檢測和修改它的行爲。
在運行時檢測硬件功能很是簡單。您只須要查詢 package manager 該特定功能是否存在便可。這樣之後你的應用程序能夠肯定哪些功能可使用了。以咱們的相機庫示例,您可能還容許用戶瀏覽照片和上傳照片,只是他們不可以在沒有相機的設備上拍照就是了。
有不少很好的第三方庫能夠節約你的開發時間以及幫助你開發更好的 SDK. 當咱們提供 SDK 給用戶,可能須要檢測用戶是否有引入這些可選的第三方庫,好比 RxJava 或者 Square 公司的 OkHttp,若是有,咱們就能夠利用它們提供相應的支持。但你不必把這些包括到你的 SDK 當中,由於咱們前面說了,保持輕量是最好的。
private boolean hasOkHttpOnClasspath() { try { Class.forName("com.squareup.okhttp.OkHttpClient"); return true; } catch (ClassNotFoundException e) { } return false; }
在這個例子中,你能夠經過 class path 來檢測 OkHttp 是否存在。你能夠告訴用戶能夠引入這個第三方庫進行更好的支持,但沒有必要主動把它們包括到你的 SDK 當中。若是有,你能夠利用,若是沒有,你也能夠有你的另外選擇。
除了運行時檢測,咱們不能知足每一個開發者的需求。咱們的 Twitter SDK 提供了易於使用和流行的一些特性讓用戶去發現。咱們預先就會準備好,幫你簡化簽名操做,咱們使用持續的 token,簽署了全部的輸出請求。
可是,若是你想使用一個咱們 SDK 目前沒有提供的 Twitter API 功能怎麼辦呢?若是是這樣,你能夠繼承 Twitter API client,而且提供你的 retroift 接口,咱們能夠接受它並幫你進行簽名。
正如咱們所知道的,開發人員有不少工具,有不少的選擇,他們須要在他們的代碼中保持靈活性。這是很現實的。咱們提供可擴展的接口,使用 Fabric,使開發人員能夠利用擴展他們想要的功能接口。
這裏還有一個例子是日誌記錄。有許多不一樣的庫來使用,咱們提供了日誌接口,以便它能夠在 Fabric 開始前提供實現,而後咱們將尊重並使用開發人員的日誌記錄需求。
但若是開發者選擇不配置他個性的日誌內容,咱們提供一個健全的默認日誌,即是標準的 Android Logger.
另外,對於 Java 以外的方面,咱們也支持讓用戶自定義他們想要的界面風格,好比開發者能夠修改 color 的值,這些值將被應用於 TwitterUI SDK.
這裏有一個須要注意的點,由於 dex 合併點時候不支持在 name 中使用空格符號,因此不要使用相似」background color」這樣的 name.
制定靈活代碼的一部分,就是容許開發人員選擇監聽一些事件發生並得到通知。在咱們的 builder,咱們能夠設置同步或異步任務結束的時候進行回調,也能夠設置若是出錯了能夠獲得回調通知。它容許開發者根據本身的狀態和所獲取的信息進行定製決策。
當你完成了你的 SDK 開發的時候,並不表明着你的 SDK 真正完成了,你還須要有不少開發以外的內容要作,要創建開發者交流的社區,還要有 Apple 文檔、Java 文檔,以及 README 文檔,另外還有很重要的就是要有使用你的庫的示例教程。
添加註釋 給你的全部 public 的內容,以及順帶說明一些使用案例。
發佈簡明的 示例(sample)代碼,讓使用的人能夠遵循着你的代碼進行初次嘗試。但切記不要把你的示例代碼寫得太複雜或者在可有可無的內容上糾結太多,否則會致使用戶花費更多時間去學習你的示例代碼,而且使用到他們的項目中時候,產生了不少疑問或者 bug.
記得思考一些你想廢棄的舊版本和方法。大家有多少人沒有回頭看舊的代碼?除了去重寫重構這些代碼和方法,你能夠須要對於舊的不要的代碼進行 廢棄 註解提醒。
尊敬版本更新日誌,這是一個你和開發者經過你的 SDK 溝通的方式,你能夠去知道他們須要什麼,而他們從大家的 SDK 更新日誌中得到他們是否須要你這個新版本的信息,若是值得,開發者們就會決定更新到目標的這一個版本。
最後,使用開發者預期的、經常使用的 交流社區或稱交流方式。若是這是一個開源庫,應該是使用 GitHub 的 Issues,關注 Stack Overflow,Freenode.
雖然咱們認爲全部的這些觀點都很是重要,但它們確實是偉大的庫/SDK建設中的冰山一角。咱們但願你喜歡咱們做爲內部開發人員在 Fabric SDK 開發中的思考,那你以爲有用的建議和特性,或許就將出如今你如今手頭或者未來要開發的 SDK 身上。謝謝!