Использование веб-служб с Xamarin на устройстве Android (не эмулятор): ошибка AOT - PullRequest
0 голосов
/ 16 марта 2020

Я изучаю Xamarin и Web App с помощью. Net. Цель состоит в том, чтобы создать веб-приложение (веб-службы) и приложение android, приложение android будет использовать веб-службы.

Я реализовал некоторые базовые вещи c в веб-приложении, и Сначала я попытался получить к нему доступ с помощью консольного приложения с HttpClient. Он работает нормально:

static HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(path); // path is the local path of my IIS server (https://localhost:44331/api/Employees)

Следующим шагом было сделать то же самое в приложении Xamarin, но, поскольку я работаю с устройством, а не с эмулятором, мое устройство не имеет доступа к локальному серверу IIS. Я обнаружил, что могу получить доступ к серверу IIS благодаря Conveyor, поэтому я добавляю подключаемый модуль Visual Studio, который показывает мне следующий путь: https://192.168.1.25: 45455 / api / Employees

I попробовал с этим путем в консольном приложении и все заработало. Я попытался получить доступ через браузер на моем устройстве android, оно работает. Но когда я использую его в приложении Xamarin, код выполняется до метода GetAsyn c (HttpResponseMessage response = await client.GetAsync("https://192.168.1.25:45455/api/Employees");), а затем ничего не происходит. Нет ошибок, нет исключений, ничего. Выполнение здесь заблокировано.

В окне вывода у меня появляется следующее сообщение:

03-15 20:27:04.261 D/Mono    ( 4779): Loading reference 11 of netstandard.dll asmctx DEFAULT, looking for System.Runtime.Serialization, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
03-15 20:27:04.262 D/Mono    ( 4779): Image addref System.Runtime.Serialization[0x789b9fc600] (asmctx DEFAULT) -> System.Runtime.Serialization.dll[0x789b9c8800]: 2
03-15 20:27:04.262 D/Mono    ( 4779): Prepared to set up assembly 'System.Runtime.Serialization' (System.Runtime.Serialization.dll)
03-15 20:27:04.262 D/Mono    ( 4779): Assembly System.Runtime.Serialization[0x789b9fc600] added to domain RootDomain, ref_count=1
03-15 20:27:04.263 D/Mono    ( 4779): AOT: image 'System.Runtime.Serialization.dll.so' not found: dlopen failed: library "System.Runtime.Serialization.dll.so" not found
03-15 20:27:04.263 D/Mono    ( 4779): AOT: image '/Users/builder/jenkins/workspace/archive-mono/2019-08/android/release/sdks/out/android-arm64-v8a-release/lib/mono/aot-cache/arm64/System.Runtime.Serialization.dll.so' not found: (null)
03-15 20:27:04.264 D/Mono    ( 4779): Config attempting to parse: 'System.Runtime.Serialization.dll.config'.
03-15 20:27:04.264 D/Mono    ( 4779): Config attempting to parse: '/Users/builder/jenkins/workspace/archive-mono/2019-08/android/release/sdks/out/android-arm64-v8a-release/etc/mono/assemblies/System.Runtime.Serialization/System.Runtime.Serialization.config'.
03-15 20:27:04.264 D/Mono    ( 4779): Assembly Ref addref netstandard[0x78b6e37a80] -> System.Runtime.Serialization[0x789b9fc600]: 2
03-15 20:27:04.264 D/Mono    ( 4779): Loading reference 0 of System.Runtime.Serialization.dll asmctx DEFAULT, looking for mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
03-15 20:27:04.264 D/Mono    ( 4779): Assembly Ref addref System.Runtime.Serialization[0x789b9fc600] -> mscorlib[0x78bdbf6700]: 52
Loaded assembly: System.Runtime.Serialization.dll [External]
[HotReload] (2020-03-15 20:26:53.4): INFO: HotReload: Initialized Agent.
[HotReload] (2020-03-15 20:27:06.6): INFO: Le rechargement à chaud XAML est connecté et prêt.
03-15 20:27:04.486 D/Mono    ( 4779): DllImport searching in: '__Internal' ('(null)').
03-15 20:27:04.486 D/Mono    ( 4779): Searching for 'java_interop_jnienv_new_string'.
03-15 20:27:04.486 D/Mono    ( 4779): Probing 'java_interop_jnienv_new_string'.
03-15 20:27:04.486 D/Mono    ( 4779): Found as 'java_interop_jnienv_new_string'.
03-15 20:27:04.492 D/Mono    ( 4779): DllImport searching in: '__Internal' ('(null)').
03-15 20:27:04.492 D/Mono    ( 4779): Searching for 'java_interop_jnienv_get_static_object_field'.
03-15 20:27:04.492 D/Mono    ( 4779): Probing 'java_interop_jnienv_get_static_object_field'.
03-15 20:27:04.492 D/Mono    ( 4779): Found as 'java_interop_jnienv_get_static_object_field'.
03-15 20:27:04.502 D/NetworkSecurityConfig( 4779): No Network Security Config specified, using platform default
03-15 20:27:04.503 I/DpmTcmClient( 4779): RegisterTcmMonitor from: com.android.okhttp.TcmIdleTimerMonitor
03-15 20:27:04.648 D/Mono    ( 4779): DllImport searching in: '__Internal' ('(null)').
03-15 20:27:04.648 D/Mono    ( 4779): Searching for 'java_interop_jnienv_call_nonvirtual_boolean_method_a'.
03-15 20:27:04.648 D/Mono    ( 4779): Probing 'java_interop_jnienv_call_nonvirtual_boolean_method_a'.
03-15 20:27:04.649 D/Mono    ( 4779): Found as 'java_interop_jnienv_call_nonvirtual_boolean_method_a'.

