2011-01-08 4 views
6

Я написал ListActivity, у которого есть собственный адаптер списка. Список обновляется с ContentProvider при запуске onCreate. У меня также есть служба, которая запускается при запуске приложения, и она сначала обновляет ContentProvider, а затем отправляет широковещательную рассылку, что контент был обновлен.
My 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

Необходимо выполнить все обновление данных адаптера в потоке пользовательского интерфейса, чтобы синхронизированный блок не был необходим. Это также бесполезно, так как ваша синхронизация на AsyncTask, которая создается каждый раз при каждом ее исполнении.

Другая проблема заключается в том, что вы вызываете notifyDataSetChanged, внешний по отношению к Adapter. Вы должны называть его в конце вашего метода setListItems. Это не должно вызывать ошибок, хотя, поскольку оно выполняется в потоке пользовательского интерфейса, но его не следует вызывать таким образом.

Вы должны убедиться, что ваш метод getReports не модифицирует резервный магазин Adapter. Поскольку он работает в отдельном потоке, он не может изменять ничего, к которому имеет доступ также Adapter. Даже если он защищен замками. Что вам нужно сделать, так это в вашем методе doInBackground сгенерировать список обновлений или новый список и т. Д., И передать его в onPostExecute, который затем записывает новые данные в Adapter в поток пользовательского интерфейса. Итак, если ваша функция getReports меняется report_list, а ваш Adapter имеет ссылку на report_list, вы делаете это неправильно. getReports должен создать новый report_list, а затем передать это обратно на ваш Adapter, когда он закончит создание его в потоке пользовательского интерфейса.

Чтобы повторить, вы можете изменять только данные Adapter, а затем ListView имеет доступ также к потоку пользовательского интерфейса. Использование синхронизации/блокировок не изменяет это требование.

+1

Это как раз проблема. Мой адаптер списка поддерживается ArrayList объектов «report_list», и я изменяю его непосредственно в методе getReports, который выполняется в отдельном потоке.Я изменил метод getReports, чтобы работать с локальным ArrayList и вернуть его (и передать его методу onPostExecute). Хотя я знал о том, что я не могу работать с данными своего адаптера списка вне потока пользовательского интерфейса, я просто не понял его на 100%, пока он не подвел меня, и заставил меня правильно разобраться (или, точнее, спросить для помощи здесь!) Спасибо Qberticus! – brockoli

-1

Если вы хотите обновить ListView 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

+0

находится впереди - это метод, который вы должны сделать. Здесь нет кормления ложкой. Познайте сначала код, затем downvote ... –

+0

Вы не можете определить статический метод, который проверяет состояние экземпляра. Также вы не можете получить доступ к экземпляру какой-либо деятельности без хотя бы ссылки на нее или установить интерфейс прослушивателя, чтобы обновить эту активность. Еще одна причина, по которой я ниспровергаюсь, заключается в том, что вам нужно получить все запущенные задачи, чтобы узнать, какая из них находится впереди, не говоря уже о том, чтобы получить экземпляр этой Activity, что совершенно не нужно. Даже если вы вручную поддерживаете логическое значение isInFront, вы не сможете легко извлечь экземпляр. – AfzalivE

 Смежные вопросы

  • Нет связанных вопросов^_^