Socket connection in background - android

I need to make an application where, while the user is authorized, it keeps the socket connection until it is logged out. For this purpose, a foreground service is created, which starts after the authorization of the user, and stops when it is logged out. It implements connection and reconnection on the socket.
All works well until you press the power button and turn off the charging. After this, the user stops receiving pongs from the server and the SocketTimeoutException is received on the OkHttp, and also stops receiving messages on the socket. On JavaWebsocket, The connection was closed because the other endpoint did not respond with a pong in time is received, after which you can successfully create a new socket connection, but it will repeat the same problem in the loop.
In the settings, the optimization of the battery for this application was disabled. What can I do to make a stable connection socket work in the background?
Implementation of activity:
class MainActivity : BaseFragmentPermissionActivity(), MainMvpView {
private var mIsSocketBound = false
private var mSocketBroadcastReceiver = SocketBroadcastReceiver(this)
private var mSocketConnection = SocketConnection(this)
private var mSocketService: SocketService? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
doBindService()
}
private fun doBindService() {
bindService(Intent(this, SocketService::class.java), mSocketConnection, Context.BIND_AUTO_CREATE)
mIsSocketBound = true
}
override fun onStart() {
super.onStart()
...
mSocketService?.doStopForeground()
}
override fun onStop() {
mSocketService?.doStartForeground()
...
super.onStop()
}
override fun onDestroy() {
doUnbindService()
...
super.onDestroy()
}
private fun doUnbindService() {
if (mIsSocketBound) {
unbindService(mSocketConnection)
mIsSocketBound = false
mSocketService = null
}
}
class SocketConnection(mainActivity: MainActivity) : ServiceConnection {
private val mMainActivity: WeakReference<MainActivity> = WeakReference(mainActivity)
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val socketService = (service as SocketService.LocalBinder).getService()
mMainActivity.get()?.mSocketService = socketService
if (socketService.isForeground()) {
socketService.doStopForeground()
}
}
override fun onServiceDisconnected(name: ComponentName?) {
mMainActivity.get()?.mSocketService = null
}
}
}
Implementation of service:
class SocketService : Service(), MvpErrorHandler {
private val mConnectingHandler = Handler()
private val mConnectingTask = ConnectingTask(this)
private var mIsRunningForeground = false
override fun onBind(intent: Intent?): IBinder {
startService(Intent(this, SocketService::class.java))
return mBinder
}
override fun onCreate() {
super.onCreate()
DaggerServiceComponent.builder()
.serviceModule(ServiceModule(this))
.applicationComponent(PatrolApplication.applicationComponent)
.build()
.inject(this)
startConnecting()
...
}
override fun onDestroy() {
...
stopConnecting()
super.onDestroy()
}
private fun startConnecting() {
if (!mIsConnecting) {
mIsConnecting = true
mConnectingHandler.post(mConnectingTask)
}
}
private fun stopConnecting() {
mConnectingHandler.removeCallbacks(mConnectingTask)
mIsConnecting = false
}
private fun openConnection() {
mCompositeDisposable.add(mDataManager.getSocketToken()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(false, this, {
stopConnecting()
mDataManager.openSocketConnection(it.token)
}, {
mConnectingHandler.postDelayed(mConnectingTask, RECONNECT_TIME.toLong())
return#subscribe ErrorHandlerUtil.handleGetSocketError(it, this)
}))
}
class ConnectingTask(socketService: SocketService) : Runnable {
private val mSocketService: WeakReference<SocketService> = WeakReference(socketService)
override fun run() {
mSocketService.get()?.openConnection()
}
}
}
Implementation of SocketHelper using JavaWebsocket:
class CustomApiSocketHelper #Inject constructor() : ApiSocketHelper {
private var mCustomSocketClient: WebSocketClient? = null
override fun openSocketConnection(token: String) {
mCustomSocketClient = CustomSocketClient(URI(CONNECTION_URL + token))
mCustomSocketClient?.connect()
}
override fun sendMessage(text: String) {
if (mCustomSocketClient?.isOpen == true) {
try {
mCustomSocketClient?.send(text)
} catch (t: Throwable) {
Log.e(TAG, Log.getStackTraceString(t))
Crashlytics.logException(t)
}
}
}
override fun closeSocketConnection() {
mCustomSocketClient?.close(CLOSE_REASON_OK)
}
class CustomSocketClient(uri: URI) : WebSocketClient(uri) {
init {
connectionLostTimeout = PING_TIMEOUT
}
override fun onOpen(handshakedata: ServerHandshake?) {
sendBroadcast(SocketActionType.OPEN.action)
}
override fun onMessage(message: String?) {
sendBroadcast(SocketActionType.MESSAGE.action, message)
}
override fun onClose(code: Int, reason: String?, remote: Boolean) {
if (code != CLOSE_REASON_OK) {
//call startConnecting() in service
sendBroadcast(SocketActionType.CLOSE.action)
}
}
override fun onError(ex: Exception?) {
sendBroadcast(SocketActionType.FAILURE.action)
}
private fun sendBroadcast(type: Int) {
val intent = Intent().apply { action = SOCKET_BROADCAST_ACTION }
intent.putExtra(SOCKET_MESSAGE_TYPE, type)
LocalBroadcastManager.getInstance(CustomApplication.application).sendBroadcast(intent)
}
private fun sendBroadcast(type: Int, text: String?) {
val intent = Intent().apply { action = SOCKET_BROADCAST_ACTION }
intent.putExtra(SOCKET_MESSAGE_TYPE, type)
intent.putExtra(SOCKET_MESSAGE, text)
LocalBroadcastManager.getInstance(CustomApplication.application).sendBroadcast(intent)
}
}
}
Implementation of SocketHelper using OkHttp:
class CustomApiSocketHelper #Inject constructor() : ApiSocketHelper {
private var mCustomSocketClient: WebSocket? = null
override fun openSocketConnection(token: String) {
val request = Request.Builder()
.url(CONNECTION_URL + token)
.build()
mCustomSocketClient = CustomApplication.applicationComponent.authorizedClient().newWebSocket(request, CustomSocketClient())
}
override fun sendMessage(text: String) {
mPatrolSocketClient?.send(text)
}
override fun closeSocketConnection() {
mCustomSocketClient?.close(CLOSE_REASON_OK, null)
}
class CustomSocketClient : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) {
super.onOpen(webSocket, response)
sendBroadcast(SocketActionType.OPEN.action)
}
override fun onMessage(webSocket: WebSocket, text: String) {
super.onMessage(webSocket, text)
sendBroadcast(SocketActionType.MESSAGE.action, text)
}
override fun onClosed(webSocket: WebSocket?, code: Int, reason: String?) {
super.onClosed(webSocket, code, reason)
if (code != CLOSE_REASON_OK) {
sendBroadcast(SocketActionType.CLOSE.action)
}
}
override fun onFailure(webSocket: WebSocket?, t: Throwable?, response: Response?) {
super.onFailure(webSocket, t, response)
sendBroadcast(SocketActionType.FAILURE.action)
}
private fun sendBroadcast(type: Int) {
val intent = Intent().apply { action = SOCKET_BROADCAST_ACTION }
intent.putExtra(SOCKET_MESSAGE_TYPE, type)
LocalBroadcastManager.getInstance(CustomApplication.application).sendBroadcast(intent)
}
private fun sendBroadcast(type: Int, text: String?) {
val intent = Intent().apply { action = SOCKET_BROADCAST_ACTION }
intent.putExtra(SOCKET_MESSAGE_TYPE, type)
intent.putExtra(SOCKET_MESSAGE, text)
LocalBroadcastManager.getInstance(CustomApplication.application).sendBroadcast(intent)
}
}
}
...
#Provides
#Singleton
#Named(AUTHORIZED_CLIENT)
fun provideAuthorizedClient(builder: OkHttpClient.Builder, interceptor: Interceptor, authenticator: Authenticator): OkHttpClient = builder
.addInterceptor(interceptor)
.authenticator(authenticator)
.pingInterval(PING_TIMEOUT.toLong(), TimeUnit.SECONDS)
.build()
#Provides
#Singleton
fun provideOkHttpBuilder() = CustomApiHelper.getOkHttpBuilder()
fun getOkHttpBuilder(): OkHttpClient.Builder {
val builder = OkHttpClient.Builder()
builder.readTimeout(NETWORK_CALL_TIMEOUT, TimeUnit.SECONDS)
builder.writeTimeout(NETWORK_CALL_TIMEOUT, TimeUnit.SECONDS)
if (BuildConfig.DEBUG) {
val logger = HttpLoggingInterceptor()
logger.level = HttpLoggingInterceptor.Level.BASIC
builder.addInterceptor(logger)
}
return builder
}