Так что я подумал, что это может происходить из этой системы ' AOT: image' .Runtime.Serialization.dll.so 'не найдено '. Просматривая в Интернете, я обнаружил, что должен установить <AotAssemblies>True</AotAssemblies> в файл project.csproj. Сначала я не знаю, как это сделать. Во-вторых, я использую сообщество Visual Studio и прочитал следующее: « Версия Visual Studio для сообщества не поддерживает AOT; и иногда в течение последних шести месяцев обновление Xamarin явно отключает его (если вы имели вручную включил AOT, отредактировав файл csproj.) Теперь необходимо иметь версию Enterprise для сборки с AOT.".

Так что сейчас я немного растерялся, потому что не знаю, что делать. Если кто-нибудь знает, что я могу сделать, это будет очень полезно.

Edit await client.GetAsync() вызывается в EmployeeServices классе с помощью следующей функции:

public static async Task<List<Employee>> GetEmployeesAsync()
    {
        List<Employee> employees = null;
        HttpResponseMessage response = await _client.GetAsync("https://192.168.1.25:45455/api/Employees");
        if (response.IsSuccessStatusCode)
        {
            string json = await response.Content.ReadAsStringAsync();
            employees = JsonConvert.DeserializeObject<List<Employee>>(json);
        }
        return employees;
    }

GetEmployeesAsync вызывается MainViewModel.cs в следующей функции:

 async Task<List<Employee>> IntermediateMethod()
    {
        return await EmployeesServices.GetEmployeesAsync();
    }

, которая сама вызывается в конструкторе MainViewModel:

public MainViewModel()
    {
        var employeesServices = new EmployeesServices();
        try
        {
             EmployeesList = IntermediateMethod().Result;
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
        //EmployeesList = employeesServices.GetEmployeesStatic();
    }

и MainViewModel находится на главной странице:

<ContentPage.BindingContext>
    <ViewModels:MainViewModel/>
</ContentPage.BindingContext>

1 Ответ

1 голос
/ 16 марта 2020

ОК, в основном то, что вы делаете в конструкторе, неверно.

Почему? Конструктор не асин c, и в зависимости от того, в каком контексте вы вызываете этот конструктор, вы попадете в тупик, поскольку вы вызываете .Result для Задачи.

Вместо этого я предлагаю вам использовать один из следующих способов: методы жизненного цикла в Xamarin.Forms, которые вы, кажется, используете. Я предлагаю использовать OnAppearing переопределение, чтобы начать получать данные:

protected override async void OnAppearing()
{
    // get data
}

Также я бы предложил инкапсулировать получение данных и в шаблоне Command:

public ICommand GetDataCommand { get; }

Затем в вашей ViewModel конструктор инициализирует его:

public MainViewModel()
{
    GetDataCommand = new Command(async () => await DoGetDataCommand());
}

Затем в DoGetDataCommand:

private async Task DoGetDataCommand()
{
    try
    {
        Employees = await EmployeesServices.GetEmployeesAsync();
    }
    catch (Exception ex)
    {
        // TODO: handle exception
    }
}

Затем в вызове OnAppearing: GetDataCommand.Execute(null);.

Дополнительно. Все ожидаемые задачи в не-пользовательском интерфейсе. Это означает, что когда они не находятся внутри ViewModel, вам следует подумать о добавлении к ним .ConfigureAwait(false), например:

var response = await _client.GetAsync("https://192.168.1.25:45455/api/Employees").ConfigureAwait(false);

То, что это будет делать, - это не пытаться переключиться обратно на поток, из которого мы пришли. Это ограничит накладные расходы на переключение между потоками назад и вперед, что может вызвать взаимные блокировки, если поток, на который вы пытаетесь переключиться, уже занят.

Ваша ViewModel будет выглядеть примерно так:

public class MainViewModel : BaseViewModel
{
    private List<Employee> _employees;

    public ICommand GetDataCommand { get; }

    public List<Employee> Employees
    {
        get => _employees;
        set => SetProperty(ref _employees, value);
    }

    public MainViewModel()
    {
         GetDataCommand = new Command(async () => await DoGetDataCommand());
    }

    private async Task DoGetDataCommand()
    {
        try
        {
            Employees = await EmployeesServices.GetEmployeesAsync();
        }
        catch (Exception ex)
        {
            // TODO: handle exception
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...