3

问题描述:

在我的 android 应用程序中,当从警报响起后启动的 AsyncTask 执行远程 HTTP(“轮询”)调用时,我遇到了连接问题。

当标准的 Android 设置“启用永远在线的移动数据” 设置 -> 无线和网络 -> 移动网络)打开时,查找工作得很好。

可行的解决方案:警报消失,Android“服务”收到警报意图,启动后台线程(AsyncTask)。新线程获取部分唤醒锁,建立连接(轮询),通知用户并释放唤醒锁。

到目前为止,一切都很好。问题是,当始终开启“关闭”时,如果手机处于待机状态一段时间(> 30 分钟),大部分时间轮询都会失败。

由于轮询线程发送了通知,我直接得到了关于不成功的轮询尝试的反馈。

动机:

许多用户打开“始终开启”以减少电池消耗。因此,应用程序用户很可能会遇到问题。我想处理或防止用户将面临的“错误”。

解决方案尝试:

我做了很多实验,没有任何重大突破:

  • 多次重试和中间休眠给手机一些时间来建立连接
  • http参数(超时等)
  • 不同的 HttpClient (Apache)

问题:

  • 设置“永远在线”到底是什么意思,开发人员必须注意什么?
  • 我想知道是否通常可以实现基于警报的轮询机制,即使“永远在线”被“关闭”也能够建立数据连接。
  • 是否有任何替代解决方案(没有 C2DM 可能)?

更新:

似乎并非所有 Android 设备都具有“始终在线”设置。它似乎与设备有关,或者更有可能是依赖于提供商。

4

2 回答 2

0

如果您使用的是 AlarmManager,它应该唤醒设备。

我想知道您是否在电话被唤醒后,在它有机会确保连接之前过快地建立连接。

考虑:

检查 AlarmManager 接收器中的连接。如果不存在连接,则:

在从 AlarmManager 接收器返回之前,启动一个单独的线程。在线程中:

  1. 获取唤醒锁
  2. 为 CONNECTIVITY_CHANGE 注册一个广播接收器
  3. 等待一段合理的时间以获得成功的连接(由广播接收器事件指示)。
  4. 如果时间到了,释放唤醒锁并停止线程,手机将重新进入睡眠状态。

仅在收到来自 CONNECTIVITY_CHANGE 广播接收器的事件后尝试建立连接。

于 2011-05-12T17:28:59.863 回答
0

如果您打算实现实时通信协议,并尝试通过短期轮询来实现,这可能会以类似的经验说明问题:

我们的应用程序也有类似的要求,并且注意到了一些事情:

  1. 并非每个网络都具有“启用永远在线的移动数据”首选项。我在 Verizon 上使用 DroidX,但此选项不在菜单中。数据始终开启。

  2. 与您的想法相反,当手机进入睡眠状态时,套接字不会被杀死,即使您没有持有部分唤醒锁。当手机进入睡眠状态时,您的应用程序会停止接收广播事件(例如连接更改事件),因此您不能依赖 Android 操作系统来告诉您何时暂时断开数据连接。与其进行警报轮询,不如考虑进行长轮询,通过 Keep-Alive 发送 HTTP 请求,并无限期地保持连接打开,直到获得响应。

  3. 阻塞 Socket 读取操作不会在您丢弃信号时抛出 IOException、EOFException 或其他情况,因此您需要有一个单独的线程定期检查网络状态。最简单的方法是使用 NetworkInterface 类并构建如下内容:

.

private OnCheckNetworkConnectivity networkConnectivityCallback = new OnCheckNetworkConnectivity() {
        String ipAddress;

        public boolean isConnected() {
            String newIpAddress = getLocalIpAddress();
            if(newIpAddress != null) {
            if(ipAddress == null) {
                ipAddress = newIpAddress;
                return true;
            }
            if(!newIpAddress.equals(ipAddress)) {
                ipAddress = newIpAddress;
                return false;
            }

            // still the same IP address, we should still have the same connection
            return true;
            }

            ipAddress = null;
            return false;
        }

        public String getLocalIpAddress() {
            try {
                for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
                    NetworkInterface intf = en.nextElement();
                    for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                        InetAddress inetAddress = enumIpAddr.nextElement();
                        if (!inetAddress.isLoopbackAddress()) {
                            return inetAddress.getHostAddress();
                        }
                    }
                }
            } catch (SocketException ex) {
            return null;
            }
            return null;
        }
    };
于 2011-05-09T20:12:43.617 回答