# 前言
今天嘗試了下把 ShadowsockAndroid 工程自主編譯打包, 走了很多彎路, 試了好久才成功. 最后回過(guò)頭來(lái)看, 其實(shí)也很簡(jiǎn)單.
## 前提條件
* Android系統(tǒng)6.0+的手機(jī)(Android SDK >=23)
* 你的電腦已裝好 Git 環(huán)境
* 你的電腦已裝好 Rust 環(huán)境
## 步驟
### 檢查 Rust 環(huán)境
在命令行中運(yùn)行 `rustup --version` , 應(yīng)該能得到如下的提示
```bash
rustup --version
rustup 1.24.3 (ce5817a94 2021-05-31)
info: This is the version for the rustup toolchain manager, not the rustc compiler.
info: The currently active `rustc` version is `rustc 1.54.0-nightly (dbe459ded 2021-06-02)`
```
### 給 Rust 配置國(guó)內(nèi)鏡像
* 添加環(huán)境變量
```bash
# 中國(guó)科學(xué)技術(shù)大學(xué)
RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static
RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup
```
將以上內(nèi)容放到你的 `bash_profile` 中就可以了 (Windows 的話, 加到環(huán)境變量中就行)
* 修改 cargo 的配置文件
找到 `~/.cargo/config` 文件(如果沒(méi)有, 就自己創(chuàng)建一個(gè), 同理, 在 Windows 上就是相應(yīng)的位置, 個(gè)人目錄下的 `.cargo/config`), 加入以下內(nèi)容
```yml
[source.crates-io]
replace-with='ustc'
# 中國(guó)科學(xué)技術(shù)大學(xué)
[source.ustc]
registry="git://mirrors.ustc.edu.cn/crates.io-index"
```
### 更新 Rust
這一步應(yīng)該也不是必要, 根據(jù)實(shí)際情況, 自行判斷吧
命令行中運(yùn)行如下命令:
更新 rustup 自身
```bash
rustup self update
```
更新 rust
```bash
rustup update
```
### Clone ShadowSocks
克隆 ShadowSocks
```bash
git clone https://github.com/shadowsocks/shadowsocks-android.git
```
倉(cāng)庫(kù)的依賴(lài)別忘了更新
```bash
cd shadowsocks-android
git submodule update --init --recursive
```
### 添加 rust target
在 shadowsocks-rust 中添加target, 運(yùn)行以下兩個(gè)命令:
```bash
cd core/src/main/rust/shadowsocks-rust
```
```bash
rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android
```
### 編譯 ShadowSocks
用 Android Studio 打開(kāi)工程 shadowsocks-android , 連接手機(jī).
這里要注意一點(diǎn), shadowsocks-android 需要 JDK 11.
如果你的本機(jī)就是 JDK 11 , 那無(wú)事.
如果你的電腦是其它版本, 那么你可以直接替換(重新安裝)本機(jī) JDK 為 11.
同時(shí)還有一種方法, 在 Android Studio 中的工程設(shè)置中, 選擇 Android Studio 中內(nèi)置的 JDK 11.
直接開(kāi)始編譯吧.
理論上, 應(yīng)該可以成功了.
源最前線(ID:OpenSourceTop) 猿妹整編
轉(zhuǎn)載請(qǐng)注明來(lái)源作者
2月份GitHub上最熱門(mén)的Python開(kāi)源項(xiàng)目排行已經(jīng)出爐啦,一起來(lái)看看上榜詳情:
1
trax
https://github.com/google/trax Star 3584
Trax是一個(gè)開(kāi)源項(xiàng)目,它的目的在于幫助我們挖掘并理解高一階的深度學(xué)習(xí)模型。谷歌大腦表示,該項(xiàng)目希望Trax代碼做到非常整潔與直觀,并同時(shí)令Reformer這類(lèi)高階深度學(xué)習(xí)達(dá)到最好的效果。
2
fastapi
https://github.com/tiangolo/fastapi Star 4337
FastAPI是一個(gè)Rails庫(kù)用來(lái)快速查詢(xún)相互依存的數(shù)據(jù)集合并返回時(shí)候人類(lèi)閱讀的、標(biāo)準(zhǔn)的API輸出。通過(guò)構(gòu)造復(fù)雜的SQL查詢(xún)并高效使用JOIN和子查詢(xún)。
3
cheat.sh
https://github.com/chubin/cheat.sh Star 11311
cheat.sh是一個(gè)網(wǎng)站,更是一個(gè)實(shí)用的速查工具。cheat.sh具有以下這些功能特性:
4
人臉識(shí)別庫(kù) face_recognition
https://github.com/ageitgey/face_recognition Star 32887
基于python的開(kāi)源人臉識(shí)別庫(kù),該庫(kù)可以通過(guò)python或者命令行即可實(shí)現(xiàn)人臉識(shí)別的功能。使用dlib深度學(xué)習(xí)人臉識(shí)別技術(shù)構(gòu)建,在戶外臉部檢測(cè)數(shù)據(jù)庫(kù)基準(zhǔn)(Labeled Faces in the Wild)上的準(zhǔn)確率為99.38%。
5
interview_internal_reference
https://github.com/0voice/interview_internal_reference Star 24248
該項(xiàng)目是關(guān)于阿里,騰訊,百度,美團(tuán),頭條等技術(shù)面試題目,以及答案,專(zhuān)家出題人分析匯總,而且是2019年最新總結(jié)。
6
virtualenv
https://github.com/pypa/virtualenv Star 3442
VirtualEnv用于在一臺(tái)機(jī)器上創(chuàng)建多個(gè)獨(dú)立的Python運(yùn)行環(huán)境,VirtualEnvWrapper為前者提供了一些便利的命令行上的封裝。使用 VirtualEnv 的理由:
7
spinningup
https://github.com/openai/spinningup Star 4343
Spinning Up是大名鼎鼎的Open AI推出的免費(fèi)強(qiáng)化學(xué)習(xí)教學(xué)項(xiàng)目,旨在降低強(qiáng)化學(xué)習(xí)的門(mén)檻,讓初學(xué)者能在項(xiàng)目實(shí)踐的過(guò)程中一步一步循序漸進(jìn)地學(xué)習(xí)。項(xiàng)目不僅提供了詳細(xì)的理論介紹,同時(shí)還有完整的練習(xí)代碼,真的不不愧是學(xué)習(xí)深度強(qiáng)化學(xué)習(xí)的良心之作。
8
binwalk
https://github.com/ReFirmLabs/binwalk Star 5842
binwalk是一個(gè)文件的分析工具,旨在協(xié)助研究人員對(duì)文件進(jìn)行分析,提取及逆向工程。簡(jiǎn)單易用,完全自動(dòng)化腳本,并通過(guò)自定義簽名,提取規(guī)則和插件模塊,還重要一點(diǎn)的是可以輕松地?cái)U(kuò)展。
9
python-small-examples
https://github.com/jackzhenguo/python-small-examples Star 2685
Python基礎(chǔ),Python坑點(diǎn),Python字符串和正則,Python繪圖,Python日期和文件,Web開(kāi)發(fā),數(shù)據(jù)科學(xué),機(jī)器學(xué)習(xí),深度學(xué)習(xí),TensorFlow,Pytorch,一切都是簡(jiǎn)單易懂的小例子。
10
shadowsocks
https://github.com/shadowsocks/shadowsocks Star 32376
Shadowsocks是新一代加密通訊軟件服務(wù),通過(guò)Web形式傳輸加密信息,用戶可自行選擇加密方式以及請(qǐng)求。Shadowsocks包括服務(wù)器和客戶端兩部分組成,服務(wù)器端用于接收客戶請(qǐng)求,將消息解密并重新請(qǐng)求解密的網(wǎng)頁(yè)請(qǐng)求,獲取消息后再加密轉(zhuǎn)發(fā)給客戶端。
本文首發(fā)于InfoQ垂直公眾號(hào)『移動(dòng)開(kāi)發(fā)前線』。【ID:bornmobile】分享嘉賓:楊干榮,微信Android客戶端基礎(chǔ)平臺(tái)、性能優(yōu)化負(fù)責(zé)人
保活,按照我們的理解包含兩部分:
網(wǎng)絡(luò)連接保活:如何保證消息接收實(shí)時(shí)性。
進(jìn)程保活:盡量保證應(yīng)用的進(jìn)程不被Android系統(tǒng)回收。
1.0 網(wǎng)絡(luò)連接保活
網(wǎng)絡(luò)保活,業(yè)界主要手段有:
a. GCM
b. 公共的第三方push通道(信鴿等)
c. 自身跟服務(wù)器通過(guò)輪詢(xún),或者長(zhǎng)連接
國(guó)產(chǎn)機(jī)器大多缺乏GMS,在國(guó)內(nèi)GCM也不穩(wěn)定(心跳原因),第三方通道需要考慮安全問(wèn)題和承載能力,最后微信選擇使用自己的長(zhǎng)連接。而國(guó)外, GCM作為輔助,微信無(wú)法建立長(zhǎng)連接時(shí),才使用GCM。
之前看到大家在聊各種Java網(wǎng)絡(luò)框架,而微信實(shí)際上都是沒(méi)用上的。早年的微信,直接通過(guò)Java socket 實(shí)現(xiàn)。微信v5.0后,考慮各系統(tǒng)平臺(tái)的統(tǒng)一,開(kāi)始使用自研c++組件。
長(zhǎng)連接實(shí)現(xiàn)包括幾個(gè)要素:
a. 網(wǎng)絡(luò)切換或者初始化時(shí) server ip 的獲取。
b. 連接前的 ip篩選,出錯(cuò)后ip 的拋棄。
c. 維護(hù)長(zhǎng)連接的心跳。
d. 服務(wù)器通過(guò)長(zhǎng)連notify。
e. 選擇使用長(zhǎng)連通道的業(yè)務(wù)。
f. 斷開(kāi)后重連的策略。
今天主題在保活, 我們重點(diǎn)討論心跳和 notify 機(jī)制。
1.1 心跳機(jī)制
心跳的目的很簡(jiǎn)單:通過(guò)定期的數(shù)據(jù)包,對(duì)抗NAT超時(shí)。以下是部分地區(qū)網(wǎng)絡(luò)NAT 超時(shí)統(tǒng)計(jì):
上表說(shuō)明:
a. GCM無(wú)法適應(yīng)國(guó)內(nèi)2G環(huán)境(GCM 28分鐘心跳)。
b. 為了兼容國(guó)內(nèi)網(wǎng)絡(luò)要求,我們至少5分鐘心跳一次。
老版本的微信是4.5分鐘發(fā)送一次心跳,運(yùn)行良好。
心跳的實(shí)現(xiàn):
a. 連接后主動(dòng)到服務(wù)器Sync拉取一次數(shù)據(jù),確保連接過(guò)程的新消息。
b. 心跳周期的Alarm 喚醒后,一般有幾秒的cpu 時(shí)間,無(wú)需wakelock。
c. 心跳后的Alarm防止發(fā)送超時(shí),如服務(wù)器正常回包,該Alarm 取消。
d. 如果服務(wù)器回包,系統(tǒng)通過(guò)網(wǎng)絡(luò)喚醒,無(wú)需wakelock。
流程基于兩個(gè)系統(tǒng)特性:
a. Alarm喚醒后,足夠cpu時(shí)間發(fā)包。
b. 網(wǎng)絡(luò)回包可喚醒機(jī)器。
特別是b項(xiàng),假如Android封堵該特性,那就只能用GCM了。API level >=23的doze就關(guān)閉所有的網(wǎng)絡(luò), alarm等。但進(jìn)入doze條件苛刻,現(xiàn)在6.0普及低,至今微信沒(méi)收到相關(guān)投訴。另Google也最終加入REQUEST_IGNORE_BATTERY_OPTIMIZATIONS權(quán)限。
1.2 動(dòng)態(tài)心跳
4.5min心跳周期是穩(wěn)定可靠的,但無(wú)法確定是最大值。通過(guò)終端的嘗試,可以獲取到特定用戶網(wǎng)絡(luò)下,心跳的最大值。
引入該特性的背景:
a. 運(yùn)營(yíng)商的信令風(fēng)暴
b. 運(yùn)營(yíng)商網(wǎng)絡(luò)換代,NAT超時(shí)趨于增大
c. Alarm耗電,心跳耗流量。
動(dòng)態(tài)心跳引入下列狀態(tài):
a. 前臺(tái)活躍態(tài):亮屏,微信在前臺(tái), 周期minHeart (4.5min) ,保證體驗(yàn)。
b. 后臺(tái)活躍態(tài):微信在后臺(tái)10分鐘內(nèi),周期minHeart ,保證體驗(yàn)。
c. 自適應(yīng)計(jì)算態(tài):步增心跳,嘗試獲取最大心跳周期(sucHeart)。
d. 后臺(tái)穩(wěn)定態(tài):通過(guò)最大周期,保持穩(wěn)定心跳。
自適應(yīng)計(jì)算態(tài)流程:
在自適應(yīng)態(tài):
a. curHeart初始值為minHeart , 步增(heartStep)為1分鐘。
b. curHeart 失敗5次, 意味著整個(gè)自適應(yīng)態(tài)最多只有5分鐘無(wú)法接收消息。
c. 結(jié)束后,如果sucHeart > minHeart,會(huì)減去10s(避開(kāi)臨界),為該網(wǎng)絡(luò)下的穩(wěn)定周期。
d. 進(jìn)入穩(wěn)定態(tài)時(shí),要求連接連續(xù)三次成功minHeart心跳周期,再使用sucHeart。
穩(wěn)定態(tài)的退出:
sucHeart 會(huì)對(duì)應(yīng)網(wǎng)絡(luò)存儲(chǔ)下來(lái), 重啟后正常使用。考慮到網(wǎng)絡(luò)的不穩(wěn)定,如NAT超時(shí)變小,用戶地理位置變換。當(dāng)發(fā)現(xiàn)sucHeart 連續(xù)5次失敗, sucHeart 置為minHeart ,重新進(jìn)入自適應(yīng)態(tài)。
1.3 notify機(jī)制
網(wǎng)絡(luò)保活的意義在于消息實(shí)時(shí)。通過(guò)長(zhǎng)連接,微信有下列機(jī)制保證消息的實(shí)時(shí)。
Sync:
通過(guò)Sync CGI直接請(qǐng)求后臺(tái)數(shù)據(jù)。Sync 通過(guò)后臺(tái)和終端的seq值對(duì)比,判斷該下發(fā)哪些消息。終端正常處理消息后,seq更新為最新值。
Sync 的主要場(chǎng)景:
a. 長(zhǎng)連無(wú)法建立時(shí),通過(guò)Sync 定期輪詢(xún)
b. 微信切到前臺(tái)時(shí),觸發(fā)Sync(保命機(jī)制)
c. 長(zhǎng)連建立完成,立即觸發(fā)Sync,防止連接過(guò)程漏消息
d. 接收到Notify 或者 gcm 后,終端觸發(fā)Sync 接收消息.
Notify:
類(lèi)似于GCM。通過(guò)長(zhǎng)連接,后臺(tái)發(fā)出僅帶seq的小包,終端根據(jù)seq決定是否觸發(fā)Sync拉取消息。
NotifyData:
在長(zhǎng)連穩(wěn)定, Notify機(jī)制正常的情況下(保證seq的同步)。后臺(tái)直接推送消息內(nèi)容,節(jié)省1個(gè)RTT (Sync) 消息接收時(shí)間。終端收到內(nèi)容后,帶上seq回應(yīng)NotifyAck,確認(rèn)成功。這里會(huì)出現(xiàn)Notify和NotifyData狀態(tài)互相切換的情況:
如NotifyData 后,服務(wù)器在沒(méi)收到NotifyAck,而有新消息的情況下,會(huì)切換回到Notify,Sync可能需要冗余之前NotifyData的消息。終端要保證串行處理NotifyData和Sync ,否則seq可能回退。
GCM:
只要機(jī)器上有GMS ,啟動(dòng)時(shí)就嘗試注冊(cè)GCM,并通知后臺(tái)。服務(wù)器會(huì)根據(jù)終端是否保持長(zhǎng)連,決定是否由GCM通知。GCM主要針對(duì)國(guó)外比較復(fù)雜的網(wǎng)絡(luò)環(huán)境。
2.0 進(jìn)程保活
在Android系統(tǒng)里,進(jìn)程被殺的原因通常為以下幾個(gè)方面:
a. 應(yīng)用Crash
b. 系統(tǒng)回收內(nèi)存
c. 用戶觸發(fā)
d. 第三方root權(quán)限app.
原因a可以單獨(dú)作為一個(gè)課題研究。原因c、d目前在微信上沒(méi)有特殊處理。這里討論的就是如何應(yīng)對(duì)Android Low Memory Killer。
下面分享幾個(gè)微信保活的方法:
2.1 進(jìn)程拆分
上圖表述的是微信主要的幾個(gè)進(jìn)程:
a. push主要用于網(wǎng)絡(luò)交互,沒(méi)有UI
b. worker就是用戶看到的主要UI
c. tools主要包含gallery和webview
拆分網(wǎng)絡(luò)進(jìn)程,確實(shí)就是為了減少進(jìn)程回收帶來(lái)的網(wǎng)絡(luò)斷開(kāi)。
可以看到push的內(nèi)存要遠(yuǎn)遠(yuǎn)小于worker。而且push的工作性質(zhì)穩(wěn)定,內(nèi)存增長(zhǎng)會(huì)非常少。這樣就可以保證,盡量的減少push 被殺的可能。
這里有個(gè)思路,但限制比較多,也拋磚引玉。啟動(dòng)一個(gè)純C/C++ 的進(jìn)程,沒(méi)有Java run time ,內(nèi)存使用極低。
這種做法限制很明顯,如:沒(méi)有Java run time ,所以無(wú)法使用Android系統(tǒng)接口。缺乏權(quán)限,也無(wú)法使用各種shell命令操作(如am)。但可以考慮一下用途:高強(qiáng)度運(yùn)算,網(wǎng)絡(luò)連接,心跳維持等。比如Shadowsocks-android就如此,通過(guò)純c命令行進(jìn)程,維護(hù)著socks5代理 (Android M運(yùn)行正常)。
tools進(jìn)程的拆分也同樣是內(nèi)存的原因:
a. 老版本的webview 是有內(nèi)存泄漏的
b. Gallery大量縮略圖導(dǎo)致內(nèi)存使用大
微信在進(jìn)入后臺(tái)后,會(huì)主動(dòng)把tools進(jìn)程kill掉。
2.2 及時(shí)拉起
系統(tǒng)回收不可避免,及時(shí)重新拉起的手段主要依賴(lài)系統(tǒng)特性。從上圖看到, push有AlarmReceiver, ConnectReceiver,BootReceiver。這些receiver 都可以在push被殺后,重新拉起。特別AlarmReceiver ,結(jié)合心跳邏輯,微信被殺后,重新拉起最多一個(gè)心跳周期。
而對(duì)于worker,除了用戶UI操作啟動(dòng)。在接收消息,或者網(wǎng)絡(luò)切換等事件, push也會(huì)通過(guò)LocalBroadcast,重新拉起worker。這種拉起的worker ,大部分初始化已經(jīng)完成,也能大大提高用戶點(diǎn)擊微信的啟動(dòng)速度。
歷史原因,我們?cè)趐ush和worker通信使用Broadcast和AIDL。實(shí)際上,我一直不喜歡這里的實(shí)現(xiàn),AIDL代碼冗余多, broadcast效率低。歡迎大家分享更好的思路或者方法。
2.3 進(jìn)程優(yōu)先級(jí)
Low Memory Killer 決定是否殺進(jìn)程除了內(nèi)存大小,還有進(jìn)程優(yōu)先級(jí):
上表的數(shù)字可能在不同系統(tǒng)會(huì)有一定的出入,但明確的是,數(shù)值越小,優(yōu)先級(jí)越高。對(duì)于優(yōu)先級(jí)相同的進(jìn)程,總是會(huì)把內(nèi)存占用多的先kill。提高進(jìn)程優(yōu)先級(jí)是保活的最好手段。
正常情況下微信的oom_adj:
而被提高優(yōu)先級(jí)后:
從統(tǒng)計(jì)上報(bào)看,提高后的效果極佳。
原理:Android 的前臺(tái)service機(jī)制。但該機(jī)制的缺陷是通知欄保留了圖標(biāo)。
對(duì)于 API level < 18 :調(diào)用startForeground(ID, new Notification),發(fā)送空的Notification ,圖標(biāo)則不會(huì)顯示。
對(duì)于 API level >=18:在需要提優(yōu)先級(jí)的service A啟動(dòng)一個(gè)InnerService,兩個(gè)服務(wù)同時(shí)startForeground,且綁定同樣的 ID。Stop 掉InnerService ,這樣通知欄圖標(biāo)即被移除。
這方案實(shí)際利用了Android前臺(tái)service的漏洞。微信在評(píng)估了國(guó)內(nèi)不少app已經(jīng)使用后,才進(jìn)行了部署。其實(shí)目標(biāo)是讓大家站同一起跑線上,哪天google 把漏洞堵了,效果也是一樣的。