记一次.NET MAUI项目中绑定Android库实现硬件控制的开发经历

前言

最近AI小智对话机器人实在是太火了,于是我就把我之前的一个吃灰的安卓桌面机器人给拿出来玩了,我想着基于安卓的系统开发一些自己的软件操作它,我翻了下官方文档也是有提供SDK的,于是我就开始了这个开发尝试。机器人本身是有丰富的传感器,也有完整的麦克风摄像头可以用,那做个会动的小智机器人刚刚好,第一步肯定是先让它能够按我的操作动起来。

这个过程虽然有一些小坑,但最终成功实现了完整的硬件控制功能。今天就来分享一下这次Android库绑定的完整经历,希望能帮助到有类似需求的小伙伴们。

问题解答

Q: 为什么选择.NET MAUI来进行开发?

A: .NET MAUI本身是支持跨平台开发的,这是选择它的主要原因之一。还有就是我之前比较熟悉WinUI开发,对xaml的语法也算是比较熟悉,当然跨平台还有Avalonia UI,这个社区活跃度比.NET MAUI还高,但是由于MAUI能够满足我的需求,暂时还没尝试这个框架,大家有兴趣的可以试试它。

名词解释

  • .NET MAUI :.NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架,用于使用 C# 和 XAML 创建本机移动和桌面应用。使用 .NET MAUI,可以从单个共享代码库开发可在 Android、iOS、macOS 和 Windows 上运行的应用。

准备工作

在开始编码之前,我们需要准备以下环境:

软件环境

  • Visual Studio 2022
  • .NET 9 SDK
  • Visual Studio 2022要安装MAUI的工作负载,并且记得创建安卓虚拟机。

项目背景

这次要集成的是一个机器人控制SDK(RobotSDK),它以AAR格式提供,包含了机器人的运动控制、传感器监听、表情控制、语音播放等功能。我们的目标是在.NET MAUI应用中使用这些原生功能,实现跨平台的机器人控制应用。

技术选型和架构设计

整体架构

复制代码
┌─────────────────────┐    ┌──────────────────────┐    ┌─────────────────────┐
│  MAUI UI Layer      │    │  Service Interface   │    │  Platform Services  │
│  (MainPage.xaml)    │◄──►│  IRobotControlService│◄──►│  AndroidRobotControl│
│  ViewModels         │    │                      │    │  DefaultRobotControl│
└─────────────────────┘    └──────────────────────┘    └─────────────────────┘
                                      │
                                      ▼
                           ┌──────────────────────┐
                           │ RobotSDK.Android     │
                           │ Binding Library      │
                           │ (AAR Wrapper)        │
                           └──────────────────────┘
                                      │
                                      ▼
                           ┌──────────────────────┐
                           │ Native Android       │
                           │ RobotSDK AAR         │
                           │ (Hardware Control)   │
                           └──────────────────────┘

核心技术栈

  • .NET 9.0 MAUI - 跨平台UI框架
  • Android Binding Library - AAR库绑定
  • Dependency Injection - 服务注册和平台特定实现
  • MVVM模式 - 数据绑定和状态管理

第一步:创建Android绑定库项目

官方参考文档如下:
Binding a Java library

首先创建一个专门的Android绑定库项目来包装原生AAR文件:

使用下面的指令进行项目的创建

复制代码
dotnet new android-bindinglib
xml 复制代码
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net9.0-android</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <SupportedOSPlatformVersion>24.0</SupportedOSPlatformVersion>
  </PropertyGroup>
  
  <ItemGroup>
    <AndroidLibrary Include="Jars\RobotSdk-release-2.5.aar" />
  </ItemGroup>
  
  <ItemGroup>
    <TransformFile Include="Transforms\Metadata.xml" />
    <TransformFile Include="Transforms\EnumFields.xml" />
    <TransformFile Include="Transforms\EnumMethods.xml" />
  </ItemGroup>
</Project>

关键配置说明

  1. 目标框架 :使用net9.0-android确保与MAUI项目兼容
  2. 最低Android版本:设置为API 24,确保设备兼容性
  3. AAR文件引用 :通过AndroidLibrary引用原生库文件
  4. 转换文件:用于处理Java到C#的类型映射

第二步:处理绑定过程中的常见问题

在绑定过程中,经常会遇到一些类型映射和命名冲突问题,这时候就需要用到Transforms文件夹中的配置文件:

由于目前的项目比较简单,这部分的映射文件我就使用了项目默认生成的了。

大家有需要可以看官方文档的一些注意事项。

自定义绑定

第三步:设计服务接口和平台实现

为了保证代码的可测试性和平台兼容性,我设计了一套清晰的服务接口:

