2011-01-08 63 views
6

我已经编写了一个具有自定义列表适配器的ListActivity。当onCreate运行时,该列表将从ContentProvider进行更新。我还有一个服务,在我运行应用程序时开始,它首先更新ContentProvider,然后发送内容已更新的广播。
我的ListActivity接收广播并尝试更新我的ListView。我的问题是,我收到ListView适配器数据更改的间歇性错误,但没有通知ListView。在我更新后,我在列表适配器上调用notifyDataSetChanged()方法。看起来发生的情况是该列表在onCreate首次调用之后仍然处于更新的过程中,当它从服务接收到广播以进行更新时,所以它会在它从第一次运行完成更新之前尝试更新我的ListView。这有意义吗?这是我的一些代码。没有通知ListView的ListView适配器数据更改

注意:该服务工作正常,它获取新数据并更新我的ContentProvider,并在更新时在我的活动中收到广播。

@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();       
     } 
    } 
} 

现在这个问题得到解答,我会发布我的更新代码,以帮助其他可能遇到它的人。

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

回答

11

您必须在UI线程上执行所有适配器数据的更新,以便不需要同步的块。它也是没有用的,因为你在每次执行时被创建新的AsyncTask进行同步。

另一个问题是您在Adapter外部调用notifyDataSetChanged。你应该在setListItems方法结束时调用它。这应该不会造成错误,尽管它正在UI线程上执行,但不应该以这种方式调用。

您应该确保您的getReports方法不以任何方式修改Adapter的后备存储。由于它在单独的线程上运行,因此它不能修改Adapter也可以访问的任何内容。即使它受锁的保护。您需要做的是在您的doInBackground方法中生成更新列表或新列表等,并将其传递到onPostExecute,然后在UI线程上将新数据提交到Adapter。因此,如果您的getReports功能正在更改report_list,并且您的Adapterreport_list的引用,那么您做错了。 getReports必须创建一个新的report_list,然后在它在UI线程上完成创建时将其传回给您的Adapter

重申一下,您只能修改数据Adapter,随后ListView也可以在UI线程上访问。使用同步/锁定不会更改此要求。

+1

这正是问题所在。我的列表适配器由对象“report_list”的ArrayList支持,我直接在我的getReports方法中修改它,该方法在单独的线程上执行。我修改了我的getReports方法以在本地ArrayList上操作并返回它(并将它传递给我的onPostExecute方法)。虽然我意识到我无法在UI线程之外操作我的列表适配器数据,但我只是没有把它抓住100%,直到它让我失败并迫使我正确地找出它(或者更准确地问在这里寻求帮助!)谢谢Qberticus! – brockoli 2011-01-09 03:41:41

-1

如果您想更新从服务的UI列表视图,那么你应该调用notifyDataSetChanged()上的onDestroy服务

的使适配器从静态的主要活动,并称之为adaptername.notifyDataSetChanged()

这样的

@Override 
     public void onDestroy() { 

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

咦?您无法使适配器变为静态,并从它调用非静态方法(如notifyDataSetChanged)。哎呀,甚至没有一个叫isInFront的方法,即使有,你不能只是调用类,因为它不会是静态的。 – AfzalivE 2014-07-26 22:11:25

相关问题