一套完整的 Electron 應用自動更新方案(上) —— 從需求到設計

在發佈一個桌面應用以前,必需要考慮的一個問題是:怎麼更新(迭代)?一個應用不可能沒有更新,除非沒人用的或沒人維護,從人肉更新到自動更新,再到複雜的更新方案,這裏有不少產品和技術的設計...git

這是 Electron 系列的第三篇文章,也是 Electron 應用自動更新方案的上篇「從需求到設計」,下篇會講「從設計到實現」。github

爲何要講從需求到設計?這不是產品的活麼?json

不要那麼仔細地去區分產品、開發,一些技術驅動的功能上,開發須要承擔產品的工做,自動更新就是這樣的一個功能。並且,前期的功能設計,直接影響了開發的實現方式,在充分理解前期設計的基礎上,開發才能作出合理的實現。後端

我設計和實現了 Electron 應用的一整套自動更新方案,而且已應用於產品上,因此寫下這篇文章和你們分享,能夠怎麼設計和實現一個 Electron 應用的自動更新,咱們對於自動更新需求的考慮可能比你想得稍微複雜一些。瀏覽器

本文將首先從需不須要更新開始談起,接着談怎麼從人肉更新一步步進化到無痛的自動更新。而後,我會向你介紹,一個完善後的自動更新需求(也是這 2 篇文章要實現的需求)能夠是怎樣的。最後,也是最關鍵的一部分 —— 咱們該採起怎樣的更新思路,又該如何設計咱們的產品邏輯。服務器

說明 :本文實現的自動更新方案只是衆多更新方案中的一種,供參考,你既能夠徹底參照着這麼作,也能夠從中獲取些許啓發,更進一步,若是你已經實現了自動更新,能夠比較本文和你所採用方案的異同優劣,歡迎留言交流網站

1、真的須要自動更新麼

首先咱們摳個字眼,「更新」包括但不等同「自動更新」,在考慮一個應用的自動更新前,要先理清楚你的應用特質和發行策略,這決定着要不要實現自動更新功能,以及「自動的程度」。命令行

1. 更新是一種追求,活着的應用都須要

更新,是爲了交付給用戶更好的產品,這是追求,活着的應用都是須要的,由於活着須要有一個盼頭,固然若是「產品活着」就是你的追求,當我沒說。設計

2. 應用的性質和發行策略決定了要不要自動更新

「更新」是必要的,而「自動更新」未必。你的 Office 套裝、Adobe 套裝(不使用 Creative Cloud 安裝的)的更新大可能是一次巨大的體驗升級,而它們並無如另外一些應用同樣時不時彈出一個通知,讓你更新。3d

這樣的應用,通常是一些複雜度高、可離線化運行、發行週期長、老牌的應用。而 Sketch 這樣的設計軟件,也是同性質的,但它倒是採用在線自動更新機制的,因此要不要自動更新,還和應用發行方的策略相關。

3. 自動更新是現代桌面應用的一個基本需求

首先,軟件的迭代方式廣泛有了改變,現代軟件的開發方式強調「快速迭代,不斷交付」,這是由於在現代的技術背景下,能夠以極小的成本作到快速調整和迭代,像 Electron 應用的迭代甚至能夠作到小成本,跨平臺的快速迭代,這也就意味着能更快地交付用戶更好的產品。而要作到這一點的技術保障就是 —— 自動更新。

再者,自動更新是用戶的需求,也是發行方的需求。對用戶而言,能方便地在第一時間獲取到更好的應用;對於發行方而言,能快速地推進版本升級,及早得到產品反饋,獲取更多的用戶。

2、從人肉更新到自動更新

「自動更新」有多自動?這是一個好問題,自動更新能夠實現「全自動」,可是全自動未必是好的,就像要作一杯完美的意式咖啡,使用全自動咖啡機的結果是差強人意的,只有半自動咖啡機加一位咖啡師才能完成。

1. 最牛逼的更新方式

最牛逼的更新方式是開一個發佈會,或者發一個新版軟件的通告,而後就有大把大把的用戶去競相下載新版本應用,還會奔走相告,引得更多人去下載和更新,這是最牛逼的更新方式,也是牛逼的人肉更新方式,好比 Adobe 系列產品。

而若是你的產品沒有這樣的煽動性,你也打算讓用戶去人肉更新,讓用戶每天關注你的下載頁或郵件,指望着在發佈了新版以後,用戶們都能很自覺地下載和更新,那麼到頭來你或許會很失望,並且你會面對「版本分佈散亂」的問題,這個問題又會帶出一系列其餘的問題。