服务接口定义

csharp 复制代码
public interface IRobotControlService : IRobotSensorEvents
{
    // 基础控制
    Task<bool> InitializeAsync();
    bool IsServiceAvailable { get; }
    
    // 传感器控制
    Task StartSensorMonitoringAsync();
    Task StopSensorMonitoringAsync();
    
    // 运动控制
    Task MoveForwardAsync(int speed = 3, int steps = 1);
    Task MoveBackwardAsync(int speed = 3, int steps = 1);
    Task TurnLeftAsync(int speed = 3, int steps = 1);
    Task TurnRightAsync(int speed = 3, int steps = 1);
    
    // 表情和语音
    Task ShowExpressionAsync(string expression);
    Task SpeakAsync(string text);
    Task SpeakWithExpressionAsync(string text, string expression);
    
    // 硬件控制
    Task EnableMotorAsync();
    Task DisableMotorAsync();
    Task SetAntennaLightAsync(int color);
    Task MoveAntennaAsync(int cmd, int step, int speed, int angle);
}

public interface IRobotSensorEvents
{
    event EventHandler? TapDetected;
    event EventHandler? DoubleTapDetected;
    event EventHandler? LongPressDetected;
    event EventHandler? FallBackwardDetected;
    event EventHandler? FallForwardDetected;
    event EventHandler? FallRightDetected;
    event EventHandler? FallLeftDetected;
    event EventHandler? TofDetected;
}

Android平台实现的核心要点

csharp 复制代码
public class AndroidRobotControlService : IRobotControlService
{
    private readonly ILogger<AndroidRobotControlService> _logger;
    private readonly Context _context;
    private RobotService? _robotService;
    private SensorCallbackImpl? _sensorCallback;
    
    public async Task<bool> InitializeAsync()
    {
        try
        {
            _logger.LogInformation("初始化Android机器人服务...");
            
            // 获取原生SDK实例
            _robotService = RobotService.GetInstance(_context);
            
            if (_robotService == null)
            {
                _logger.LogError("无法获取RobotService实例");
                return false;
            }
            
            // 创建回调桥接
            _sensorCallback = new SensorCallbackImpl(
                onTap: () => TapDetected?.Invoke(this, EventArgs.Empty),
                onDoubleTap: () => DoubleTapDetected?.Invoke(this, EventArgs.Empty),
                onLongPress: () => LongPressDetected?.Invoke(this, EventArgs.Empty),
                // ... 其他传感器事件
            );
            
            // 自动启用电机
            _robotService.RobotOpenMotor();
            await Task.Delay(500);
            
            _isInitialized = true;
            _logger.LogInformation("Android机器人服务初始化成功");
            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "初始化Android机器人服务失败");
            return false;
        }
    }
}

回调桥接的巧妙设计

为了将Java回调转换为C#事件,我设计了一个回调桥接类:

csharp 复制代码
public class SensorCallbackImpl : Java.Lang.Object, ISensorCallback
{
    private readonly Action _onTap;
    private readonly Action _onDoubleTap;
    private readonly Action _onLongPress;
    // ... 其他事件委托
    
    public SensorCallbackImpl(
        Action onTap,
        Action onDoubleTap,
        Action onLongPress,
        // ... 其他参数
    )
    {
        _onTap = onTap;
        _onDoubleTap = onDoubleTap;
        _onLongPress = onLongPress;
        // ... 赋值操作
    }
    
    // 实现Java接口方法,转发到C#委托
    public void OnTapResponse() => _onTap?.Invoke();
    public void OnDoubleTapResponse() => _onDoubleTap?.Invoke();
    public void OnLongPressResponse() => _onLongPress?.Invoke();
    // ... 其他方法
}

第四步:MAUI项目集成和依赖注入配置

项目引用配置

在MAUI项目的csproj文件中,需要有条件地引用Android绑定库:

xml 复制代码
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
  <ProjectReference Include="..\RobotSDK.Android.Binding\RobotSDK.Android.Binding.csproj" />
</ItemGroup>

服务注册和平台特定实现

MauiProgram.cs中配置依赖注入:

csharp 复制代码
public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
            });

        // 注册服务
        builder.Services.AddSingleton<MainPageViewModel>();
        
        // 平台特定服务注册
#if ANDROID
        builder.Services.AddSingleton<IRobotControlService, AndroidRobotControlService>();
#else
        builder.Services.AddSingleton<IRobotControlService, DefaultRobotControlService>();
#endif

        // 添加调试日志
        builder.Logging.AddDebug();

        return builder.Build();
    }
}

为什么要有Default实现?