After some research and testing on different devices, it was found that for stable operation on the network, it is necessary that the device is charging or has a screen enabled. In the other case, neither PARTIAL_WAKE_LOCK nor the disabling of battery optimization in the settings itself can solve the problem.
The recommended way to solve this problem is to add this code to your activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
This prevents the screen from turning off and provides a stable socket connection. But we still have the situation that the user can press the power button. And, if at this moment the device is charging, everything will work as before, but otherwise, we will get the socket disconnect. To solve this problem, you need to periodically wake the device, in order to support the ping-pong process. This is not a recommended solution because it will lead to battery draining, and can not guarantee 100% performance, but if this moment is critical for you, then you can use this solution. You need to add this code, in a suitable place for you, in this example is used at the time of ping.
#Suppress("DEPRECATION")
override fun onWebsocketPing(conn: WebSocket?, f: Framedata?) {
if (mSocketWakeLock == null) {
mSocketWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK or PowerManager.SCREEN_BRIGHT_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG)
}
mSocketWakeLock?.takeIf { !it.isHeld }?.run { acquire(WAKE_TIMEOUT) }
super.onWebsocketPing(conn, f)
mSocketWakeLock?.takeIf { it.isHeld }?.run { release() }
}
Using this solution, on the test devices socket connection, with good Internet, stays stable for 2 hours or more. Without it, it is constantly disconnect.

