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

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

    正在检查是否收录...
一言准备中...

前言

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

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

问题解答

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

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

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

名词解释

  • .NET MAUI

    :.NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架,用于使用 C# 和 XAML 创建本机移动和桌面应用。使用 .NET MAUI,可以从单个共享代码库开发可在 Android、iOS、macOS 和 Windows 上运行的应用。
    记一次.NET MAUI项目中绑定Android库实现硬件控制的开发经历

准备工作

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

软件环境

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

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

项目背景

这次要集成的是一个机器人控制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 
<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文件夹中的配置文件:

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

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

自定义绑定

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

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

服务接口定义

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平台实现的核心要点

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#事件,我设计了一个回调桥接类:

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绑定库:

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

服务注册和平台特定实现

MauiProgram.cs中配置依赖注入:

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是一个很重要的设计决策:

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设计也做了特殊适配:

<!-- 圆形屏幕容器 (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> 

这种设计的特点:

  • 圆形适配

    :所有内容都在圆形区域内显示
  • 分页滚动

    :采用"冰糖葫芦"式的垂直分页
  • 视觉层次

    :使用深色主题和圆角设计
  • 响应式布局

    :自动适配不同屏幕尺寸

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

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

总结感悟

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

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

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

参考资料

  • Binding a Java library
  • Microsoft Docs - Binding Android Libraries
  • .NET MAUI Documentation
  • Android AAR Format Specification
  • 示例代码
  • 自定义绑定

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



  • 本文作者:WAP站长网
  • 本文链接: https://wapzz.net/post-26946.html
  • 版权声明:本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 4.0 许可协议。
本站部分内容来源于网络转载,仅供学习交流使用。如涉及版权问题,请及时联系我们,我们将第一时间处理。
文章很赞!支持一下吧 还没有人为TA充电
为TA充电
还没有人为TA充电
0
  • 支付宝打赏
    支付宝扫一扫
  • 微信打赏
    微信扫一扫
感谢支持
文章很赞!支持一下吧
关于作者
2.7W+
8
1
1
WAP站长官方

数据库隔离级别

上一篇

使用Semantic Kernel实现Claude Code的Agents TODO能力

下一篇
评论区
内容为空

这一切,似未曾拥有

  • 复制图片
按住ctrl可打开默认菜单