網易 Android工程模板化實踐
背景
我們網易前端技術部 - 移動技術組作為公司的移動端基礎技術部門,主要為其他部門提供解決方案、技術支持和產品孵化。在幾年的積累過程中,我們擁有一些自己的框架和 SDK,如輕應用框架、熱更新 SDK、網絡請求庫、本地存儲庫、頁面管理等,服務過網易新聞、云音樂、考拉、易信等億級產品,先后孵化過青果攝像頭、二次元Gacha、嚴選等重要產品。
在多年的Android開發中,對于 Android 端產品開發,我們有如下幾點體會:
-
產品孵化排期緊張
產品經理一般關心的是具體的業務邏輯,而前期基礎模塊的搭建,如各模塊如何組織,使用代碼結構如何選擇,圖片、網絡、本地存儲等選用哪個 sdk 等,一般不會有專門排期。
-
基礎模塊的需求具有相似性
內容型產品,其搭建的基礎模塊基本上都會包含圖片顯示、網絡請求、本地存儲、通信等。
-
基礎模塊的選型和工具類具有可重用性
網上相關的第三方庫有很多,當然一般的公司也是會有自己開發或者維護的各個基礎 SDK。很多時候,SDK 選型會更偏向于自己公司開發維護的 SDK,或者選擇自己最熟悉,或最主流、最可靠的 SDK。因此當開發多個相同類型產品時,這里的技術選型是可重用的。
-
網絡請求的代碼具有機械性
客戶端開發需要根據網絡接口協議,編寫相關的 GET、POST 等請求代碼和對應的 JavaBean,這部分的代碼編寫其實是非常機械的。
網易工程模板是什么?
對于各個基礎模塊,我們團隊封裝了自己的 SDK,如網絡庫、本地存儲庫、頁面管理庫、圖片庫等。使用我們的工程模板生成的初始工程,就已經包含了我們提供的基礎模塊,產品團隊的開發不需要再花費重復的時間做技術調研、選型、SDK封裝集成等工作,而只需要關心自己的業務邏輯編寫。我們期望產品團隊只需 1 分鐘就能得到自己的初始工程,并能馬上投入業務邏輯開發,既能縮短開發周期,也能保證工程代碼質量。
此外,我們也提供了 Android Studio 插件 (NEIPlugin),集成插件后,就能在 Android Studio 中通過菜單點擊自動下載集成我們的工程模板,也能自動生成網絡請求相關的代碼。
(點擊放大圖像)

代碼生成結果示例
Android 模板工程實現
最初我們使用終端腳本命令的方式,通過文件拷貝和文本查找替換(主要是替換包名等)的方式實現。但終歸對 Android 開發人員不太友好,畢竟大家更習慣使用 Android Studio 生成工程。所幸,強大的 Android Studio 已經提供了較為全面的模板功能,這里大概可以分為以下幾類:
- 工程模板 (本文內容)
- 文件模板
- 注釋模板
- 編碼模板(Living Template)
Android 工程模板基礎知識
工程模板實例介紹
對于 Android Studio,模板位置:
Windows 的路徑在 `${android studio 安裝路徑}/plugins/android/lib/templates/`
MacOS 的路徑在 `${Android Studio.app 存放路徑}/Contents/plugins/android/lib/templates/`
有關模板的文件夾:
- activities:工程模板相關,如 EmptyActivity 文件夾用于創建一個空頁面的模板,GoogleMapsActivity 文件夾對應創建一個地圖頁面的模板等
- gradle:放置了 gradle 模板,用于在新建工程的根目錄下生成 gradle 文件夾,支持用戶不用安裝 gradle 就能使用 gradlew 命令
- gradle-project:工程模板相關,用于構建 module,Android Project,Java Library 等
- other:構建文件模板等
這里我們關心的是 activities 文件夾里面的內容
首先查看下 EmtpyActivity (空白頁面模板) 里面的內容
-
globals.xml.ftl: 全局變量文件,保存一些全局變量,當中可以引用其他文件的全局變量
-
recipe.xml.ftl: 配置要引用的模板路徑以及文件的生成規則
-
template.xml: 模板的配置信息,包括模板的顯示圖標,界面的表現,全局變量文件和執行文件的指定等
(點擊放大圖像)