Related

How can i keep background android services running when app killed or closed

Hello guys
I have News app, I want to push notification every updates.
I use socketIO to make listen from the server,
My problem with Android Services I want to keep Service running while up closed | killed.
I have start my services when app running and I return in onStartCommand START_STICKY_COMPATIBILITY
its work correctly when app close but while of hour services killed by OS but not start again
any help ?
Note: I ignore battery optimize for the app
My Services Class:
class ServicesMain: Service() {
override fun onBind(p0: Intent?): IBinder? {
return null;
}
#SuppressLint("TrulyRandom")
fun handleSSLHandshake() {
try {
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
override fun getAcceptedIssuers(): Array<X509Certificate>? {
return null;
}
override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) {}
override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) {}
})
val sc = SSLContext.getInstance("SSL")
sc.init(null, trustAllCerts, SecureRandom())
HttpsURLConnection.setDefaultSSLSocketFactory(sc.socketFactory)
HttpsURLConnection.setDefaultHostnameVerifier { arg0, arg1 -> true }
} catch (ignored: Exception) {
}
}
#RequiresApi(Build.VERSION_CODES.O)
fun startSocket(){
handleSSLHandshake()
runBroadcast()
Log.d("TAG", "BroadcastReceiver: ")
Timer().schedule(object : TimerTask() {
override fun run() {
Log.d("TAG", "run: ${socket!!.connected()} ")
}
}, 0, 10000)
}
private var socket: Socket? = null;
#SuppressLint("RestrictedApi")
#RequiresApi(Build.VERSION_CODES.O)
fun runBroadcast() {
val myHostnameVerifier = HostnameVerifier { _, _ ->
return#HostnameVerifier true
}
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {}
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {}
override fun getAcceptedIssuers(): Array<X509Certificate> {
return arrayOf()
}
})
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, trustAllCerts, null)
val okHttpClient = OkHttpClient.Builder()
.hostnameVerifier(myHostnameVerifier)
.sslSocketFactory(sslContext.socketFactory, trustAllCerts[0] as X509TrustManager)
.readTimeout(0, TimeUnit.MILLISECONDS).writeTimeout(0, TimeUnit.MILLISECONDS).build()
val options = IO.Options()
options.transports = arrayOf(Polling.NAME)
options.webSocketFactory = okHttpClient
options.callFactory=okHttpClient
options.secure = true
socket = IO.socket("https://....", options);
socket!!.connect().on("message", Emitter.Listener { args ->
val jsonObject = JSONObject(args[0] as String)
val calendarTime = jsonObject.getLong("starttime") - (Calendar.getInstance().timeInMillis)
println(calendarTime)
val builder = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
val data: Data.Builder = Data.Builder()
data.putStringArray("data", arrayOf<String>(jsonObject.getString("title"), jsonObject.getString("subject"), jsonObject.getString("id")))
val oneTimeWorkRequest = OneTimeWorkRequest.Builder(NotificationEvetns::class.java).setInputData(data.build()).setConstraints(builder.build())
.setInitialDelay(calendarTime, TimeUnit.MILLISECONDS).addTag(jsonObject.getString("id")).build()
this.let { WorkManager.getInstance(it).enqueue(oneTimeWorkRequest) }
})
}
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
Log.d("TAG", "onTaskRemoved: ")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d("TAG", "onStartCommand: ")
return START_STICKY_COMPATIBILITY
}
#RequiresApi(Build.VERSION_CODES.O)
override fun onCreate() {
super.onCreate()
startSocket()
}
override fun onDestroy() {
super.onDestroy()
socket?.disconnect()
}
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
Log.d("TAG", "onTrimMemory: ")
}
}
You can't, not on modern versions of Android. You can make a foreground service which is the least likely thing to be killed (outside of the foreground app), but you can't count on it not being killed. Instead write your code so that it doesn't need to- mainly by using WorkManager for triggered background work. Unless you're writing a server, in which case I'd say that you should use another OS, Android isn't suitable.
For messages from a server like you mentioned, I'd use FCM push messaging rather than a long lived direct server connection.