因此,不要期待着用戶會時刻關注你的產品更新,讓用戶主動去發現新版本(尤爲是在應用外的地方,好比網站)而後人肉更新,這對於大部分產品來講都是一個夢想。

2. 假自動更新:應用內通知,應用外下載,手動安裝

那麼稍稍地進一步,稍稍地自動化一點,不用用戶主動去發現新版本,而是在應用內告訴用戶,這相比於上面的人肉更新,已經進步一大截了。

這種更新方式下,一旦發行方有新版的應用發佈,用戶 會在應用內收到新版可用的更新消息推送 ,而後可能須要你點開一個連接,這時會用瀏覽器打開下載頁,你須要手動地下載新版應用,完了後手動地安裝。

但注意,這只是實現一個通知功能,雖然有很大進步,但並無到能夠稱爲「自動更新」的程度,因此我稱之爲「假自動更新」。

3. 自動更新:應用內通知、下載後,喚起安裝過程

天然地再進一步,咱們會指望在應用內能夠通知用戶,而且應用自己還能幫助咱們下載新版應用安裝包,下載後,應用還能幫助咱們喚起安裝過程(和你的應用具體的安裝方式有強關係)。

這樣的更新方式下,咱們纔算是自動更新,應用自己能夠通知、下載和喚起安裝程序,這一套邏輯是應用自己具有的。在用戶角度來看,每次有更新就會收到通知,只要輕輕點一下「當即更新」,應用就會下載讀條,下載完了後就能夠自動安裝,沒有多餘操做。

4. 無痛的自動更新:下次打開即新版

人啊,每每會得寸進尺,我能不能在用戶都沒有感知的狀況下,就「偷偷」地更新個人應用呢?答案是能夠的。

一次更新無非是,檢查更新、通知用戶、下載文件、安裝,若是你把這些步驟都藏在後臺,那麼用戶天然是無感知的了。有了更新不通知,直接下載好,至於安裝,覆蓋的安裝必然須要退出應用,那麼就在用戶退出應用的時候喚起安裝,而且使安裝過程不可見,那麼就實現了,等用戶下次打開的時候就是新版了。

不過不告知用戶,徹底透明地去更新,這種作法通常不推薦,最好仍是通知一下用戶,可是能夠提供用戶選擇「退出應用後更新」,咱們下面會講到的更新方案裏就有這一項。

3、一個完善的自動更新需求

在這一節,我將描述一個完整的自動更新需求,咱們目前在使用的更新方案就是基於此創建的。

1. 自動更新的基本需求

無論每一個應用的自動更新有多少的細節差異,有一些基本的需求(也能夠說環節)是必需要實現的。

  • 檢查更新 :檢查是否有新版本可用,何時檢查,有哪些方式觸發檢查,能夠自定
  • 通知用戶 :當有更新可用的時候須要通知用戶,能夠檢查到有更新就通知,也能夠在更新文件下載好了後再通知,前者用戶能夠見到下載過程,後者用戶感覺不到下載過程
  • 下載更新 :應用有能力下載更新,而且保證下載的完整性,能夠後臺下載,也能夠前臺下載
  • 安裝更新 :應用能喚起下載好的新版應用安裝程序,而後自身退出,至於何時喚起安裝,能夠根據須要作設計

2. 區分強弱更新

若是實現了以上的基本需求,自動更新已經完整了,可是上面每一步發生的時機、和用戶的交互、視覺等方面均可以作一些設計,歸納地說,就是自動更新這個需求能夠再拆分。

2.1 爲何要劃分出強弱更新方式

咱們把自動更新劃分紅了強弱兩種更新方式,背後的出發點是,咱們對某次更新的定性。

若是某次更新只是功能特性的改進、小功能的上線、普通 BUG 的修復,總之是一次讓你「 情緒保持穩定 」的更新,那麼這被定性爲一次弱更新,咱們對於此次更新的期待是,但願用戶更新,可是不強制。

若是某次更新包含了殺手級功能的上線、重大 BUG 的修復、大的改版,總之是一次讓你「 沒法保持平靜 」的更新,那麼這被定性爲一次強更新,咱們對於強更新的期待是,強烈推薦用戶更新,具備強制性。

咱們把「弱更新」稱爲「自動更新」,對應英文「Update」,把「強更新」稱爲「自動升級」,對應英文「Upgrade」。

