Xamarin Android不再需要调用异步方法

时间:2016-01-13 05:21:40

标签: c# android asynchronous xamarin

我有一个片段,用于返回从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;
    }
}

3 个答案:

答案 0 :(得分:7)

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

此外,如果您将OnCreate()设置为异步方法,则将在await行之后立即调用其他片段生命周期事件(请参阅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);
}

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

此问题的解决方案是创建类型为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;
    }
}