Proper way to unregister a callback from an Application class

I have implemented a custom Application class in my app which handles updating the app theme before the app start up.
I also registered a network callback to set a variable each time there is a connection change.
My application class is as such:
Application.kt
package com.th3pl4gu3.mes.ui
.....
class MesApplication : Application() {
companion object {
#Volatile
private var INSTANCE: MesApplication? = null
fun getInstance() =
INSTANCE ?: synchronized(this) {
INSTANCE
?: MesApplication().also { INSTANCE = it }
}
}
override fun onCreate() {
super.onCreate()
// Assigns 'this' to the singleton object
INSTANCE = this
// Updates the application's theme
updateAppTheme()
// Start a network callback to monitor internet connection
startNetworkCallback()
}
private fun startNetworkCallback(){
try{
val cm = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val builder = NetworkRequest.Builder()
cm.registerNetworkCallback(builder.build(), object: ConnectivityManager.NetworkCallback(){
override fun onAvailable(network: Network) {
super.onAvailable(network)
Log.v("INTERNET_TEST", "AC: Network Available")
Global.isNetworkConnected = true
}
override fun onLost(network: Network) {
super.onLost(network)
Log.v("INTERNET_TEST", "AC: Network Lost")
Global.isNetworkConnected = false
}
})
Global.isNetworkConnected = false
}catch (e: Exception){
Global.isNetworkConnected = false
}
}
}
However, from the docs, they recommend to unregister this callback but the Application class lifecycle doesn't have any onPause or onDestroy function.
Is there any proper way to unregister this callback to not cause any memory leaks?
Also feel free to suggest any alternatives in case I am coding this wrong
In this case , you can use ActivityLifecycleCallbacks, to detect are any Activity of your is in Foreground?
ActivityLiveCycleListener
class ActivityLiveCycleListener(private val appStateListener: AppStateListener) : Application.ActivityLifecycleCallbacks {
companion object {
var foregroundActivities = 0
}
override fun onActivityPaused(p0: Activity) {
}
override fun onActivityStarted(p0: Activity) {
if(foregroundActivities == 0){
appStateListener.onAppForeGround()
}
foregroundActivities++
}
override fun onActivityDestroyed(p0: Activity) {
}
override fun onActivitySaveInstanceState(p0: Activity, p1: Bundle) {
}
override fun onActivityStopped(p0: Activity) {
foregroundActivities--
if(foregroundActivities == 0){
appStateListener.onAppBackground()
}
}
override fun onActivityCreated(p0: Activity, p1: Bundle?) {
}
override fun onActivityResumed(p0: Activity) {
}
}
And your interface can have two methods to indicate background/foreground state
interface AppStateListener{
fun onAppForeGround()
fun onAppBackground()
}
Now in Application onCreate(), register to ActivityLifeCycleListener
override fun onCreate(){
registerActivityLifecycleCallbacks(ActivityLiveCycleListener(object : AppStateListener{
override fun onAppForeGround() {
//start network listener
}
override fun onAppBackground() {
//remove network listener
}
}))
}

How to really disconnect from WebSocket using OkHttpClient?

