开始使用本机库互操作

本文介绍了如何使用 Maui.NativeLibraryInterop 开始使用本机库互操作来简化设置。

这些说明概述了通过本机库互操作创建绑定的基本步骤、关键决策点和指导示例。 有关特定 API 和实现详细信息的进一步指南,请参阅相关本机 SDK 和库的文档。

先决条件

安装系统必备组件:

注意

可以独立安装Android SDK和/或Xcode 命令行工具。 但是,通常通过Xcode处理Xcode 命令行工具的安装。 同样,Android SDK安装通常也根据.NET MAUI 入门文档通过Android Studio和/或.NET MAUI VS Code 扩展进行处理。

新建绑定

开始新建绑定的最简单方法是在maui.NativeLibraryInterop存储库中克隆模板,并从那里进行修改。 要更好地了解 Maui.NativeLibraryInterop 当前如何设置的完整范围,请阅读概述文档

设置 .NET 绑定库

模板包括适用于 Android 的初学者 .NET 和适用于 iOS 绑定库的 .NET。

更新绑定库,以反映 .NET 应用中所需的目标平台和 .NET 版本。

注意

例如:如果只想使用 .NET 9 创建 iOS 绑定,则可以:

  1. template/android/NewBinding.Android.Binding中删除 Android 绑定库,并
  2. template/macios/NewBinding.MaciOS.Binding/NewBinding.MaciOS.Binding.csproj中更新目标框架,以设置为net9.0-ios

设置本机包装器项目和库

模板还包括初学者 Android Studio 项目和 Xcode 项目。

更新本机项目以反映 .NET 应用中所需的目标平台和版本,并在以下步骤中包含感兴趣的本机库。

设置:iOS 和 Mac Catalyst

Xcode 项目位于 template/macios/native/NewBinding。

更新 Xcode 项目,以反映 .NET 应用中受支持的目标平台和版本。 在 Xcode 项目中,点击顶级框架,并在目标 >常规中:

  1. 根据需要添加/删除任何支持目标
  2. 根据需要调整 iOS 版本。

通过最适合库和需求的方法(例如 CocoaPods、Swift 包管理器),将适用于 iOS 和/或 MacCatalyst 的本机库引入 Xcode 项目。

设置:Android

Android Studio 项目位于template/android/native上。

更新 Android Studio 项目,以反映 .NET 应用中受支持的目标版本。

  1. 导航到build.gradle.kts (:app)文件
  2. 根据需要更新compileSdk版本

通过 gradle 引入 Android 本机库

  1. build.gradle.kts (:app)文件的依赖项块中添加包依赖项。
  2. 将存储库添加到settings.gradle.kts文件中的 dependencyResolutionManagement repositories 块。
  3. 使用 gradle 文件同步项目(通过 Android Studio 右上角的按钮)。

创建 API 接口

使用以下步骤在本机项目和 .NET 绑定项目之间创建 API 接口。

API 定义:iOS 和 Mac Catalyst

在本机端,在template/macios/native/NewBinding/NewBinding/DotnetNewBinding.swift中进行更新:

  1. 添加 import 语句以导入刚添加的本机库。
  2. 为感兴趣的本机库 API 编写 API 定义。
  3. 确保 Xcode 项目成功生成,并且你对 API 感到满意。

回到 .NET 端,我们现在可以与本机库进行互操作:

  1. template/macios/NewBinding.MaciOS.Binding运行dotnet build,以测试一切已正确插入并正常运行。 如果成功,应在 template/macios/native/NewBinding/bin/Release/net8.0-ios/sharpie/NewBinding/ApiDefinitions.cs 看到生成的 C# 绑定
  2. 通过将 template/macios/NewBinding.MaciOS.Binding/ApiDefinition.cs 的内容替换为 template/macios/native/NewBinding/bin/Release/net8.0-ios/sharpie/NewBinding/ApiDefinitions.cs 的内容以进行更新
  3. 再次从 template/macios/NewBinding.MaciOS.Binding运行dotnet build

