從最初的移動端運營活動到深度鏈接(deep link),再到現在的移動端原生廣告,魔窗sdk經歷了多個版本的迭代之后,功能逐步完善,開始步入4.x版本的時代。
去年五月份的時候,我寫過一篇文章《移動端SDK的優化之路》,現在回過頭再來看看發現過去一年多的時間里我們又做了很多事情,所以有了新的一篇文章。
一. deep link
1.1 支持deep link和deferred deep link
早在2015年下半年就開始做deep link的功能,當時的版本已經支持了deep link以及deferred deep link(場景還原)的功能。
所謂deep link,是解決各個App之間信息孤島的問題,實現App之間能夠像網頁一樣能夠自由跳轉。
deferred deep link 是指用戶打開一個h5頁面的時候并沒有安裝對應的 app,在安裝 app 以后可以直接通過 deep link 到 app 對應的內容。
從后臺的數據分析顯示,大多數的客戶對我們deferred deep link(場景還原)更感興趣。為此,我們也一直在努力提高場景還原的匹配度。
1.2 為了能從微信朋友圈回流到App,Android版本使用應用寶跳轉
iOS能夠借助Universal Link從微信朋友圈跳轉到App的具體頁面,Android就沒有這么幸運了,雖然谷歌早就提出了App Links但是國內很多手機并不支持,我們借助應用寶的鏈接來跳轉到App的具體頁面。
應用寶跳轉原理跟 deferred deep link 是一樣的,并不會100%的準確匹配,但絕大多數情況是可以成功跳轉的。
1.3 iOS10之后,第一時間優化WebView的跳轉
iOS 10之后,用戶在WebView中使用uri scheme做應用間的跳轉時,必須把目標App的uri scheme加到Info.plist中。
對于那些在 WebView 中使用魔窗的短鏈接客戶而言,如果僅僅是做應用內的跳轉,那是不需要把自己的Scheme放到Info.plist就可以直接調用。魔窗的短鏈會自動匹配操作系統版本和瀏覽器信息,在支持Universal Link的瀏覽器中自動使用Universal Link,如果不支持Universal Link的瀏覽器則用Scheme進行跳轉。
1.4 去年11月初,sdk跟服務端通信的接口全面使用https
2016年的WWDC規定在2017.1.1之后iOS App必須全面支持https協議。我們在2016年10月的版本開始做支持https協議的功能,android 和 iOS兩個平臺的sdk都支持了https。趕在了11月初上線,給開發者留足時間,讓他們替換新版本上架。
二. 原生廣告
我們的原生廣告是基于魔窗位的,魔窗位可以埋在App的任意位置包括開機畫面、Banner位、任意文字或圖片的地方等等。
在新版本中,我們還新增了信息流廣告。
三. 信息流廣告
什么是信息流廣告?不了解信息流廣告的童鞋可以看我之前的文章《對信息流廣告以及未來移動端廣告的簡單思考》
我們的sdk支持原生的信息流廣告。提供原生的控件給到開發者,屏蔽了其中的技術細節,方便開發者直接使用到項目中(或者feed流中)。
原生控件能夠給用戶帶來更好的體驗,無縫地插入到App Native的頁面中。除了原生控件之外,還支持將信息流廣告的metadata返回給開發者,供開發者自行渲染。
信息流展示的策略,可以在后臺進行配置。
四. sdk的設計原則和架構
4.1 模塊化設計
從最初的所有代碼都在一個主工程,到現在拆分成多個module,結構更加清晰。
在下一個版本中,android 和 iOS 都會考慮將原生控件的功能拆分成一個單獨的sdk。
4.2 面向對象的設計原則
在設計sdk時,我們一定會遵循面向對象的法則。
4.2.1 單一職責原則(Single responsibility principle)
單一職責原則是指:對一個類而言,應該僅有一個引起它變化的原因。簡單來說,一個類中應該是一組相關性很高的函數、數據的封裝。
sdk的網絡框架并沒有使用android 、iOS流行的okhttp、retrofit、AFNetworking等。因為需要考慮到sdk包大小的問題,我們使用對應操作系統底層的API來實現。因此,在android和iOS我們都做了一套簡化的框架,大致流程是這樣的:
我們遵循了單一職責原則,它主要由四個部分組成:Request、RequestQueue、NetworkExecutor和ResponseDelivery,每一個部分只負責自己的工作。
Request是各種請求類型。
RequestQueue是消息隊列,維護了提交給網絡框架的請求列表,并且根據相應的規則進行排序。
NetworkExecutor是網絡的執行者,從消息隊列中取出Request,請求完成之后將結果投遞給UI線程。
最后,由ResponseDelivery來封裝Response的投遞,保證Response執行在UI線程。
4.2.2 接口隔離原則(Interface Segregation Principle)
接口隔離原則是指:建立單一接口,不要建立龐大臃腫的接口,盡量細化接口,接口中的方法盡量少。
為各個類建立專用的接口,而不要試圖去建立一個很龐大的接口供所有依賴它的類去調用。 在程序設計中,依賴幾個專用的接口要比依賴一個綜合的接口更靈活。接口是設計時對外部設定的“契約”,通過分散定義多個接口,可以預防外來變更的擴散,提高系統的靈活性和可維護性。
我們在處理廣告時,對外只暴露AdManager類。廣告展示類(AdDisplay)被AdManager所依賴,不對外開放。對于不同的廣告,可以通過setAdStrategy()方法來設置不同的廣告策略,進行廣告展示。廣告策略(AdStrategy)也是一個單獨的接口。
4.2.3 迪米特法則(Law of Demeter)
迪米特法則又叫最少知道原則,一個類對自己依賴的類知道的越少越好。對于被依賴的類來說,無論邏輯多么復雜,都盡量地的將邏輯封裝在類的內部,對外除了提供的public方法,不對外泄漏任何信息。
所以,sdk對外暴露出去的方法需要嚴格控制,只給開發者足夠使用的API,無關的方法是不會開放出來。
4.2.4 常用的設計模式
除了上述原則之外,sdk還采用了多種設計模式,工廠和單例模式就不必說了,例如:
魔窗位或者廣告位的點擊,需要傳遞的參數眾多,為了避免混淆這些參數采用了Builder模式。
sdk內部處理多種類型的廣告時,對不同的廣告需要使用不同的策略,因此采用了策略模式。
還有空對象模式,因為sdk內部的代碼也存在著鏈式調用,如果鏈式調用出現了空指針那絕對是災難,必然會導致App Crash。可以參考之前寫的文章《為了程序的健壯性,我們可以使用空對象模式》
總之,在設計sdk時,盡量會采用符合高內聚、低耦合以及開閉的原則。
4.3 懂得取舍
處理取與舍是一個哲學的問題,能夠不斷地舍棄原先的代碼的人,才能寫出更好的代碼。有人會說,那不就是重構嗎?經常我們所舍棄的代碼并不是不好,而不是最合適的解決方案。
曾經有一段時間我特別喜歡RxJava的風格,甚至考慮在sdk4.0中引入Rx的寫法。在去年,我寫了幾個簡單的操作符比如map、flatMap、forEach等來模擬RxJava的寫法,并引入到sdk中使用,后來我理解了Java 8的lambda表達式以后,立刻明白完全沒必要自己在sdk中寫這些東西,果斷刪除相關的代碼。
五. 測試
5.1 靜態代碼分析工具
sdk每一次發布之前,都需要先使用靜態代碼分析工具查找代碼的缺陷。靜態代碼工具還能給出提示讓開發者糾正不正確的寫法。
在android平臺上我們使用的工具有findbugs、pmd、checkstyle、facebook infer。
在iOS平臺上我們使用Xcode自帶的靜態分析工具Analyze 和 facebook infer。
糾正完這些工具所提示的缺陷,才會交給測試進入下一輪的測試階段。
5.2 內存泄漏分析
說實話,自從有客戶給我們報sdk的內存泄漏bug之后,我們就特別重視這一塊的問題。一方面,使用專業的工具來進行測試。目前在android平臺使用的工具是LeakCanary,在iOS平臺還是使用Analyze。另一方面,多做code review,基于經驗來查找可能存在潛在的內存泄漏的地方。所以,sdk對每次開放出去的callback接口都會非常謹慎。
未來,在android版本的sdk中會考慮采用類似glide的方式,內部的Request可以隨Activity或Fragment的onStart而resume,onStop而pause,onDestroy而clear,從而節約流量和內存,并且防止內存泄露。
5.3 后臺收集sdk的bug
sdk遇到最大的困難可能不是來自功能上的,而是一個bug在我們這兒無法重現,但是在客戶的手機上卻能100%地穩定重現。
我曾經讓平安wifi的研發同學寄出一臺能夠重現sdk bug的Android手機給到我們,我們debug并修復完之后,再寄還給他們。
除了這些,也經常會遇到一些奇奇怪怪無法想象的bug,比如之前《記錄兩個神奇的android bug》。
雖然,sdk本身能夠上報bug到后臺,但是最初僅限于客戶能夠看到自己的app crash相關信息。作為sdk的開發者,我們也無法看到這些信息。后來,終于有了一個單獨的系統能夠專門過濾出屬于魔窗sdk bug的信息,供sdk開發人員進行查詢。每次發版前,我們都會先修復上一個版本存在的bug,然后交給測試。
六. 總結
本文是對將近兩年來移動端sdk開發的小結,此過程可謂是踩坑無數,但是sdk的開發還要繼續,未來也遠遠不止于移動端的原生廣告,sdk還會提供更多的優質內容給到開發者。