来自服务的Android ListView notifyDataSetChanged()

3
我有一个后台服务,它从服务器接收消息,并使用这些消息更新在ListView中显示的对象的内部属性。
我总是使用runOnUiThread方法运行listArrayAdapter.notifyOnDataSetChanged()命令。
由于某些原因,有时候ListView会刷新并显示属性更新,有时候则不会。
为了测试,我在我的ListView中添加了一个“刷新”按钮,当按下它时,listArrayAdapter.notifyOnDataSetChanged()被执行。
每次点击按钮时,视图都会完美地刷新。
我真的无法理解为什么尝试从服务中刷新时它不总是起作用,但我认为我可能没有始终在UIThread上运行...
我真的很绝望,希望能得到帮助。 我的代码 ServerConnectionManager.java - 扩展自Service。
//example of a command executed when a specific message received from the server:
//app is the Application variable
    public void unFriend(int userId)
    {
        serverResponseManager.onUnFriend(app.getApplicationFriend(userId),false);
    }

ServerResponseManager.java - 一个处理所有应用程序对服务器消息响应的类:

    public void onUnFriend(FacebookUser facebookUser, boolean isYouRemovedClient) {                 

         //this is the property which will effect the ListView view when calling the  
         //arrayListAdataper.notifyOnDataSetChanged();
        facebookUser.setApplicationFriend(false);        
        app.getApplicationFriends().remove(facebookUser);
        app.getDatabaseManager().deleteApplicationFriend(facebookUser.getId());         

         //if the application is currently running in the UI (not on the background) it will run a method inside the BaseActivity
        if (app.isApplicationInForeground())
        {           
            app.getCurrentActivity().onUnFriend(facebookUser);
            if (isYouRemovedClient)
                app.showToast(facebookUser.getName() + " has removed from your friends", true);
            else
                app.showToast(facebookUser.getName() + " has removed you from friends", true);
        }
    }

BaseActivity.java - 一种活动(Activity),它为扩展它的所有活动设置了所有默认配置。

//in this exemple the BaseActivity method does nothing but the ListViewActivity.java method override it
public void onUnFriend(FacebookUser facebookUser)
{                   

}

ListViewActivity.java - 继承自BaseActivity,并且其中有一个ListView,它应该反映在ServerResponseManager中的public void onUnFriend(FacebookUser facebookUser,boolean isYouRemovedClient)中进行的FacebookUser对象属性更改。

@Override
public void onUnFriend(FacebookUser facebookUser)
{       
    updateView();
}

private void updateView()
{
    runOnUiThread(updateViewRunnable());
}

private Runnable updateViewRunnable()
{
    Runnable run = new Runnable() {

        @Override
        public void run() {             
            listArrayAdapter.notifyDataSetChanged();
        }
    };
    return run;
}

使用runOnUiThread肯定会使传递的Runnable在UI线程上运行。如果您发布一些代码,那将是一个好主意,因为这可能是某种并发问题。 - Trevor
你说得很对...我已经添加了一些代码示例。 - Asaf Nevo
我认为你应该在listArrayAdapter.notifyDataSetChanged()之前添加Log,这样你就可以知道notifyDataSetChange是否执行以及何时执行。这将帮助你更快地找到问题。 - landry
我猜测你的 updateView() 方法并不总是在你认为它应该被调用时被调用。你是否添加了一些日志来检查呢?另外,isApplicationInForeground()getCurrentActivity() 方法是如何实现的?使用服务而不是线程可能会改变这些方法的行为。 - David Wasser
如果您只是想要解决方案并需要修复问题,我建议您注册一个BroadcastReceiver来接收更新(您的服务应该广播而不是通知ListView)。 - Sherif elKhatib
5个回答

9

不要混合业务逻辑,这会让代码变得复杂难以阅读。

  1. 在你的服务中,广播一个包含更新信息的意图。
  2. ListView所在的Activity中,创建并注册带有用于更新事件的IntentFilterBroadcastReceiver
  3. 在你的BroadcastReceiveronReceive方法中处理更新事件,例如更新列表。

4
这也适用于LocalBroadcastManager,该工具可在Android支持包中使用,仅用于在应用程序内部保持您的“广播”。或者,尝试使用Otto:http://square.github.com/otto/ - CommonsWare
是的,说得好。顺便一提,@CommonsWare,感谢您在droidconUK上的照片;) - pawelzieba

1
一个服务通常应该独立于UI相关的问题。解耦服务和UI相关内容的好方法是使用事件总线模式。对于Android,可以查看https://github.com/greenrobot/EventBus
在ServerConnectionManager中,您可以发布一个事件:
EventBus.getDefault().post(new UnfriendEvent(userId));

现在将您的活动注册到事件总线,通过调用onEvent方法将事件传递给活动:
public void onEventMainThread(UnfriendEvent event) {...}

这样做可以解耦组件,从而实现整洁、清晰的软件设计,非常灵活适应变化。


1
您可以在ListView中使用游标来显示数据。服务将数据写入/更新到ContentProvider中。在数据库事务结束时,您只需使用以下代码:
getContext().getContentResolver().notifyChange(PROVIDER_URI,null);

你的ListView会自动更新。


基本上,你需要一个ContentProvider,它比BroadcastReceiver的解决方案更复杂一些。例如,重写你的Content Provider的#insert方法并进行插入操作。非常重要的是,你的Cursor和Content Provider指向__相同__的URI,这样在ContentProvider内部进行更新就变得非常容易。 - Kitesurfer

1

相反,在服务的 onDestroy 中使用 notifyDataSetChanged。

列表视图将被刷新。


0

您可以使用此教程来正确构建代码架构。

使用后台服务开发应用程序

它展示了如何从服务接收通知并更新UI。

在调用AsyncTask之前,runOnUiThread主要被使用。我认为您应该改用handler(它可以更新UI,并允许线程运行)。请尝试使用handler并查看发生了什么。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接