2011-01-08 25 views
6

Özel bir liste bağdaştırıcısı olan bir ListActivity yazdım. OnCreate çalıştırıldığında liste bir ContentProvider'dan güncellenmektedir. Uygulamayı çalıştırdığımda başladığım ve önce ContentProvider'ı güncellediği, ardından da içeriğin güncellendiği bir Yayın gönderen bir hizmetim var.
My ListActivity yayını alır ve ListView'imi güncellemeyi dener. Benim sorunum, ListView bildirilmeksizin ListView adaptör verileri değiştirerek aralıklı hataları alıyorum. Güncellemeden hemen sonra liste bağdaştırıcımdaki notifyDataSetChanged() yöntemini arıyorum. Neye benziyorsa, liste, hizmetin güncellenmesi için yayını aldığında onCreate'da ilk aramayı yaptıktan sonra güncellenme aşamasındadır, bu yüzden ListView'imi ilk çalıştırmadan güncellemeyi bitirmeden güncellemeye çalışır. Bu mantıklı mı? İşte bazı kodlarım.ListView adaptör veri değişikliği bildirilmeksizin ListView olmaksızın

NOT: Hizmet düzgün çalışıyor, yeni veriler alıyor ve ContentProvider cihazımı güncelleştiriyor ve güncellendiğinde etkinliğimde yayın alıyorum.

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    ctx = this; 
    getPrefs(); 
    setContentView(R.layout.main); 

    // Setup preference listener 
    preferences = PreferenceManager.getDefaultSharedPreferences(this); 
    preferences.registerOnSharedPreferenceChangeListener(listener); 


    // Setup report list adapter 
    ListView nzbLv = (ListView) findViewById(R.id.report_list); 
    nzbla = new NZBReportListAdaptor(ctx); 
    getReports(); 
    nzbla.setListItems(report_list);    
    nzbLv.setAdapter(nzbla);   
    // Broadcast receiver to get notification from NZBService to update ReportList 
    registerReceiver(receiver, 
      new IntentFilter(NZBService.BROADCAST_ACTION)); 

    startService(new Intent(ctx, NZBService.class)); 
} 

@Override 
public void onResume() { 
    super.onResume(); 
    timerHandler.resume();  
new updateSabQueue().execute(); 
    //updateList(); 
} 

@Override 
public void onPause() { 
    super.onPause(); 
    timerHandler.pause(); 
    unregisterReceiver(receiver); 
} 


private BroadcastReceiver receiver = new BroadcastReceiver() { 
    public void onReceive(Context context, Intent intent) { 
     Toast.makeText(ctx, "NZBService broadcast recieved", Toast.LENGTH_SHORT).show(); 
     updateReportList(); 
    } 
}; 


private void updateReportList() { 
    new updateReportList().execute(); 
} 



private class updateReportList extends AsyncTask<Void, Void, Boolean> { 

    /* (non-Javadoc) 
    * @see android.os.AsyncTask#onPreExecute() 
    * Show progress dialog 
    */ 
    protected void onPreExecute() { 
    } 

    /* (non-Javadoc) 
    * @see android.os.AsyncTask#doInBackground(Params[]) 
    * Get new articles from the internet 
    */ 
    protected Boolean doInBackground(Void...unused) { 
     getReports(); 
     return true; 
    } 

    /** 
    * On post execute. 
    * Close the progress dialog 
    */ 
    @Override 
    protected void onPostExecute(Boolean updated) { 
     if (updated) { 
      Log.d(TAG, "NZB report list adapter updated"); 
      synchronized(this) { 
       nzbla.setListItems(report_list);    
      } 
      Log.d(TAG, "NZB report list notified of change"); 
      nzbla.notifyDataSetChanged();       
     } 
    } 
} 

Şimdi bu soruya cevap olduğunu, bunu rastlamak olabilir başkalarına yardım etmek amacıyla zaman güncellenmiş kodu yayınlayacağız.

@Override 
    public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    ctx = this; 
    getPrefs(); 
setContentView(R.layout.main); 

    // Setup preference listener 
    preferences = PreferenceManager.getDefaultSharedPreferences(this); 
    preferences.registerOnSharedPreferenceChangeListener(listener); 

    // Setup report list adapter 
    ListView nzbLv = (ListView) findViewById(R.id.report_list); 
    nzbla = new NZBReportListAdaptor(ctx); 
    report_list.addAll(getReports()); 
    nzbla.setListItems(report_list);    
    nzbLv.setAdapter(nzbla);   
    // Broadcast receiver to get notification from NZBService to update ReportList 
    registerReceiver(receiver, 
      new IntentFilter(NZBService.BROADCAST_ACTION)); 

    startService(new Intent(ctx, NZBService.class)); 
} 


private class updateReportList extends AsyncTask<Void, Void, ArrayList<Report>> { 

    /* (non-Javadoc) 
    * @see android.os.AsyncTask#onPreExecute() 
    * Show progress dialog 
    */ 
    protected void onPreExecute() { 
    } 

    /* (non-Javadoc) 
    * @see android.os.AsyncTask#doInBackground(Params[]) 
    * Get new articles from the internet 
    */ 
    protected ArrayList<Report> doInBackground(Void...unused) { 
     return getReports(); 
    } 

    /** 
    * On post execute. 
    * Close the progress dialog 
    */ 
    @Override 
    protected void onPostExecute(ArrayList<Report> updated) { 
     nzbla.setListItems(updated);    
     nzbla.notifyDataSetChanged();       
    } 
} 


