Xamarin Android中不等待对异步方法的调用

本文关键字:异步方法 调用 等待 Android Xamarin | 更新日期: 2023-09-27 18:30:01

我有一个片段,用于返回从REST API接收的文章列表。REST API是async,我使用await来等待它完成。完成后,我将数据(即List<Article>)传递给一个名为listofarticles的变量,并使用它创建一个ArrayAdapter

据我所知,我在这个片段的OnCreate方法中执行了上述代码,该方法应该在OnCreateView之前运行。

我的代码可以在活动中工作,但不能在片段中工作。

问题:未等待对不可用REST方法的调用。

当逐行调试时,它是这样的:

  • 调用CCD_ 8。处理到,在那里我调用awaitable方法(应该填充listofarticles),然后简单地跳到OnCreateView。因此listofarticles为空。

  • 调用并处理CCD_ 12。在这一点上,程序只是返回到它所调用的活动。

  • 我无法再调试它,因为Xamarin Studio认为我已经完成了,但在模拟器中,它刚刚开始调用REST方法,因此listofarticles中的数据是无用的,因为我现在无法将其传递给Adapter。

为什么会发生这种情况,如何解决??

public class LatestNewsFragment : Android.Support.V4.App.Fragment, ListView.IOnItemClickListener
{
    private Context globalContext = null;
    private List<Article> listofarticles;
    private ArrayAdapter<Article> listAdapter;
    public LatestNewsFragment()
    {
        this.RetainInstance = true;
    }
    public async override void OnCreate (Bundle savedInstanceState)
    {
        base.OnCreate (savedInstanceState);
        globalContext = this.Context;
        //Create a progress dialog for loading
        ProgressDialog pr = new ProgressDialog(globalContext);
        pr.SetMessage("Loading data");
        pr.SetCancelable(false);
        var rest = new RestAccess();
        pr.Show();
        //Download the data from the REST API
        listofarticles = await rest.ListArticlesAsync ("SomeSource");
        pr.Hide();
        Console.WriteLine (listofarticles.Capacity);
        listAdapter = new ArrayAdapter<Article>(globalContext, Android.Resource.Layout.SimpleListItem1, listofarticles);
    }
    public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        var view = inflater.Inflate(Resource.Layout.Fragment_LatestNews, null);
        ListView listView = view.FindViewById<ListView>(Resource.Id.list_latestnews);
        listView.Adapter = listAdapter;
        listView.OnItemClickListener = this;
        return view;
    }
}

Xamarin Android中不等待对异步方法的调用

如本答案所述https://stackoverflow.com/a/32656807/1230302在OnCreate()中分段执行任何与视图相关的操作都不是一个好主意。

此外,如果您将OnCreate()设为异步方法,则其他片段生命周期事件(请参阅http://developer.android.com/guide/components/fragments.html)将在等待线后立即呼叫。现在的问题是,你不能依赖等待后代码的运行顺序,如果你的REST API很慢,OnCreateView()可能已经完成了,你会很好,但如果API很快,OnCreateView()可能仍在运行,你的应用程序将崩溃。

为什么不使用OnActivityCreated()加载数据?你可以确定所有的东西都在这个文件中初始化了,比如:

private ListView listView;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
    var view = inflater.Inflate(Resource.Layout.Fragment_LatestNews, null);
    listView = view.FindViewById<ListView>(Resource.Id.list_latestnews);    
    listView.OnItemClickListener = this;
    return view;
}
public override async void OnActivityCreated(Bundle savedInstanceState)
{
    base.OnActivityCreated(savedInstanceState);
    //Download the data from the REST API
    var rest = new RestAccess();    
    var listofarticles = await rest.ListArticlesAsync("SomeSource");
    listView.Adapter = new ArrayAdapter<Article>(Context, Android.Resource.Layout.SimpleListItem1, listofarticles);
}

async并不意味着"等待,不运行任何其他代码"。它的意思是"允许其他代码在这个上下文中等待这个调用时运行。"每个async块都是自己的上下文,没有等待将运行自己的程序。因此,对其他方法的调用在等待该调用完成甚至启动时运行是很正常的。您不应该以这种方式依赖订单,而应该正确地同步代码。

最好是在创建完所有这些视图之后获取数据,然后将其分配给适配器。这样,一切都是可以预测的,如果必要的话,你甚至可以显示一个合适的装载指示器。

这个问题的解决方案是创建一个类型为View的字段变量来存储片段将返回的膨胀视图,并使用该视图在OnCreate方法中获取布局文件。在await之后,我们可以用数据填充ListView。所以最后的代码是:

public class LatestNewsFragment : Android.Support.V4.App.Fragment, ListView.IOnItemClickListener
{
    private Context globalContext = null;
    private List<Article> listofarticles;
    View view;
    public LatestNewsFragment()
    {
        this.RetainInstance = true;
    }
    public async override void OnCreate (Bundle savedInstanceState)
    {
        base.OnCreate (savedInstanceState);
        globalContext = this.Context;
        //Create a progress dialog for loading
        Android.App.ProgressDialog pr = new Android.App.ProgressDialog(globalContext);
        pr.SetMessage("Loading data");
        pr.SetCancelable(true);
        var rest = new RestAccess();
        pr.Show();
        listofarticles = await rest.ListArticlesAsync ("SomeSource");
        pr.Hide();
        ArrayAdapter<Article> listAdapter = new ArrayAdapter<Article>(globalContext, Android.Resource.Layout.SimpleListItem1, listofarticles);
        ListView listView = view.FindViewById<ListView>(Resource.Id.list_latestnews);
        listView.Adapter = listAdapter;
        listView.OnItemClickListener = this;
    }
    public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        view = inflater.Inflate(Resource.Layout.Fragment_LatestNews, null);
        return view;
    }
}