2016-01-13 22 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; 
    } 
}