【问题标题】:Call to async method is not awaited in Xamarin AndroidXamarin Android 中不等待调用异步方法
【发布时间】:2016-01-13 05:21:39
【问题描述】:

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

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

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

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

逐行调试时是这样的:

  • OnCreate 被调用。处理到,我在其中调用等待方法(应该填充listofarticles)并简单地跳到OnCreateView。因此listofarticles 为空。

  • OnCreateView 被调用和处理。此时程序只是回到它被调用的活动。

  • 我无法再调试它,因为 Xamarin Studio 认为我已经完成了,但在模拟器上它只是开始调用 REST 方法,因为 listofarticles 中的数据是无用的,因为我可以'现在不要将它传递给适配器。

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

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;
    }
}

【问题讨论】:

    标签: c# android asynchronous xamarin


    【解决方案1】:

    正如这个答案https://stackoverflow.com/a/32656807/1230302 中所解释的那样,在片段中执行与 OnCreate() 相关的任何视图都不是一个好主意。

    此外,如果您将 OnCreate() 设为异步方法,则其他片段生命周期事件(请参阅 http://developer.android.com/guide/components/fragments.html)将在 await 行之后立即调用。现在的问题是你不能依赖等待之后代码​​运行的顺序,如果你的 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);
    }
    

    【讨论】:

    • 您也可以以同步方式使用异步 REST API。这样做会简化您的代码,但会阻塞 UI 线程,直到您从中获得结果。
    【解决方案2】:

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

    最好在创建完所有视图后获取数据,然后将其分配给适配器。这样,一切都将是可预测的,如果需要,您甚至可以显示适当的加载指示器。

    【讨论】:

    • 你会建议我如何改变我的班级以正常工作?
    • @VSG24 很可能将所有异步代码添加到 OnCreateView 中会解决您的问题
    • 不,它没有解决它。事实上,这让情况变得更糟,因为现在我收到了一个关于 listofarticles 为空的异常。它甚至不会在查看后运行等待的方法。
    • @VSG24 你不能只是移动线条。你必须重写它。首先制作一个空适配器。然后加载数据,添加到适配器,刷新。
    • 如果您有与我不同的想法,请提供代码。
    【解决方案3】:

    解决这个问题的方法是创建一个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;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-30
      • 2018-12-19
      相关资源
      最近更新 更多