我有一个应用程序使用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
。有关如何在手机休眠时使网络通话正常工作的任何想法?
答案 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秒甚至更多。
(长期工作应该被惩罚到另一个系统设施,如 JobScheduler,Service,或特别注意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。
快乐编码。谢谢;)