2.2 弱更新和強升級的區別在哪

  • 通知形式不一樣
    • 弱更新以輕量的形式進行提醒,下載後提醒,不打斷用戶操做,依附於已有窗口
    • 強升級以重量的形式進行提醒,下載前提醒,打斷用戶操做,是一個新的窗口
  • 用戶可選餘地不一樣
    • 弱更新下,用戶每次均可以選擇下次安裝
    • 強升級下,用戶除了選擇「當即升級」外,只能選擇「退出應用再升級」,通常沒法跳過
  • 下載方式不一樣
    • 弱更新下,下載是在後臺的,對用戶是透明的,下載完成後才喚起通知
    • 強升級下,下載是在前臺的,用戶可見(下載進度條),下載前就喚起通知
  • 安裝時機不一樣
    • 弱更新下,用戶點「當即更新」,即開始運行安裝程序
    • 強升級下,選擇「當即升級」,那麼下載完成後自動開始安裝;選擇「退出應用再升級」,那麼等退出應用後再自動運行安裝程序(而且是靜默的)

3. 異常處理

一次更新必然要保證異常不會引發更新有問題,而在一次更新過程當中,主要就 2 類異常。

3.1 下載過程當中的異常

這會引發下載不徹底的問題,因此在應用下載時必定要保障應用是下載徹底的,若是由於用戶的緣由,好比下載過程過,用戶退出了應用,那麼就應該在下次檢查到更新時,從新下載。

但若是檢查到更新後發現用戶本地已經有完整的可用的最新更新包,那就不用下載。

3.2 安裝過程當中的異常

安裝過程通常交給安裝程序去保證,若是安裝過程當中退出,安裝程序應該要保證安裝的原子性。若是安裝方式沒有采用安裝程序,好比 Mac 下使用 dmg 的形式分發的應用,那麼把安裝過程交給用戶,通常能夠避免風險,如針對 Mac 下以 dmg 形式分發的應用,把「拖拽新應用到應用文件夾覆蓋原應用的動做」交由用戶本身去操做。

4、更新的思路

這一節簡單談一下更新的本質是什麼,作了什麼,以及咱們曾嘗試過的方式。

1. 不徹底更新:在線替換部分文件

以前在張鑫旭的博客上看到過一種更新方式,是在線請求核心的 js 文件,而後替換舊的文件。這樣作的出發點是很好的,由於一次更新,大部分僅僅是部分文件的改動,那麼請求改動的文件,替換掉就能夠了。

可是這裏有兩個問題,第一個問題是這樣作是一次不徹底的更新,只能替換部分的文件,這也有辦法解決,請求一個壓縮包,而後起一個子進程去解壓覆蓋原應用文件夾同時退出應用防止文件佔用,可是你怎麼保證其可靠性;第二個問題,用戶若是安裝在某些文件夾下,寫入是有權限問題的,應用自己的運行是不在管理員權限下的,喚起的進程默認是繼承父進程的令牌的,那麼也沒有管理員權限,對於某些文件夾,須要提權,這又是一個難點。

2. 徹底更新

也是基於自動更新實現考慮,咱們從新設計了咱們應用的安裝方式,從「解壓」變成「構建安裝程序」(僅針對 Windows),如此咱們分發應用就變成了分發安裝包,咱們採用的安裝程序是 Inno Setup。

受啓發於 Electron 官方推薦的更新方式以及 @Qquanwei 的 github(感謝這位開發者),再結合 Inno Setup 的官方資料,最終咱們考慮清楚了更新的思路。

  • 咱們分發的是使用 Inno Setup 構建的應用安裝包,運行便可使用 Inno Setup 提供的安裝過程來安裝咱們的應用
  • 咱們在服務器部署了一個 json 文件,包含了咱們發佈的應用信息,包括每一個版本的版本號、發行日期、更新概要、新版安裝包下載連接
  • 用戶每次打開應用,應用會去請求這個 json 文件,對比最新的版本號和當前的版本號以檢查是否有更新
  • 下載更新就是下載最新版的應用安裝包
  • 安裝更新就是喚起一個子進程運行安裝包,並退出自身,安裝包會處理提權的事
  • 用戶安裝後,一次更新就完成了

5、設計自動升級/更新的產品邏輯

在產品邏輯(流程設計)這一階段,能夠分爲 3 大塊:弱更新邏輯、強升級邏輯和檢查更新以決定進入強升級/弱更新流程的邏輯。

1. 檢查更新

檢查更新有兩個問題,第一個問題是咱們檢查什麼?這裏咱們能夠不依賴後端接口去實現檢查,上一節也說了,咱們把各次版本發行記錄在一個 json 文件中,而後部署這個 json 文件到服務器,檢查更新就是去服務器請求這個 json 文件,而後解析,對比裏面的最新版和當前使用的客戶端版本,知道有沒有更新。

