我花了好几个小时 - 如果不是几天 - 关于以下看似简单的问题。
我正在开发一个Xamarin-app,它通过调用webservice来从后端使用数据。它还会在下载后将这些数据存储在内部SQLite-Database中,以避免不必要的Web服务调用。如果后端的数据已更新,则在第一个应用程序启动时下载一次数据,然后仅更新数据。
假设我有一个存储库,它将通过调用webservice或(同步)从内部SQLite-Database从后端(异步)加载数据。存储库负责传递数据,无论数据来源如何。存储库将数据的对象引用存储在可以公开访问的内部列表中。存储库是一个通用的抽象类,它只是由实现类子类化以提供有关Type的信息。所有与后端同步的逻辑都封装在存储库中(据我所知,这是存储库模式)
构建存储库后,通过调用Load()方法加载它。此方法是异步的,因为它执行以下操作:
GetAll()
- 方法存储数据的对象引用。此方法标记为异步,可以是异步或同步的,具体取决于数据的加载位置(请参阅下面的代码)
GetAll()
- 方法通过查询SQLite数据库来返回数据。这是SQLite.NET-Package提供的同步调用。什么不起作用(以及我不理解的)是以下行为:
NeedsUpdate
返回true
,一切正常。到目前为止一切顺利。System.NullReferenceException
异常。调试代码表明Refresh()
- 方法确实没有从GetAll()
调用(=预期的行为)。我知道您不应该使用async void方法,并且标记为async的方法应始终返回Task。显然,后者在我的代码中并非如此。那么我怎样才能做到这一点呢?
这是我的代码:
public abstract class BaseViewModel<T> : MvxViewModel
{
protected IRepository<T> Repository;
public virtual async Task Init()
{
if (Repository != null)
{
await Repository.Load();
}
}
}
public abstract class Repository<T> : IRepository<T>
protected List<T> ItemList = new List<T>();
public List<T> Items
{
get { return ItemList; }
set
{
ItemList = value;
if (ItemsChanged != null)
ItemsChanged(this, ItemList);
}
}
public virtual async Task Load()
{
if (CurrentUpdate.Equals(DateTime.MaxValue) || CurrentUpdate.Equals(DateTime.MinValue))
CurrentUpdate = await WebService.Status(exception => { });
Items = await GetAll();
}
public virtual async Task<List<T>> GetAll()
{
if (NeedsUpdate)
await Refresh();
return DbService.All();
}
public async Task Refresh()
{
var result = await WebService.DoGet(error => { });
if (ItemsDownloaded != null)
ItemsDownloaded(this, result);
ReloadDatabase(result);
}
protected void ReloadDatabase(IEnumerable<T> items)
{
DbService.DeleteAll();
foreach (var item in items)
{
DbService.Insert(item);
}
LastUpdate = DateTime.Now;
FileService.WriteFile(FileName, LastUpdate.ToString());
}
}
public abstract class DatabaseService<T> {
public List<T> All()
{
// ISQLiteConnection was instantiated before
return Connection.Table<T>().ToList();
}
}
public abstract class WebService<T>
public virtual async Task<List<T>> DoGet(Action<Exception> error)
{
using (var client = new HttpClient())
{
string result = await client.GetStringAsync("http://example.com/myApi");
return JsonConvert.DeserializeObject<T>(result);
}
}
}
06-17 14:21:43.701 D/AndroidRuntime( 2897): Shutting down VM
The thread 'Unknown' (0x4) has exited with code 0 (0x0).
An unhandled exception occured.
06-17 14:21:45.760 E/AndroidRuntime( 2897): FATAL EXCEPTION: main
06-17 14:21:45.760 E/AndroidRuntime( 2897): Process: ch.bbv.fumetto, PID: 2897
06-17 14:21:45.760 E/AndroidRuntime( 2897): java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
06-17 14:21:45.760 E/AndroidRuntime( 2897): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
06-17 14:21:45.760 E/AndroidRuntime( 2897): Caused by: java.lang.reflect.InvocationTargetException
06-17 14:21:45.760 E/AndroidRuntime( 2897): at java.lang.reflect.Method.invoke(Native Method)
06-17 14:21:45.760 E/AndroidRuntime( 2897): at java.lang.reflect.Method.invoke(Method.java:372)
06-17 14:21:45.760 E/AndroidRuntime( 2897): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
06-17 14:21:45.760 E/AndroidRuntime( 2897): ... 1 more
06-17 14:21:45.760 E/AndroidRuntime( 2897): Caused by: md52ce486a14f4bcd95899665e9d932190b.JavaProxyThrowable: System.NullReferenceException: Object reference not set to an instance of an object
06-17 14:21:45.760 E/AndroidRuntime( 2897): at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () <IL 0x00011, 0x0004b>
06-17 14:21:45.760 E/AndroidRuntime( 2897): at System.Runtime.CompilerServices.AsyncMethodBuilderCore.<ThrowAsync>m__0 (object) <IL 0x00006, 0x0003b>
06-17 14:21:45.760 E/AndroidRuntime( 2897): at Android.App.SyncContext/<Post>c__AnonStorey0.<>m__0 () [0x00000] in /Users/builder/data/lanes/monodroid-mavericks-monodroid-5.1-series/d419c934/source/monodroid/src/Mono.Android/src/Android.App/SyncContext.cs:18
06-17 14:21:45.760 E/AndroidRuntime( 2897): at Java.Lang.Thread/RunnableImplementor.Run () [0x0000b] in /Users/builder/data/lanes/monodroid-mavericks-monodroid-5.1-series/d419c934/source/monodroid/src/Mono.Android/src/Java.Lang/Thread.cs:36
06-17 14:21:45.760 E/AndroidRuntime( 2897): at Java.Lang.IRunnableInvoker.n_Run (intptr,intptr) [0x00009] in /Users/builder/data/lanes/monodroid-mavericks-monodroid-5.1-series/d419c934/source/monodroid/src/Mono.Android/platforms/android-21/src/generated/Java.Lang.IRunnable.cs:71
06-17 14:21:45.760 E/AndroidRuntime( 2897): at (wrapper dynamic-method) object.5b2f2ea1-978e-4306-9701-97a7ac2d86c0 (intptr,intptr) <IL 0x00011, 0x0001f>
06-17 14:21:45.760 E/AndroidRuntime( 2897):
06-17 14:21:45.760 E/AndroidRuntime( 2897): at mono.java.lang.RunnableImplementor.n_run(Native Method)
06-17 14:21:45.760 E/AndroidRuntime( 2897): at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:29)
06-17 14:21:45.760 E/AndroidRuntime( 2897): at android.os.Handler.handleCallback(Handler.java:739)
06-17 14:21:45.760 E/AndroidRuntime( 2897): at android.os.Handler.dispatchMessage(Handler.java:95)
06-17 14:21:45.760 E/AndroidRuntime( 2897): at android.os.Looper.loop(Looper.java:135)
06-17 14:21:45.760 E/AndroidRuntime( 2897): at android.app.ActivityThread.main(ActivityThread.java:5257)
06-17 14:21:45.760 E/AndroidRuntime( 2897): ... 4 more
06-17 14:21:49.772 I/Process ( 2897): Sending signal. PID: 2897 SIG: 9