private ArrayList<Report> getReports() { 
    ArrayList<Report> reports = new ArrayList<Report>(); 
    ContentResolver r = getContentResolver(); 
    Cursor c = r.query(NZBReportProvider.CONTENT_URI, null, null, null, NZBReportProvider.ARTICLE_KEY_ROWID + " DESC"); 
    startManagingCursor(c); 
    Log.d(TAG, "NZBReport cursor.getCount=" + c.getCount()); 
    int title = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_TITLE); 
    int desc = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_DESCRIPTION); 
    int cat = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_CAT); 
    int size = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_SIZE); 
    int link = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_LINK); 
    int catid = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_CATID); 
    int date = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_DATE_ADDED); 
    int group = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_GROUP); 

    if (c.getCount() > 0) { 
     c.moveToFirst(); 
     do { 
      URL url = null; 
      try { 
       url = new URL(c.getString(link)); 
      } catch (MalformedURLException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
      reports.add(new Report(c.getString(title), url, c.getString(desc), c.getString(cat), c.getString(date), c.getString(size), c.getInt(catid), c.getString(group)));    
     } while (c.moveToNext());     
    } 
    return reports; 
} 

cevap

11

Senkronizasyon bloğunun gerekli olmaması için tüm Adaptör verilerini UI iş parçacığına güncellemeniz gerekir. Aynı zamanda, her yürütüldüğünde yeni oluşturulan AsyncTask'daki senkronizasyonunuzdan da yararsızdır.

Başka bir sorun,numaralı telefona notifyDataSetChanged numaralı telefonu aramanızdır. setListItems yönteminizin sonunda çağırmalısınız. Bu, UI iş parçacığı üzerinde yürütüldüğünden hatalara neden olmamalıdır, ancak bu şekilde çağrılmamalıdır.

getReports yönteminizin Adapter destek deposunun hiçbir şekilde değiştirilmediğinden emin olmalısınız. Ayrı bir iş parçacığı üzerinde çalıştığından, Adapter erişiminin de herhangi bir şekilde değiştirilemez. Kilitlerle korunuyor olsa bile. Yapmanız gereken şey, doInBackground yönteminizde güncellemelerin listesini veya yeni bir liste vb. Oluşturmaktır ve bunu'a geçirir ve bu da yeni verileri UI iş parçacığı üzerinde Adapter'a gönderir. Yani, getReports işleviniz report_list değişiyorsa ve Adapter ürününüzün report_list numaralı referansa sahip olmanız yanlıştır. getReports, yeni bir report_list oluşturmalı ve ardından bunu, UI iş parçacığı üzerinde oluşturmayı bitirdiğinde Adapter numaranıza geri geçirmelidir. Yinelemek gerekirse, yalnızca Adapter numaralı verileri değiştirebilir ve daha sonra ListView UI iş parçacığı üzerinde de erişime sahip olabilirsiniz. Senkronizasyonu/kilitleri kullanmak bu gerekliliği değiştirmez. Eğer bir hizmetten UI listview güncellemek istiyorsanız

+1

Bu tam olarak sorun. Liste bağdaştırıcım, "report_list" nesnesinin bir ArrayList'i tarafından destekleniyor ve bunu, doğrudan bir iş parçacığı üzerinde yürütülen getReports yöntemimde değiştiriyorum.Yerel bir ArrayList üzerinde çalışmak ve onu iade (ve onPostExecute yöntemime iletmek) için getReports yöntemimi değiştirdim. UI iş parçacığı dışındaki liste bağdaştırıcı verilerimde çalışamayacağımı bilmeme rağmen, beni başarısızlığa uğrayana kadar% 100 tutmuyordum ve düzgün bir şekilde anlamaya zorluyordum (veya daha doğru bir şekilde soruyorum) yardım için burada!) Teşekkür ederim Qberticus! – brockoli

-1

o zaman ana etkinlikten statik adaptör yapmak hizmete

ait OnDestroy üzerinde notifyDataSetChanged() çağırmaları gerektiğini adaptername.notifyDataSetChanged()

bunu beğendi

@Override 
     public void onDestroy() { 

       if (MainActivity.isInFront == true) { 
         if (MainActivity.adapter != null) 
           MainActivity.adapter.notifyDataSetChanged(); 
         MainActivity.listView.setAdapter(MainActivity.adapter); 
       } 
}    
+0

Huh? Bağdaştırıcıyı statik yapamaz ve notifyDataSetChanged gibi statik olmayan yöntemleri çağıramazsınız. Heck, isInFront adında bir yöntem bile yok, eğer olsa bile, duramazsınız çünkü sınıfta duramazsınız. – AfzalivE

+0

önünüzde yapmak zorunda olduğunuz bir yöntemdir .. burada kaşık besleme yok ... önce kodu anlayın sonra downvote ... –

+0

Bir örneğin durumunu kontrol eden statik bir yöntem tanımlayamazsınız. Ayrıca, herhangi bir etkinliğin örneğine en az bir başvuru olmadan erişemezsiniz veya bu etkinlik aracılığıyla etkinliği güncelleştirmek için bir dinleyici arabirimi ayarlayamazsınız. Önemsiz kalmamın bir başka nedeni de, hangi aktivitenin ön tarafta olduğunu bilmek için tüm çalışan görevleri almanız gerektiğinden, bu aktivitenin bir örneğini tamamen gereksiz yere almamanızdır. IsInFront boole'unu elle tutsanız bile, örneği bu kadar kolay bir şekilde getiremezsiniz. – AfzalivE