Android - фоновый сервис с сетевым вызовом
Мне нужно создать фоновый сервис, который запускается каждые 10-20 минут, и выполнить сетевой вызов API. Что я пробовал: 1. Taditional Service с AlarmManager и Receiver с действием BOOT_COMPLETED. 2. Firebase JobService
В первом случае служба запускается каждые 10-20 минут и работает нормально, пока я не заблокирую свое устройство. Если устройство обнаружено, служба работает, но я получаю исключение:
java.net.UnknowHostException: невозможно разрешить хост "api.somehost.com": нет адреса, связанного с именем хоста
Я считаю, что устройство находится в режиме Doze, и мое приложение не может получить доступ к сети.
Во втором случае работа не срабатывает, когда я блокирую свое устройство.
Эта служба также выбирает местоположение устройства, но в обоих случаях работает нормально, поэтому для простоты я пропустил этот код.
AndroidManifest:
<service
android:name=".service.GeolocationService"
android:process=":geolocationServiceUM"
android:enabled="true"
android:exported="false"/>
<receiver
android:name=".receiver.GeolocationBootReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.REBOOT"/>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<category android:name="android.intent.category.HOME" />
</intent-filter>
</receiver>
<service
android:exported="false"
android:name=".service.GeolocationJobService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:process=":geolocationJobService">
<intent-filter>
<action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE"/>
</intent-filter>
</service>
Получатель:
class GeolocationBootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Log.d(GeolocationBootReceiver::class.java.simpleName, "GeolocationBootReceiver::onReceiver()")
val ctx = context ?: UpMenuApplication.appContext
GeolocationService.startService(ctx)
}
}
Обслуживание:
class GeolocationService : Service() {
companion object {
val TAG: String = GeolocationService::class.java.simpleName
val INTERVAL: Long = 1000 * 60 * 15
var notificationId: Int = 100
fun isServiceRunning(): Boolean {
val activityManager = UpMenuApplication.appContext.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
for (service in activityManager.getRunningServices(Int.MAX_VALUE)) {
if (GeolocationService::class.java.name == service.service.className) {
return true
}
}
return false
}
fun startService(context: Context) {
if (!GeolocationService.isServiceRunning()) {
val intent = Intent(context, GeolocationService::class.java)
val pendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
if (Build.VERSION.SDK_INT >= 23) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, INTERVAL, pendingIntent)
}
else if (Build.VERSION.SDK_INT >= 19) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, INTERVAL, pendingIntent)
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, INTERVAL, pendingIntent)
}
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), GeolocationService.INTERVAL, pendingIntent)
}
}
}
@Inject
lateinit var geolocationRepository: GeolocationRepository
private var screenWakeLock: PowerManager.WakeLock? = null
override fun onBind(intent: Intent?): IBinder {
return null!!
}
@SuppressLint("InvalidWakeLockTag")
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
try {
val pm = baseContext.getSystemService(Context.POWER_SERVICE) as PowerManager
screenWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ScreenLock tag from AlarmListener");
screenWakeLock?.acquire(30000)
} catch (e: Exception) {
Log.d(TAG, e.message)
}
updateCustomerPositionOnServer(15,49,"token")
return START_STICKY
}
override fun onCreate() {
super.onCreate()
Log.d(TAG, "onCreate")
}
private fun updateCustomerPositionOnServer(lat: Double, lng: Double, token: String) {
val location = Location(lat, lng)
geolocationRepository.updateCustomerPosition(token, location).enqueue(object: Callback<Void> {
override fun onFailure(call: Call<Void>?, t: Throwable?) {
Log.d(TAG, call.toString())
Log.d(TAG, call?.request()?.url()?.toString())
Log.d(TAG, t?.message)
Log.d(TAG, call?.request()?.body()?.toString())
Log.d(TAG, "updateCustomerPosition failed." + t?.toString())
screenWakeLock?.release()
}
override fun onResponse(call: Call<Void>?, response: Response<Void>?) {
Log.d(TAG, "updateCustomerPosition success")
Log.d(TAG,"isSuccess: ${response?.isSuccessful}")
screenWakeLock?.release()
}
})
}
}
Начальный код:
private fun checkAndStartGeolocation() {
val jobDispatcher = FirebaseJobDispatcher(GooglePlayDriver(UpMenuApplication.appContext))
val geolocationJob = jobDispatcher.newJobBuilder()
.setService(GeolocationJobService::class.java)
.setTag(GeolocationJobService.TAG)
.setRecurring(true)
.setLifetime(Lifetime.FOREVER)
.setTrigger(Trigger.executionWindow(120, 125))
.setReplaceCurrent(true)
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
.build()
jobDispatcher.mustSchedule(geolocationJob)
GeolocationService.startService(this)
}