被通知的ListView适配器,数据更改,恕不的ListView [英] ListView adapter data change without ListView being notified

查看:110
本文介绍了被通知的ListView适配器,数据更改,恕不的ListView的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经写了ListActivity,有一个自定义列表适配器。列表正在从ContentProvider的更新时的onCreate运行。我也有当我运行的应用程序,它首先更新的ContentProvider是被启动的服务,然后发送一个广播,内容已经更新。照片   我ListActivity临危广播,并试图更新我的ListView。我的问题是,我收到有关的ListView适配器的数据变化间歇性的错误,不通知的ListView。我呼吁我的名单适配器notifyDataSetChanged()方法之后我更新。这好像是发生在列表中仍处于时临危从服务广播,以更新的onCreate第一个电话后,正在更新的过程中,所以它会试图在它之前完成更新,从它第一次运行时更新我的​​ListView。这是否有意义?下面是我的一些code。

注:该服务工作正常,它获得新的数据和更新我的ContentProvider,而我得到我的活动广播更新时

  @覆盖
公共无效的onCreate(包savedInstanceState){
    super.onCreate(savedInstanceState);
    CTX =这一点;
    获得preFS();
    的setContentView(R.layout.main);

    //设置preference监听器
    preferences = preferenceManager.getDefaultShared preferences(本);
    preferences.registerOnShared preferenceChangeListener(听众);


    //设置报表列表适配器
    ListView控件nzbLv =(ListView控件)findViewById(R.id.report_list);
    nzbla =新NZBReportListAdaptor(CTX);
    getReports();
    nzbla.setListItems(report_list);
    nzbLv.setAdapter(nzbla);
    //广播接收器从NZBService得到通知更新ReportList
    registerReceiver(接收器,
            新的IntentFilter(NZBService.BROADCAST_ACTION));

    startService(新意图(CTX,NZBService.class));
 

}

  @覆盖
公共无效onResume(){
    super.onResume();
    timerHandler.resume();
新updateSabQueue()执行();
    // updateList();
}

@覆盖
公共无效的onPause(){
    super.onPause();
    timerHandler.pause();
    unregisterReceiver(接收机);
}


私人BroadcastReceiver的接收器=新的BroadcastReceiver(){
    公共无效的onReceive(上下文的背景下,意图意图){
        Toast.makeText(CTX,NZBService播出:收到,Toast.LENGTH_SHORT).show();
        updateReportList();
    }
};


私人无效updateReportList(){
    新updateReportList()执行()。
}



私有类updateReportList扩展的AsyncTask<虚空,虚空,布尔> {

    / *(非Javadoc中)
     * @see android.os.AsyncTask#在preExecute()
     *显示进度对话框
     * /
    在preExecute保护无效(){
    }

    / *(非Javadoc中)
     * @see android.os.AsyncTask#doInBackground(PARAMS [])
     *从网上得到新的文章
     * /
    保护布尔doInBackground(空...未使用){
        getReports();
        返回true;
    }

    / **
     *在后执行。
     *关闭进度对话框
     * /
    @覆盖
    保护无效onPostExecute(布尔更新){
        如果(更新){
            Log.d(TAG,NZB报表列表适配器更新);
            同步(本){
                nzbla.setListItems(report_list);
            }
            Log.d(TAG,NZB报告列表改变的通知);
            nzbla.notifyDataSetChanged();
        }
    }
}
 

现在,这个问题的回答,我将张贴在努力帮助别人谁可能遇到过我的更新code。

  @覆盖
  公共无效的onCreate(包savedInstanceState){
  super.onCreate(savedInstanceState);
  CTX =这一点;
    获得preFS();
的setContentView(R.layout.main);

    //设置preference监听器
    preferences = preferenceManager.getDefaultShared preferences(本);
    preferences.registerOnShared preferenceChangeListener(听众);

    //设置报表列表适配器
    ListView控件nzbLv =(ListView控件)findViewById(R.id.report_list);
    nzbla =新NZBReportListAdaptor(CTX);
    report_list.addAll(getReports());
    nzbla.setListItems(report_list);
    nzbLv.setAdapter(nzbla);
    //广播接收器从NZBService得到通知更新ReportList
    registerReceiver(接收器,
            新的IntentFilter(NZBService.BROADCAST_ACTION));

    startService(新意图(CTX,NZBService.class));
 

}

 私有类updateReportList扩展的AsyncTask<虚空,虚空,ArrayList的<报告>> {

    / *(非Javadoc中)
     * @see android.os.AsyncTask#在preExecute()
     *显示进度对话框
     * /
    在preExecute保护无效(){
    }

    / *(非Javadoc中)
     * @see android.os.AsyncTask#doInBackground(PARAMS [])
     *从网上得到新的文章
     * /
    受保护的ArrayList<报告> doInBackground(空...未使用){
        返回getReports();
    }

    / **
     *在后执行。
     *关闭进度对话框
     * /
    @覆盖
    保护无效onPostExecute(ArrayList中<报告>更新){
        nzbla.setListItems(更新);
        nzbla.notifyDataSetChanged();
    }
}