第二個問題是,依據什麼判斷是強升級仍是弱更新?基於上面的需求拆分,咱們須要劃分出強弱更新,強弱更新的邏輯是不同的,那麼在檢查更新階段如何斷定該進入哪一個更新邏輯呢?

第一個辦法,在 json 文件中對於每一個版本標明相比於上一個版本是一次強升級仍是弱更新,好比有一個 updateType 字段。這個方式的優勢是簡單明瞭,就算用戶當前的版本和最新版相隔了好幾個版本, 只要知足這其中跨越的有任何一個版本是強升級,那麼此次的更新就是一次強升級 ,不然是一次弱更新。

第二個辦法,一切根據版本號而來,「X.Y.Z」 這樣的版本號,對比當前客戶端的版本和最新版,咱們能夠把 X、Y 不變,僅僅 Z (修訂版本號)改變的當作一次弱更新,若是對比發現,X 或 Y 有任何一個比當前的高了,那麼就定義爲一次強升級。這種方式不用額外的字段,也不用比對中間跨越的版本,僅僅須要對比當前的版本和最新的版本。

假設咱們採用第二種方式,那麼檢查更新的流程是如下這樣的:

2. 強升級邏輯

根據以前的需求分析,咱們的強升級邏輯是如下這樣的:

強升級下,一旦發行有可用的更新,在下載前就直接通知用戶,而通知的方式是新建一個通知窗口,在 Electron 中也就是新建一個渲染進程。

用戶沒法選擇「下次提醒」或「跳過」,而是隻有當即升級和退出應用再升級兩種選項。值得注意的是, 咱們在對於退出應用再升級流程下的安裝更新採用的是「靜默的安裝方式」 ,在用戶看來,退出應用時,會出現一個 Windows 的 UAC 彈窗(系統級別,避免不了,至於 UAC 是什麼,這裏不展開),過了 UAC 以後, 以後的安裝過程對用戶來講是透明的,用戶下次打開就是新版 。這是由於既然用戶選擇了退出,那麼咱們就認爲用戶再也不須要看到應用安裝後再啓動,而後用戶再關一次,而是在下一次打開再看到新版。

而非靜默的安裝方式,安裝過程和進度對用戶來講是可見的,並且安裝好,應用會自動打開。

那這是怎麼作到的呢?這是 Inno Setup 提供的一種特性,在運行安裝包的時候能夠給命令行參數以控制安裝程序的表現,具體實現咱們下一篇再說。

3. 弱更新邏輯

咱們的弱更新邏輯相比強升級就比較簡單了,並且是使用頻率更高的更新方式:

能夠從流程裏看到,弱更新就像咱們常常能看到的更新同樣,首先它不該該打擾用戶的操做,因此咱們選擇輕量的提醒方式,實際應用中咱們採用的是如 VS Code 那樣的更新提醒方式,是依附於主應用窗口的浮條形式來提醒。

並且咱們隱去了下載過程,下載完了(即本地有下載徹底的安裝包)才提醒用戶,用戶點擊更新,實際上僅僅是運行已經下載好了的安裝程序,安裝好了後自動打開新版應用。而下次提醒功能,就是本次應用打開期間再也不提醒。

4. 擴展

上面的更新中異常怎麼解決?審視一下最可能的異常(指由用戶操做引發的,由程序編寫引發的叫 BUG),就是下載過程當中止和安裝過程當中止。這都好解決,下載停止,咱們爲了保險起見和簡單,沒有采用繼續下載的方式,而是若是下載停止,那麼就等於下載不徹底,咱們把這種狀況看作「沒有下載」處理,那就是從新下載。而安裝停止,安裝程序會幫你的。

還有一些應用中咱們能看到「跳過這個版本」的選項,這其實也好實現。咱們上面的「下次提醒」,本次使用期間不提醒,實際上是給程序一個全局變量的標誌來實現,而對於「跳過這個版本」來講,是每次打開都再也不顯示這個版本的更新提醒,天然咱們想到數據的本地持久化。說白了,在用戶的本地(或瀏覽器存儲)中放一個標記,每次更新提醒前,去本地取出來再檢查下就能夠了。

6、小結

本次的「Electron 自動更新方案:從需求到設計」就到此爲止,但願以上的設計方案能給你們一些參考,通常的應用更新,實現上面的弱更新邏輯就能夠了,強升級的邏輯實際上是爲咱們本身快速推進大版本的更新和迭代留了一個口子。此外,仍是那句話,每一個應用都有其特殊性,本文的更新方式只是衆多方案中的一種,供參考,下一篇文章將講基於這樣的設計,咱們是怎麼實現的呢。

相關文章
相關標籤/搜索