2017/12/1

使用遠端的 AVD 進行測試

測試上遇到的困擾

測試一直是軟體開發中重要的一環,在「軟體測試經驗分享」中,提到了一些測試相關的概念。Android 的開發裡,AVD 則是測試不可或缺的一部份。透過 AVD 可以不用準備大量的實體設備,就可以讓所開發的 App 在不同的環境中檢視其運作的情況。

使用 Notebook 進行 Android 開發的人,通常都會遇到一個困擾是:Notebook 的磁碟空間大多不是很充裕。在開發 Android App 時,常常要使用多個 AVD,來測試 App 在不同的環境下都可以正常地運作,畢竟 Android 的平台破碎化已經是一種特色了。

然而在空間有限的情況下,建一個 AVD 就要上 Giga 的空間,這還不包含不同版本的 Image 檔。每一個版本還有分 32bit、64 bit,有 Google API、沒有 Google API,再加上 TV 和 Wear 這二種設備種類。林林總總的 Image 加起來,配上 AVD 可能要產生出不同畫面尺寸的組合,對於有限的磁碟空間來說更是捉襟見肘。

如果要擴充 Notebook 的儲存空間,除了價格上的因素以外,往往還會受限於 Notebook 硬體架構上的侷限性。就這個缺點來說,PC 相對是一個比較好的選項。但是改為使用 PC 開發,就會失去了原本 Notebook 俱備的機動性。想要二者優點兼俱,替代性的選項是在二台機器間進行 Source Code 同步,需要機動性就在 Notebook 上開發,需要空間時就轉移到 PC 上開發。

這個選項對大型公司而言,是一個再直接不過的選項,因為公司內通常都會有一定程度的版本控管系統在協助,檔案移轉的負擔比較小,甚至更豪華的有專用 Lab 環境。反過來說,不在這樣體制內的人,只能苦命地手動將 Source Code 反覆傳來傳去,費工又容易出錯。

對於這樣的族群來說,需要將選項的內容再進化,也就是開發仍在 Notebook 上保持機動性,同時選擇關鍵的環境來建立 AVD,做基本的確認。而 PC 上則是可以廣泛地建立不同選項的 AVD,要進行測試時就將二個環境結合起來,將 Notebook 建置好的結果傳送到 PC 上執行測試。

所以現在的問題是,這樣的要求做得到嗎?要怎麼做?

可行的方案

首先,先來了解負責管理受測裝置的工具 - ADB 的運作原理,這個工具包含了三個部份 Client、Daemon、Server:
  • Client: 負責接收我們輸入的指令,並且傳送給所連接的 Server,一般就是我們在 Command-line 使用的 adb 指令。
  • Daemon: 駐紮在行動裝置上,可以是實體設備或是 AVD,負責在裝置上執行由 Server 傳來的指令。
  • Server: 在電腦上執行,負責管理 Client 與 Daemon 之間的訊息傳遞。


從上圖來看,可行的方案有三個:第一個是透過 Server 轉送訊息給 Daemon,第二個是讓 Client 連接到遠端的 Server,第三個方案則是讓 Server 連到遠端的 Daemon。

第一個方案是使用 adb forward 的指令,讓 Daemon 可以收到所處 PC 向 127.0.0.1 發出的訊息,並轉給 AVD 指定的 Port。這個動作最直接的效果,是讓所在的 PC 可以不用透過 Console,直接與 AVD 中的 Service 進行溝通。但似乎與跨機器無關,所以先略過。

第二個方案要達成不難,在 adb 的指令中有參數分別是 -H-P 可以指定 Server 的 Address 及 Port。例如以下的指令可以列出 192.168.1.1 電腦上,ADB Server 所管理的裝置清單。

adb devices -H 192.168.1.1 -P 5038

但有一個很關鍵的問題是,如何設定讓 Android Studio 使用遠端的 ADB Server 來進行部署及測試?IDE 和 Gradle 我都沒有找到答案,也許是這樣的選項還不存在吧!會有這個結果,最主要的問題應該是卡在如何讓遠端的 ADB Server 拿到建置好的結果,以傳送進 AVD 內。

不過,倒是可以使用以下示範的指令來啟動遠端ADB Server 的測試,只不過連部署的工作都要在建置完成後,用 adb 自己動手。

adb shell am instrument -w <test_package_name>/<runner_class> -H 192.168.1.1 -P 5038

方案三,也是可以用adb 的指令來達成,以下的指令可以讓 ABD Server 連接到 192.168.1.1 電腦上的 AVD。

adb connect 192.168.1.1:3333

