【问题标题】:Network call on alarm times out网络报警超时
【发布时间】:2017-11-22 05:00:22
【问题描述】:

我有一个应用程序,它使用AlarmManager 来安排每隔 X 时间重复警报。当我的接收者收到Intent时,它必须发出一个http请求。

警报本身可以正常工作并在应该触发时触发。但是,当电话不使用时,网络呼叫开始超时。具体来说:

当我安排它每分钟触发一次时(我知道这是不好的做法,但只是为了说明),前 5-8 分钟请求成功。之后,我收到了java.net.SocketTimeoutException: connect timed out。有时它确实会成功,但大多数情况下都会发生这种情况。

我尝试将连接/读/写超时设置为一分钟,但后来我得到了这个异常而不是上面的那个:java.net.ConnectException: Failed to connect to myapp.example.com/123.45.67.89:80

我的代码:

public class AlarmReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // Consider mApi and myBody to be initialised and valid
        mApi.myPostRequest(myBody).enqueue(new Callback<Void> {

            @Override
            public void onResponse(Call<Void> call, Response<Void> response) {
                //Does not get here
            }

            @Override
            public void onFailure(Call<Void> call, Throwable t) {
                t.printStackTrace();
            }
        }
    }
}

我尝试过的事情:

  • 如前所述,增加超时时间
  • 获取WakeLock in onReceive 并在通话完成时释放它(添加 许可)

其他信息:

  • 警报设置使用 alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), interval, pendingIntent); 来自我的 Activity
  • 我正在使用 Retrofit (2.1.0) 进行网络通信,但您可能已经从我的代码中猜到了 ;)

关于如何在手机休眠时让网络通话正常工作有什么想法吗?

【问题讨论】:

  • 听起来像Doze mode interference。对于您的 Android 5.0+ 设备,请考虑切换到 JobScheduler 并将作业配置为仅在有 Internet 连接时才能获得控制权。
  • @jelsief 你找到解决方案了吗?
  • @dor506 我没有继续这个项目..

标签: android networking retrofit alarmmanager


【解决方案1】:

你应该在这里使用 JobService,它有很多约束来处理不同的场景,而且你的 Job 保证被系统执行。

这里的问题是打盹模式,使用JobService可以轻松解决。

实现也很简单,您只需创建一个 JobService 并在其中的 onStartJob() 启动您的网络线程,然后分派您的工作。

更多详情

https://developer.android.com/reference/android/app/job/JobService.html

【讨论】:

    【解决方案2】:

    来自https://developer.android.com/reference/android/content/BroadcastReceiver.html

    作为一般规则,广播接收器最多允许运行 10 个 在系统将其视为无响应和 ANR 之前的几秒钟 应用程序。由于这些通常在应用程序的主线程上执行,它们 已经受到各种操作的约 5 秒时间限制的约束 这可能发生在那里(更不用说只是避免 UI jank),所以 接收限制通常不值得关注。然而,一旦你使用 {@goAsync },虽然可以脱离主线程,但是广播 执行限制仍然适用,其中包括花费的时间 在调用此方法和最终 PendingResult.finish() 之间。

    进一步阅读说

    如果您利用这种方法有更多时间 执行,知道可用时间可以更长是有用的 某些情况。特别是,如果您收到的广播 不是前台广播(即发送方没有使用 FLAG_RECEIVER_FOREGROUND),则允许接收器有更多时间 运行,允许它们执行 30 秒甚至更长时间。

    (长时间的工作应该放在另一个系统设施上,例如 JobScheduler, Service, 或者特别看JobIntentService),

    您可以尝试使用@goAsync。或者你可以将你的逻辑切换到JobIntentService

    我没有测试过这些。

    【讨论】:

      【解决方案3】:

      您的代码中有一个基本错误 - 您无法在广播接收器中发出请求(或任何长时间运行的操作) - 它会在大约 10 秒后死亡,因此这可能是您的一些失败的原因。

      您应该将请求逻辑移至服务 (IntentService),您将从广播接收器开始并在那里发出请求。

      应该没问题。

      【讨论】:

        【解决方案4】:

        来自开发者文档:https://developer.android.com/reference/android/app/AlarmManager.html

        只要闹钟响起,闹钟管理器就会持有 CPU 唤醒锁 接收者的 onReceive() 方法正在执行。这保证了 在您完成广播处理之前,手机不会休眠。 一旦 onReceive() 返回,警报管理器就会释放这个唤醒锁。 这意味着手机在某些情况下会在您的 onReceive() 方法完成

        在你的代码中 onReceive 将在mApi.myPostRequest(myBody).enqueue ...task 执行之前返回,那么这个任务可能永远不会执行,因为 CPU 将在 onReceive 返回时立即停止。

        您说您测试了获取唤醒锁,但新的 Android 6.0 打盹模式会忽略唤醒锁

        看来 OnReceive 将不得不等待任务结束

        一些想法:

        使用 thread.sleep 在循环中检查某些终止标志?

        如果任务使用 Thread 对象然后使用 thread.join() ?

        【讨论】:

          【解决方案5】:

          您应该使用AlarmManager.RTC_WAKEUP,而不是AlarmManager.ELAPSED_REALTIME_WAKEUP 来唤醒设备,并且您应该使用服务来完成您在收到onReceivestartWakefulService(context, service.class) 中开始的工作。这将确保设备完全唤醒并在不超时的情况下进行网络呼叫。

          alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, SystemClock.elapsedRealtime(), interval, pendingIntent);
          

          【讨论】:

            【解决方案6】:

            如果问题是由忽略打盹和唤醒锁引起的,您应该尝试https://developer.android.com/training/monitoring-device-state/doze-standby.html 中提供的提示:

            标准 AlarmManager 警报(包括 setExact() 和 setWindow())>推迟到下一个维护窗口。

            • 如果您需要设置在打瞌睡时触发的警报,请使用 setAndAllowWhileIdle() >或 setExactAndAllowWhileIdle()。
            • 使用 setAlarmClock() 设置的警报继续正常触发 - 系统在警报触发前不久退出打盹。

            【讨论】:

              【解决方案7】:

              你的实现中有两个问题:

              1) 如果广播接收器没有在 10 秒内完成执行,则会发生 ANR。 2) 所有网络调用都在后台进行了优化,所以当设备被唤醒时它可能会工作,否则不会触发 HTTP 请求以节省电池和其他资源。

              您应该做的是在服务内部创建一个循环(睡眠时间为几秒钟),检查每次迭代的时间,当达到该时间然后执行任务时,我在尝试将文件上传到服务器时也遇到了此类问题间隔 1 小时,所以我决定自己工作而不是使用 AlarmManager 类...

              希望对你有帮助……

              【讨论】:

                【解决方案8】:

                使用前台服务完成工作。 请参阅官方链接以深入了解Foreground service

                通过使用前台服务,我们可以设置在后台开始执行 API 调用的时间间隔,而不受打盹模式的影响。关注此simple example,其中包括完整的代码和演练。

                快乐的编码。谢谢;)

                【讨论】:

                  猜你喜欢
                  • 2013-06-02
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-01-01
                  • 2014-02-25
                  • 2014-04-15
                  • 2022-09-28
                  • 1970-01-01
                  相关资源
                  最近更新 更多