I'm trying to disconnect from WebSocket connection, but it's listener is still alive, I can see that websockets are still recreating by "OPEN/FAIL" messages using System.out messages.
On the one hand I've tried to release connection in finally block using client.dispatcher().executorService().shutdown() method, but it rejects other future calls (so it doesn't fit), on the other using client.connectionPool().evictAll() doesn't help either (even if I wait, because it may not exit immediately based on docs https://square.github.io/okhttp/4.x/okhttp/okhttp3/-ok-http-client/)
There is WebSocketListener code:
class WebSocketConnectionListener(
private val updateConnectionValue: (internetConnection: Boolean) -> Unit,
private val okHttpClient: OkHttpClient,
private val urlAddress: String
) : WebSocketListener() {
companion object {
private const val NORMAL_CLOSURE_STATUS = 1000
}
private var isConnected = true
private var webSocketN: WebSocket? = null
init {
createWebSocket()
}
override fun onOpen(webSocket: WebSocket, response: Response) {
println("OPEN: ${response.code}")
isConnected = true
updateConnectionValue(true)
reconnect(webSocket)
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
println("FAIL: $t")
if (!isConnected) {
updateConnectionValue(false)
}
isConnected = false
reconnect(webSocket)
}
private fun createWebSocket() {
val request = Request.Builder().url(urlAddress).build()
webSocketN = okHttpClient.newWebSocket(request, this)
}
private fun reconnect(webSocket: WebSocket) {
webSocket.close(NORMAL_CLOSURE_STATUS, null)
webSocketN?.close(NORMAL_CLOSURE_STATUS, "Connection closed")
webSocketN = null
Thread.sleep(3_000)
createWebSocket()
}
}
There is DataSource implementation code:
class InternetConnectionDataSourceImpl(
private val okHttpClient: OkHttpClient,
private val urlAddress: String
) : InternetConnectionDataSource {
private fun createWebSocketListener(
internetConnectionFlow: MutableStateFlow<Boolean>,
) = WebSocketConnectionListener(
updateConnectionValue = { internetConnectionFlow.value = it },
okHttpClient = okHttpClient,
urlAddress = urlAddress
)
override suspend fun checkConnection(): Flow<Boolean> =
withContext(Dispatchers.IO) {
val internetConnectionFlow = MutableStateFlow(true)
createWebSocketListener(internetConnectionFlow)
flow {
try {
internetConnectionFlow.collect {
emit(it)
}
} finally {
println("finally")
okHttpClient.connectionPool.evictAll()
}
}
}
}
As the result I'm getting this messages in logcat
Use WebSocket.close() for a graceful shutdown or cancel() for an immediate one.
https://square.github.io/okhttp/4.x/okhttp/okhttp3/-web-socket/

How to maintain Singleton class for Web Socket Connection in Android Hilt-dagger?

I am new to Android Dagger-Hilt and I found it useful for my project. However, recently I want to use this concept to get my ServerConnection class to become Singleton across different view (fragment and activity). How can I achieve that?
I had tried to approach below but I can't get it Singleton as it will create 2 ServerConnection instance in my fragment/activity view. Where had I do wrong?
Current approach
AppModule.kt
#Singleton
#Provides
fun provideSocketConnection(tokenDao: TokenDao) : ServerConnection{
val token = runBlocking(Dispatchers.IO) { tokenDao.find() }
val tok = token!!.token
val refreshToken = token.refresh_token
return ServerConnection(URL)
}
ServerConnection.kt
class ServerConnection(url: String) {
enum class ConnectionStatus {
DISCONNECTED, CONNECTED
}
interface ServerListener {
fun onNewMessage(message: String?)
fun onStatusChange(status: ConnectionStatus?)
}
private var mWebSocket: WebSocket? = null
private val mClient: OkHttpClient
private val mServerUrl: String
private var mMessageHandler: Handler? = null
private var mStatusHandler: Handler? = null
private var mListener: ServerListener? = null
init {
mClient = OkHttpClient.Builder()
.readTimeout(3, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build()
mServerUrl = url
}
private inner class SocketListener : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) {
val m = mStatusHandler!!.obtainMessage(0, ConnectionStatus.CONNECTED)
mStatusHandler!!.sendMessage(m)
}
override fun onMessage(webSocket: WebSocket, text: String) {
val m = mMessageHandler!!.obtainMessage(0, text)
mMessageHandler!!.sendMessage(m)
}
override fun onClosed(
webSocket: WebSocket,
code: Int,
reason: String
) {
val m =
mStatusHandler!!.obtainMessage(0, ConnectionStatus.DISCONNECTED)
mStatusHandler!!.sendMessage(m)
}
override fun onFailure(
webSocket: WebSocket,
t: Throwable,
response: Response?
) {
disconnect()
}
}
fun connect(listener: ServerListener?) {
val request = Request.Builder()
.url(mServerUrl)
.build()
mWebSocket = mClient.newWebSocket(request, SocketListener())
mListener = listener
mMessageHandler =
Handler(Handler.Callback { msg: Message ->
mListener?.onNewMessage(msg.obj as String)
true
})
mStatusHandler = Handler(Handler.Callback { msg: Message ->
mListener!!.onStatusChange(msg.obj as ConnectionStatus)
true
})
}
fun disconnect() {
mWebSocket?.cancel()
mListener = null
mMessageHandler?.removeCallbacksAndMessages(null)
mStatusHandler?.removeCallbacksAndMessages(null)
}
fun sendMessage(message: String?) {
mWebSocket!!.send(message!!)
}
}
View (Fragment/Activity)
#AndroidEntryPoint
class RoomFragment : Fragment(), ServerConnection.ServerListener {
#Inject lateinit var socketConnection: ServerConnection
}
You need to annotate your AppModule.kt class with #InstallIn(SinggltonComponent::class).
To know more about the hilt component, check this detail, here.