Android Studio 使用的是 FreeMarker 模板引擎,所以文件后綴都是 .ftl
常用標簽使用
-
${}: FreeMarker 的語法,如 ${packageName}, ${superClass} 是 globals.xml.ftl 全局變量文件或template.xml.ftl 中定義變量引用
-
<#if></#if>: FreeMarker 的語法,條件判斷語句
-
<#include>: FreeMarker 的語法,包含語句
-
copy: 將文件或者文件夾從 from 標簽拷貝到 to 標簽指定的路徑
-
instantiate: 將文件或者文件夾,執行 FreeMarker 語法,從 from 標簽實例化到 to 標簽指定的路徑
-
merge: 合并 from 和 to 標簽分別指定的文件
-
open: 在工程打開后,默認打開指定的文件
實例:使用空白頁面模板生成工程并打開后,可以看到默認打開了 MainActivity.java 和 activity_main.xml 文件
工程模板創建
新建 HTTemplate 文件夾內容如下:
-
template.xml
指定模板名、描述、最低支持 sdk 版本、類別等,輸入界面要求指定包名和 Application 類名
-
globals.xml.ftl
引用公共文件內容
-
recipe.xml.ftl
-
merge AndroidManifest.xml 文件
-
copy 或者 merge 資源文件
-
copy 或 instantiate java 代碼
-
merge build.gradle 文件
-
merge settings.gradle 文件
-
copy lib 文件夾里面的全部內容
-
copy module 工程
-
copy proguard-rules.pro 文件
-
-
root 文件夾
放置相關模板源文件,其中將源工程中依賴于配置的代碼,按照 FreeMarker 語法進行替換
-
添加工程模板圖標,并在 template.xml 中添加引用
(點擊放大圖像)

settings.gradle 合并
查看 RecipeMergeUtils.mergeGradleSettingsFile 方法,基本邏輯如下:
-
讀取目標文件的每一行內容,并判斷每行內容的開頭是否是 include 開頭
-
是:在 include 后面插入內容
-
否:拋出異常
-
-
返回合并的內容
</ul>
-
讀取文件 source 和 dest 文件的內容,并轉化得到 GroovyFile 類型對象
-
執行 mergePsi 方法
-
提供 ht-template 支持生成我們的模板工程
-
提供 Android Studio 插件 (NEIPlugin)
-
支持 ht-template 的下載安裝
-
nei-toolkit 和 Node.js 的下載安裝
-
nei-toolkit 和 Node.js 的使用,生成網絡請求代碼
-
-
無法在 settings.gradle 指定 module 路徑
-
無法合并 proguard-rules.pro 文件,暫時生成 proguard-rules.pro.template 文件
-
由于 build.gradle 對 apply 命令合并會出錯和無法合并 dependencies 中的 apt 命令,所以無法在 build.gradle 中集成 ht-universalrouter
- 支持根據 NEI 平臺 定制生成項目初始結構及代碼
- 支持 NEJ 發布工具 配置文件自動生成
- 支持 Fiddler 和 Charles 工具代理本地模擬數據,接口配置文件導出
- 支持自動生成移動端數據模型、請求類代碼
- 支持自動導出模擬數據
- 集成了本地模擬容器
build.gradle 合并
查看 GradleFileMerger.mergeGradleFiles 方法,里面會調用 mergePsi 方法,其基本邏輯如下:
這里 mergePsi 執行合并的邏輯是
(點擊放大圖像)

小結和后續工作
到此,基本上完成了我們原先期望實現的工程模板和網絡請求代碼自動生成的工作:
這里還是有一些因為 Android 工程模板自身的限制而無法完成的內容點:
再次,除了網絡請求代碼編寫是機械性的,其他的基于我們的工程模板生成的初始工程,也存在一定的代碼編寫機械性:初始頁面代碼生成、RecycleView 中的各個 ViewHolder 類、本地數據讀取保存等,而這些工作將會是我們的后續工作。
標題
Q:本次的分享是不是需要有idea的插件化知識背景?
A:idea 插件開發的內容,可以查看官方文檔 http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/setting_up_environment.html ,里面有比較詳細的介紹。
如果需要自己學習插件開發的話,就需要學習官方文檔,不過我的分享中并沒有講述插件開發中的一些細節,應該不會有影響。
這次大家如果覺得聽起來有點難,我想應該是這次分享需要有工程模板開發的背景,不然確實會有點難。
Q:請問neitoolkit是做什么的?
A:neitoolkit 在移動端,是一個配合我們的 NEI 接口管理平臺( http://nei.hz.netease.com/ ),用來生成網絡請求相關代碼的一個工具,當然可以查看 README 介紹
Q:請問上文的runtime怎么理解呀?
A:這里的 Runtime,其實就是執行了 java 中的 Runtime.getRuntime().exec(command); 方法。
這個方法的作用就是執行 sh (windows中cmd) 中的腳本命令。
Q:如果模版中有需要apt處理的代碼,模版是不支持的是么?
A:恩,工程模板是不支持的,dependencies 中 非 compile 命令全部都是不支持的,這個可以從前面的源碼分析中看出來。
來自:http://www.infoq.com/cn/articles/practice-of-netease-android-project-template