Android客户端使用线程与服务器通信的最佳实践

15

我正在构建一个Android应用程序,只要应用程序运行,就会定期与服务器通信。

我是通过在应用程序启动时初始化到服务器的连接,然后拥有一个名为ReceiverThread的单独线程来接收消息。这个threadsocket中读取消息,分析它,并将其转发到应用程序的适当部分。

这个thread在循环中运行,读取所有需要读取的内容,然后在read()命令上阻塞,直到有新数据到达,所以它大部分时间都被阻塞了。

我通过另一个名为SenderThread的线程处理发送消息。我想知道的是:我是否应该以类似的方式结构化SenderThread?也就是说,我是否应该为该线程维护某种形式的队列,让它发送队列中的所有消息,然后阻塞,直到有新消息进入队列,或者每次需要发送消息时都启动一个新的线程,让它发送消息,然后“死亡”?我倾向于采用第一种方法,但我不知道哪一种方法在性能(在内存中保留被阻塞的线程与初始化新线程)和代码正确性方面更好。

另外,由于我所有的活动都需要能够发送和接收消息,所以我在Application类中持有对这两个线程的引用,这是可接受的方法吗,还是应该以不同方式实现?

我遇到的一个问题是,有时如果我关闭我的应用程序并重新运行它,我实际上会有两个ReceiverThread的实例,因此我会收到一些消息两次。

我猜这是因为我的应用程序实际上没有关闭,先前的线程仍然活动(在read()操作上被阻塞),当我再次打开应用程序时,新线程被初始化,但两者都连接到服务器,因此服务器将消息发送给了两个线程。有关如何解决此问题或如何重新组织它以使其正确的任何提示吗?

我尝试查找这些问题,但发现我的第一个问题存在冲突的例子,而且没有有用的内容适用于我的第二个问题...


关闭应用程序并不会真正终止它。它仍在运行,特别是线程不会自动停止。 - zapl
@zapl 是的,我意识到了这一点,这就是我的问题。问题是:如何可靠地停止线程?或者如何以避免此问题的方式实现相同的整体应用程序? - Bob
确实很棘手。可以使用Thread#interrupt()(+ Socket#close())来停止它,但难点在于跟踪应用程序是运行还是仅在后台空闲。例如,每个活动都可以从onStartonStop进行跟踪。 - zapl
1个回答

20

1. 如果你确实需要在任何时候都保持服务器和客户端之间的开放连接,那么你的方法是可行的。但是我会使用异步连接,比如向服务器发送HTTP请求,然后在服务器感觉合适时获得回复。

如果你需要服务器在稍后的某个时间回复客户端,但不知道何时,你还可以查看Google Cloud Messaging框架,它为你提供了一种透明且一致的方式,从服务器向客户端发送小型消息。

当你开发移动应用程序时,需要考虑一些事情。

  1. 智能手机电池没有无限的电量。

  2. 智能手机的互联网连接有些不稳定,你将在不同的时间失去互联网连接。

当你始终与服务器保持直接连接时,你的应用程序会不断发送保持活动数据包,这意味着你很快就会耗尽手机电量。当移动宽带上的互联网连接不稳定时,有时会失去连接并需要从中恢复。因此,如果你使用TCP,因为你想确保你的数据包被接收,你将不得不多次重发同样的数据包,从而增加了很多开销。
此外,如果你自己在服务器上打开线程,你可能会遇到服务器端的线程问题。假设你有200个客户端同时连接到服务器。每个客户端在服务器上打开1个线程。如果服务器需要同时服务于200个不同的线程,那么这对服务器来说可能是一个相当耗费性能的任务,并且你也需要自己进行大量的工作。
当你退出你的应用程序时,你需要进行清理。这应该在你正在使用的Activity的onPause方法中完成。
这意味着,终止所有活动线程(或至少中断它们),保存您的UI状态(如果需要)并刷新和关闭您与服务器的任何开放连接。
关于使用Threads,我建议使用一些内置的线程工具,如Handlers或实现AsyncTask
如果你真的认为Thread是正确的选择,我强烈建议使用Singleton pattern作为线程的“管理器”。
这个管理器将控制你的线程,所以即使你在应用程序的另一个部分,也不会出现多个Thread同时与服务器通信的情况。
关于Application类的实现,可以查看Application class documentation
此为维护全局应用程序状态所需的基类。您可以通过在AndroidManifest.xml的标签中指定其名称来提供自己的实现,这将导致在为您的应用程序/包创建进程时实例化该类。
通常不需要对Application进行子类化。在大多数情况下,静态单例可以以更模块化的方式提供相同的功能。因此,建议避免实现自己的Application类。但是,如果让您的一个Activity初始化自己的Singleton类以管理线程和连接,可能会遇到麻烦,因为单例的初始化可能会“绑定”到特定的Activity上,因此,如果特定的Activity从屏幕中删除并暂停,它可能会被杀死,因此单例也可能会被杀死。因此,在您的Application实现中初始化单例可能会很有用。

谢谢你,虽然我还不完全确定要做什么,但你的回答确实有所帮助。我知道为移动电话开发有许多方面需要考虑,例如保持电池寿命,但由于我的应用程序使用情况,我确实需要一个恒定的连接。应用程序的使用时间应该很短,5分钟通常足够了,但在这5分钟内应该会有许多互联网交互。我真的不认为保持连接5分钟会耗尽电池,会吗? - Bob
@Bob 五分钟可以。在后台进行定期更新可能会有问题。请参考http://developer.android.com/training/efficient-downloads/index.html。 - zapl

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