MAUI 迁移指南(1)
前言
为了能够让大家更好的理解全新的MAUI框架, 那么本次迁移指南主要给大家讲解从Xamarin.Forms升级到MAUI带来了哪些全新的变化, 下面将围绕一下几点给大家重点介绍。
- 单个代码库演变
- 启动配置演变
- 统一资源管理
- 依赖注入
- 隐式using 指令
- Essentials合并
- 全新命名空间
您仅需要具备Xamarin.Forms的一些基础概念
单个代码库演变
如下,演示了一个Xamarin.Forms典型应用的项目结构, 包含了一个共享类库以及2个原生平台的应用(Android/IOS)。
- MyApp(共享类库): 用于编写非特定平台的业务逻辑实现代码
- MyApp.Android(原生安卓应用):包含Android平台的特定配置、资源、程序集相关。
- MyApp.iOS(原生安卓应用): 包含IOS平台的特定配置、资源、程序集相关。
如果您的应用选择Windows平台, 则会多一个UWP项目选项, 类似,这仅仅是针对UWP平台的相关配置。
下面, 则演示了一个MAUI的典型应用的项目结果, 仅包含了一个项目。
MAUI 应用的项目包含 一个 Platform 文件夹,每个子文件夹都表示 .NET 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单项目解决了这个问题, 在 Resources 文件夹当中, 包含了所有的资源文件定义。
依赖注入
在Xamarin.Forms中,如果我们想要使用依赖注入,来实现特定平台的实现, 首先需要在共享类库中定义标准接口, 然后每个平台单独实现一遍。
在具体的实现类当中, 我们还需要程序集级别的特性标记, 以便于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);
MAUI则将Essentials全部融合到了MAUI项目当中, 命名控件则是 Microsoft.MAUI下, 例如访问设备。
using Microsoft.Maui.Devices
-
全新命名空间
Xamarin.Forms 当中我们使用的 using Xamarin.Forms 变成了 using Microsoft.Maui 相关。 -
性能改进
性能也带来了很大的改进, 可参考文章: .NET MAUI 中的性能改进 -
关于更多
1.事件处理程序
2.兼容现有的渲染器模式
后面再接着说...