DevOps 是一種廣為人知的活動乞旦,其主要目的是使軟件交付自動化贼穆。的確题山,DevOps 的目標(biāo)是持續(xù)測試、代碼質(zhì)量故痊、功能開發(fā)和更輕松地進(jìn)行維護(hù)更新顶瞳。因此,DevOps 的終極目標(biāo)之一是讓開發(fā)者可以執(zhí)行快速可靠愕秫、自動化的發(fā)布慨菱,理想狀態(tài)下,整個流程都不需要人為操作戴甩。這被稱為持續(xù)交付符喝。撰寫本文的目的是展示我們現(xiàn)在也能在安卓上實(shí)現(xiàn)這一目標(biāo),同時分享筆者的想法和反饋意見甜孤。
以持續(xù)集成為起點(diǎn)
為了實(shí)現(xiàn)持續(xù)交付协饲,必須確保強(qiáng)大的持續(xù)集成。這已經(jīng)在安卓環(huán)境實(shí)行一段時間了缴川,但是為了清楚起見茉稠,咱們還是回顧一下吧。
首先把夸,任何安卓應(yīng)用程序都應(yīng)具備持續(xù)集成而线。是的,筆者就是這個意思恋日。實(shí)際上膀篮,這為現(xiàn)代應(yīng)用程序開發(fā)帶來了不可忽視的幾個好處。在筆者看來岂膳,最大的優(yōu)點(diǎn)是以下幾項(xiàng):
構(gòu)建自動化:告別“但是在我的機(jī)器上可以構(gòu)建成功”誓竿。應(yīng)用程序在哪里都能構(gòu)建。
盡早試錯:每次推送后都進(jìn)行構(gòu)建可以確保盡早發(fā)現(xiàn)錯誤闷营。
測試持續(xù)化:確保測試始終進(jìn)行
持續(xù)打包:在打包二進(jìn)制代碼時避免人為錯誤烤黍。
發(fā)布速度更快:因?yàn)槲覀儗γ恳徊綐?gòu)建都有信心知市,發(fā)布也變得更加簡單。
信心增強(qiáng):終于速蕊,我們可以信任自己的代碼和流程嫂丙,并且減少意料之外的錯誤。
典型的持續(xù)集成過程
First, we need an integration server like Jenkins or Travis. The following jobs are my standard configuration:
首先规哲,我們需要一個 Jenkins 或者 Travis 那樣的集成服務(wù)器跟啤。以下作業(yè)是筆者的標(biāo)準(zhǔn)配置:
? 一旦源碼存儲庫(Git、SVN等等)中的推送完成唉锌,就會開始一個作業(yè)隅肥。它會查看 dev 分支、編譯代碼袄简、運(yùn)行單元測試腥放,并打包調(diào)試 APK。
? 第一個作業(yè)成功完成后绿语,運(yùn)行下一個作業(yè)秃症。它會運(yùn)行集成測試(通過Espresso 或 Robotium),通過重現(xiàn)場景和檢查圖像內(nèi)容來確保用戶體驗(yàn)吕粹。你可以使用連接設(shè)備(如果你的 CI 服務(wù)器不容易獲取的話种柑,就有些困難)、Genymotion 或隨 Android Studio 2.0 發(fā)布的最新內(nèi)置模擬器(快去試試吧Fジ)來操作聚请。
? 一個作業(yè)會運(yùn)行代碼測量(舉個例子,通過 Sonarqube)以監(jiān)測代碼質(zhì)量稳其。例如驶赏,筆者會在每天半夜運(yùn)行這個作業(yè)。
? 最后欢际,在我們推送 master 分支或發(fā)布分支后運(yùn)行一個作業(yè)母市。它會編譯代碼,并生成發(fā)布 APK损趋。
好啦患久!如你所見,操作非常簡單浑槽,而且能保證筆者之前列舉的所有優(yōu)點(diǎn)蒋失。
測試是關(guān)鍵
筆者曾寫過一篇關(guān)于安卓測試的文章。測試非常重要桐玻,因?yàn)樗悄茏詣幼C明我們的應(yīng)用按計(jì)劃運(yùn)行的唯一方法篙挽。有很多工具可以幫我們寫出優(yōu)秀的測試代碼,所以要明智地進(jìn)行選擇镊靴。
同時铣卡,在選擇集成到應(yīng)用中的函數(shù)庫時要實(shí)事求是链韭。實(shí)際上,你應(yīng)該明白如果函數(shù)庫的測試覆蓋率較高煮落,測試應(yīng)用程序就會更輕松敞峭。他們已經(jīng)考慮到如何正確測試和通過測試促進(jìn)開發(fā)的問題了(IMO、OkHttp 和 Retrofit 都是很好的范例)蝉仇。相當(dāng)于你在使用的同時就進(jìn)行了測試旋讹。
最后,類似 Dagger 的函數(shù)庫可以增加可測試性轿衔。實(shí)際上沉迹,它會迫使你遵照單一職責(zé)原則,并且將代碼正確分離害驹,這樣測試就更加容易鞭呕。
一旦你擁有了強(qiáng)大的持續(xù)集成,我們來看看怎么升級吧裙秋。
持續(xù)交付:無限升級
舉個例子琅拌,在 Captain Train缨伊,我們每6周發(fā)布一個新版本摘刑,并且對此非常謹(jǐn)慎。目前:
? We have a beta phase.
? We support 4 locales.
? We support 3 types of device (phone, 7 and 9 inches tablets).
? We always check our Play Store listings.
? We create release notes.
? We use the rollout feature.
? We upload our 72 screenshots (6 screenshots * 4 locales * 3 types)
? We have a wear companion.
- 我們有一個測試階段刻坊。
- 我們支持4 種語言環(huán)境枷恕。
- 我們支持3種設(shè)備(手機(jī)、7寸和9寸平板電腦)谭胚。
- 我們時刻關(guān)注在谷歌電子市場的排名徐块。
- 我們創(chuàng)建發(fā)布說明。
- 我們采用發(fā)布功能灾而。
- 我們上傳72張截圖(6種截圖X4中語言環(huán)境X3中設(shè)備類型)胡控。
- 我們有一個穿戴伴侶。
這個流程需要花時間旁趟,而且不止一點(diǎn)兒昼激。最近,我們決定是時候嘗試把這個流程自動化了锡搜。雖然主要目的是為了減少發(fā)布版本需要的時間橙困,但是如果同時也能避免人為錯誤,保持各個發(fā)布版本之間的連貫性耕餐,就更好了凡傅。這也讓我們擔(dān)負(fù)了重大的責(zé)任,因?yàn)殚_發(fā)者控制了整個發(fā)布流程肠缔。實(shí)際上夏跷,市場和傳播團(tuán)隊(duì)也要跟我們一起看看怎么將他們的變化集成到發(fā)布版本中去哼转。
但是坦白說……在安卓,開發(fā)者并不能控制所有的事情槽华。谷歌可以释簿。但是,它提供了 HTTP API硼莽,使開發(fā)者輕易地與谷歌電子商店控制臺互動萧朝。他們還提供了各種開發(fā)語言的客戶端,例如 Java(這是必須的:帜怼)痪宰、Phython、Ruby匆光,等等……
在本文中套像,筆者將主要討論 Java 客戶端,因?yàn)檫@很可能是安卓開發(fā)者最了解的開發(fā)語言终息。
編寫你自己的 publisher
讓我們看看如何編寫定制化的谷歌電子市場 publisher夺巩。分為兩步:首先,我們要配置控制臺周崭,讓客戶端能夠操作柳譬,然后探索 API。通常跟谷歌建立連接時最困難的就是配置……相關(guān)文檔可以在這里找到续镇。請注意美澳,文檔也許不是最新版的。
配置
首先摸航,如果沒有現(xiàn)成的制跟,你需要在谷歌控制臺新建一個項(xiàng)目。接下來酱虎,我們需要啟用 Google Play Android Developer API
雨膨。
Once it is done, we must create a credential of type Service account key:
完成之后,我們必須創(chuàng)建一個 Service account key
類型的證書:
最后读串,填寫小表聊记,下載 JSON 格式的證書文件。你需要保存三個值:private_key_id
爹土、private_key
和 client_email
甥雕。把 private_key
的值保存到它自己的 secret.pem
文件。
開發(fā)者控制臺搞定了……現(xiàn)在我們來看第二個控制臺吧胀茵!
連接到你的電子市場控制臺社露。你需要依次訪問 Settings
> API access
:
接下來,你只需要連接你的項(xiàng)目琼娘。最后在 Service accounts
峭弟,授權(quán)你在 JSON 文件的 client_email
處填寫的郵箱地址附鸽。
現(xiàn)在好了。一切搞定瞒瘸!
探索 API
現(xiàn)在讓我們通過 Java 客戶端訪問接口坷备。我們在 publisher 上面創(chuàng)建一個單獨(dú)的 Java 項(xiàng)目,并且添加以下依賴項(xiàng)(在 maven central 有):
compile 'com.google.apis:google-api-services-androidpublisher:
v2-rev20-1.21.0'
下一步是新建一個 AndroidPublisher
情臭。首先省撑,我們通過以下信息來實(shí)例化 GoogleCredential
:一個客戶端連接,一個 JSON factory俯在,一個與 private_key_id
對應(yīng)的私有關(guān)鍵 ID竟秫,一個與 client_email
對應(yīng)的賬戶 ID,ANDROIDPUBLISHER
范圍跷乐,以及與 private_key
對應(yīng)的肥败、包含私有關(guān)鍵信息的重要文件。是的愕提,所有這些都需要馒稍。
然后,我們用應(yīng)用程序包創(chuàng)建一個 AndroidPublisher
例子浅侨。
http = GoogleNetHttpTransport.newTrustedTransport();
json = JacksonFactory.getDefaultInstance();
Set<String> scopes =
Collections.singleton(AndroidPublisherScopes.ANDROIDPUBLISHER);
GoogleCredential credential = new GoogleCredential.Builder().
setTransport(http).
setJsonFactory(json).
setServiceAccountPrivateKeyId(KEY_ID).
setServiceAccountId(SERVICE_ACCOUNT_EMAIL).
setServiceAccountScopes(scopes).
setServiceAccountPrivateKeyFromPemFile(secretFile).
build();
publisher = new AndroidPublisher.Builder(http, json, credential).
setApplicationName(PACKAGE).
build();
AndroidPublisher 是谷歌 API 的主要入口纽谒。它有一個 edits 方法,可以讓我們修改想從控制臺獲取的數(shù)據(jù)仗颈。
要開始一個新版本佛舱,你必須用 insert 請求來開始,然后保存它的 id挨决,接下來每次調(diào)用都會使用該 id。
AndroidPublisher.Edits edits = publisher.edits();
AppEdit edit = edits.insert(PACKAGE, null).execute();
String id = edit.getId();
現(xiàn)在订歪,我們可以開始修改控制臺數(shù)據(jù)了脖祈。舉個例子,如果你想修改排名:
Listings listings = edits.listings();
Listing listing = new Listing().
setFullDescription(description).
setShortDescription(shortDescription).
setTitle(title);
listings.update(PACKAGE, id, "en_US", listing).execute();
You can also upload screenshots:
也可以上傳截圖:
Images images = edits.images();
FileContent content = new FileContent(PNG_MIME_TYPE, file);
images.upload(PACKAGE, id, "en_US", "phone5", content).execute();
最后一個例子刷晋,上傳一個 APK:
// APK upload
Apks apks = edits.apks();
FileContent apkContent = new FileContent(APK_MIME_TYPE, apkFile);
Apk apk = apks.upload(PACKAGE, id, apkContent).execute();
int version = apk.getVersionCode();
// Assign APK to Track
Tracks tracks = edits.tracks();
List<Integer> versions = Collections.singletonList(version)
Track track = new Track().setVersionCodes(versions);
tracks.update(PACKAGE, id, "production", track).execute();
// Update APK listing
Apklistings apklistings = edits.apklistings();
ApkListing whatsnew = new ApkListing().setRecentChanges(changes);
apklistings.update(PACKAGE, id, version, "en_US", whatsnew).execute();
筆者鼓勵你多多探索這個接口盖高。它既強(qiáng)大,又不復(fù)雜眼虱。
最后一步喻奥,你必須提交自己的版本。實(shí)際上捏悬,谷歌會記錄你的每一個更改請求撞蚕,但是只有在你提交版本之后,這些更改才會被保存过牙。筆者同時建議你在提交之前先驗(yàn)證這些更改甥厦。
edits.validate(PACKAGE, id).execute();
edits.commit(PACKAGE, id).execute();
如你所見纺铭,你在代碼開頭找回的 id 可以被當(dāng)做事務(wù) id。通過調(diào)用 insert,update/upload
你的更改以開始事務(wù)刀疙,validate
以及最終 commit
舶赔。非常簡單。
結(jié)論
現(xiàn)在谦秧,安卓應(yīng)用程序也可以呼應(yīng) DevOps 活動竟纳,擁有強(qiáng)有力的持續(xù)交付了。在 Captain Train疚鲤,我們選擇編寫自己的 publisher 工具來確保自己控制每個步驟和重要步驟的字節(jié)蚁袭。一旦發(fā)布任務(wù)成功完成,我們就把它當(dāng)做一個腳本來運(yùn)行石咬。不過也有 Jenkins 插件或 Gradle 插件可以幫你完成這些工作揩悄。只是要注意了解幕后的運(yùn)行情況。畢竟你在處理生產(chǎn)鬼悠。必須小心謹(jǐn)慎删性。
這樣的工具和流程讓你只需推送 master 分支即可在生產(chǎn)環(huán)境中發(fā)布新版本。既簡單焕窝、快捷蹬挺,又可靠、節(jié)約時間它掂。
顯然巴帮,在一個應(yīng)用程序中可行的方法不一定適用于所有的團(tuán)隊(duì)、公司和應(yīng)用程序虐秋。還要依靠你的團(tuán)隊(duì)以及團(tuán)隊(duì)與市場傳播團(tuán)隊(duì)的關(guān)系榕茧。不過,在此筆者想要總結(jié)的是客给,持續(xù)交付應(yīng)該成為每個開發(fā)團(tuán)隊(duì)的目標(biāo)用押,往這個目標(biāo)邁進(jìn)的每一步都是一種勝利。
OneAPM Mobile Insight 以真實(shí)用戶體驗(yàn)為度量標(biāo)準(zhǔn)進(jìn)行 Crash 分析靶剑,監(jiān)控網(wǎng)絡(luò)請求及網(wǎng)絡(luò)錯誤蜻拨,提升用戶留存。訪問 OneAPM 官方網(wǎng)站感受更多應(yīng)用性能優(yōu)化體驗(yàn)桩引,想閱讀更多技術(shù)文章缎讼,請?jiān)L問 OneAPM 官方技術(shù)博客。
本文轉(zhuǎn)自 OneAPM 官方博客