Xamarin forms хочу использовать один проект с платформ зависимым кодом для Android и Ios

создал новый solution Xamarin.forms и начал разбираться с библиотекой Shiny, т.к. нужна работа с BluetoothLE и Beacon маяками. Подключил модуль Shiny.BluetoothLE и написал сканер Bluetooth устройств, не понятны были некоторые моменты работы библиотеки, поэтому для изучения решил подключить исходники проекта (а не nuget либу) Shiny.BluetoothLE. https://github.com/shinyorg/shiny/tree/master/src/Shiny.BluetoothLE

Я создал проект и решил по частям добавлять код из Shiny.BluetoothLE.

для компиляции проекта под разные платформы используется "MSBuild.Sdk.Extras"

 добавил global.json в solution 
 {
    "msbuild-sdks": {
        "MSBuild.Sdk.Extras": "3.0.23"
    }
}

Настройки проекта:

<Project Sdk="MSBuild.Sdk.Extras">
    <PropertyGroup>
        <TargetFrameworks>netstandard2.1;monoandroid10.0</TargetFrameworks>
        <Nullable>enable</Nullable>
        <TargetFrameworks Condition=" $(OS) == 'WINDOWS_NT' ">$(UwpTargetFramework);$(TargetFrameworks)</TargetFrameworks>
    </PropertyGroup>

    <ItemGroup>
        <Compile Remove="**\Platforms\**\*.cs" />
        <None Include="**\Platforms\**\*.cs" />
    </ItemGroup>

    <ItemGroup Condition=" $(TargetFramework.StartsWith('netstandard')) ">
        <PackageReference Include="Shiny.BluetoothLE.Common" Version="2.1.0.2769" />
        <PackageReference Include="Shiny.Core" Version="2.1.0.2769" />
        <Compile Include="**\Platforms\Shared\**\*.cs" />
    </ItemGroup>

    <ItemGroup Condition=" $(TargetFramework.StartsWith('monoandroid')) ">
        <PackageReference Include="Shiny.BluetoothLE.Common" Version="2.1.0.2769" />
        <PackageReference Include="Shiny.Core" Version="2.1.0.2769" />
        <Compile Include="**\Platforms\Shared\**\*.cs" />
        <Compile Include="**\Platforms\Android\**\*.cs" />
    </ItemGroup> 
</Project>

введите сюда описание изображения

Вначале решил добиться работоспособности условной компиляции и внедрение через DI нужных реализаций сервиса IBleManager (под каждую платформу свою). Добавил IBleManager, затем его реализацию BleManager для Android (пока добавил только 1 платформу) и его зависимость ManagerContext. КОД УРЕЗАННЫЙ ДЛЯ ДЕМОНСТРАЦИИ.

using System;
using Shiny;
public interface IBleManager
{
    IObservable<AccessState> RequestAccess();
}

using System;
using Infrastructure.BluetoothLE.Platforms.Android.Internals;
using Shiny;
public class BleManager : IBleManager
{
    readonly ManagerContext _context;
    public BleManager(ManagerContext context) => this._context = context;
    
    public IObservable<AccessState> RequestAccess()
    {
        throw new NotImplementedException();
    }
}

using System;
using System.Reactive.Linq;
using Android.Bluetooth;
using Android.Bluetooth.LE;
using Microsoft.Extensions.Logging;
using Shiny;
using Shiny.BluetoothLE;
using Shiny.Infrastructure; 
public class ManagerContext : IShinyStartupTask
{
    readonly ShinyCoreServices services;
    readonly ILogger logger;
    
    
    public ManagerContext(ShinyCoreServices services,
                          BleConfiguration config,
                          ILogger<ManagerContext> logger)
    {
        this.Configuration = config;
        this.services = services;
        this.logger = logger;
    }


    public void Start()
    {
        var builder = new ScanSettings.Builder();   //Android.Bluetooth.LE зависимость
    }


    public IServiceProvider Services => this.services.Services;
    public AccessState Status => this.Manager.GetAccessState();
    public IObservable<AccessState> StatusChanged() => ShinyBleAdapterStateBroadcastReceiver
        .WhenStateChanged()
        .StartWith(this.Status);


    public BleConfiguration Configuration { get; }
    public BluetoothManager Manager { get; }
    public IAndroidContext Android => this.services.Android;
}

затем зарегистрировать все зависимости в DI. ShinyModule - модуль для регистрации по аналогии с Autofac. ShinyModule и вся библиотека Shiny использует Microsoft.Extensions.DependencyInjection в качестве DI

#if !NETSTANDARD
using Infrastructure.BluetoothLE.Platforms.Android;
using Infrastructure.BluetoothLE.Platforms.Android.Internals;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Shiny;
namespace Infrastructure.BluetoothLE.Platforms.Shared
{
    public class BleShinyModule : ShinyModule
    {
        readonly BleConfiguration config;
        public BleShinyModule(BleConfiguration config) => this.config = config ?? new BleConfiguration();


        public override void Register(IServiceCollection services)
        {
            services.AddSingleton(this.config);
            services.TryAddSingleton<ManagerContext>();
            services.TryAddSingleton<IBleManager, BleManager>();
        }
    }
}
#endif

using Microsoft.Extensions.DependencyInjection;
using Shiny;
namespace Infrastructure.BluetoothLE.Platforms.Shared
{
    public static class ServiceCollectionExtensions
    {
        public static bool UseBleClient_Infr(this IServiceCollection builder, BleConfiguration? config = null)
        {
            
    #if NETSTANDARD
                 return false;
    #else
                 builder.RegisterModule(new BleShinyModule(config));
                 return true;
    #endif
        }
    }
}

Вызов из Xamarin.Forms проекта. Shiny вызывает ConfigureServices класса Startup для регистрации зависимостей

public class Startup : ShinyStartup
{
    public override void ConfigureServices(IServiceCollection services, IPlatform platform)
    {
        services.AddSingleton<IDialogs, Dialogs>();
        
        // register all of the shiny stuff you want to use
        var r1= services.UseBleClient(); //Shiny
        var r2= services.UseBleClient_Infr(); //моя реализация
        
        //r1 - true,  IBleManager регистрируется нормально
        //r2 - false !!!!      
    }
}

Запускаю Debug на Android устройстве и срабатывает ветка NETSTANDARD условной компиляции и ничего не регистрируется. Хотя регистрируяShiny.BluetoothLE из nuget пакета возвращается true и все работает.

Если оставить только код

 builder.RegisterModule(new BleShinyModule(config));
 return true;

и снять условие #if !NETSTANDARD с BleShinyModule То будет ошибка: BleShinyModule.cs(2, 44): [CS0234] Тип или имя пространства имен "Android" не существует в пространстве имен "Infrastructure.BluetoothLE.Platforms" (возможно, отсутствует ссылка на сборку).

От чего зависит условная компиляция? где-то нужно выставлять флаги под что компилировать? Я думал что при запуске на Android автоматически будет выбран вариант Android.
Т.е. я как-то не верно использую MSBuild.Sdk.Extras для компиляции разных вариантов или условную компиляцию? Почему аналогичный код работает у Shiny?

И более глобальный вопрос) такой подход является нормальным для реализации специфических вешей для каждой платформы в одном проекте или лучше как то еще делать?


Ответы (0 шт):