繁体   English   中英

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

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

我正在使用 .Net 学习 Xamarin 和 Web App。 目标是构建一个 web 应用程序(web 服务)和一个 android 应用程序,android 应用程序将使用 web 服务。

我已经在 web 应用程序上实现了一些基本的东西,我首先尝试使用带有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 服务器。 我发现我可以通过 Conveyor 访问 IIS 服务器,所以我添加了 Visual Studio 插件,它显示了以下路径: https ://192.168.1.25:45455/api/Employees

我在控制台应用程序中尝试使用此路径并且它有效。 我试图通过我的 android 设备上的浏览器访问,它可以工作。 但是当我在 Xamarin 应用程序中使用它时,代码一直执行到 GetAsync 方法( 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:图像'System.Runtime.Serialization.dll.so' not found '。 在网上查看,我发现我必须将<AotAssemblies>True</AotAssemblies>到 project.csproj 文件中。 首先我不知道该怎么做。 其次,我正在使用 Visual Studio 社区,并且我已经阅读了以下内容:“ Visual Studio 的社区版本不支持 AOT;并且,在过去六个月的某个时间,Xamarin 的更新明确将其关闭(如果您已手动关闭 AOT通过编辑 csproj 文件。现在必须有企业版才能使用 AOT 构建。 ”。

所以现在我有点迷茫,因为我不知道该怎么办。 如果有人知道我可以做什么,那将非常有帮助。

编辑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绑定在 MainPage 中:

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

好的,基本上你在构造函数中所做的都是错误的。

为什么? 构造函数不是异步的,并且根据您调用该构造函数的上下文,您将最终陷入死锁,因为您正在任务上调用.Result

相反,我建议您使用 Xamarin.Forms 中的生命周期方法之一,您似乎正在使用它。 我建议使用OnAppearing覆盖来开始获取数据:

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

此外,我还建议在命令模式中封装获取数据:

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); .

此外。 非 UI 代码中的所有等待任务。 这意味着当它们不在 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
        }
    }

暂无
暂无

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

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