7月16是什么星座| 纳财是什么意思| alt什么意思| 水垢是什么| 尿里红细胞高什么原因| 什么什么泪下| 配制酒是什么意思| 牙齿痛吃什么好| 知否知否应是绿肥红瘦什么意思| 舌头开裂是什么原因| 发烧42度是什么概念| 磨牙齿有什么方法可以治| 不耐受和过敏有什么区别| 面基什么意思| 什么玩意儿| 骨折吃什么好的快| 结石吃什么药| 血管瘤吃什么药| 颈椎反弓是什么意思| 接站是什么意思| 肝功能不全是什么意思| 客厅挂钟放在什么位置好| 给孩子测骨龄应该挂什么科| 脚气是什么菌引起的| 肮脏是什么意思| 1957年属什么生肖| 恃宠而骄什么意思| 晚上七点多是什么时辰| 落差是什么意思| 叶酸片是治什么的| 98属什么| 淋巴结肿大用什么药| 女性吃什么改善更年期| 血压忽高忽低是什么原因| 东方蝾螈吃什么| 中国的国树是什么| 体温低是什么原因| 变异性哮喘咳嗽吃什么药| 什么是热病| 粉皮是什么做的| 什么原因引起血糖高| 嘴里发甜是什么原因| 儿童牙龈肿痛吃什么药| 纤维蛋白原是什么意思| 鼻子痒是什么原因| 壮字五行属什么| 养胃喝什么| 解酒喝什么| 舌苔厚是什么原因引起的| 靶向治疗是什么意思| tbs和tct有什么区别| 迦字五行属什么| 什么是神经性皮炎| exo是什么意思啊| 男性生殖痒是什么原因| 小孩割包皮挂什么科室| 新白娘子传奇许仙为什么用女的演| 网络拒绝接入什么意思| 及笄是什么意思| pio是什么意思| 咸鸭蛋为什么会出油| 黄昏是什么时候| 左眼皮一直跳是什么原因| 病毒性扁桃体炎吃什么药| 胡萝卜和什么榨汁好喝| 做b超前需要注意什么| 冷暖自知上一句是什么| 5月22号是什么星座| 月经期间适合吃什么食物| 成人用品是什么| 脂溢性皮炎是什么引起的| 背上长痘痘是什么原因| 嘴唇发白是什么原因| hh是什么品牌| 什么药可以治早迣| 吃什么水果减肥| 吃什么清肺效果最好| 826是什么星座| 胰腺的作用和功能是什么| 人间仙境是什么意思| 梦见自己穿孝衣有什么征兆| 望梅止渴什么意思| 省人大代表是什么级别| 什么鸡| 变应性鼻炎是什么意思| 外聘是什么意思| 西瓜什么时候种| 胎儿什么时候入盆| 梦见好多死人是什么征兆| 2月2是什么星座| 为什么总是放屁| 肠胃消化不好吃什么食物| 脾门区结节是什么意思| 白细胞高说明什么| 做腋臭手术挂什么科室| 番茄和蕃茄有什么区别| 甲状腺适合吃什么食物| 开眼镜店需要什么设备| 天相是什么意思| 减肥早餐吃什么| 什么病可以申请低保| 失眠吃什么食物效果最好| dw是什么牌子的手表| 鹅蛋什么人不能吃| 十二生肖为什么老鼠排第一| hrv是什么意思| 太妃是皇上的什么人| 七月出生的是什么星座| 6月23日什么星座| 姨妈期可以吃什么水果| 与众不同是什么意思| 九寨沟在四川什么地方| 考验是什么意思| 曲解什么意思| 水瓶是什么星座| 腿麻木是什么原因引起的| 下巴下面长痣代表什么| 猪心炖什么好吃又营养| 手上长痣代表什么| 葫芦是什么生肖| 刘诗诗是什么样的人| ms是什么| 双子座爱吃什么| nt检查是什么意思| 一心一意是什么生肖| 现在有什么好的创业项目| 什么的名字| 天天吃玉米有什么好处和坏处| 后背疼是什么原因引起的| 女生私密部位长什么样| 吃饭吧唧嘴有什么说法| 仟字五行属什么| 50岁眼睛模糊吃什么好| 什么颜色加什么颜色等于棕色| 杀生电影讲的什么意思| 什么食物补血| 心脑血管供血不足吃什么药| 喝水都会胖是什么原因| 前列腺液是什么东西| 尿偏红色是什么原因| 辅酶q10的作用是什么| 肚子疼去医院挂什么科| 霉菌性阴道炎是什么| 屁股痛是什么原因| 感冒吃什么药好| 为什么不敢挖雍正陵墓| 招财进宝是什么生肖| 先敬罗衣后敬人是什么意思| b细胞淋巴肿瘤是一种什么病| 7号来的月经什么时候是排卵期| 氯雷他定片主治什么病| 扁平疣用什么药膏| 骨皮质扭曲是什么意思啊| 为什么得甲亢| 提前吃什么药喝酒不醉| 蒸鱼豉油什么时候放| 侍郎是什么官职| 幽门螺杆菌阴性是什么意思| 特异性生长因子指什么| 俄罗斯用什么货币| 小丑代表什么生肖| 平安夜送女朋友什么| 折煞是什么意思| 7月份是什么季节| 透骨草治什么病最有效| 蟑螂吃什么| 世界第一长河是什么河| 什么食物含胶原蛋白最多| 老年人打嗝不止是什么原因| 健康证照片用什么底色| 全性向是什么意思| 做蹲起有什么好处| 外阴过敏用什么药| 狗的五行属什么| 梦见怀孕的女人是什么意思| 男人梦见蛇是什么预兆| 耳朵真菌感染用什么药最好| 红绿色盲是什么遗传病| 冰枕对人有什么危害吗| 英雄难过美人关是什么生肖| nrc是什么意思| 什么是痤疮图片| 失眠吃什么中药| 肤专家抑菌软膏主要治什么| 艾滋病有什么危害| 祛是什么意思| 舌头溃疡吃什么药| 11楼五行属什么| 什么是社会考生| 心什么什么什么| 华侨是什么| 贫血吃什么维生素| 除草剂中毒有什么症状| 七月十三日是什么日子| 喝椰子汁有什么好处| 净身出户什么意思| 安可什么意思| 孟字五行属什么| 胸闷气短是什么病| 小孩便秘有什么办法| 什么叫人均可支配收入| 什么颜色衣服显白| 少阳是什么意思| px是什么| 阑尾炎疼吃什么药| 人设崩塌是什么意思| 大学什么时候开始收费| 破冰是什么意思| spyder是什么品牌| 党参和丹参有什么区别| 动物园里有什么动物| 怀孕什么时候能测出来| 怀孕一个月吃什么对宝宝发育好| 脚底烧热是什么原因| 风湿病是什么原因造成的| 为什么会得痛风| 吃了西瓜不能吃什么| 三七粉是什么| 肺痈是什么意思| 西南属什么五行| 什么水用不完| 吃了避孕药后几天出血是什么原因| 老是胃疼是什么原因| 九月十三号是什么星座| 胰尾显示不清什么意思| 人授和试管有什么区别| 什么时候补钙最佳时间| 有样学样是什么意思| 窝沟封闭是什么意思| 女猴配什么属相最好| 膛目结舌是什么意思| luxury什么牌子| 白发越来越多是什么原因造成的| 仪轨是什么意思| 病逝是什么意思| 胃病四联疗法是什么药| 马桶为什么叫马桶| 手掌心发热是什么原因| 纺织业属于什么行业| 花生有什么营养| 部分导联st段改变是什么意思| 睡觉出汗多是什么原因| 狗肉配什么菜好吃| 兔子的耳朵有什么作用| 泡脚不出汗是什么原因| 孢子阳性是什么意思| 生育能力检查挂什么科| 蚂蚁上树什么姿势| 套作是什么意思| 空气炸锅能做什么| 女人左下眼皮跳是什么预兆| 宫保鸡丁是什么菜系| 加拿大现在什么季节| ambush是什么牌子| 静电是什么| 头麻是什么病的前兆| 公鸡为什么会打鸣| 尽虚空遍法界什么意思| 菡什么意思| 辰五行属什么| 一个丝一个鸟读什么| 夏天猪骨煲什么汤最好| 门可罗雀是什么意思| 历程是什么意思| 百度

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

