開發一流的 Android SDK:Fabric SDK 的創建經驗
來自: https://realm.io/cn/news/oredev-ty-smith-building-android-sdks-fabric/
About the Speaker: Ty Smith
Ty 是一個在 推ter 的 Android 技術負責人,專職于 Fabric 開發工具團隊。他曾經負責架構了 Fabric 平臺和 推ter 的 Android SDK,推動了 Digits 和 推ter SDK 的開源事業,可以說是他一手創建了更大的 推ter 體系結構。他一直專注于 Android 超過7年了,也是 GDE(谷歌開發專家)組織的一員。他經常在 Android 開發者的國際會議和各大 Android 開發者大會上進行演講。在到 推ter 之前,Ty 曾在 Evernote 的 Android 團隊工作,他帶領構建 SDK 和合作伙伴的集成工作,帶領開發短信平臺,并建立 Zagat for Android,Zagat 之后被谷歌收購。
</div> </div>APIs & Fabric(0:00)
作為開發者,我們中的大多數人不得不使用 SDK 和 API,但經常地,我們會遇到一些令人沮喪的或設計不好的東西。要使得開發偉大的應用程序更加輕松,作為核心的軟件開發工具包還有很長的路要走。在 Fabric 上,我們很關心開發者的經驗,我們花了大量的時間,使我們的 SDK 更容易和更有趣。我的名字是 Ty,在 推ter 的 Android Fabric 工程師團隊。我想與你們分享一些我們在開發 SDK 過程中學到的經驗。
那么 Fabric 究竟是什么呢?它是一套 推ter 開發的模塊化開發工具包,推ter 共享了一個共同的移動和Web平臺,用于減少占用空間和提供一致性的解決問題方案,旨在提升第三方移動 App 的質量。我們去年發布了 Fabric,這是完全免費的。我們相信這是一個偉大的方式來引導你的移動應用程序。就在上個月,我們宣布了多個外部合作伙伴,現在正在基于Fabric 進行開發,未來將有更多高質量的內容提供給大家。
我們建立 Fabric SDK,我們保持了幾個目標,幫助引導我們進行開發。這些原則決定了我們開發 API 和做決策的選擇。這些想法可以融入你自己的SDK或甚至你的應用。我們很高興能夠看到大家離開這里后,在未來開發的你們的 SDK 中采納和我們一致的想法。
注意事項(1:19)
在我們進入 library 或 SDK 編碼之前,我們有必要考慮幾個方面。
客戶需求(1:27)
首先要考慮的是要找出你在 library 里的實際服務的對象是誰,是內部開發人員還是公共開發人員?誰會使用它?它帶來的新價值是什么?市場上已經有了一個解決辦法嗎?如果是這樣的話,你應該是去對其進行開發和貢獻而不是重新創造一個“輪子”。
開源 vs. 閉源(1:42)
考慮開源與閉源是一個大問題。開源通常會讓你更好地通過社區,獲得更穩定的軟件,以及更熱心的內部工程師。然而,需要思考的事,你的 SDK 僅僅是集中于一個工程點呢?還是說它是一個完整的產品,但有一個后臺服務呢?
因此,仔細考慮你將采用哪一種許可證(開源協議)。例如,如果你使用 GPL 許可,那么將會使得用了你的 SDK 或 library 的人也必須得使用 GPL 開源協議。更靈活的許可證可能是 Apache 2 或 MIT 許可。
Binary 打包(2:18)
特別是對于 Android,打包你的代碼并不一定是簡單的。你有三個問題需要考慮。首先是對于一個標準的庫項目,開發者將他們包含到他們的代碼中,并且由 IDE 幫忙連接它們,它是非常靈活的,但是如果他們需要分叉(fork),他們得如何保持更新?
在 Java 的世界 jar 包是另一個好例子——很標準的二進制包裝。不幸的是,對于 Android,它們不能捆綁打包自己的資源文件,所以對于一些和視圖相關的 library 一般都不能采用 jar 方式打包。
最后,在 Android 世界還有一種打包方式即 aar,它是谷歌現在支持二進制打包方式。這是一個壓縮的容器,包含了編譯的源代碼以及資源文件,它可以通過 Gradle Maven 依賴源從而非常方便快捷地分發給開發者。
Hosting the artifacts(3:10)
最后的考慮是在哪里托管你的打包結果。Maven Central,是標準的倉庫。然而,他們都需要開源許可,因為他們想保護他們的服務的用戶,他們不希望有人會隱式地拉下來一個二進制包,并選擇一個他們沒有得到審查的服務條款。如果使用了另外的資源庫(如果你有一個專有的二進制文件),則開發人員必須手動添庫到編譯腳本中。
創建偉大的 SDK(3:38)
在創造 Fabric SDK 的工作是一個夢幻般的學習過程。我們的目標就是涵蓋這五大方面:易用、穩定、輕巧、靈活,很好的支持。我們相信偉大的 SDK 要實現這些得走很長的路。
易用(3:50)
其中的一個關鍵就是可用性。我們認為產品應該是易于使用的。
那么所謂的易用到底是什么呢?我們想創造一種最簡單的方式,讓人們在他們的應用中開始使用 Fabric. 如果它是易用的,它應該是不需要侵入太多你的代碼或者你需要做很多繁瑣的集成工作。只要在你的代碼中新增一行我們的代碼,就可以使用它了,類似這樣:
Fabric.with(this, new Crashlytics());
但易用的同時,有時還得能夠定制,許多開發者可能希望更多的定制。要做到這一點,我們使用的 Builder 生成器模式設置一些選項,比如設置一個監聽器好讓程序在應用程序崩潰之前通知你。
Crashlytics crashlytics = new Crashlytics.Builder()
.delay(1)
.listener(createCrashlyticsListener())
.pinningInfo(createPinningInfoProvider())
.build();
Fabric.with(this, crashlytics);</pre></div>
對于 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 不想開源)。例如,您可以創建一個屬性文件,然后我們將在運行時讀取該目錄文件內容。
易用性的表征(5:05)
除了我剛才提到的實施細節,我們喜歡在設計 API 時考慮這些特點:第一個是 直覺 。如果一個接口調用的行為恰好是開發人員預期的方式,而無需參考文檔。
我們發現,在你的 SDK API 中使用 一致 的命名,也是有助于使用者理解。使用平常的表達語言來命名你的方法,以及類似的設計模式。并且遵循各個平臺約定俗成的命名規則,比如 iOS 和 Android 平臺,它們各有不同的命名規則。
最后,如果 API 很難被誤用 ,將可以防止一些錯誤的發生。驗證輸入的參數,和書寫明確的文檔,將使得開發者在使用的時候,能夠有信心和避免錯誤。也會帶來一個更愉快的體驗。
直觀性(5:56)
讓我們看一個反直覺的例子:
URL url1 = new URL("
url1.equals(url2)</pre></div>
這一個感覺,將影響很深,也是 API 中最難的部分。當使用一個偉大的 API 時,我們可以猜測它是如何表現的。在這個例子里,我們將期望 equals 執行某種標準化的字符串比較。
但是實際上反直覺的是,equals 代表如果這兩個 URL 解析到相同的IP地址,在 Java 中的實現,將返回 true,這里的原因是這個 API 的實現十分有趣:它發射同步的 DNS 請求。誰會想到?阻塞調用線程是一個意外行為的例子,在 API 中應該是非常明確指出的。
一致性(6:38)
舉一個例子,Fabric 和 Crashlytics 的初始化方式便都是一致的。在初始化 Fabric 或 Crashlytics,兩個不同的二進制依賴庫文件,正如我們之前看到的我們允許它們使用同一模式建造。用戶可以使用無參數構造函數,或定義輔助方法來設置默認值,另外,這兩者都提供了一個可用于重定義對象的生成器(builder)。
防止誤用(6:59)
最后該講到如何防止誤用了。例如,從 Fabric Builder 的構造函數我們可以得知,Context 對象是必須的,而其它一些 setter 是可選的。一旦我們在構建的階段中創建實例,這些可選參數也就一并被初始化。
這樣設計的話,開發人員使用 API 將不能不提供 context,但可以使用其它 setters 在另外的閑暇時間。我們相信這樣就很難被誤用了。
怎么設計 API(7:30)
我們如何才能設計出高品質的 API 呢?讓我們來看看我們的設計流程。設計 API 是很難的,它通常不只是一個工程師獨自坐在一個黑暗的房間,決定該是什么樣子,它需要整個團隊付出大量的工作。
我們在 Fabric 的 API 設計上第一個重點就著眼于我們將支持的幾個平臺。我們創建一個設計文檔之前,任何實施工作都是這樣做的,進行討論在這些平臺上,不同的方法的優點和缺點。
有一句話我很喜歡:一個 API 就像一個嬰兒。他們很有趣,但他們需要18年的支持。任何 API 我們都必須要長期地支持,所以我們要讓大家感覺到,我們正走在正確的路上,才能才久堅持支持下去。
最后,即使我們可以讓 iOS 或者 Android 開發中愉快地使用我們的 API 了,我們還需要建立相關的平臺,首要的就是讓開發者們感到最舒服的。
API 穩定性(8:26)
現在我們已經設計了一些很容易使用的東西,讓我們來討論一下我們如何能獲得開發者的信任,相信這是非常重要的。因此,確保軟件開發工具包是可靠的,他們不影響應用程序本身的穩定性。大家都知道,相比開發應用程序,開發一個 SDK 需要更高的穩定性要求。讓我們來看看如果產生了一個錯誤將會有什么影響。
如果一個應用程序有一個關鍵的錯誤,阻礙了它的用戶使用,它可能僅僅需要發送一個新版應用程序給顧客進行更新即可。而如果是我們 SDK 發現了一個漏洞,我們很快修復它,它可能還需要一個月才能到達你的用戶,在此期間,你的用戶就會有很不好的體驗了。
顯然,如果一個 SDK 有一個嚴重的 bug,它的修復更新到達時間要長得多。這可能需要幾個月,用你的SDK應用程序的用戶才能得到錯誤修正。應用程序開發人員可能需要數周才能注意或升級您的 SDK 版本,并進行修復、測試 bug。所以說確保一個 SDK 的穩定性是我們的最高優先事項之一。
如何確保穩定?(9:36)
作為開發人員我們可以做什么,以確保盡可能高的穩定性?有一些事情是我們開發過程中的關鍵。首先,代碼審查是非常重要的,必須得認真對待它們。然后,通過不斷地問自己“這個代碼有什么問題嗎?”我們可以這樣試著去問自己,以達到盡可能的防守。
如果能夠自動獲得一些基本的正確性保證,也可以在早期幫助捕捉錯誤,所以單元測試是非常有用的。
另一方面,人們經常忽略的是:在用戶使用初次使用進行測試時候,使它能夠運行你的一些 SDK 代碼,這樣做他們可以在你的 SDK 集成時進行捕捉 bug。
最后,持續整合(譯者注:維基百科詞條 - 持續整合 )和”吃你自家的狗糧”(譯者注:維基百科詞條 - Eating your own dog food )也都可以作為你的保護層,可能有助于早期快速識別問題。
使 SDK 具備可測試性和可模擬性(10:16)
有一些技巧可以讓你的 SDK 具備更好的可測試性。其中,為了測試,有時我們需要進行模擬,模擬(mock)類作為真實類的仿制類,它沒有真實操作,并且允許被重寫調用和驗證方式。
通過避免靜態方法,您可以允許在模擬實例上進行操作任何方法的調用。如果您將使用靜態方法,需要確保它可以被隔離,并且您將提供所有的依賴關系,并且沒有基于任何狀態。
許多 mocking libraries 對于 final 的類也會產生許多問題 ,所以要考慮你的類擴展。在你的模擬類中不應該存在 public 屬性,所以需要被訪問的一切都應該通過一個訪問的方法來運行。
在你的 API 中使用接口。如果您的輸入點使用接口,設置類來測試將更容易。該接口允許開發人員進行重寫的行為,比如契合模擬服務器或在內存中存儲,來替代真實場景真實存儲的開銷。
最后,需要考慮到測試人員不需要構造多個層次深度的模擬。這個鼓勵測試的原則應該被寫入你的指引文檔,并提供更穩定的測試框架。
測試用例(11:39)
有一些 class 很難被模擬,比如 final 類型的,它將創建它自己的依賴關系,并且是一個基于狀態的單例。這在 Java 中是很常見的,雖然它通常是一個反模式。這使得它在隔離測試中非常具有挑戰性。那么,我們能做什么來解決它?其實只要有一些小修改,我們可以使這些難解的點變得更容易測試一些。
public class Tweeter {
private Network network;
private public Tweeter(Network network) {
this.network = network;
}
public List<Tweet> getTweets() {
return getNetwork.getTweets();
}
}</pre></div>
與其把它作為一個單例,為什么不把它做為一個實例?開發者可以自己去重用它,緩存,或者做其它的事情。刪除在 class 中的 final Mockito 或其他框架就可以模擬它了,同時也能讓 SDK 在初始化、構造時候管理它的依賴。依賴注入不僅僅是一個框架,它還是一個幫助組織代碼的設計模式,使得代碼更具有模塊性和可測試性。
優美的降解(12:29)
開發者經常是容易不耐煩的,所以有一些錯誤越盡早拋出就越好。如果你一直在使用 Gradle 你應該會明白我的意思,一些錯誤如果在 build 期間不能通過總是好于 build 完成之后5分鐘才出現錯誤。你應該 把一些可以預期的異常拋出 ,以便于開發者能夠盡快知道這些異常,比如在這種情況下,開發者試圖設置一個 null 的 logger 到我們的 builder 里,我們得馬上拋出一個異常,這樣他們就可以很快知道并解決他們的錯誤。
然而,你得保證你的 SDK 在生產環境中絕不會出錯,讓你的代碼持續運行在他們的應用中,是你保證開發者們信心的唯一方法。他們的應用程序往往是他們生計的依賴,所以他們不會喜歡去賭著使用一個經常崩潰的庫。所以當他們在調試過程中出現問題的時候,你可以提供額外的信息,寫清楚這個 Exception,但要隱藏在生產過程中可能出現的問題,這樣允許他們的應用程序的其余部分繼續運行。你的 SDK 的出現問題可能對你來說是一個大問題,但并不是世界末日。
作為一個開發者,你使用 SDK,你的應用程序應該增加價值,而最糟糕的事情就是你引進了某物反而使得原本多價值降低了甚至完全破壞用戶體驗。開發者們,包括我自己,不需要任何人來幫助我們寫一個糟糕的 App。
輕量(13:43)
除了穩定,用戶不太可能下載大的應用程序,這意味著安裝包的大小是一個關鍵內容。下載軟件產生的流量需要用戶去付錢,所以即使你的應用是完全免費的,用戶也得為下載它付出流量。在許多新興市場,因為下載速度太慢,所以很多用戶不愛下載大型應用程序;在某些市場,用戶主動選擇更新的應用僅僅基于更新日志和添加新的特性是否值得,因為他們需要支付每千字節流量費用。
讓我們來討論一些 Fabric 用于保持輕量的技術吧。有一些偉大的第三方庫,可以真正給予貢獻于你的應用程序,但當涉及到 size 規模和影響時,他們會他們顯得不自由。例如,圖像加載方面有各種不同體積大小的圖片加載庫。其中 Fresco,是比其他任何一個第三方庫都還大量級的一個庫。然而,它對于舊設備有更好的支持,加載速度快而且內存友好,并支持漸進式JPEG。
作為一個 SDK 你應該努力平衡你的尺寸與功能。因此,要注意引入第三方庫,以確保它們只滿足所需的內容。
使用開源庫有很大的優勢,因為這些庫往往經過很多的測試了,大家都使用得很好,并且他們有定期向他們提供更新的社區。這通常提供了一個更好的方案。在我們的 推ter SDK,我們利用 Square 的 RetroFit 這個庫作為一個依賴來簡化我們的 API,而且也能使我們提供更好的可擴展性,這是值得的。
報告二進制文件(binary)的大小(15:22)
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 方法數量限制(15:52)
很多人應該都會遇到過 Dalvik 65K 的限制吧?不過你們可能不熟悉這個錯誤的具體原因,對于方法的調用,可以通過在 Android 一個 DEX 文件,而它的引用的數量有限。問題的關鍵是,DEX 的工具,在編譯時,試圖把所有的方法引用到一定的空間中,但引用數目大于空間所能容納的數目,導致分配失敗。
現在你可以使用 multidex 來解決這個問題,但這增加了在較舊的設備上應用程序的加載時間,它的初始化時間顯著增加,它并不適合一些較新的設備用戶以外的人。它甚至某些流行的三星設備上會導致出錯,并導致應用程序崩潰。這些特定的設備有數以百萬計的設備,在歐洲和亞洲都是很流行的,所以說這個解決方案真是迫不得已,能不觸及盡量不要觸及。
但如果開發者遇到這個問題,在他們去使用 multidex 或類似的東西之前,他們經常決定審核他們的第三方庫,選擇是否可以減少應用程序方法數量的庫。所以我們的目標和建議是盡可能地使你的庫模塊化和精益。
我們用一個偉大的庫被稱為 dex-method-counts 在 Gradle 中,它包裝了一些Android 構建工具對每個第三方庫的方法數量并給出了詳細的數據分析。這讓我們快速洞察到我們的庫大小和我們的依賴的大小。
模塊化(17:31)
我們想要模塊化開發人員需要的特定功能。我們這樣做是通過指定一個樹的傳遞依賴關系。所以,我們有兩個例子展示如何初始化 Fabric:
容易整合的:
Fabric.with(this, new 推ter());
更多控制的:
Fabric.with(this, new TweetUi(), new TweetComposer());
第一個例子讓你馬上開始,其次是一個擁有更多定制的版本,在這里你可以選擇需要的特定組件,然后它們才生效。
讓我們的 SDK 盡可能小,我們著眼于模塊化來設計我們的架構。就像我們討論過的,在 Android 上二進制文件的大小和方法計數是非常重要的,所以這使我們能夠盡可能高效。因為如果我們利用 AAR(標準的通過 Maven 提供標準的庫),我們可以使用分解依賴來滿足我們的需求。
在架構 推ter SDK 堆棧,所有的一切都建立在 Fabric 上,它提供共享的 common 代碼給所有我們的 SDKs. 我們建立了 推terCore 層,它提供了登錄和 API 客戶端和其他一些核心功能。
然后,我們才有基于特征的 SDKs,像我們的 Tweets,我們的 composer,我們的短信登錄基礎設施;這些特點都可能是你使用我們 SDK 的理由。最后,我們提供了一個接口,通過傳遞依賴關系將其包裝起來。這允許應用程序開發人員根據他們需要的層次結構選擇組件。
壓縮數據(19:04)
除了注意 SDK 本身的大小之外,我們建議你在“壓線”之前進行壓縮。我們可以明顯看到,不同格式之間有壓縮大小差異,如 XML 和 JSON,一起使用jzip壓縮格式,或二進制格式 Protobuf 。有些人可能不熟悉 Protobuf,它是一個二進制的壓縮格式,在服務器和客戶端接口使用一致的預期協議,使得不需要將所有字段都進行傳輸。它可以非常高效地進行序列化和反序列化。
批量處理請求(19:36)
有效地發射是避免消耗過多電源功率的關鍵。Android 有三種典型的能量狀態:全功率、低功耗、空閑狀態或稱待機狀態。
在高功耗的時候進行統一的網絡傳輸,比在空閑的時候斷斷續續進行多次傳輸來得節約時間和節約電源。
對于在典型的3G設備上每一個數據連接,網絡接收模塊會產生約20秒的活躍時間。這意味著如果你每分鐘有三個網絡連接,我們將會保持這個網絡接收模塊不斷地處于激活。通過批處理這三個連接,我們可以在這里減少20秒的啟用速率,以及至少40秒的待機或空閑時間。
不過對于這個方面,有一個例子,說的是天真統計分析 SDK,它們有時會不停地 ping 你的服務器,大約20秒一次,僅僅是為了告知服務器你的應用當前處于前臺。這么做的后果就是會造成網絡接收模塊一直處于激活狀態,并且把電源耗盡又沒有傳輸什么實質上的數據內容。
異步任務(20:54)
保持 Fabric 的輕量部分方式是,我們應該清楚地知道何時在主線程做的工作何時在后臺線程工作,我們建議在你的應用程序啟動時候初始 Fabric,因此它的初始化將在主線程,我們從我們周圍很多顧客那里聽到大家都是比較關注啟動時間的。
為了緩解這一點,我們做了非常有限的一些同步工作,然后立馬返回到開發者的 application,同時在后臺繼續做一些畢竟耗時的運行工作,以保持您的應用程序下次能夠快速啟動。
有些事情需要用同步做的一個例子:如果你使用在使用我們的 Crashlytics ,你得立即使用它的 crash handler,因為一旦崩潰異常發生在異步初始化 crash handler 完成之前,就會捕捉不到這個異常。
靈活性(22:36)
一個 SDK 的開發者沒有像應用開發者那么多的選擇權。你不能選擇你的設備,API level,或客戶。你需要支持更大范圍的設備,應用程序開發者并不局限在選擇你的SDK,所以提供最大程度的靈活性是很重要的。
建立工具支持(22:53)
靈活性的一個體現是,可以讓開發者選擇不同的依賴管理器或者構件工具來引入或集成你的庫。
我們提供一些主要的開發工具插件支持,包括 Gradle, Maven, 和 Ant. 我們還為通常的 IDE 提供 GUI 插件,以及一個幫助開發 Mac 和 iOS app 的應用。盡管我們這是主要在講 Android 方面開發 SDK 的內容,但我還是忍不住想告訴大家一個好消息就是我們最近完成了令人興奮的 CocoaPods 支持,這將非常方便于 iOS 開發者使用我們的 SDK.
盡量選擇兼容最低的系統版本(23:26)
靈活性的關鍵是了解您的 SDK 用戶的需求。然后做出需要支持的最低系統版本的決定。我們很希望我們的 SDK 能夠盡可能支持更多的系統設備。對于這一點,降低支持最低操作系統版本是很有必要盡力去做的。
但另一方面,兼容低版本也是要付出代價的。并沒有什么直接的法則能夠告訴我們如何才能在繁瑣度和更好的兼容性上確定平衡。支持舊的操作系統版本,通常意味著不利用更加好用的新接口,同時還要面對一些舊版本存在的問題。初次之后,你還要花費更多精力去測試你的代碼之正確性。
一般來說,SDK 會比應用軟件在支持的最低系統版本的選擇上,更近保守。例如,Crashlytics 提供了 Android 2.2 的支持,因為這是一個還基本實用的 Android 應用程序。而不是像一些應用程序,它們僅支持某一操作系統的版本,它們關注用戶數量而去選擇最佳系統版本。
另一個重要的部分是能夠檢測出你正在運行的 Android 版本,所以你才能知道,哪些是可以調用的方法。通常,SDK 支持更老版本的 Android SDK。這對我們來說是非常重要的,因為我們想提供最大限度的設備支持。
Android Manifests(24:57)
在 Android 系統上,運行時兼容性是靈活性的重要組成部分。Android 開發者都知道,一個應用程序的元數據(metadata)在 manifest 文件中聲明,一個 apk 一個 manifest 文件。
此外,每一個你包含進來的 aar 都會帶一個 Manifest 文件,只是在 build 的過程中,Android 工具會自動幫它們合并到一個 Manifest 里面。這意味著,如果一個 AAR 的 Manifest 會影響最終 Manifest 的生成,比如新增一個權限聲明,這對于開發者來說是很難注意到的。
權限(25:37)
權限是任何 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);
}</pre></div>
我們只需要檢查上下文對象中的權限,以判斷是否已授予該權限。如果沒有獲得該權限也沒辦法了,我們不能為了權限而可能影響到安全異常。一個 SDK 可以在運行時檢查是否被允許使用某權限,如果可用再調用相應的 API.
很多時候我們都需要回退機制,但是在這種情況下,如果我們不知道WiFi或互聯網的狀態,我們必須假定它總是連接嘗試,讓超時發生。
可選的功能(26:42)
有很多 Android 設備的存在,同時有很多各式各樣的特性或者功能可能其它機器設備并沒有,比如有的有 Kindle Fire 而有的沒有,有的設備甚至還沒有攝像頭。
如果你正在構建一個基于相機的 SDK,你會在清單文件中聲明使用相機。這就要求商店不要將應用程序展現給一個沒有攝像頭的設備。我鼓勵你,把這個功能列為可選的,并允許你的庫在運行時檢測和修改它的行為。
在運行時檢測硬件功能非常簡單。您只需要查詢 package manager 該特定功能是否存在即可。這樣以后你的應用程序可以確定哪些功能可以使用了。以我們的相機庫示例,您可能還允許用戶瀏覽照片和上傳照片,只是他們不能夠在沒有相機的設備上拍照就是了。
Classpath 檢測(27:41)
有很多很好的第三方庫可以節約你的開發時間以及幫助你開發更好的 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 當中。如果有,你可以利用,如果沒有,你也可以有你的另外選擇。
可擴展類和接口(29:16)
除了運行時檢測,我們不能滿足每個開發者的需求。我們的 推ter SDK 提供了易于使用和流行的一些特性讓用戶去發現。我們預先就會準備好,幫你簡化簽名操作,我們使用持續的 token,簽署了所有的輸出請求。
但是,如果你想使用一個我們 SDK 目前沒有提供的 推ter API 功能怎么辦呢?如果是這樣,你可以繼承 推ter API client,并且提供你的 retroift 接口,我們可以接受它并幫你進行簽名。
正如我們所知道的,開發人員有很多工具,有很多的選擇,他們需要在他們的代碼中保持靈活性。這是很現實的。我們提供可擴展的接口,使用 Fabric,使開發人員可以利用擴展他們想要的功能接口。
這里還有一個例子是日志記錄。有許多不同的庫來使用,我們提供了日志接口,以便它可以在 Fabric 開始前提供實現,然后我們將尊重并使用開發人員的日志記錄需求。
但如果開發者選擇不配置他個性的日志內容,我們提供一個健全的默認日志,便是標準的 Android Logger.
另外,對于 Java 之外的方面,我們也支持讓用戶自定義他們想要的界面風格,比如開發者可以修改 color 的值,這些值將被應用于 推terUI SDK.
這里有一個需要注意的點,因為 dex 合并點時候不支持在 name 中使用空格符號,所以不要使用類似”background color”這樣的 name.
Callbacks(31:09)
制定靈活代碼的一部分,就是允許開發人員選擇監聽一些事件發生并獲得通知。在我們的 builder,我們可以設置同步或異步任務結束的時候進行回調,也可以設置如果出錯了可以得到回調通知。它允許開發者根據自己的狀態和所獲取的信息進行定制決策。
良好支持的 SDK(31:37)
當你完成了你的 SDK 開發的時候,并不代表著你的 SDK 真正完成了,你還需要有很多開發之外的內容要做,要建立開發者交流的社區,還要有 Apple 文檔、Java 文檔,以及 README 文檔,另外還有很重要的就是要有使用你的庫的示例教程。
添加注釋給你的所有 public 的內容,以及順帶說明一些使用案例。
發布簡明的 示例(sample)代碼 ,讓使用的人可以遵循著你的代碼進行初次嘗試。但切記不要把你的示例代碼寫得太復雜或者在無關緊要的內容上糾結太多,不然會導致用戶花費更多時間去學習你的示例代碼,并且使用到他們的項目中時候,產生了很多疑問或者 bug.
記得思考一些你想廢棄的舊版本和方法。你們有多少人沒有回頭看舊的代碼?除了去重寫重構這些代碼和方法,你可以需要對于舊的不要的代碼進行 廢棄 注解提醒。
尊敬版本更新日志,這是一個你和開發者通過你的 SDK 溝通的方式,你可以去知道他們需要什么,而他們從你們的 SDK 更新日志中獲得他們是否需要你這個新版本的信息,如果值得,開發者們就會決定更新到目標的這一個版本。
最后,使用開發者預期的、常用的 交流社區或稱交流方式 。如果這是一個開源庫,應該是使用 GitHub 的 Issues,關注 Stack Overflow,Freenode.
雖然我們認為所有的這些觀點都非常重要,但它們確實是偉大的庫/SDK建設中的冰山一角。我們希望你喜歡我們作為內部開發人員在 Fabric SDK 開發中的思考,那你覺得有用的建議和特性,或許就將出現在你現在手頭或者將來要開發的 SDK 身上。謝謝!
See the discussion on Hacker News .
Sign up to be notified of new videos — we won’t email you for any other reason, ever.
</div>
</span></code></code></code></code></code></code></code></code></code></code>
本文由用戶 WarMonti 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!
相關經驗
相關資訊