從最初的移動端運營活動到深度鏈接(deep link)丽猬,再到現(xiàn)在的移動端原生廣告,魔窗sdk經(jīng)歷了多個版本的迭代之后婚瓜,功能逐步完善宝鼓,開始步入4.x版本的時代。
去年五月份的時候巴刻,我寫過一篇文章《移動端SDK的優(yōu)化之路》愚铡,現(xiàn)在回過頭再來看看發(fā)現(xiàn)過去一年多的時間里我們又做了很多事情,所以有了新的一篇文章胡陪。
一. deep link
1.1 支持deep link和deferred deep link
早在2015年下半年就開始做deep link的功能沥寥,當時的版本已經(jīng)支持了deep link以及deferred deep link(場景還原)的功能。
所謂deep link柠座,是解決各個App之間信息孤島的問題邑雅,實現(xiàn)App之間能夠像網(wǎng)頁一樣能夠自由跳轉(zhuǎn)。
deferred deep link 是指用戶打開一個h5頁面的時候并沒有安裝對應(yīng)的 app妈经,在安裝 app 以后可以直接通過 deep link 到 app 對應(yīng)的內(nèi)容淮野。
從后臺的數(shù)據(jù)分析顯示,大多數(shù)的客戶對我們deferred deep link(場景還原)更感興趣吹泡。為此骤星,我們也一直在努力提高場景還原的匹配度。
1.2 為了能從微信朋友圈回流到App爆哑,Android版本使用應(yīng)用寶跳轉(zhuǎn)
iOS能夠借助Universal Link從微信朋友圈跳轉(zhuǎn)到App的具體頁面洞难,Android就沒有這么幸運了,雖然谷歌早就提出了App Links但是國內(nèi)很多手機并不支持揭朝,我們借助應(yīng)用寶的鏈接來跳轉(zhuǎn)到App的具體頁面队贱。
應(yīng)用寶跳轉(zhuǎn)原理跟 deferred deep link 是一樣的,并不會100%的準確匹配潭袱,但絕大多數(shù)情況是可以成功跳轉(zhuǎn)的柱嫌。
1.3 iOS10之后,第一時間優(yōu)化WebView的跳轉(zhuǎn)
iOS 10之后屯换,用戶在WebView中使用uri scheme做應(yīng)用間的跳轉(zhuǎn)時慎式,必須把目標App的uri scheme加到Info.plist中。
對于那些在 WebView 中使用魔窗的短鏈接客戶而言趟径,如果僅僅是做應(yīng)用內(nèi)的跳轉(zhuǎn),那是不需要把自己的Scheme放到Info.plist就可以直接調(diào)用癣防。魔窗的短鏈會自動匹配操作系統(tǒng)版本和瀏覽器信息蜗巧,在支持Universal Link的瀏覽器中自動使用Universal Link,如果不支持Universal Link的瀏覽器則用Scheme進行跳轉(zhuǎn)蕾盯。
1.4 去年11月初幕屹,sdk跟服務(wù)端通信的接口全面使用https
2016年的WWDC規(guī)定在2017.1.1之后iOS App必須全面支持https協(xié)議。我們在2016年10月的版本開始做支持https協(xié)議的功能,android 和 iOS兩個平臺的sdk都支持了https望拖。趕在了11月初上線渺尘,給開發(fā)者留足時間,讓他們替換新版本上架说敏。
二. 原生廣告
我們的原生廣告是基于魔窗位的鸥跟,魔窗位可以埋在App的任意位置包括開機畫面、Banner位盔沫、任意文字或圖片的地方等等医咨。
在新版本中,我們還新增了信息流廣告架诞。
三. 信息流廣告
什么是信息流廣告拟淮?不了解信息流廣告的童鞋可以看我之前的文章《對信息流廣告以及未來移動端廣告的簡單思考》
我們的sdk支持原生的信息流廣告。提供原生的控件給到開發(fā)者谴忧,屏蔽了其中的技術(shù)細節(jié)很泊,方便開發(fā)者直接使用到項目中(或者feed流中)。
原生控件能夠給用戶帶來更好的體驗沾谓,無縫地插入到App Native的頁面中委造。除了原生控件之外,還支持將信息流廣告的metadata返回給開發(fā)者搏屑,供開發(fā)者自行渲染争涌。
信息流展示的策略,可以在后臺進行配置辣恋。
四. sdk的設(shè)計原則和架構(gòu)
4.1 模塊化設(shè)計
從最初的所有代碼都在一個主工程亮垫,到現(xiàn)在拆分成多個module,結(jié)構(gòu)更加清晰伟骨。
在下一個版本中饮潦,android 和 iOS 都會考慮將原生控件的功能拆分成一個單獨的sdk。
4.2 面向?qū)ο蟮脑O(shè)計原則
在設(shè)計sdk時携狭,我們一定會遵循面向?qū)ο蟮姆▌t继蜡。
4.2.1 單一職責(zé)原則(Single responsibility principle)
單一職責(zé)原則是指:對一個類而言,應(yīng)該僅有一個引起它變化的原因逛腿。簡單來說稀并,一個類中應(yīng)該是一組相關(guān)性很高的函數(shù)、數(shù)據(jù)的封裝单默。
sdk的網(wǎng)絡(luò)框架并沒有使用android 碘举、iOS流行的okhttp、retrofit搁廓、AFNetworking等引颈。因為需要考慮到sdk包大小的問題耕皮,我們使用對應(yīng)操作系統(tǒng)底層的API來實現(xiàn)。因此蝙场,在android和iOS我們都做了一套簡化的框架凌停,大致流程是這樣的:
我們遵循了單一職責(zé)原則,它主要由四個部分組成:Request售滤、RequestQueue罚拟、NetworkExecutor和ResponseDelivery,每一個部分只負責(zé)自己的工作趴泌。
Request是各種請求類型舟舒。
RequestQueue是消息隊列,維護了提交給網(wǎng)絡(luò)框架的請求列表嗜憔,并且根據(jù)相應(yīng)的規(guī)則進行排序秃励。
NetworkExecutor是網(wǎng)絡(luò)的執(zhí)行者,從消息隊列中取出Request吉捶,請求完成之后將結(jié)果投遞給UI線程夺鲜。
最后,由ResponseDelivery來封裝Response的投遞呐舔,保證Response執(zhí)行在UI線程币励。
4.2.2 接口隔離原則(Interface Segregation Principle)
接口隔離原則是指:建立單一接口,不要建立龐大臃腫的接口珊拼,盡量細化接口食呻,接口中的方法盡量少。
為各個類建立專用的接口澎现,而不要試圖去建立一個很龐大的接口供所有依賴它的類去調(diào)用仅胞。 在程序設(shè)計中,依賴幾個專用的接口要比依賴一個綜合的接口更靈活剑辫。接口是設(shè)計時對外部設(shè)定的“契約”干旧,通過分散定義多個接口,可以預(yù)防外來變更的擴散妹蔽,提高系統(tǒng)的靈活性和可維護性椎眯。
我們在處理廣告時,對外只暴露AdManager類胳岂。廣告展示類(AdDisplay)被AdManager所依賴编整,不對外開放。對于不同的廣告乳丰,可以通過setAdStrategy()方法來設(shè)置不同的廣告策略闹击,進行廣告展示。廣告策略(AdStrategy)也是一個單獨的接口成艘。
4.2.3 迪米特法則(Law of Demeter)
迪米特法則又叫最少知道原則赏半,一個類對自己依賴的類知道的越少越好。對于被依賴的類來說淆两,無論邏輯多么復(fù)雜断箫,都盡量地的將邏輯封裝在類的內(nèi)部,對外除了提供的public方法秋冰,不對外泄漏任何信息仲义。
所以,sdk對外暴露出去的方法需要嚴格控制剑勾,只給開發(fā)者足夠使用的API埃撵,無關(guān)的方法是不會開放出來。
4.2.4 常用的設(shè)計模式
除了上述原則之外虽另,sdk還采用了多種設(shè)計模式暂刘,工廠和單例模式就不必說了,例如:
魔窗位或者廣告位的點擊捂刺,需要傳遞的參數(shù)眾多谣拣,為了避免混淆這些參數(shù)采用了Builder模式。
sdk內(nèi)部處理多種類型的廣告時族展,對不同的廣告需要使用不同的策略森缠,因此采用了策略模式。
還有空對象模式仪缸,因為sdk內(nèi)部的代碼也存在著鏈式調(diào)用贵涵,如果鏈式調(diào)用出現(xiàn)了空指針那絕對是災(zāi)難,必然會導(dǎo)致App Crash恰画”雒可以參考之前寫的文章《為了程序的健壯性,我們可以使用空對象模式》
總之锣尉,在設(shè)計sdk時刻炒,盡量會采用符合高內(nèi)聚、低耦合以及開閉的原則自沧。
4.3 懂得取舍
處理取與舍是一個哲學(xué)的問題坟奥,能夠不斷地舍棄原先的代碼的人,才能寫出更好的代碼拇厢。有人會說爱谁,那不就是重構(gòu)嗎?經(jīng)常我們所舍棄的代碼并不是不好孝偎,而不是最合適的解決方案访敌。
曾經(jīng)有一段時間我特別喜歡RxJava的風(fēng)格,甚至考慮在sdk4.0中引入Rx的寫法衣盾。在去年寺旺,我寫了幾個簡單的操作符比如map爷抓、flatMap、forEach等來模擬RxJava的寫法阻塑,并引入到sdk中使用蓝撇,后來我理解了Java 8的lambda表達式以后,立刻明白完全沒必要自己在sdk中寫這些東西陈莽,果斷刪除相關(guān)的代碼渤昌。
五. 測試
5.1 靜態(tài)代碼分析工具
sdk每一次發(fā)布之前,都需要先使用靜態(tài)代碼分析工具查找代碼的缺陷走搁。靜態(tài)代碼工具還能給出提示讓開發(fā)者糾正不正確的寫法独柑。
在android平臺上我們使用的工具有findbugs、pmd私植、checkstyle忌栅、facebook infer。
在iOS平臺上我們使用Xcode自帶的靜態(tài)分析工具Analyze 和 facebook infer兵琳。
糾正完這些工具所提示的缺陷狂秘,才會交給測試進入下一輪的測試階段。
5.2 內(nèi)存泄漏分析
說實話躯肌,自從有客戶給我們報sdk的內(nèi)存泄漏bug之后者春,我們就特別重視這一塊的問題。一方面清女,使用專業(yè)的工具來進行測試钱烟。目前在android平臺使用的工具是LeakCanary,在iOS平臺還是使用Analyze嫡丙。另一方面拴袭,多做code review,基于經(jīng)驗來查找可能存在潛在的內(nèi)存泄漏的地方曙博。所以拥刻,sdk對每次開放出去的callback接口都會非常謹慎。
未來父泳,在android版本的sdk中會考慮采用類似glide的方式般哼,內(nèi)部的Request可以隨Activity或Fragment的onStart而resume,onStop而pause惠窄,onDestroy而clear蒸眠,從而節(jié)約流量和內(nèi)存,并且防止內(nèi)存泄露杆融。
5.3 后臺收集sdk的bug
sdk遇到最大的困難可能不是來自功能上的楞卡,而是一個bug在我們這兒無法重現(xiàn),但是在客戶的手機上卻能100%地穩(wěn)定重現(xiàn)。
我曾經(jīng)讓平安wifi的研發(fā)同學(xué)寄出一臺能夠重現(xiàn)sdk bug的Android手機給到我們蒋腮,我們debug并修復(fù)完之后淘捡,再寄還給他們。
除了這些池摧,也經(jīng)常會遇到一些奇奇怪怪無法想象的bug案淋,比如之前《記錄兩個神奇的android bug》。
雖然险绘,sdk本身能夠上報bug到后臺,但是最初僅限于客戶能夠看到自己的app crash相關(guān)信息誉碴。作為sdk的開發(fā)者宦棺,我們也無法看到這些信息。后來黔帕,終于有了一個單獨的系統(tǒng)能夠?qū)iT過濾出屬于魔窗sdk bug的信息代咸,供sdk開發(fā)人員進行查詢。每次發(fā)版前成黄,我們都會先修復(fù)上一個版本存在的bug呐芥,然后交給測試。
六. 總結(jié)
本文是對將近兩年來移動端sdk開發(fā)的小結(jié)奋岁,此過程可謂是踩坑無數(shù)思瘟,但是sdk的開發(fā)還要繼續(xù),未來也遠遠不止于移動端的原生廣告闻伶,sdk還會提供更多的優(yōu)質(zhì)內(nèi)容給到開發(fā)者滨攻。