Kotlin coroutine scope & job cancellation in non-lifecycle classes

Multi tool use
Multi tool use












0















How to use new Kotlin v1.3 coroutines in classes which do not have lifecycles, like repositories?
I have a class where I check if the cache is expired and then decide whether I fetch the data from the remote API or local database. I need to start the launch and async from there. But then how do I cancel the job?



Example code:



class NotesRepositoryImpl @Inject constructor(
private val cache: CacheDataSource,
private val remote: RemoteDataSource
) : NotesRepository, CoroutineScope {

private val expirationInterval = 60 * 10 * 1000L /* 10 mins */
private val job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.IO + job

override fun getNotes(): LiveData<List<Note>> {
if (isOnline() && isCacheExpired()) {
remote.getNotes(object : GetNotesCallback {
override fun onGetNotes(data: List<Note>?) {
data?.let {
launch {
cache.saveAllNotes(it)
cache.setLastCacheTime(System.currentTimeMillis())
}
}
}
})
}
return cache.getNotes()
}

override fun addNote(note: Note) {
if (isOnline()) {
remote.createNote(note, object : CreateNoteCallback {
override fun onCreateNote(note: Note?) {
note?.let { launch { cache.addNote(it) } }
}
})
} else {
launch { cache.addNote(note) }
}
}

override fun getSingleNote(id: Int): LiveData<Note> {
if (isOnline()) {
val liveData: MutableLiveData<Note> = MutableLiveData()
remote.getNote(id, object : GetSingleNoteCallback {
override fun onGetSingleNote(note: Note?) {
note?.let {
liveData.value = it
}
}
})
return liveData
}
return cache.getSingleNote(id)
}

override fun editNote(note: Note) {
if (isOnline()) {
remote.updateNote(note, object : UpdateNoteCallback {
override fun onUpdateNote(note: Note?) {
note?.let { launch { cache.editNote(note) } }
}
})
} else {
cache.editNote(note)
}
}

override fun delete(note: Note) {
if (isOnline()) {
remote.deleteNote(note.id!!, object : DeleteNoteCallback {
override fun onDeleteNote(noteId: Int?) {
noteId?.let { launch { cache.delete(note) } }
}
})
} else {
cache.delete(note)
}
}

private fun isCacheExpired(): Boolean {
var delta = 0L
runBlocking(Dispatchers.IO) {
val currentTime = System.currentTimeMillis()
val lastCacheTime = async { cache.getLastCacheTime() }
delta = currentTime - lastCacheTime.await()
}
Timber.d("delta: $delta")
return delta > expirationInterval
}

private fun isOnline(): Boolean {
val runtime = Runtime.getRuntime()
try {
val ipProcess = runtime.exec("/system/bin/ping -c 1 8.8.8.8")
val exitValue = ipProcess.waitFor()
return exitValue == 0
} catch (e: IOException) {
e.printStackTrace()
} catch (e: InterruptedException) {
e.printStackTrace()
}
return false
}


}










