3

I want to get request to network every 3 seconds and in the some condition stop it. I am using Coroutine for network request.I used postDelayed() method and it work correctly. But I want to make the next request after previous response of the previous request is complete.I used delay method of Coroutine but But The UI have freeze and My app is remained in the infinite loop .How to handle this task using postDelayed or coroutine? I create network request in the this repository:

     class ProcessRepository  @Inject constructor(private val apiService: ApiService) {
    val _networkState = MutableLiveData<NetworkState>()
    val _networkState_first = MutableLiveData<NetworkState>()

    val completableJob = Job()
    private val coroutineScope = CoroutineScope(Dispatchers.IO + completableJob)

    private val brokerProcessResponse = MutableLiveData<BrokerProcessResponse>()
 fun repeatRequest(processId:String):MutableLiveData<BrokerProcessResponse>{
        var networkState = NetworkState(Status.LOADING, userMessage)
        _networkState.postValue(networkState)
        coroutineScope.launch {
            val request = apiService.repeatRequest(processId, token)
            withContext(Dispatchers.Main) {
                try {
                    val response = request.await()
                    if (response.isSuccessful) {
                        brokerProcessResponse.postValue(response.body())
                        var networkState = NetworkState(Status.SUCCESS, userMessage)
                        _networkState.postValue(networkState)
                    } else {
                        var networkState = NetworkState(Status.ERROR, userMessage)
                        _networkState.postValue(networkState)
                    }
                } catch (e: IOException) {
                    var networkState = NetworkState(Status.ERROR, userMessage)
                    _networkState.postValue(networkState)
                } catch (e: Throwable) {
                    var networkState = NetworkState(Status.ERROR, userMessage)
                    _networkState.postValue(networkState)
                }
            }
            delay(3000) // I only just add this line for try solution using coroutine 
        }

        return brokerProcessResponse
    }

And this is code in my fragment:

     private fun repeatRequest(){
        viewModel.repeatRequest(processId).observe(this, Observer {

                if(it!=null){
                    process=it.process
                    if(it.process.state== FINDING_BROKER || it.process.state==NONE){
                        inProgress(true)
                    }else{
                        inProgress(false)
                 }
                    setState(it!!.process.state!!,it.process)
                }

        })
    }
 private fun pullRequest(){
        while (isPullRequest){
            repeatRequest()
        }

    }

And My solution using postDelayed:

     private fun init() {
        mHandler = Handler()
}


private fun pullRequest() {

        mRunnable = Runnable {

            repeatRequest()
            Log.e(TAG, "in run")
            mHandler.postDelayed(
                mRunnable,
                3000
            )
        }

        mHandler.postDelayed(
            mRunnable,
            3000
        )
    }
 private fun cancelRequest() {
    viewModel.cancelRequest(processId).observe(this, Observer {
        if (it != null) {
            processShareViewModel.setProcess(it.process)
            mHandler.removeCallbacks(mRunnable)
            mHandler.removeCallbacksAndMessages(null)

            isPullRequest = false
            if (findNavController().currentDestination?.id == R.id.requstBrokerFragment) {
                val nextAction = RequstBrokerFragmentDirections.actionRequstBrokerFragmentToHomeFragment()
                // nextAction.keyprocess = it.process
                findNavController().navigate(nextAction)
            }

        }
        Log.e(TAG, "response request borker: " + it.toString())
    })
}


 override fun onDestroy() {
        super.onDestroy()
        // Stop the periodic task
        isPullRequest = false
        mHandler.removeCallbacks(mRunnable)
        mHandler.removeCallbacksAndMessages(null)
    }
maryam
  • 1,437
  • 2
  • 18
  • 40

3 Answers3

7

For those who are new to Coroutine

add Coroutine in Build.gradle

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2'

Add a repeating Job

    /**
     * start Job
     * val job = startRepeatingJob()
     * cancels the job and waits for its completion
     * job.cancelAndJoin()
     * Params
     * timeInterval: time milliSeconds 
     */
    private fun startRepeatingJob(timeInterval: Long): Job {
        return CoroutineScope(Dispatchers.Default).launch {
            while (NonCancellable.isActive) {
                // add your task here
                doSomething()
                delay(timeInterval)
            }
        }
    }

To start:

  Job myJob = startRepeatingJob(1000L)

To Stop:

    myJob .cancel()
Hitesh Sahu
  • 41,955
  • 17
  • 205
  • 154
6

Your UI is freezing because your while loop is running without a break:

while (isPullRequest){
     repeatRequest()
}

Your are starting a coroutine asynchronously in repeatRequest and invoke delay there. This will not suspend the pullRequest function.

You should run the loop within the coroutine (repeatRequest function). Then you can give the job object of this coroutine to the caller and invoke cancel whenever you like to stop the loop:

fun repeatRequest(): Job {
    return coroutineScope.launch {  
        while(isActive) {
            //do your request
            delay(3000)
        }
    }
}

//start the loop
val repeatJob = repeatRequest()

//Cancel the loop
repeatJob.cancel()
Rene
  • 5,730
  • 17
  • 20
  • how to define `coroutineScope` variable? – maryam Sep 12 '19 at 07:06
  • The same way like you `CoroutineScope(Dispatchers.IO + completableJob)` – Rene Sep 12 '19 at 07:43
  • Is your solution satisfy this condition? ''I want to make the next request after previous response of the previous request is complete.'' – maryam Sep 14 '19 at 10:21
  • Yes, if the request in the loop is blocking until the result has arrived. If the request in the loop is asynchronous, then not. – Rene Sep 14 '19 at 11:08
  • How to restart my job after canceling it? When I cancel the `repeatRequest()` job,it did not cancel and my loop is continue and I have to cancel `completableJob` and when call `completableJob.start()`, My job did not restart. – maryam Sep 24 '19 at 07:01
  • I just tested this sample code: https://gist.github.com/rpreissel/84ef62a41abd5dfd78e42fc865a60a88. And the repeat job doos stop. And it is possible to call the function `repeatRequest` to restart the loop. – Rene Sep 24 '19 at 16:01
1

Have a look at this question, it has a custom loop and it's usage can look like this


    // if loop block takes less than 3 seconds, then this loop will iterate after every 3 seconds
    // if loop block takes more than 3 seconds, then this loop will iterate after block is finished
    // loop iterate after block duration or provided minimum interval whichever is maximum
    loop(3.seconds) {
     // make request
     // process response

     stopIf(condition)
    }

Pritam Kadam
  • 2,388
  • 8
  • 16