我正在使用.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:image'System.Runtime.Serialization.dll.so'not found'”。在网上查找时,我发现必须将<AotAssemblies>True</AotAssemblies>
设置到project.csproj文件中。首先,我不知道该怎么做。其次,我正在使用Visual Studio社区,并且已经阅读了以下内容:“ Visual Studio的社区版本不支持AOT;并且在过去六个月的某个时间,对Xamarin的更新明确将其强制关闭(如果您手动通过编辑csproj文件打开AOT。)现在必须具有企业版才能通过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
被绑定在主页中:
<ContentPage.BindingContext>
<ViewModels:MainViewModel/>
</ContentPage.BindingContext>
好,基本上您在构造函数中所做的事情是错误的。
为什么?构造函数不是异步的,并且由于您在哪个上下文中调用该构造函数,您将最终陷入死锁,因为您在Task上调用了.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);
。
另外。非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
}
}