创建DefaultRobotControlService是一个很重要的设计决策:

csharp 复制代码
public class DefaultRobotControlService : IRobotControlService
{
    private readonly ILogger<DefaultRobotControlService> _logger;

    public bool IsServiceAvailable => false;

    public Task<bool> InitializeAsync()
    {
        _logger.LogWarning("机器人控制服务仅在Android平台可用");
        return Task.FromResult(false);
    }
    
    public Task MoveForwardAsync(int speed = 3, int steps = 1)
    {
        _logger.LogWarning("动作控制仅在Android平台可用");
        return Task.CompletedTask;
    }
    
    // ... 其他方法的空实现
}

这样做的好处:

  1. 开发效率:可以在Windows上进行UI开发和测试
  2. 代码安全:避免运行时出现服务注册失败
  3. 团队协作:团队成员无需Android设备即可进行开发

第五步:UI设计和圆形屏幕适配

考虑到目标设备是圆形屏幕的机器人,UI设计也做了特殊适配:

xaml 复制代码
<!-- 圆形屏幕容器 (480x480) -->
<Grid>
    <!-- 圆形边框指示器 -->
    <Ellipse Fill="Transparent" 
             Stroke="DarkGray" 
             StrokeThickness="2"
             Margin="10" />
    
    <!-- 冰糖葫芦式垂直滚动容器 -->
    <ScrollView x:Name="MainScrollView" 
                Orientation="Vertical" 
                HorizontalScrollBarVisibility="Never"
                VerticalScrollBarVisibility="Never"
                BackgroundColor="Transparent"
                Padding="0,0,0,50">
        
        <StackLayout Spacing="0" BackgroundColor="Transparent">
            <!-- 第1个圆形区域 - 状态和连接控制 -->
            <Grid HeightRequest="480" WidthRequest="480" BackgroundColor="Transparent">
                <Ellipse Fill="#1A1A2E" 
                         Stroke="#16213E" 
                         StrokeThickness="3"
                         Margin="40" />
                
                <!-- 内容区域 -->
                <StackLayout Spacing="25" Margin="60" VerticalOptions="Center">
                    <!-- UI内容 -->
                </StackLayout>
            </Grid>
        </StackLayout>
    </ScrollView>
</Grid>

这种设计的特点:

  • 圆形适配:所有内容都在圆形区域内显示
  • 分页滚动:采用"冰糖葫芦"式的垂直分页
  • 视觉层次:使用深色主题和圆角设计
  • 响应式布局:自动适配不同屏幕尺寸

总结感悟

在调试的时候遇到一个小坑,明明代码是根据机器人官方的SDK文档进行的初始化,但是不生效,机器人的舵机就是动不了,后面发现是因为代码要加一些延时,不然机器反应不过来就控制不了了。后来想想不同类别的开发,思考问题的角度还是不太一样。

AI发展速度真的是太快了,这个项目我是自己通过调试简单的代码,然后通过让AI反编译aar的文件,最后整理了一些文档,再让AI根据整理的文档实现的代码是很详细了,节省了大量的时间,感觉有了AI效率提高很多了,你们对AI写代码是怎么看待的,欢迎评论区讨论讨论。

希望这篇文章能够为大家在.NET MAUI项目中集成Android原生库提供一些参考和帮助。如果在实践过程中遇到问题,欢迎在评论区交流讨论!

参考资料


本文示例代码已上传至GitHub,欢迎大家参考学习。如果觉得有帮助,请给个Star支持一下!

相关推荐
一个帅气昵称啊3 小时前
.Net通过EFCore和仓储模式实现统一数据权限管控并且相关权限配置动态生成
.net·efcore·仓储模式
helloworddm5 小时前
CalculateGrainDirectoryPartition
服务器·c#·.net
步步为营DotNet6 小时前
深度剖析.NET中HttpClient的请求重试机制:可靠性提升与实践优化
开发语言·php·.net
ChaITSimpleLove6 小时前
使用 .net10 构建 AI 友好的 RSS 订阅机器人
人工智能·.net·mcp·ai bot·rss bot
专注VB编程开发20年6 小时前
vb.net宿主程序通过统一接口直接调用,命名空间要一致
服务器·前端·.net
ChaITSimpleLove21 小时前
基于 .NET Garnet 1.0.91 实现高性能分布式锁(使用 Lua 脚本)
分布式·.net·lua
用户4488466710601 天前
.NET进阶——深入理解线程(2)Thread入门到精通
c#·.net
一个帅气昵称啊1 天前
.Net——AI智能体开发基于 Microsoft Agent Framework 实现第三方聊天历史存储
人工智能·microsoft·.net