API 定义:Android

在本机端,在template/android/native/app/src/main/java/com/example/newbinding/DotnetNewBinding.java中进行更新:

  1. 添加 import 语句以导入刚添加的本机库。
  2. 为感兴趣的本机库 API 编写 API 定义。
  3. 确保 Android Studio 项目成功生成,并且你对 API 感到满意。

回到 .NET 端,我们现在可以与本机库进行互操作:

  1. 从 template/android/NewBinding.Android.Binding 运行 dotnet build,以测试一切已正确插入并正常运行。 (注意:此步骤需要安装 JDK 17)
  2. 通过为每个依赖项将以下代码添加到 template/sample/MauiSample.csproj 来引用任何 Android 绑定依赖项。 将 {yourDependencyLibrary.aar} 替换为要绑定的依赖项所需的 .aar 文件。 (注意:通常需要显式引用 gradle/maven 依赖项,因为它们不会自动捆绑到库中。build.gradle.kts 文件配置为将依赖项复制到 bin/outputs/deps 文件夹中,以便在应用程序中引用
<ItemGroup Condition="$(TargetFramework.Contains('android'))">
    <AndroidLibrary Include="..\android\native\newbinding\bin\Release\net8.0-android\outputs\deps\{yourDependencyLibrary.aar}">
        <Bind>false</Bind>
        <Visible>false</Visible>
    </AndroidLibrary>
</ItemGroup>

注意

可以重命名占位符DotnetNewBinding类以更好地反映正在包装的本机库。 有关编写 API 定义的更多示例和提示,请阅读以下部分中的详细信息:修改现有绑定

在 .NET 应用中使用 API

模板包括template/sample/MauiSample上的 .NET MAUI 示例应用,该应用引用 .NET 绑定项目,并使本机库立即可供使用!

但是,如果有兴趣使用自己的 .NET MAUI、适用于 Android 的 .NET、适用于 iOS 的 .NET 和/或适用于 Mac Catalyst 的 .NET 应用,则可以通过修改 .NET 应用项目文件以引用绑定库来执行此操作:

<!-- Reference to MaciOS Binding project -->
<ItemGroup Condition="$(TargetFramework.Contains('ios')) Or $(TargetFramework.Contains('maccatalyst'))">
    <ProjectReference Include="..\..\macios\NewBinding.MaciOS.Binding\NewBinding.MaciOS.Binding.csproj" />
</ItemGroup>

<!-- Reference to Android Binding project -->
<ItemGroup Condition="$(TargetFramework.Contains('android'))">
    <ProjectReference Include="..\..\android\NewBinding.Android.Binding\NewBinding.Android.Binding.csproj" />
</ItemGroup>

修改现有绑定

如果现有 API 面未公开你自己的项目中所需的功能,则是时候进行自己的修改了!

iOS 和 Mac Catalyst

在 Xcode 项目中,可以找到一个或多个 Swift 文件,用于定义绑定的公共 API 面。 例如,Firebase Messaging 的register方法定义为:

@objc(MauiFIRMessaging)
public class MauiFIRMessaging : NSObject {

    @objc(register:completion:)
    public static func register(apnsToken: NSData, completion: @escaping (String?, NSError?) -> Void) {
        let data = Data(referencing: apnsToken);
        Messaging.messaging().apnsToken = data
        Messaging.messaging().token(completion: { fid, error in
            completion(fid, error as NSError?)
        })
    }
    // ...
}

注意

.NET 绑定将使用的本机包装器 API 类型必须声明为public,并且需要使用@objc(NameOfType)进行注释,方法还需要为public,并且还可以受益于类似的注释@objc(methodName:parameter1:),其中指定名称和参数,这有助于影响 objective sharpie 将生成的绑定。

在此方法中可以看到,公共 API 面仅使用适用于 iOS 的 .NET 已知道NSDataStringNSError和回调的类型。

Firebase.MaciOS.Binding项目中,ApiDefinitions.cs文件包含此本机包装器 API 的绑定定义:

using System;
using Foundation;

namespace Firebase
{
    // @interface MauiFIRMessaging : NSObject
    [BaseType (typeof(NSObject))]
    interface MauiFIRMessaging
    {
        [Static]
        [Export ("register:completion:")]
        [Async]
        void Register (NSData apnsToken, Action<string?, NSError?> completion);
        // ...
    }

假设你要添加用于注销的方法。 Swift 代码如下所示:

@objc(unregister:)
public static func unregister(completion: @escaping (NSError?) -> Void) {
    // need delegate to watch for fcmToken updates
    Messaging.messaging().deleteToken(completion: { error in
        completion(error as NSError?)
    })
}

另一半是更新绑定项目中的ApiDefinitions.cs文件以公开此新方法。 可通过两种方式实现此目的:

  1. 可以手动添加所需的代码
  2. 绑定项目生成时,会运行 objective sharpie,并在native/macios/bin/sharpie(当然,此路径因你正在处理的项目而异)文件夹中生成ApiDefinitions.cs文件。 可以尝试查找此文件中的相关更改并手动复制它们,或者尝试复制整个文件并查看差异以查找所需的部分。

在这种情况下,对ApiDefinitions.cs的更改会是:

[Static]
[Export("unregister:")]
[Async]
void UnRegister(Action completion);

进行这些更改后,可以重新生成绑定项目,新的 API 将准备好从 .NET MAUI 项目使用。

注意

Mac/iOS 的绑定项目不使用源生成器,因此,在重新生成绑定项目并重新加载解决方案,以便项目引用选取较新的程序集之前,项目系统和 intellisense 可能不知道新 API。 无论是什么 Intellisense 错误,应用项目仍应编译。

Android

在 Android Studio 项目中,你将找到模块目录,其中包含定义绑定的公共 API 面的 java 文件。 例如,Facebook initialize方法的定义如下:

package com.microsoft.mauifacebook;

import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;

import com.facebook.LoggingBehavior;
import com.facebook.appevents.AppEventsLogger;

public class FacebookSdk {

    static AppEventsLogger _logger;

    public static void initialize(Activity activity, Boolean isDebug) {
        Application application = activity.getApplication();

        if (isDebug) {
            com.facebook.FacebookSdk.setIsDebugEnabled(true);
        }

        com.facebook.FacebookSdk.addLoggingBehavior(LoggingBehavior.APP_EVENTS);

        AppEventsLogger.activateApp(application);

        _logger = AppEventsLogger.newLogger(activity);
    }

    // ...
}

在此方法中可以看到,公共 API 面仅使用适用于 Android 的 .NET 已知道ActivityBoolean的类型。

Facebook.Android.Binding项目中,Transforms/Metadata.xml文件仅包含一些 xml,用于描述如何将 java 包名称(com.microsoft.mauifacebook)映射到更友好的 C# 命名空间(Facebook)。 通常,此时 Android 绑定比 Mac/iOS 更“自动”,你很少需要对这些转换文件进行更改。

<metadata>
    <attr path="/api/package[@name='com.microsoft.mauifacebook']" name="managedName">Facebook</attr>
</metadata>

假设你要添加用于记录事件的方法。 Java 代码如下所示:

public static void logEvent(String eventName) {
    _logger.logEvent(eventName);
}

在此简单更改中,绑定项目无需更新Transforms/Metadata.xml或其他文件。 只需重新生成绑定项目,新的 API 即可从 .NET MAUI 项目使用。

注意

Android 的绑定项目不使用源生成器,因此,在重新生成绑定项目并重新加载解决方案,以便项目引用选取较新的程序集之前,项目系统和 intellisense 可能不知道新 API。 无论是什么 Intellisense 错误,应用项目仍应编译。