連上之後,就可以在 IDE 的「Select Deployment Target」的視窗中,看到遠端的 AVD 被列在「Connected Devices」清單中。接下來的動作就跟過往一樣,選擇遠端 AVD 的項目後,按下【OK】,等建置完成就可以看到 App 出現在遠端 AVD 的畫面裡。

這個方案比第二個更具優勢的是,可以直接在 IDE 或是 Gradle 中就完成部署及測試,維持原本的開發經驗一氣呵成,不用再另外做設定。

需注意的技術細節

在方案二或方案三的指令中,如果直接進行連線,應該會遇到問題、無法完成連線。主要的原因可以看一下圖示:


ADB Server 聆聽的 Address 是 127.0.0.1,而外部傳入的 Port 是在真實的 IP 上,也就是說訊息並沒有傳到 ADB Server 所在的 Port 上。為了要解決這樣的情況就必須要把真實 IP 接到的訊息 Forward 到 127.0.0.1 所在的 Port。

以 Windows 為例,內建有 netsh 指令來進行這項工作。以下的指令就是把真實 IP 在 5038 上收到的訊息,轉送到 ADB 預設的 5037 Port 上:

netsh interface port proxy add v4tov4 listenport=5038 connectaddress=127.0.0.1 connectport=5037

這裡有個小細節要注意,在設定 ADB Server 的聆聽 Port 轉送時,不能使用同一個 5037,會導致 ADB Server 無法啟動。

同時,AVD 的 Port 是成對配置的,ADB 管理的範圍是由 5554 到 5585,每二個號碼分配給一個 AVD,偶數的號碼是給 Console 連線用,奇數才是保留給 ADB。第一個 AVD 分配到的會是 5554,而 ADB 使用的則為 5555。以下圖的 AVD 為例,顯示的是 5558,但要進行遠端連線的 Port 應該加一號為 5559。



AVD 的 Port 也不要佔用到 5554 到 5585 的範圍,否則 ADB 會認為 Port 被分配走,而跳過改取下一組。

這個設定是有持績性的,不會因為重新開機而消失。而執行這個指令需要有 Administrator 的權限,所以可以一次就設定好清單,畢竟範圍也就只有從 5555 到 5585。如此一來,不論 AVD 被分配到哪一個 Port 上,都可以被遠端連接上。

所能達到的效果

最後總結一下以這種方式進行開發的好處:
  • 節省開發環境的磁碟空間,並且可以更彈性地選擇測試設備的規格,不用被開發的機器所限制。
  • 有效利用閒置資源、增加工作效率。用 Notebook 開發的人,另外擁有 PC 是很常見的情況,只是很有可能在開發時是閒置的。而開發時最耗資源的都是測試,所以如果可以把耗資源的部份轉移到效能相對較好的 PC 上,可以有效地提高效率。
  • 減少空轉與等待的情況。延續上一點,耗資源的工作被轉出去,開發的機器就可以接續進行開發的工作,或是放鬆休閒一下,上上網、看個影片,等待測試完成。

額外的異常狀況

在嘗試以上解決方案的過程中,遇到了一些異常的情況,順便記錄一下解決的方法。

首先,用 Windows 做為遠端測試設備時,除了要設定 Port Forward 之外,還要確認 Firewall 是否有阻擋訊息的交換。如果 Firewall 是在開啟的狀態之下,有很大的機會訊息是無法傳遞的。此時,就要調整 Firewall 的設定,增加適合的允許規則。

在使用最新版的 Android SDK Tools 時,與先前的版本不同的是,不再提供像 Android Studio 所附的 GUI 工具,完全是由 Command-line 執行工作。不過,也還好,問題不大,就是打字多了點。

有問題的是 avdmanager 在參數被大幅減化之下,產生出來的 AVD 似乎與 Android Studio 建立出來的有差距。再來是 sdkmanager 可下載的 package 中,似乎也沒看到 AVD Skin 這個選項。結果用 avdmanagerdevice 參數做出來的 AVD,縮放都不太正常,畫面大到不合理,只能看到 AVD Desktop 的左上角。而用來啟動 AVD 的 emulator 看似可以調整這個問題的參數,在新版也早就被廢棄了。

為了這個問題,查了好久,試了很多參數,都沒有辦法解決。根據網路上的說法,似乎是個 Bug,而且有一段時間了、歷經了好幾個版本都沒有修正,真是好樣的,Google。

原本想要放棄,改為安裝 Android Studio 算了。只是突然靈機一閃,把其他機器產生好的 AVD 移過來,有缺 Skins 這個目錄的也要一併把 Skins 移到 SDK 下。調整一下 ini 裡路徑的設定,居然就可以正常地顯示了,目前看來堪用中。