share|improve this question



























    0















    How to use new Kotlin v1.3 coroutines in classes which do not have lifecycles, like repositories?
    I have a class where I check if the cache is expired and then decide whether I fetch the data from the remote API or local database. I need to start the launch and async from there. But then how do I cancel the job?



    Example code:



    class NotesRepositoryImpl @Inject constructor(
    private val cache: CacheDataSource,
    private val remote: RemoteDataSource
    ) : NotesRepository, CoroutineScope {

    private val expirationInterval = 60 * 10 * 1000L /* 10 mins */
    private val job = Job()
    override val coroutineContext: CoroutineContext
    get() = Dispatchers.IO + job

    override fun getNotes(): LiveData<List<Note>> {
    if (isOnline() && isCacheExpired()) {
    remote.getNotes(object : GetNotesCallback {
    override fun onGetNotes(data: List<Note>?) {
    data?.let {
    launch {
    cache.saveAllNotes(it)
    cache.setLastCacheTime(System.currentTimeMillis())
    }
    }
    }
    })
    }
    return cache.getNotes()
    }

    override fun addNote(note: Note) {
    if (isOnline()) {
    remote.createNote(note, object : CreateNoteCallback {
    override fun onCreateNote(note: Note?) {
    note?.let { launch { cache.addNote(it) } }
    }
    })
    } else {
    launch { cache.addNote(note) }
    }
    }

    override fun getSingleNote(id: Int): LiveData<Note> {
    if (isOnline()) {
    val liveData: MutableLiveData<Note> = MutableLiveData()
    remote.getNote(id, object : GetSingleNoteCallback {
    override fun onGetSingleNote(note: Note?) {
    note?.let {
    liveData.value = it
    }
    }
    })
    return liveData
    }
    return cache.getSingleNote(id)
    }

    override fun editNote(note: Note) {
    if (isOnline()) {
    remote.updateNote(note, object : UpdateNoteCallback {
    override fun onUpdateNote(note: Note?) {
    note?.let { launch { cache.editNote(note) } }
    }
    })
    } else {
    cache.editNote(note)
    }
    }

    override fun delete(note: Note) {
    if (isOnline()) {
    remote.deleteNote(note.id!!, object : DeleteNoteCallback {
    override fun onDeleteNote(noteId: Int?) {
    noteId?.let { launch { cache.delete(note) } }
    }
    })
    } else {
    cache.delete(note)
    }
    }

    private fun isCacheExpired(): Boolean {
    var delta = 0L
    runBlocking(Dispatchers.IO) {
    val currentTime = System.currentTimeMillis()
    val lastCacheTime = async { cache.getLastCacheTime() }
    delta = currentTime - lastCacheTime.await()
    }
    Timber.d("delta: $delta")
    return delta > expirationInterval
    }

    private fun isOnline(): Boolean {
    val runtime = Runtime.getRuntime()
    try {
    val ipProcess = runtime.exec("/system/bin/ping -c 1 8.8.8.8")
    val exitValue = ipProcess.waitFor()
    return exitValue == 0
    } catch (e: IOException) {
    e.printStackTrace()
    } catch (e: InterruptedException) {
    e.printStackTrace()
    }
    return false
    }


    }










    share|improve this question

























      0












      0








      0








      How to use new Kotlin v1.3 coroutines in classes which do not have lifecycles, like repositories?
      I have a class where I check if the cache is expired and then decide whether I fetch the data from the remote API or local database. I need to start the launch and async from there. But then how do I cancel the job?



      Example code:



      class NotesRepositoryImpl @Inject constructor(
      private val cache: CacheDataSource,
      private val remote: RemoteDataSource
      ) : NotesRepository, CoroutineScope {

      private val expirationInterval = 60 * 10 * 1000L /* 10 mins */
      private val job = Job()
      override val coroutineContext: CoroutineContext
      get() = Dispatchers.IO + job

      override fun getNotes(): LiveData<List<Note>> {
      if (isOnline() && isCacheExpired()) {
      remote.getNotes(object : GetNotesCallback {
      override fun onGetNotes(data: List<Note>?) {
      data?.let {
      launch {
      cache.saveAllNotes(it)
      cache.setLastCacheTime(System.currentTimeMillis())
      }
      }
      }
      })
      }
      return cache.getNotes()
      }

      override fun addNote(note: Note) {
      if (isOnline()) {
      remote.createNote(note, object : CreateNoteCallback {
      override fun onCreateNote(note: Note?) {
      note?.let { launch { cache.addNote(it) } }
      }
      })
      } else {
      launch { cache.addNote(note) }
      }
      }

      override fun getSingleNote(id: Int): LiveData<Note> {
      if (isOnline()) {
      val liveData: MutableLiveData<Note> = MutableLiveData()
      remote.getNote(id, object : GetSingleNoteCallback {
      override fun onGetSingleNote(note: Note?) {
      note?.let {
      liveData.value = it
      }
      }
      })
      return liveData
      }
      return cache.getSingleNote(id)
      }

      override fun editNote(note: Note) {
      if (isOnline()) {
      remote.updateNote(note, object : UpdateNoteCallback {
      override fun onUpdateNote(note: Note?) {
      note?.let { launch { cache.editNote(note) } }
      }
      })
      } else {
      cache.editNote(note)
      }
      }

      override fun delete(note: Note) {
      if (isOnline()) {
      remote.deleteNote(note.id!!, object : DeleteNoteCallback {
      override fun onDeleteNote(noteId: Int?) {
      noteId?.let { launch { cache.delete(note) } }
      }
      })
      } else {
      cache.delete(note)
      }
      }

      private fun isCacheExpired(): Boolean {
      var delta = 0L
      runBlocking(Dispatchers.IO) {
      val currentTime = System.currentTimeMillis()
      val lastCacheTime = async { cache.getLastCacheTime() }
      delta = currentTime - lastCacheTime.await()
      }
      Timber.d("delta: $delta")
      return delta > expirationInterval
      }

      private fun isOnline(): Boolean {
      val runtime = Runtime.getRuntime()
      try {
      val ipProcess = runtime.exec("/system/bin/ping -c 1 8.8.8.8")
      val exitValue = ipProcess.waitFor()
      return exitValue == 0
      } catch (e: IOException) {
      e.printStackTrace()
      } catch (e: InterruptedException) {
      e.printStackTrace()
      }
      return false
      }


      }










      share|improve this question














      How to use new Kotlin v1.3 coroutines in classes which do not have lifecycles, like repositories?
      I have a class where I check if the cache is expired and then decide whether I fetch the data from the remote API or local database. I need to start the launch and async from there. But then how do I cancel the job?



      Example code:



      class NotesRepositoryImpl @Inject constructor(
      private val cache: CacheDataSource,
      private val remote: RemoteDataSource
      ) : NotesRepository, CoroutineScope {

      private val expirationInterval = 60 * 10 * 1000L /* 10 mins */
      private val job = Job()
      override val coroutineContext: CoroutineContext
      get() = Dispatchers.IO + job

      override fun getNotes(): LiveData<List<Note>> {
      if (isOnline() && isCacheExpired()) {
      remote.getNotes(object : GetNotesCallback {
      override fun onGetNotes(data: List<Note>?) {
      data?.let {
      launch {
      cache.saveAllNotes(it)
      cache.setLastCacheTime(System.currentTimeMillis())
      }
      }
      }
      })
      }
      return cache.getNotes()
      }

      override fun addNote(note: Note) {
      if (isOnline()) {
      remote.createNote(note, object : CreateNoteCallback {
      override fun onCreateNote(note: Note?) {
      note?.let { launch { cache.addNote(it) } }
      }
      })
      } else {
      launch { cache.addNote(note) }
      }
      }

      override fun getSingleNote(id: Int): LiveData<Note> {
      if (isOnline()) {
      val liveData: MutableLiveData<Note> = MutableLiveData()
      remote.getNote(id, object : GetSingleNoteCallback {
      override fun onGetSingleNote(note: Note?) {
      note?.let {
      liveData.value = it
      }
      }
      })
      return liveData
      }
      return cache.getSingleNote(id)
      }

      override fun editNote(note: Note) {
      if (isOnline()) {
      remote.updateNote(note, object : UpdateNoteCallback {
      override fun onUpdateNote(note: Note?) {
      note?.let { launch { cache.editNote(note) } }
      }
      })
      } else {
      cache.editNote(note)
      }
      }

      override fun delete(note: Note) {
      if (isOnline()) {
      remote.deleteNote(note.id!!, object : DeleteNoteCallback {
      override fun onDeleteNote(noteId: Int?) {
      noteId?.let { launch { cache.delete(note) } }
      }
      })
      } else {
      cache.delete(note)
      }
      }

      private fun isCacheExpired(): Boolean {
      var delta = 0L
      runBlocking(Dispatchers.IO) {
      val currentTime = System.currentTimeMillis()
      val lastCacheTime = async { cache.getLastCacheTime() }
      delta = currentTime - lastCacheTime.await()
      }
      Timber.d("delta: $delta")
      return delta > expirationInterval
      }

      private fun isOnline(): Boolean {
      val runtime = Runtime.getRuntime()
      try {
      val ipProcess = runtime.exec("/system/bin/ping -c 1 8.8.8.8")
      val exitValue = ipProcess.waitFor()
      return exitValue == 0
      } catch (e: IOException) {
      e.printStackTrace()
      } catch (e: InterruptedException) {
      e.printStackTrace()
      }
      return false
      }


      }







      android kotlin kotlinx.coroutines






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Jan 1 at 12:21









      AngelinaAngelina

      413214




      413214
























          1 Answer
          1






          active

          oldest

          votes


















          2














          You can create some cancelation method in the repository and call it from class which has lifecycle (Activity, Presenter or ViewModel), for example:



          class NotesRepositoryImpl @Inject constructor(
          private val cache: CacheDataSource,
          private val remote: RemoteDataSource
          ) : NotesRepository, CoroutineScope {

          private val job = Job()
          override val coroutineContext: CoroutineContext
          get() = Dispatchers.IO + job

          fun cancel() {
          job.cancel()
          }

          //...

          }

          class SomePresenter(val repo: NotesRepository) {

          fun detachView() {
          repo.cancel()
          }
          }


          Or move launching coroutine to some class with lifecycle.






          share|improve this answer


























          • what if I have more layers between the Repository and the lifecycle-aware class?

            – Angelina
            Jan 1 at 13:03











          • I would create additional cancellation method in each layer, propagating call to the repository.

            – Sergey
            Jan 1 at 13:07











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53995389%2fkotlin-coroutine-scope-job-cancellation-in-non-lifecycle-classes%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          2














          You can create some cancelation method in the repository and call it from class which has lifecycle (Activity, Presenter or ViewModel), for example:



          class NotesRepositoryImpl @Inject constructor(
          private val cache: CacheDataSource,
          private val remote: RemoteDataSource
          ) : NotesRepository, CoroutineScope {

          private val job = Job()
          override val coroutineContext: CoroutineContext
          get() = Dispatchers.IO + job

          fun cancel() {
          job.cancel()
          }

          //...

          }

          class SomePresenter(val repo: NotesRepository) {

          fun detachView() {
          repo.cancel()
          }
          }


          Or move launching coroutine to some class with lifecycle.






          share|improve this answer


























          • what if I have more layers between the Repository and the lifecycle-aware class?

            – Angelina
            Jan 1 at 13:03











          • I would create additional cancellation method in each layer, propagating call to the repository.

            – Sergey
            Jan 1 at 13:07
















          2














          You can create some cancelation method in the repository and call it from class which has lifecycle (Activity, Presenter or ViewModel), for example:



          class NotesRepositoryImpl @Inject constructor(
          private val cache: CacheDataSource,
          private val remote: RemoteDataSource
          ) : NotesRepository, CoroutineScope {

          private val job = Job()
          override val coroutineContext: CoroutineContext
          get() = Dispatchers.IO + job

          fun cancel() {
          job.cancel()
          }

          //...

          }

          class SomePresenter(val repo: NotesRepository) {

          fun detachView() {
          repo.cancel()
          }
          }


          Or move launching coroutine to some class with lifecycle.






          share|improve this answer


























          • what if I have more layers between the Repository and the lifecycle-aware class?

            – Angelina
            Jan 1 at 13:03











          • I would create additional cancellation method in each layer, propagating call to the repository.

            – Sergey
            Jan 1 at 13:07














          2












          2








          2







          You can create some cancelation method in the repository and call it from class which has lifecycle (Activity, Presenter or ViewModel), for example:



          class NotesRepositoryImpl @Inject constructor(
          private val cache: CacheDataSource,
          private val remote: RemoteDataSource
          ) : NotesRepository, CoroutineScope {

          private val job = Job()
          override val coroutineContext: CoroutineContext
          get() = Dispatchers.IO + job

          fun cancel() {
          job.cancel()
          }

          //...

          }

          class SomePresenter(val repo: NotesRepository) {

          fun detachView() {
          repo.cancel()
          }
          }


          Or move launching coroutine to some class with lifecycle.






          share|improve this answer















          You can create some cancelation method in the repository and call it from class which has lifecycle (Activity, Presenter or ViewModel), for example:



          class NotesRepositoryImpl @Inject constructor(
          private val cache: CacheDataSource,
          private val remote: RemoteDataSource
          ) : NotesRepository, CoroutineScope {

          private val job = Job()
          override val coroutineContext: CoroutineContext
          get() = Dispatchers.IO + job

          fun cancel() {
          job.cancel()
          }

          //...

          }

          class SomePresenter(val repo: NotesRepository) {

          fun detachView() {
          repo.cancel()
          }
          }


          Or move launching coroutine to some class with lifecycle.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Jan 1 at 13:04

























          answered Jan 1 at 12:49









          SergeySergey

          3,82121733




          3,82121733













          • what if I have more layers between the Repository and the lifecycle-aware class?

            – Angelina
            Jan 1 at 13:03











          • I would create additional cancellation method in each layer, propagating call to the repository.

            – Sergey
            Jan 1 at 13:07



















          • what if I have more layers between the Repository and the lifecycle-aware class?

            – Angelina
            Jan 1 at 13:03











          • I would create additional cancellation method in each layer, propagating call to the repository.

            – Sergey
            Jan 1 at 13:07

















          what if I have more layers between the Repository and the lifecycle-aware class?

          – Angelina
          Jan 1 at 13:03





          what if I have more layers between the Repository and the lifecycle-aware class?

          – Angelina
          Jan 1 at 13:03













          I would create additional cancellation method in each layer, propagating call to the repository.

          – Sergey
          Jan 1 at 13:07





          I would create additional cancellation method in each layer, propagating call to the repository.

          – Sergey
          Jan 1 at 13:07




















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53995389%2fkotlin-coroutine-scope-job-cancellation-in-non-lifecycle-classes%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          POs1Mp
          bQzK9yR UuATbx7Z8YiQWK1Af44J,SLSa QlCRDIKaPEr0 4pzaI L4,cczBhIpEMOoYuSWk,JA,j

          Popular posts from this blog

          Monofisismo

          Angular Downloading a file using contenturl with Basic Authentication

          Olmecas