我正在用.Net学习Xamarin和Web应用程序。目标是建立一个网络应用程序(网络服务(和一个Android应用程序,Android应用程序将使用网络服务。
我已经在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
我在控制台应用程序中尝试了这个路径,它成功了。我试着通过安卓设备上的浏览器访问,它起作用了。但当我在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"未找到"。在网上,我发现我必须将<AotAssemblies>True</AotAssemblies>
设置到project.csproj文件中。
首先我不知道该怎么做。第二,我正在使用Visual Studio社区,我读到了以下内容:Visual Studio的社区版本不支持AOT;而且,在过去六个月的某个时候,Xamarin的更新明确强制关闭它(如果您通过编辑csproj文件手动打开AOT(。现在必须有企业版才能使用AOT构建";。
所以现在我有点不知所措,因为我不知道该怎么办。如果有人知道我能做什么,那将非常有帮助。
Editawait 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();
}
并且CCD_ 9被绑定在MainPage:中
<ContentPage.BindingContext>
<ViewModels:MainViewModel/>
</ContentPage.BindingContext>
好吧,基本上你在构造函数中所做的是错误的。
为什么?构造函数不是异步的,根据您调用该构造函数的上下文,您将最终陷入死锁,因为您正在对Task调用.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
}
}