前言

百度 他说。

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

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

问题解答

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

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

img

名词解释

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

准备工作

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

软件环境

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

img

项目背景

这次要集成的是一个机器人控制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>

这种设计的特点:

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

img

img

总结感悟

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

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

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

参考资料


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

posted @ 2025-08-04 00:17  绿荫阿广  阅读(2052)  评论(19)    收藏  举报
猛吸气胸口疼什么原因 什么叫石女 长溃疡是缺什么维生素 经期肚子疼是什么原因 mpa什么意思
过什么不什么 首台套是什么意思 红斑狼疮吃什么药最好 血型o型rh阳性是什么意思 行房时硬度不够是什么原因
吃什么提高免疫力和增强体质 什么牙什么嘴 11月11日是什么星座 为什么右眼皮一直跳 dr检查是什么
kb什么意思 木节念什么 硫酸对人体有什么危害 吃什么记忆力增强 肠穿孔有什么症状
下葬有什么讲究或忌讳hcv8jop3ns9r.cn 利福喷丁和利福平有什么区别hcv9jop6ns4r.cn 温煦是什么意思hcv9jop5ns5r.cn 职称是什么hcv9jop0ns7r.cn 天孤星是什么意思hcv9jop0ns4r.cn
fast什么意思hcv9jop5ns2r.cn 多事之秋是什么意思hcv8jop7ns8r.cn 冻顶乌龙茶是什么茶hcv8jop6ns7r.cn 什么是虫草hcv9jop5ns9r.cn 白带是什么味道dayuxmw.com
松香有毒吗对人体有什么危害hcv8jop3ns7r.cn 几成是什么意思hcv9jop0ns3r.cn 阴道发臭是什么原因hcv9jop6ns0r.cn 皮肤黑适合穿什么颜色的衣服hcv9jop5ns0r.cn 痛风吃什么水果最好hlguo.com
摄政王是什么意思hcv9jop0ns9r.cn 血常规白细胞偏高是什么原因hcv7jop9ns8r.cn 幽门螺杆菌阳性吃什么药hcv9jop2ns9r.cn 血清铁蛋白是检查什么hcv7jop7ns0r.cn 右下眼皮跳是什么预兆hcv8jop1ns3r.cn
百度