私人的ArrayList<报告> getReports(){
    ArrayList的<报告>报告=新的ArrayList<报告>();
    ContentResolver的R = getContentResolver();
    光标C = r.query(NZBReportProvider.CONTENT_URI,NULL,NULL,NULL,NZBReportProvider.ARTICLE_KEY_ROWID +降序);
    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猫= c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_CAT);
    INT大小= c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_SIZE);
    INT链接= c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_LINK);
    INT CATID = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_CATID);
    INT日期= c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_DATE_ADDED);
    INT组= c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_GROUP);

    如果(c.getCount()大于0){
        c.moveToFirst();
        做 {
            网址URL = NULL;
            尝试 {
                URL =新的URL(c.getString(链接));
            }赶上(MalformedURLException异常E){
                // TODO自动生成的catch块
                e.printStackTrace();
            }
            reports.add(新报告(c.getString(职称),网址,c.getString(递减),c.getString(猫),c.getString(日期),c.getString(大小),c.getInt(CATID) ,c.getString(集团)));
        }而(c.moveToNext());
    }
    返回报告;
}
 

解决方案

您需要做的适配器数据的全部更新UI线程上,这样的synchronized块是没有必要的。这也是无用的,因为你同步的的AsyncTask 这是每一个新的它执行时创建的。

另一个问题是,要调用 notifyDataSetChanged 外部的适配器。你应该在你的 setListItems 方法的最后调用它。这不应该是导致错误,但因为它被在UI线程上执行,但它不应该被调用的方式。

您应该确保你的 getReports 方法不改变以任何方式适配器的后备存储。由于它是在一个单独的线程运行它不能修改任何的适配器访问过。即使是锁保护。你需要做的就是在你的 doInBackground 方法生成更新或新的列表等列表,并传递到 onPostExecute 然后提交该UI线程上的新数据适配器。所以,如果你的 getReports 功能正在发生变化 report_list 和您的适配器引用了 report_list 你做错了。 getReports 必须创建一个新的 report_list 然后再传递回你的适配器,当它完成了UI线程上创建它。

要重申的是,你可以只修改数据的适配器,随后的的ListView 访问过的UI线程上。使用同步/锁不会改变这一要求。

I've written a ListActivity that has a custom list adapter. The list is being updated from a ContentProvider when onCreate is run. I also have a service that gets started when I run the app and it first updates the ContentProvider, then sends a Broadcast that the content has been updated.
My ListActivity recieves the broadcast and tries to update my ListView. My problem is, I'm getting intermittent errors about the ListView adapter data changing without the ListView being notified. I call the notifyDataSetChanged() method on my list adapter right after I update it. What it seems like is happening is the list is still in the process of being updated after first call in onCreate when it recieves the broadcast from the service to update, so it tries to update my ListView before it's finished updating from it's first run. Does this make sense? Here is some of my code.

NOTE: The service is working properly, it gets new data and updates my ContentProvider, and I do get the broadcast in my activity when it is updated.

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

Now that this question is answered, I will post my updated code in an effort to help others who might come across it.

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

解决方案

You have to do all your updating of Adapter data on the UI thread so the synchronized block isn't necessary. It's also useless since your synchronizing on the AsyncTask which is created new each time it's executed.

Another problem is that you are calling notifyDataSetChanged external to the Adapter. You should call it at the end of your setListItems method. That shouldn't be causing errors though since it's being executed on the UI thread but it should not be called in that way.

You should make sure that your getReports method is not modifying the backing store of the Adapter in any way. Since it is running on a separate thread it cannot modify anything the Adapter has access too. Even if it's protected by locks. What you need to do is in your doInBackground method is generate the list of updates or a new list, etc., and pass that into onPostExecute which then commits the new data to the Adapter on the UI thread. So, if your getReports function is changing report_list and your Adapter has a reference to report_list you are doing it wrong. getReports must create a new report_list and then pass that back to your Adapter when it's finished creating it on the UI thread.

To reiterate, you can only modify data the Adapter and subsequently the ListView has access too on the UI thread. Using synchronization/locks does not change this requirement.

这篇关于被通知的ListView适配器,数据更改,恕不的ListView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