简体   繁体   English

在 Android 设备(不是模拟器)上使用 Xamarin 使用 Web 服务:AOT 错误

[英]Consuming Web Services with Xamarin on Android device (not emulator) : AOT error

I am learning Xamarin and Web App with .Net.我正在使用 .Net 学习 Xamarin 和 Web App。 The goal is to build a web app (web services) and an android app, the android app will consume web services.目标是构建一个 web 应用程序(web 服务)和一个 android 应用程序,android 应用程序将使用 web 服务。

I have implemented some basic stuff on the web app, and I first tried to access it with a console application with an HttpClient .我已经在 web 应用程序上实现了一些基本的东西,我首先尝试使用带有HttpClient的控制台应用程序访问它。 It works fine :它工作正常:

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)

The next step was to do the same in a Xamarin app, but since I am working with a device and not an emulator, my device hasn't access to local IIS server.下一步是在 Xamarin 应用程序中执行相同操作,但由于我使用的是设备而不是模拟器,因此我的设备无法访问本地 IIS 服务器。 I found that I could acess to the IIS server thanks to Conveyor, so I add the Visual Studio pluging wich shows me the following path : https://192.168.1.25:45455/api/Employees我发现我可以通过 Conveyor 访问 IIS 服务器,所以我添加了 Visual Studio 插件,它显示了以下路径: https ://192.168.1.25:45455/api/Employees

I tried with this path in the console app and it worked.我在控制台应用程序中尝试使用此路径并且它有效。 I tried to access through a brower on my android device, it works.我试图通过我的 android 设备上的浏览器访问,它可以工作。 But when I use it in the Xamarin app, the code is executed until the GetAsync method ( HttpResponseMessage response = await client.GetAsync("https://192.168.1.25:45455/api/Employees"); ) and then nothing happened.但是当我在 Xamarin 应用程序中使用它时,代码一直执行到 GetAsync 方法( HttpResponseMessage response = await client.GetAsync("https://192.168.1.25:45455/api/Employees"); ),然后什么也没发生。 No error, no exception, nothing.没有错误,没有例外,什么都没有。 The execution is like blocked here.执行就像在这里被阻止。

In the output window, I have the following message :在输出窗口中,我收到以下消息:

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'.

So I thought it may come from this ' AOT: image 'System.Runtime.Serialization.dll.so' not found '.所以我认为它可能来自这个' AOT:图像'System.Runtime.Serialization.dll.so' not found '。 Looking on the Web, I have found that I have to set <AotAssemblies>True</AotAssemblies> into the project.csproj file.在网上查看,我发现我必须将<AotAssemblies>True</AotAssemblies>到 project.csproj 文件中。 First I don't know how to do that.首先我不知道该怎么做。 Second, I am using Visual Studio Community and I have read this : " The Community version of Visual Studio does not support AOT; and, sometime in the past six months, an update to Xamarin explicitly forces it off (if you had manually turned AOT on by editing the csproj file). Now must have Enterprise version to build with AOT. ".其次,我正在使用 Visual Studio 社区,并且我已经阅读了以下内容:“ Visual Studio 的社区版本不支持 AOT;并且,在过去六个月的某个时间,Xamarin 的更新明确将其关闭(如果您已手动关闭 AOT通过编辑 csproj 文件。现在必须有企业版才能使用 AOT 构建。 ”。

So right now I am a bit lost because I don't know what to do.所以现在我有点迷茫,因为我不知道该怎么办。 If anyone has a clue on what I could do, it would be very helpfull.如果有人知道我可以做什么,那将非常有帮助。

Edit await client.GetAsync() is called in EmployeeServices class by the following function :编辑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 is called MainViewModel.cs in the following function : GetEmployeesAsync在以下函数中称为MainViewModel.cs

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

which is itself called in MainViewModel constructor :它本身在MainViewModel构造函数中被调用:

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

and the MainViewModel is binded in the MainPage :并且MainViewModel绑定在 MainPage 中:

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

OK, basically what you are doing in the constructor is wrong.好的,基本上你在构造函数中所做的都是错误的。

Why?为什么? A constructor is not async, and depending on which context you invoke that constructor you will end up in a deadlock, since you are calling .Result on the Task.构造函数不是异步的,并且根据您调用该构造函数的上下文,您将最终陷入死锁,因为您正在任务上调用.Result

Instead I suggest you use one of the life-cycle methods in Xamarin.Forms, which you seem to be using.相反,我建议您使用 Xamarin.Forms 中的生命周期方法之一,您似乎正在使用它。 I suggest using a OnAppearing override to start getting data:我建议使用OnAppearing覆盖来开始获取数据:

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

Also I would suggest encapsulating getting the data in the Command pattern as well:此外,我还建议在命令模式中封装获取数据:

public ICommand GetDataCommand { get; }

Then in your ViewModel constructor initialize it:然后在您的 ViewModel 构造函数中初始化它:

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

Then in DoGetDataCommand :然后在DoGetDataCommand

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

Then in OnAppearing call: GetDataCommand.Execute(null);然后在 OnAppearing 调用中: GetDataCommand.Execute(null); . .

Additionally.此外。 All awaited tasks in non-UI code.非 UI 代码中的所有等待任务。 Meaning when they are not inside a ViewModel, you should consider adding .ConfigureAwait(false) on them like:这意味着当它们不在 ViewModel 中时,您应该考虑在它们上添加.ConfigureAwait(false) ,例如:

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

What this will do is not trying to switch back to the thread we came from.这将做的不是试图切换回我们来自的线程。 This will limit overhead of switching back and forth between threads, which can cause deadlocks if the thread you are trying to switch to is already busy.这将限制在线程之间来回切换的开销,如果您尝试切换到的线程已经很忙,这可能会导致死锁。

Your ViewModel will roughly look like:您的 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
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM