MAUI 迁移指南

前言

为了能够让大家更好的理解全新的MAUI框架, 那么本次迁移指南主要给大家讲解从Xamarin.Forms升级到MAUI带来了哪些全新的变化, 下面将围绕以下几点给大家重点介绍。.

  • 单个代码库演变

  • 启动配置演变

  • 统一资源管理

  • 依赖注入

  • 隐式using 指令

  • Essentials合并

  • 全新命名空间

您仅需要具备Xamarin.Forms的一些基础概念

单个代码库演变

如下,演示了一个Xamarin.Forms典型应用的项目结构, 包含了一个共享类库以及2个原生平台的应用(Android/IOS)。
MAUI 迁移指南

  • MyApp(共享类库): 用于编写非特定平台的业务逻辑实现代码

  • MyApp.Android(原生安卓应用):包含Android平台的特定配置、资源、程序集相关。

  • MyApp.iOS(原生安卓应用):包含IOS平台的特定配置、资源、程序集相关。

如果您的应用选择Windows平台, 则会多一个UWP项目选项, 类似,这仅仅是针对UWP平台的相关配置。

下面, 则演示了一个MAUI的典型应用的项目结果, 仅包含了一个项目。
MAUI 迁移指南
MAUI 应用的项目包含 一个 Platform 文件夹,每个子文件夹都表示 .NET MAUI 可以面向的平台
MAUI 迁移指南
在生成时,生成系统仅在为该特定平台生成时包含每个文件夹中的代码。例如,在为 Android 生成平台时Android文件夹中的文件>将内置到应用包中,但其他平台文件夹中的文件不会。

有了单代码库的支持, 我们不再需要单独维护多个平台的项目, 重复的SDK引用, 资源文件定义等。

关于基于多目标的配置, 可参考 MAUI 中配置多目标

启动配置演变

在Xamarin.Forms当中, 无论是我们启动Android还是IOS项目, 我们都需要在启动之前初始化Xamarin.Forms框架。
Android

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
            //初始化Xamarin.Forms
            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
            LoadApplication(new App());
        }

IOS中

        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            //初始化Xamarin.Forms
            global::Xamarin.Forms.Forms.Init();
            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }

整个初始化过程中, 我们是无法进行配置的, 例如: 扫描程序集配置容器服务、导出字体、加载默认的控件渲染器、平台绑定等。这也就意味着, 对于Xamarin.Forms而言,
大多数功能都是按照约定配置, 无法进行自定义, 以及服务大都是扫描程序集进行反射加载的。

在MAUI当中, 使用了类似通用主机结构来初始化 .NET MAUI 应用程序。这将为用户提供非常微软的体验,并且让许多代码符合 ASP.NET Core 范式。

public static MauiApp CreateMauiApp()
	{
		var builder = MauiApp.CreateBuilder();
		builder
			.UseMauiApp<App>()
			.ConfigureFonts(fonts =>
			{
				fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
				fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
			});

		return builder.Build();
	}

在默认情况下, 每个单独的平台都会调用 CreateMauiApp 来构建应用程序的服务依赖。

统一资源管理

在Xamarin.Forms中, 每个项目都包含各自的资源文件, 大多数情况下, 我们会重复定义几组相同的资源(字体、样式、图像)在对应的平台
MAUI 迁移指南

MAUI单项目解决了这个问题, 在 Resources 文件夹当中, 包含了所有的资源文件定义。
MAUI 迁移指南

依赖注入

在Xamarin.Forms中,如果我们想要使用依赖注入,来实现特定平台的实现, 首先需要在共享类库中定义标准接口, 然后每个平台单独实现一遍。
MAUI 迁移指南

在具体的实现类当中, 我们还需要程序集级别的特性标记, 以便于Xamarin.Forms初始化的过程中能够扫描程序集并且加载到容器当中。

[assembly: Xamarin.Forms.Dependency(typeof(LocalService))]
namespace MyApp.Droid
{
    public class LocalService : ILocalService
    {
        public void SetValue(string key, object value)
        {

        }
    }
}

获取容器服务的方式, 则可以通过DependencyService的静态方法获取。

 var localService= DependencyService.Get<ILocalService>();

同样, 针对字体的访问, 也是通过扫描程序集的方式加载

[assembly: ExportFont("iconfont.ttf", Alias = "iconfont")]

那么, 在MAUI当中, 通过通用主机的形式, 提供了容器可以进行自定义配置服务。

 public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                //注入字体
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });

        //注入服务
        builder.Services.AddScoped<ILocalService, LocalService>(); 
        return builder.Build();
    }

是不是很轻松?不仅仅非常直观, 也大大的提高的开发效率以及性能

隐式using 指令

有了global using , 我们无需在多个类当中重复定义命名空间, 只需要一句 global using, 从此别的类当中会自动引用。

global using System;
global using System.Threading;
global using System.Linq;
global using System.ComponentModel;

针对一些非常常见的命名空间, 我们都可以使用 global using。

  • Essentials合并
    Xamarin.Forms时期, 我们使用Essentials它是单独进行nuget分发以及维护, 在配置的时候, 我们也需要单独的初始化代码。

Xamarin.Essentials.Platform.Init(this, savedInstanceState);

PLAINTEXT复制全屏

MAUI则将Essentials全部融合到了MAUI项目当中, 命名控件则是 Microsoft.MAUI下, 例如访问设备。

using Microsoft.Maui.Devices
  • 全新命名空间
    Xamarin.Forms 当中我们使用的 using Xamarin.Forms 变成了 using Microsoft.Maui 相关。

  • 性能改进
    性能也带来了很大的改进, 可参考文章: .NET MAUI 中的性能改进

  • 关于更多
    1.事件处理程序
    2.兼容现有的渲染器模式
    后面再接着说...