网络呼叫报警超时

时间:2017-06-19 20:46:20

标签: android networking retrofit alarmmanager

我有一个应用程序使用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)进行网络通信,但您可能已经从我的代码中猜到了这一点;)

有关如何在手机休眠时使网络通话正常工作的任何想法?

8 个答案:

答案 0 :(得分:2)

您应该在这里使用JobService,它有许多约束来处理不同的场景,并且您的作业也保证由系统执行。

这里的问题是打盹模式,使用JobService可以很容易地解决这个问题。

实施也很简单,您只需要创建一个JobService,然后在其内部onStartJob()启动您的网络线程然后调度您的工作。

了解更多详情

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

答案 1 :(得分:2)

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

  

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

进一步阅读说

  

如果您正在利用此方法有更多时间   执行,知道可用时间可以更长是很有用的   某些情况。特别是,如果您正在接收广播   不是前台广播(即发件人未使用过   FLAG_RECEIVER_FOREGROUND),接收器允许更多时间   运行,允许它们执行30秒甚至更多。

     

(长期工作应该被惩罚到另一个系统设施,如   JobSchedulerService,或特别注意JobIntentService),

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

我还没有测试过这些。

答案 2 :(得分:1)

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

您应该将请求逻辑移动到您将从广播接收器启动的服务(IntentService)并在那里发出请求。

这应该可以正常工作。

答案 3 :(得分:1)

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

  

只要报警,报警管理器就会保持CPU唤醒锁定   接收者的onReceive()方法正在执行。这保证了   在完成广播处理后,手机才会睡眠。   一旦onReceive()返回,Alarm Manager就会释放此唤醒锁定。   这意味着手机在某些情况下会尽快睡觉   onReceive()方法完成

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

你说你测试过购买了WakeLock,但新的Android 6.0 Doze模式忽略了唤醒锁

似乎OnReceive必须等待任务结束

一些想法:

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

如果任务使用Thread对象,那么使用thread.join()?

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

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

  

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

     
      
  • 如果您需要设置在Doze中触发的警报,请使用setAndAllowWhileIdle()&gt;或setExactAndAllowWhileIdle()。
  •   
  • 使用setAlarmClock()设置的警报继续正常启动 - 系统在警报触发前不久退出Doze。
  •   

答案 6 :(得分:0)

您的实施中存在两个问题:

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

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

我希望这会对你有帮助......

答案 7 :(得分:0)

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

通过使用 Foreground 服务,我们可以设置在后台开始执行 API 调用的时间间隔,而不受打盹模式的影响。请遵循包含完整代码和演练的 simple example

快乐编码。谢谢;)