Xamarin作為移動端的跨平臺原生開發框架的老牌勁旅,一直被視作Mono Project寄予厚望的當家花旦之一。近年來,雖然React Native/Ionic等後起之秀奪去大半江山,但隨著Xamarin入駐微軟並宣告免費,加之.NET/C#生態的日益完善與精進,Xamarin已然重煥青春!
那麼,如果我們將它與上期技術分享介紹的業界新貴WebAssembly雙劍合璧,又會迸發出怎樣的化學反應呢?今天我們就將Autodesk Forge Viewer離線方案整合到Xamarin,併發布為基於WebAssembly的Progressive Web App(簡稱PWA,漸進式應用) - 即實現瀏覽器URL訪問直接安裝的神奇效果!
引言彩蛋
首先,讓我們First things first:
?Autodesk ADN(開發者社群團隊)祝您在新的一年大吉大利!!?事順利!!?
為什麼要WebAssembly和PWA?
在原生和H5應用如火純青的今天,WebAssembly和PWA的相對意義與優勢在於:
- 如上期技術分享介紹,WebAssembly賦能我們在瀏覽器中以原生的效能執行CC#、Java、Rust等及基於它們的框架和技術
- 編譯後的執行包(wasm)為二進位制,精簡高效的同時保護原始碼,各種安全機制防範惡意程式碼的篡改與攻擊,增強應用的安全性
- PWA給予使用者非原生應用勝似原生應用的瀏覽器客戶端體驗,透過瀏覽器訪問URL即可完成PWA的安裝,無需釋出至應用商店
Xamarin的優勢
相比Cordova/PhoneGap、Ionic、Appgyver等H5混合框架,以及React Native、Titanium等原生框架,我們為什麼要關注Xamarin呢?
- 相較Cordova/PhoneGap、Ionic、Appgyver等混合框架具有原生優勢
- 相較基於JavaScript的React Native、Titanium,.NET/C#在語言某些特性上具備相對優勢,也便於熟悉.NET/C#桌面與後臺開發的朋友迅速上手
- .NET/C#生態的相對優勢,如可以使用我們的Forge .NET Client SDK
- 相較基於狀態的Functional Approach具有更底層原生UI的優勢
實戰開始!
今天實戰的環節為:Forge Viewer漸進應用 > Xamarin應用 > 釋出為WebAssembly的PWA > 移動端測試
Forge Viewer PWA
往期我們有介紹過利用ServiceWorker和Cache等API實現Forge Viewer離線方案,但是悉心的朋友或許已經發現該方案仍有不少瑕疵(將在下期著重闡述),現在我們更一進步:將整個載入過程離線快取與客戶端! 其成果是獨立的Forge Viewer PWA,滿足移動和桌面端的離線使用。
-
首先定義Viewer漸進應用的ServiceWorker,有關該API的詳細介紹可以參考往期。不同於往期中介紹的方案,這次我們將要快取所有Viewer指令碼、樣式和載入模型的請求,且在預設的快取列表中只有CSS,其餘指令碼依賴與模型資料全部在應用首次載入階段快取,省去手動適配不同Viewer版本與模型資源的麻煩。首先我們來監聽請求事件,收集所有需要快取的請求,並記錄從後臺獲取的Access Token,以供Forge API認證所需。出於效能考慮,待模型載入完成後再統一快取所有資源:
const urlsToCache = [ 'viewer.html', //Viewer頁面路徑 'viewer-serviceworker.js', //本ServiceWorker路徑 'https://developer.api.autodesk.com/modelderivative/v2/viewers/6.*/style.min.css' //Viewer.js樣式 ]; ... self.addEventListener('fetch', event => { event.respondWith( caches.match(event.request) .then( async response => { if (response) return response; if (event.request.url.endsWith('/api/token')) { \\判斷請求指向獲取Access Token的後臺服務 const response = await fetch(event.request); fetchOptions.headers = { 'Authorization': 'Bearer ' + response.access_token } \\設定訪問Forge API請求的Access Token return response; } else fetches.push(event.request.url); return fetch(event.request) }) ) });
-
在ServiceWorker中定義快取操作
self.addEventListener('message', async event => { switch (event.data.operation) { case 'EXECUTE_CACHE': await caches.open(CACHE_NAME).then(async cache => await Promise.all(fetches.map(url=>fetch(url, fetchOptions).then(resp => cache.put(url, resp))))); event.ports[0].postMessage({ status: 'ok', fetches }); break; } });
-
待Viewer觸發
GEOMETRY_LOADED_EVENT
,即模型載入完畢,一切所需資源已記錄在案,再統一觸發快取navigator.serviceWorker.register('/service-worker.js').then((registration) => { alert('Service worker registered', registration.scope); let script = document.createElement('script'); script.onload = function () { const viewer = new Autodesk.Viewing.Private.GuiViewer3D(myViewerDiv); Autodesk.Viewing.Initializer(options, () => { ... //按需以線上或離線模式初始化Viewer並載入模型 viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, () => { const channel = new MessageChannel(); channel.port1.onmessage = (event) => console.log(event); navigator.serviceWorker.controller.postMessage({ operation: 'EXECUTE_CACHE' }, [channel.port2]); // 模型載入完成,該模型所需資源已作記錄,遂向ServiceWorker傳送訊息,開始快取所需資源 }) }); }; script.src = "https://developer.api.autodesk.com/modelderivative/v2/viewers/6.*/viewer3D.min.js"; document.head.appendChild(script) });
- 最後定義後臺服務,獲取Access Token,.NET、Node、PHP的教程可以參看這裡
整合Viewer PWA至Xamarin應用
- 在Visual Studio中建立Xamarin專案,注意引用Xamarin.Forms v2.5而非v3.x!
-
由於Viewer採用WebGL實現,其PWA的整合亦採用WebView,建立基於Xamarin XAML的Top Banner+WebView的UI介面,其中WebView指向我們之前定義的Viewer PWA頁面
<Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <StackLayout BackgroundColor="DimGray" VerticalOptions="FillAndExpand" HorizontalOptions="Fill"> <StackLayout Orientation="Horizontal" HorizontalOptions="Center" VerticalOptions="Center"> <ContentView Padding="0,10,0,10" VerticalOptions="FillAndExpand"> <Image Source="{Binding logo}" VerticalOptions="Center" HeightRequest="24" /> </ContentView> </StackLayout> </StackLayout> <ScrollView Grid.Row="1"> <StackLayout Orientation="Vertical" > <ContentView VerticalOptions="FillAndExpand" > <WebView Source="URL/TO/YOUR/VIEWER/PWA.html"></WebView> </ContentView> </StackLayout> </ScrollView> </Grid>
- 可用所見即所得的設計器(如GorillaPlayer)設計XAML,和Visual Studio 2017自帶的XAML Previewer預覽UI效果(如箭頭所示於XAML編輯器右上角按鈕進入)
- 編譯執行並檢查在UI和WebView載入剛才完成的Forge Viewer PWA的效果
釋出為基於WebAssembly的PWA!
- 在Visual Studio中建立基於.NET Core 2.x的Console(控制檯應用)專案,並引用方才建立的Xamarin專案
-
透過NuGet安裝以下依賴,其中Ooui系列用於釋出WebAssembly
- Ooui: https://github.com/praeclarum...
- Ooui WASM: 釋出WebAssembly
- Ooui Forms: Xamarin.Forms的Ooui實現
- Xamarin.Forms 2.5 //重要!一定要使用2.5版本以相容Ooui
-
在
Program.cs
中定義釋出過程using OurXamarinApp static void Main(string[] args) { Forms.Init(); var mainPage = new MainPage(); //以MainPage為應用入口為例 UI.Publish("/", mainPage.GetOouiElement()); }
- 執行專案,將在專案的
\bin\Debug\netcoreapp2.1\dist
路徑下生成WebAssembly和配套的前端頁面與指令碼:
- 定義快取WebAssembly的ServiceWorker:
const urlsToCache = [
'index.html',
"https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css", //Ooui依賴
"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css", //Ooui依賴
'service-worker.js' //本ServiceWorker路徑
];
...
self.addEventListener('fetch', function (event) {
event.respondWith(
caches.match(event.request)
.then(function (response) {
// Cache hit - return response
if (response) {
return response;
}
return fetch(event.request);
}
)
);
});
-
在釋出生成的
index.html
頁面中註冊該ServiceWorkerwindow.addEventListener('load', function () { navigator.serviceWorker.register('service-worker.js') })
-
定義應用清單 (manifest.json),讓瀏覽器識別我們的PWA並定義主題顏色、應用圖示等後設資料
{ "name": "Forge Viewer PWA", "short_name": "FVPWA", "icons": [ { "src": "icons/icon-128x128.png", "sizes": "128x128", "type": "image/png" }, { "src": "icons/icon-144x144.png", "sizes": "144x144", "type": "image/png" }, { "src": "icons/icon-152x152.png", "sizes": "152x152", "type": "image/png" }, { "src": "icons/icon-192x192.png", "sizes": "192x192", "type": "image/png" }, { "src": "icons/icon-512x512.png", "sizes": "512x512", "type": "image/png" } ], "start_url": "index.html", "display": "standalone", "background_color": "#3498DB", "theme_color": "#3498DB" }
-
在
index.html
頁面中引用清單<header> ... <link rel="manifest" href="/manifest.json"> </header>
移動端測試
- 用各大Web伺服器直接靜態託管應用所在目錄即可,並以支援ServiceWorker的瀏覽器訪問,支援情況可參考:https://caniuse.com/#search=s...
- 在非HTTPS/SSL測試環境下,如遇瀏覽器安全限制:“不安全上下文”錯誤或無法註冊ServiceWorker (navigator.serviceWorker為null),則需給伺服器啟用HTTPS/SSL,或使用ngrok等雲管道服務為本地環境作代理
- 用訪問應用URL時瀏覽器會提示儲存快捷方式到桌面,圖示由我們的應用清單定義:
- 儲存後進入飛航模式開啟應用,測試離線狀態的載入:
- 大功告成!
延伸閱讀
- Xamarin開發資源彙總:https://blog.csdn.net/qq_4147...
- WebAssembly遇上React Native: https://github.com/vincentrie...
- PWA和小程式:https://www.cnblogs.com/softi...