發布日期:2020 年 12 月 2 日
自受信任的網路活動推出以來,Chrome 團隊已讓 Bubblewrap 更容易使用。我們新增了其他功能,例如整合 Google Play 帳款服務,並支援 ChromeOS 等更多平台。
Bubblewrap 和受信任的網路活動功能
Bubblewrap 可協助您建立應用程式,在受信任的網路活動中啟動 PWA,而不必瞭解平台專屬工具。
簡化設定流程
先前使用 Bubblewrap 時,必須手動設定 Java Development Kit 和 Android SDK,這兩者都容易出錯。現在,這項工具在首次執行時,會提供自動下載外部依附元件的選項。
如果您偏好使用現有的依附元件安裝,仍可選擇這麼做,而新的 doctor 指令可協助找出問題並建議修正設定,現在可以使用 updateConfig 指令從指令列更新設定。
改良版精靈
使用 init 建立專案時,Bubblewrap 需要資訊才能產生 Android 應用程式。這項工具會從網頁應用程式資訊清單中擷取值,並盡可能提供預設值。
建立新專案時,您可以變更這些值,但先前每個欄位的意義並不明確。初始化對話方塊已重新建構,每個輸入欄位都有更完善的說明和驗證。
顯示全螢幕和螢幕方向支援
在某些情況下,您可能希望應用程式盡可能使用螢幕空間,建構 PWA 時,只要將網頁應用程式資訊清單中的 display 欄位設為 fullscreen,即可達到這個效果。
Bubblewrap 偵測到網頁應用程式資訊清單中的全螢幕選項時,會將 Android 應用程式設為以全螢幕啟動,或以 Android 專用術語來說,以沉浸模式啟動。
網頁應用程式資訊清單中的 orientation 欄位,定義了應用程式應以直向模式、橫向模式,還是裝置目前使用的方向啟動。Bubblewrap 現在會讀取網頁應用程式資訊清單欄位,並在建立 Android 應用程式時將其做為預設值。
您可以在 bubblewrap init 流程中自訂這兩項設定。
AppBundles 輸出
App Bundle 是應用程式的發布格式,可將最終 APK 的產生和簽署作業委派給 Play。實際上,這可讓使用者從商店下載應用程式時,提供較小的檔案。
Bubblewrap 現在會將應用程式封裝為 App Bundle,檔案名稱為 app-release-bundle.aab。在 Play 商店發布應用程式時,建議您優先使用這種格式,因為自 2021 年起,Play 商店已要求使用這種格式。
地理位置委派
使用者希望裝置上安裝的應用程式行為一致,無論技術為何。在受信任的網路活動中使用時,GeoLocation 權限現在可以委派給作業系統,啟用後,使用者會看到與使用 Kotlin 或 Java 建構的應用程式相同的對話方塊,並在相同位置找到管理權限的控制項。
這項功能可透過 Bubblewrap 新增,由於會為 Android 專案新增額外的依附元件,因此只有在網頁應用程式使用地理位置權限時,才應啟用這項功能。
最佳化二進位檔
在世界某些地區,儲存空間有限的裝置很常見,而這些裝置的擁有者通常偏好較小的應用程式。使用受信任的網路活動的應用程式會產生小型二進位檔,可消除部分使用者的焦慮。
Bubblewrap 經過最佳化,減少了所需的 Android 程式庫清單,因此產生的二進位檔縮小了 800k。在實際操作中,這不到先前版本產生的平均大小的一半。如要利用較小的二進位檔,只需使用最新版本的 Bubblewrap 更新應用程式即可。
如何更新現有應用程式
Bubblewrap 產生的應用程式由網頁應用程式和輕量級 Android 包裝函式組成,可開啟 PWA。雖然在受信任的網路活動中開啟的 PWA 與任何網頁應用程式遵循相同的更新週期,但原生包裝函式可以且應該更新。
請務必更新應用程式,確保使用最新版包裝函式,並取得最新的錯誤修正和功能。安裝最新版本 Bubblewrap 後,update 指令會將最新版本包裝函式套用至現有專案:
npm update -g @bubblewrap/cli
bubblewrap update
bubblewrap build
更新這些應用程式的另一個原因,是確保網頁資訊清單的變更會套用至應用程式。請使用新的 merge 指令:
bubblewrap merge
bubblewrap update
bubblewrap build
品質條件更新
Chrome 86 導入了受信任的網路活動品質條件異動,詳情請參閱「使用受信任的網路活動的 PWA 品質條件異動」。
簡而言之,您應確保應用程式能處理下列情況,以免發生異常終止:
- 應用程式啟動時無法驗證數位資產連結
- 離線網路資源要求未傳回 HTTP 200
- 應用程式傳回 HTTP 404 或 5xx 錯誤。
除了確保應用程式通過Digital Asset Links 驗證外,其餘情境可由 Service Worker 處理:
self.addEventListener('fetch', event => {
event.respondWith((async () => {
try {
return await fetchAndHandleError(event.request);
} catch {
// Failed to load from the network. User is offline or the response
// has a status code that triggers the Quality Criteria.
// Try loading from cache.
const cachedResponse = await caches.match(event.request);
if (cachedResponse) {
return cachedResponse;
}
// Response was not found on the cache. Send the error / offline
// page. OFFLINE_PAGE should be pre-cached when the service worker
// is activated.
return await caches.match(OFFLINE_PAGE);
}
})());
});
async function fetchAndHandleError(request) {
const cache = await caches.open(RUNTIME_CACHE);
const response = await fetch(request);
// Throw an error if the response returns one of the status
// that trigger the Quality Criteria.
if (response.status === 404 ||
response.status >= 500 && response.status < 600) {
throw new Error(`Server responded with status: ${response.status}`);
}
// Cache the response if the request is successful.
cache.put(request, response.clone());
return response;
}
Workbox 內建最佳做法,並在使用 Service Worker 時移除樣板。或者,您也可以考慮使用 Workbox 外掛程式來處理這些情況:
export class FallbackOnErrorPlugin {
constructor(offlineFallbackUrl, notFoundFallbackUrl, serverErrorFallbackUrl) {
this.notFoundFallbackUrl = notFoundFallbackUrl;
this.offlineFallbackUrl = offlineFallbackUrl;
this.serverErrorFallbackUrl = serverErrorFallbackUrl;
}
checkTrustedWebActivityCrash(response) {
if (response.status === 404 || response.status >= 500 && response.status <= 600) {
const type = response.status === 404 ? 'E_NOT_FOUND' : 'E_SERVER_ERROR';
const error = new Error(`Invalid response status (${response.status})`);
error.type = type;
throw error;
}
}
// This is called whenever there's a network response,
// but we want special behavior for 404 and 5**.
fetchDidSucceed({response}) {
// Cause a crash if this is a Trusted Web Activity crash.
this.checkTrustedWebActivityCrash(response);
// If it's a good response, it can be used as-is.
return response;
}
// This callback is new in Workbox v6, and is triggered whenever
// an error (including a NetworkError) is thrown when a handler runs.
handlerDidError(details) {
let fallbackURL;
switch (details.error.details.error.type) {
case 'E_NOT_FOUND': fallbackURL = this.notFoundFallbackUrl; break;
case 'E_SERVER_ERROR': fallbackURL = this.serverErrorFallbackUrl; break;
default: fallbackURL = this.offlineFallbackUrl;
}
return caches.match(fallbackURL, {
// Use ignoreSearch as a shortcut to work with precached URLs
// that have _WB_REVISION parameters.
ignoreSearch: true,
});
}
}