Problems with JobScheduler - JobsService firing multiple times

I'm trying to do some background work in my android application. As the web suggested I'm using JobScheduler to do so.
The jobs are sometimes firing 5-15 times instead of once. Sometimes they are never firing.
My testdevices run on 5.1.1 and 7.0. The one with Nougat fires way less then the one with lollipop.
This is how I enable my jobs (the 5 seconds interval is only for test purpose):
fun enableTasks() {
val jobScheduler = App.getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
if (PreferenceDao.getInstance().shouldUpdateJob()) jobScheduler.cancelAll()
scheduleJob(jobScheduler, MoniInfoJob.getJob())
scheduleJob(jobScheduler, QueueJob.getJob())
scheduleJob(jobScheduler, MontageOrderUpdateJob.getJob())
PreferenceDao.getInstance().setJobUpdated()
}
private fun scheduleJob(jobScheduler: JobScheduler, jobInfo: JobInfo) {
val jobExists = jobScheduler.allPendingJobs.any { it.id == jobInfo.id }
if (!jobExists) jobScheduler.schedule(jobInfo)
}
All three jobs look kind of the same so I only post one:
The JobService
class QueueJob : JobService() {
override fun onStartJob(jobParameters: JobParameters?): Boolean {
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, p1: Intent?) {
unregisterBroadcastReceiver(this)
jobFinished(jobParameters, false)
}
}
registerBroadcastReceiver(receiver)
MainController.startQueueService()
return true;
}
override fun onStopJob(jobParameters: JobParameters): Boolean {
Log.d(MontageOrderUpdateJob.TAG, "onStopJob")
return false;
}
private fun registerBroadcastReceiver(receiver: BroadcastReceiver) {
LocalBroadcastManager.getInstance(this).registerReceiver(receiver, IntentFilter(JOB_FINISHED))
}
private fun unregisterBroadcastReceiver(receiver: BroadcastReceiver) {
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver)
}
companion object {
val TAG = QueueJob::class.java.name
val jobId: Int = 2
val JOB_FINISHED = TAG + "_finished"
fun getJob(): JobInfo {
val builder = JobInfo.Builder(jobId, ComponentName(App.getContext(), TAG))
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
builder.setPeriodic(5000L)
builder.setPersisted(true)
return builder.build()
}
}
}
And the JobIntentService:
class QueueService : JobIntentService() {
private val TAG = QueueService::class.java.name
override fun onHandleWork(intent: Intent) {
try {
Log.d(TAG, "Jobservice started")
TimerecordQueue().workThroughQueue()
DangerAllowanceQueue().workThroughQueue()
ProjektEndQueue().workThroughQueue()
PhotoUploadQueue().workThroughQueue()
} finally {
sendFinishedBroadcast()
}
}
private fun sendFinishedBroadcast() {
val jobFinishedIntent = Intent(QueueJob.JOB_FINISHED)
LocalBroadcastManager.getInstance(this).sendBroadcast(jobFinishedIntent)
}
}
I had a similar problem once. My problem then was that I didn't check for preexisting schedules.
Could it be you need to do the same?

Categories

Resources