Electron控制應用是否更新
1更新需要提示用戶,需要控制應用是否更新
1. 方案一
在檢測到更新后提示用戶,讓用戶選擇更新。
設置autoDownload參數(shù)為false,讓應用檢測到更新不自動下載,改成手動下載更新包。
通過在鉤子update-available中,加入對話框提示用戶,讓用戶選擇。
response為0用戶選擇確定,觸發(fā)downloadUpdate方法下載應用更新包進行后續(xù)更新操作。否則,不下載更新包。
如果我們不配置autoDownload為false,那么問題來了:在彈出對話框的同時,用戶還來不及選擇,應用自動下載并且更新完成,做不到阻塞。
重要代碼如下:
autoUpdater.autoDownload = false
在update-available鉤子中彈出對話框
autoUpdater.on('update-available', (ev, info) => {
// // 不可逆過程
const options = {
type: 'info',
buttons: ['確定', '取消'],
title: '更新提示',
// ${info.version} Cannot read property 'version' of undefined
message: '發(fā)現(xiàn)有新版本,是否更新?',
cancelId: 1
}
dialog.showMessageBox(options).then(res => {
if (res.response === 0) {
autoUpdater.downloadUpdate()
logger.info('下載更新包成功')
sendStatusToWindow('下載更新包成功');
} else {
return;
}
})
})
2. 方案二
在更新下載完后提示用戶,讓用戶選擇更新。
先配置參數(shù)autoInstallOnAppQuit為false,阻止應用在檢測到更新包后自動更新。
在鉤子update-downloaded中加入對話框提示用戶,讓用戶選擇。
response為0用戶選擇確定,更新應用。否則,當前應用不更新。
如果我們不配置autoInstallOnAppQuit為false,那么問題是:雖然第一次應用不更新,但是第二次打開應用,應用馬上關閉,還沒讓我們看到主界面,應用暗自更新,重點是更新完后不重啟應用。
重要代碼如下:
// 表示下載包不自動更新
autoUpdater.autoInstallOnAppQuit = false
在update-downloaded鉤子中彈出對話框
autoUpdater.on('update-downloaded', (ev, releaseNotes, releaseName) => {
logger.info('下載完成,更新開始')
sendStatusToWindow('下載完成,更新開始');
// Wait 5 seconds, then quit and install
// In your application, you don't need to wait 5 seconds.
// You could call autoUpdater.quitAndInstall(); immediately
const options = {
type: 'info',
buttons: ['確定', '取消'],
title: '應用更新',
message: process.platform === 'win32' ? releaseNotes : releaseName,
detail: '發(fā)現(xiàn)有新版本,是否更新?'
}
dialog.showMessageBox(options).then(returnVal => {
if (returnVal.response === 0) {
logger.info('開始更新')
setTimeout(() => {
autoUpdater.quitAndInstall()
}, 5000);
} else {
logger.info('取消更新')
return
}
})
});
3. 源碼分析
未打包目錄位于:electron-builder/packages/electron-updater/src/AppUpdater.ts中。打包后在electron-updater\out\AppUpdater.d.ts中
首先進入checkForUpdates()方法,開始檢測更新 正在更新不需要進入 開始更新前判斷autoDownload,為true自動下載,為false不下載等待應用通知。
export declare abstract class AppUpdater extends EventEmitter {
/**
* 當被發(fā)現(xiàn)有更新時,是否要自動下載更新
* 場景:可以適用于electron檢查更新包提示,用戶操作是否需要更新
*/
autoDownload: boolean;
/**
* 在app.quit()后,是否自動將下載下載的更新包更新
* 場景:可以適用于electron下載完更新包提示,用戶操作是否需要更新。在第二次打開應用,應用不會自動更新。
*/
autoInstallOnAppQuit: boolean;
}
/**
* 檢測是否需要更新
*/
checkForUpdates(): Promise < UpdateCheckResult > {
let checkForUpdatesPromise = this.checkForUpdatesPromise
// 正在檢測更新跳過
if (checkForUpdatesPromise != null) {
this._logger.info("Checking for update (already in progress)")
return checkForUpdatesPromise
}
const nullizePromise = () => this.checkForUpdatesPromise = null
// 開始檢測更新
this._logger.info("Checking for update")
checkForUpdatesPromise = this.doCheckForUpdates()
.then(it => {
nullizePromise()
return it
})
.catch(e => {
nullizePromise()
this.emit("error", e, `Cannot check for updates: ${(e.stack || e).toString()}`)
throw e
})
this.checkForUpdatesPromise = checkForUpdatesPromise
return checkForUpdatesPromise
}
// 檢測更新具體函數(shù)
private async doCheckForUpdates(): Promise < UpdateCheckResult > {
// 觸發(fā) checking-for-update 鉤子
this.emit("checking-for-update")
// 取更新信息
const result = await this.getUpdateInfoAndProvider()
const updateInfo = result.info
// 判斷更新信息是否有效
if (!await this.isUpdateAvailable(updateInfo)) {
this._logger.info(`Update for version ${this.currentVersion} is not available (latest version: ${updateInfo.version}, downgrade is ${this.allowDowngrade ? "allowed" : "disallowed"}).`)
this.emit("update-not-available", updateInfo)
return {
versionInfo: updateInfo,
updateInfo,
}
}
this.updateInfoAndProvider = result
this.onUpdateAvailable(updateInfo)
const cancellationToken = new CancellationToken()
//noinspection ES6MissingAwait
// 如果設置autoDownload為true,則開始自動下載更新包,否則不下載
return {
versionInfo: updateInfo,
updateInfo,
cancellationToken,
downloadPromise: this.autoDownload ? this.downloadUpdate(cancellationToken) : null
}
}
如果需要配置updater中的其他參數(shù)達到某種功能,我們可以仔細查看其中的配置項。
export declare abstract class AppUpdater extends EventEmitter {
/**
* 當被發(fā)現(xiàn)有更新時,是否要自動下載更新
* 場景:可以適用于electron檢查更新包提示,用戶操作是否需要更新
*/
autoDownload: boolean;
/**
* 在app.quit()后,是否自動將下載下載的更新包更新
* 場景:可以適用于electron下載完更新包提示,用戶操作是否需要更新。在第二次打開應用,應用不會自動更新。
*/
autoInstallOnAppQuit: boolean;
/**
* *GitHub provider only.* Whether to allow update to pre-release versions. Defaults to `true` if application version contains prerelease components (e.g. `0.12.1-alpha.1`, here `alpha` is a prerelease component), otherwise `false`.
** GitHub提供者。
*是否允許升級到預發(fā)布版本。
如果應用程序版本包含預發(fā)布組件,默認為“true”。
“0.12.1-alpha。
1 ',這里' alpha '是預發(fā)布組件),否則' false '。
* If `true`, downgrade will be allowed (`allowDowngrade` will be set to `true`).
如果' true ',則允許降級(' allow降級'將被設置為' true ')。
*/
allowPrerelease: boolean;
/**
* *GitHub provider only.* Get all release notes (from current version to latest), not just the latest.
* * GitHub提供者。
*獲取所有發(fā)布說明(從當前版本到最新版本),而不僅僅是最新版本。
* @default false
*/
fullChangelog: boolean;
/**
*是否允許版本降級(當用戶從測試通道想要回到穩(wěn)定通道時)。
*僅當渠道不同時考慮(根據(jù)語義版本控制的預發(fā)布版本組件)。
* @default false
*/
allowDowngrade: boolean;
/**
* 當前應用的版本
*/
readonly currentVersion: SemVer;
private _channel;
protected downloadedUpdateHelper: DownloadedUpdateHelper | null;
/**
* Get the update channel. Not applicable for GitHub. Doesn't return `channel` from the update configuration, only if was previously set.
* 獲取更新通道。
不適用于GitHub。
從更新配置不返回' channel ',只有在以前設置。
*/
get channel(): string | null;
/**
* Set the update channel. Not applicable for GitHub. Overrides `channel` in the update configuration.
*設置更新通道。
不適用于GitHub。
覆蓋更新配置中的' channel '。
* `allowDowngrade` will be automatically set to `true`. If this behavior is not suitable for you, simple set `allowDowngrade` explicitly after.
“allow降級”將自動設置為“true”。
如果這個行為不適合你,簡單設置“allow降級”后顯式。
*/
set channel(value: string | null);
/**
* 請求頭
*/
requestHeaders: OutgoingHttpHeaders | null;
protected _logger: Logger;
get netSession(): Session;
/**
* The logger. You can pass [electron-log](https://github.com/megahertz/electron-log), [winston](https://github.com/winstonjs/winston) or another logger with the following interface: `{ info(), warn(), error() }`.
* Set it to `null` if you would like to disable a logging feature.
* 日志,類型有:info、warn、error
*/
get logger(): Logger | null;
set logger(value: Logger | null);
/**
* For type safety you can use signals, e.g. `autoUpdater.signals.updateDownloaded(() => {})` instead of `autoUpdater.on('update-available', () => {})`
* 為了類型安全,可以使用signals
*/
readonly signals: UpdaterSignal;
private _appUpdateConfigPath;
/**
* test only
* @private
*/
set updateConfigPath(value: string | null);
private clientPromise;
protected readonly stagingUserIdPromise: Lazy < string > ;
private checkForUpdatesPromise;
protected readonly app: AppAdapter;
protected updateInfoAndProvider: UpdateInfoAndProvider | null;
protected constructor(options: AllPublishOptions | null | undefined, app ? : AppAdapter);
/**
* 獲取當前更新的url
*/
getFeedURL(): string | null | undefined;
/**
* Configure update provider. If value is `string`, [GenericServerOptions](/configuration/publish#genericserveroptions) will be set with value as `url`.
* @param options If you want to override configuration in the `app-update.yml`.
*
* 配置更新提供者。通過提供url
* @param options 如果你想覆蓋' app-update.yml '中的配置。
*/
setFeedURL(options: PublishConfiguration | AllPublishOptions | string): void;
/**
* 檢查服務其是否有更新
*/
checkForUpdates(): Promise < UpdateCheckResult > ;
isUpdaterActive(): boolean;
/**
*
* @param downloadNotification 詢問服務器是否有更新,下載并通知更新是否可用
*/
checkForUpdatesAndNotify(downloadNotification ? : DownloadNotification): Promise < UpdateCheckResult | null > ;
private static formatDownloadNotification;
private isStagingMatch;
private computeFinalHeaders;
private isUpdateAvailable;
protected getUpdateInfoAndProvider(): Promise < UpdateInfoAndProvider > ;
private createProviderRuntimeOptions;
private doCheckForUpdates;
protected onUpdateAvailable(updateInfo: UpdateInfo): void;
/**
*
* 作用:開始下載更新包
*
* 如果將`autoDownload`選項設置為false,就可以使用這個方法。
*
* @returns {Promise<string>} Path to downloaded file.
*/
downloadUpdate(cancellationToken ? : CancellationToken): Promise < any > ;
protected dispatchError(e: Error): void;
protected dispatchUpdateDownloaded(event: UpdateDownloadedEvent): void;
protected abstract doDownloadUpdate(downloadUpdateOptions: DownloadUpdateOptions): Promise < Array < string >> ;
/**
* 作用:下載后重新啟動應用程序并安裝更新。
*只有在' update- downloads '被觸發(fā)后才會調(diào)用。
*
* 注意:如果在update-downloaded鉤子中,讓用戶選擇是否更新應用,選擇不更新,那就是沒有執(zhí)行autoUpdater.quitAndInstall()方法。
* 雖然應用沒有更新,但是當?shù)诙未蜷_應用的時候,應用檢測到本地有更新包,他就會直接更新,最后不會重啟更新后的應用。
*
* 為了解決這個問題,需要設置`autoInstallOnAppQuit`為false。關閉應用自動更新。
*
* **Note:** ' autoUpdater.quitAndInstall() '將首先關閉所有的應用程序窗口,然后只在' app '上發(fā)出' before-quit '事件。
*這與正常的退出事件序列不同。
*
* @param isSilent 僅Windows以靜默模式運行安裝程序。默認為false。
* @param isForceRunAfter 即使無提示安裝也可以在完成后運行應用程序。不適用于macOS。忽略是否isSilent設置為false。
*/
abstract quitAndInstall(isSilent ? : boolean, isForceRunAfter ? : boolean): void;
private loadUpdateConfig;
private computeRequestHeaders;
private getOrCreateStagingUserId;
private getOrCreateDownloadHelper;
protected executeDownload(taskOptions: DownloadExecutorTask): Promise < Array < string >> ;
}
最后,希望大家一定要點贊、關注并轉(zhuǎn)發(fā)。
一個學習編程技術的公眾號。每周推送高質(zhì)量的優(yōu)秀博文、開源項目、實用工具、面試技巧、編程學習資源等等。目標是做到個人技術與公眾號一起成長。歡迎大家關注,一起進步,走向全棧大佬的修煉之路
