实战Forge Viewer渐进应用 - 当Xamarin遇上WebAssembly

栏目: IOS · Android · 发布时间: 5年前

内容简介:那么,如果我们将它与上期技术分享介绍的业界新贵

Xamarin 作为移动端的跨平台原生开发框架的老牌劲旅,一直被视作 Mono Project 寄予厚望的当家花旦之一。近年来,虽然React Native/Ionic等后起之秀夺去大半江山,但随着Xamarin 入驻微软并宣告免费 ,加之.NET/C#生态的日益完善与精进,Xamarin已然重焕青春!

实战Forge Viewer渐进应用 - 当Xamarin遇上WebAssembly

那么,如果我们将它与上期技术分享介绍的业界新贵 WebAssembly 双剑合璧,又会迸发出怎样的化学反应呢?今天我们就将 Autodesk Forge Viewer 离线方案整合到Xamarin,并发布为基于WebAssembly的 Progressive Web App (简称PWA,渐进式应用) - 即实现浏览器URL访问直接安装的神奇效果!

引言彩蛋

首先,让我们First things first:

实战Forge Viewer渐进应用 - 当Xamarin遇上WebAssembly

:heartpulse:Autodesk ADN(开发者社区团队)祝您在新的一年大吉大利!!:pig2:事顺利!!:heartpulse:

为什么要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

实战开始!

今天实战的环节为: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编辑器右上角按钮进入)

实战Forge Viewer渐进应用 - 当Xamarin遇上WebAssembly

  • 编译运行并检查在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和配套的前端页面与脚本:

实战Forge Viewer渐进应用 - 当Xamarin遇上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 页面中注册该ServiceWorker

    window.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时浏览器会提示保存快捷方式到桌面,图标由我们的应用清单定义:

实战Forge Viewer渐进应用 - 当Xamarin遇上WebAssembly

  • 保存后进入飞行模式打开应用,测试离线状态的加载:

实战Forge Viewer渐进应用 - 当Xamarin遇上WebAssembly

  • 大功告成!

延伸阅读


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Algorithms on Strings, Trees and Sequences

Algorithms on Strings, Trees and Sequences

Dan Gusfield / Cambridge University Press / 1997-5-28 / USD 99.99

String algorithms are a traditional area of study in computer science. In recent years their importance has grown dramatically with the huge increase of electronically stored text and of molecular seq......一起来看看 《Algorithms on Strings, Trees and Sequences》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

html转js在线工具
html转js在线工具

html转js在线工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具