如何設(shè)計(jì)一款穩(wěn)定、好用、安全的推送SDK?
一款穩(wěn)定、易用、安全、小巧靈活的推送SDK是怎么樣的?本文將從“小”、“穩(wěn)”、“好用”以及“安全”四個(gè)角度來(lái)具體闡述。
對(duì)于非技術(shù)出身的產(chǎn)品經(jīng)理來(lái)說(shuō),如果突然接到一個(gè)要“設(shè)計(jì)SDK”的活兒,其實(shí)并不容易。畢竟,SDK是主要面向開(kāi)發(fā)者的,更像一個(gè)toD產(chǎn)品。那么,產(chǎn)品經(jīng)理在設(shè)計(jì)SDK時(shí),需要注意哪些點(diǎn)呢?換句話(huà)說(shuō),一款好的SDK應(yīng)該具備哪些特性?本文將從“小”、“穩(wěn)”、“好用”以及“安全”四個(gè)角度來(lái)具體闡述。
1. 小
1.1 65535限制
我們以一款好的推送SDK為例,那么首要需考慮到SDK包體的小巧靈活性。
為什么選擇更小體積的包體?
對(duì)于商務(wù)人員來(lái)說(shuō),包體體積小,他們更容易接受。對(duì)于技術(shù)人員來(lái)說(shuō),他們?cè)陂_(kāi)發(fā)產(chǎn)品時(shí),普遍追求“代碼少、功能全”,這是來(lái)自程序員的代碼潔癖。
那么從代碼層面來(lái)看,是因?yàn)橄到y(tǒng)有“65535限制”。
如上圖(左)所示,程序最終會(huì)生成dex文件,dex文件主要由以下幾部分組成:header(標(biāo)頭)、一連串的ids(標(biāo)識(shí)符列表)、data(數(shù)據(jù)區(qū))以及l(fā)ink_data(靜態(tài)鏈接文件中使用的數(shù)據(jù))。
細(xì)看上圖(右),它包含了一個(gè)method_ids_size字段,該字段的主要作用是定義個(gè)數(shù)。根據(jù)谷歌的定義,uint是一個(gè)16位的short類(lèi)型,最長(zhǎng)長(zhǎng)度是65535。如果將dex工程反編譯,會(huì)生成很多smali的文件,再去看smali里的函數(shù)調(diào)用(比如invoke direct {***} 函數(shù)名@BBB),會(huì)發(fā)現(xiàn)調(diào)用的地址其實(shí)就是剛才unit里定義的偏移量計(jì)算得出的。因此,這個(gè)函數(shù)地址最多也只能有65535個(gè)。
1.2 如何使包體體積變小
如何減小包體的體積,建議從以下幾個(gè)方面考慮:
(1)自研,不嵌套
在設(shè)計(jì)研發(fā)SDK時(shí),不建議在SDK內(nèi)嵌套一系列框架,例如三方網(wǎng)絡(luò)框架、db框架或任務(wù)調(diào)度框架等。我們主張選擇最核心的一部分進(jìn)行自主研發(fā)。
(2)代碼優(yōu)化
從算法層面,在效果相同的情況下,可適當(dāng)減少代碼的行數(shù);對(duì)于有默認(rèn)賦值的變量不需要進(jìn)行初始化賦值;選擇合適的字符串拼接方式,建議使用StringBuilder方法拼接字符串,可以解決字符串頻繁修改帶來(lái)的內(nèi)存消耗,也有利于減少包體體積大小。
(3)追求實(shí)用,放棄完美
SDK包體應(yīng)當(dāng)追求實(shí)用性,以完善主功能為主,其他相對(duì)次要的部分可以適當(dāng)減少時(shí)間或精力投入,放棄完美主義思維。
(4)代碼混淆
借助代碼混淆實(shí)現(xiàn)更小體積的包體,且不易被逆向。
1.3 省電省流量
省電省流量是“小”的另一個(gè)方面。SDK如果沒(méi)有對(duì)流量和電量有嚴(yán)格的限制,否則會(huì)出現(xiàn)手機(jī)發(fā)燙、高耗電提醒、流量浪費(fèi)、內(nèi)置SDK APP難以上架等問(wèn)題。
針對(duì)上述問(wèn)題,我們可以設(shè)置通過(guò)Lock殺手,智能心跳、自定義協(xié)議、鏈路合并、按需活躍等方式盡可能地降低SDK對(duì)電量以及流量造成的消耗。
(1)Lock殺手:代碼中WiFiLock、WakeLock等會(huì)強(qiáng)制喚醒APP,導(dǎo)致APP產(chǎn)生較大耗電量。在不影響功能的前提下,我們應(yīng)盡量減少或者不用該類(lèi)鎖。
(2)智能心跳:應(yīng)根據(jù)不同的運(yùn)營(yíng)商、網(wǎng)絡(luò)狀態(tài)等,選擇不同的心跳策略,并且根據(jù)不同的應(yīng)用場(chǎng)景探索心跳的最大邊界,盡量延長(zhǎng)心跳周期,減少電量和網(wǎng)絡(luò)的消耗。
(3)自定義協(xié)議:市場(chǎng)上常用的json、xml、甚至PB協(xié)議,都有比較好的兼容擴(kuò)展性,但同樣也帶來(lái)了空間浪費(fèi)的問(wèn)題,自定義協(xié)議可以充分利用空間,精確利用每一個(gè)byte甚至bit,極簡(jiǎn)化封裝,承載最大的信息量,減少流量和電量浪費(fèi)。
(4)鏈路合并:當(dāng)一個(gè)設(shè)備有多個(gè)APP的推送鏈路同時(shí)活躍時(shí),我們會(huì)運(yùn)用合并鏈路技術(shù),將使用同一款SDK的 APP 之間的長(zhǎng)連接鏈路進(jìn)行合并,減少流量電量的浪費(fèi)。
2. 穩(wěn)
2.1 提升穩(wěn)定性
在設(shè)計(jì)SDK時(shí),還要考慮到SDK的使用穩(wěn)定性。否則在實(shí)際應(yīng)用中,可能會(huì)遇到ANR、OOM、Crash、內(nèi)存泄露、閃退等棘手的問(wèn)題,我們需要通過(guò)持續(xù)的迭代和優(yōu)化來(lái)將錯(cuò)誤最小化。
(1)做好代碼管理
除了借助SVN、GIT等工具做好代碼托管外,還需遵循一定的代碼規(guī)范,借助類(lèi)似gerrit等工具進(jìn)行代碼review,使用verify流程。在保證機(jī)器找不到問(wèn)題的前提下,再用人眼去辨別是否符合業(yè)務(wù)邏輯。
(2)自動(dòng)化測(cè)試
自動(dòng)化測(cè)試可以大幅提升回歸測(cè)試的效率,非常適合敏捷的開(kāi)發(fā)過(guò)程。此外,自動(dòng)化測(cè)試可以替代大量的手工機(jī)械重復(fù)性操作,測(cè)試工程師可以把更多的時(shí)間花在更全面的用例設(shè)計(jì)和新功能測(cè)試上。
(3)運(yùn)用代碼模塊化等小技巧
代碼模塊化能以最少的模塊、零部件,更快速地滿(mǎn)足更多的個(gè)性化需求。異常處理可以提高系統(tǒng)的容錯(cuò)性,讓程序更加穩(wěn)定。代碼檢測(cè)能及時(shí)發(fā)現(xiàn)程序中的缺陷和錯(cuò)誤,比如檢測(cè)內(nèi)存是否泄露,是否有安全漏洞等,保證代碼質(zhì)量。
(4)線(xiàn)上灰度
實(shí)際場(chǎng)景中,我們很難去覆蓋所有的環(huán)境,例如機(jī)型、網(wǎng)絡(luò)等,需要通過(guò)線(xiàn)上用戶(hù)的反饋去驗(yàn)證代碼的健壯性。因此在產(chǎn)品大規(guī)模推向用戶(hù)之前,我們需要進(jìn)行少量的真實(shí)用戶(hù)測(cè)試,即灰度上線(xiàn)來(lái)幫助減少風(fēng)險(xiǎn)。
(5)日志系統(tǒng)
系統(tǒng)有問(wèn)題是必然的,在盡量保持系統(tǒng)穩(wěn)定的前提下,要考慮容錯(cuò)性。當(dāng)問(wèn)題發(fā)生時(shí),需要第一時(shí)間以最快的速度排查,因此需要有一套完整的日志系統(tǒng)。此外,平時(shí)我們也可以通過(guò)日志系統(tǒng)的撥測(cè)檢測(cè)系統(tǒng)的健壯性,可以在用戶(hù)反饋之前及時(shí)發(fā)現(xiàn)并解決問(wèn)題。
2.2 兼容性
兼容性也是保證SDK穩(wěn)定性的一個(gè)重要條件,主要考慮以下幾個(gè)方面:???
(1)接口兼容
每次版本更新后,對(duì)外接口要盡可能保持不變。對(duì)于改動(dòng)較大的接口,可以使用 @Deprecated 注解對(duì)老接口進(jìn)行標(biāo)記,并且做新接口調(diào)用的兼容,而不是直接刪除老接口。
(2)主鍵兼容
當(dāng)主鍵發(fā)生變更(例如去掉service、provider)時(shí),部分老的安卓系統(tǒng)會(huì)有組件緩存,運(yùn)行時(shí)直接告知“類(lèi)”找不到。建議在AndroidManifest中保留聲明,且對(duì)應(yīng)“類(lèi)”進(jìn)行代碼空實(shí)現(xiàn),以減少發(fā)生crash的概率。
(3)安卓系統(tǒng)兼容
可使用Build.VERSION.SDK_INT做API區(qū)分。
(4)真機(jī)兼容
可以借助云測(cè)等平臺(tái)進(jìn)行兼容性測(cè)試。
3. 好用
SDK的易用性可以從下面幾個(gè)方面考慮:
(1)接入簡(jiǎn)單
接入SDK時(shí)有集成demo直接可以運(yùn)行,且接入文檔清晰、步驟簡(jiǎn)單,最好能實(shí)現(xiàn)一鍵集成。
(2)保持核心優(yōu)勢(shì)
一款好的消息推送SDK,我們主要考慮及時(shí)性、到達(dá)率、穩(wěn)定性和準(zhǔn)確性。
例如:新聞媒體類(lèi)APP對(duì)推送的及時(shí)性要求較高;通知類(lèi)推送(如轉(zhuǎn)賬信息)會(huì)特別注重消息的到達(dá)率;穩(wěn)定性指的是要保證推送SDK在不同環(huán)境下的正常運(yùn)行,尤其是11.11等高并發(fā)場(chǎng)景;準(zhǔn)確性主要針對(duì)廣告營(yíng)銷(xiāo)類(lèi)推送,需要在合適的時(shí)間、合適的地點(diǎn)和合適的場(chǎng)景把合適的內(nèi)容推送給合適的人。其中,關(guān)于如何保證穩(wěn)定性,可以從多線(xiàn)路、多IDC、熱備份等角度考慮。
①多線(xiàn)路調(diào)整:例如預(yù)埋三線(xiàn)域名,做一些輪詢(xún)策略,防止域名被劫持。
②多IDC設(shè)置:除了域名被劫持外,還可能遇到網(wǎng)絡(luò)攻擊,物理性損壞的情況。設(shè)置多IDC一方面是為了實(shí)現(xiàn)分流,另一方面也降低了風(fēng)險(xiǎn)。
③熱備份:系統(tǒng)處于正常運(yùn)轉(zhuǎn)狀態(tài)下的備份,一旦系統(tǒng)出現(xiàn)問(wèn)題,可以快速恢復(fù)。
(3)多樣化需求
通過(guò)豐富的畫(huà)像標(biāo)簽,對(duì)用戶(hù)進(jìn)行場(chǎng)景化的智能推送,滿(mǎn)足用戶(hù)的多樣化需求。
(4)策略可控
我們還提供靜默時(shí)間、推送控量、短信補(bǔ)量、定時(shí)展示等附加功能,滿(mǎn)足客戶(hù)的實(shí)際使用場(chǎng)景。
4. 安全
SDK設(shè)計(jì)開(kāi)發(fā)過(guò)程中,我們還需要注意安全性。安全性不僅僅代表網(wǎng)絡(luò)數(shù)據(jù)交互的安全、本地?cái)?shù)據(jù)存儲(chǔ)的安全,也涉及到 SDK 的加固、混淆、第三方安全軟件審核等。
其中,我們重點(diǎn)講解SDK的加固。目前安卓平臺(tái)SDK絕大部分都是Java語(yǔ)言編寫(xiě),容易被反編譯。SDK如果只是進(jìn)行了簡(jiǎn)單的混淆,很容易被窺探到內(nèi)部實(shí)現(xiàn)細(xì)節(jié),此外還可能存在SDK被二次打包、植入惡意廣告等現(xiàn)象。因此,我們需要對(duì)SDK進(jìn)行加固,以提升安全性。
如上圖所示,SDK的加固主要是Java層面和so層面的操作。Java層面可以進(jìn)行SSR IR指令轉(zhuǎn)換或者做Java2C處理,把實(shí)現(xiàn)細(xì)節(jié)放入native中;so層面可以做一些扁平化、虛假控制等來(lái)混淆代碼,也可以通過(guò)指令替換、指令跳轉(zhuǎn)邏輯來(lái)增加逆向難度。此外,也可以通過(guò)常量字符串加密加固SDK,這是目前較為簡(jiǎn)單實(shí)用的一種方式。
現(xiàn)在看來(lái),要設(shè)計(jì)開(kāi)發(fā)一款好的SDK最難的還是如何讓自己設(shè)計(jì)的SDK在復(fù)雜的環(huán)境下穩(wěn)定運(yùn)行,這需要我們對(duì) SDK 的架構(gòu)有比較清晰的認(rèn)知,并不斷迭代和優(yōu)化。
那么以上提到的四點(diǎn),也是各位產(chǎn)品經(jīng)理和開(kāi)發(fā)者需要注意的,希望對(duì)大家有所幫助。
本文由 @個(gè)推 原創(chuàng)發(fā)布于人人都是產(chǎn)品經(jīng)理,未經(jīng)許可,禁止轉(zhuǎn)載。
題圖來(lái)自?Unsplash,基于 CC0 協(xié)議
- 目前還沒(méi)評(píng)論,等你發(fā)揮!