2016-01-13 88 views
0

期待已久的我有使用返回的从REST API接收到的文章列表的片段。该REST API是async我用await等待它完成。当完成时,我的数据(即List<Article>)传递给被叫listofarticles变量,并用它来创建一个ArrayAdapter呼叫到异步方法不Xamarin的Android

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

我的代码工作中的活动而不是一个片段。

问题: 到awaitable REST方法的调用不等待。

当线调试线它是这样的:

  • OnCreate被调用。加工,我叫awaitable方法行(即是为了填补listofarticles),并简单地跳到OnCreateView。因此listofarticles为空。

  • OnCreateView被称为和处理。此时,程序只能返回到它所称的活动。

  • 我不能再调试,因为Xamarin工作室认为我已经结束了,但在模拟器,它只是开始后,这一切都调用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; 
    } 
} 

回答

6

正如这个答案https://stackoverflow.com/a/32656807/1230302解释说这不是一个好主意,做在片段的OnCreate()相关的任何视图。

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

似乎更好的解决方案,接受。 – VSG24

+0

您也可以以同步的方式使用异步REST API。这样做会简化你的代码,但是会阻塞UI线程直到你得到结果。 – Kalitsov

1

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

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

+0

您会如何改变我的课程以正常工作? – VSG24

+0

@ VSG24最有可能将所有异步代码添加到OnCreateView中会解决您的问题 –

+0

不,它没有解决它。事实上,它变得更糟,因为现在我得到一个关于'listofarticles'为空的例外。查看后甚至不会运行等待方法。 – VSG24

0

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