I want to Stop my function to execute, which is written in post delay, whenever I interrupt it with textChangedListner In Kotlin

Viewed 268

I have a function written in OntextChangedListner that takes input from edittext and use it to call an API everytime you type something in edittext i.e.(if I am writing something with the string length of 10 it is calling the API 10 times).

I want that it should not call API everytime I write some thing in edittext, Whenever i stop writing then should hit API, so i tried to give a post delay so that whenever i stop writing then it will call for the API, but it is not working as i expected(if my string length is 10 then it is calling for api 10 time after some delay)

Here is My Code

override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
                val runnable = Runnable {
                    val lengthoftext = searchCompany.text.length
                    mycompanies.clear()
                    callforapi(s.toString())
                }
    
                val handler = Handler(Looper.getMainLooper())
                handler.postDelayed({ runnable.run() }, 500)
    }



fun callforapi(tempchar: String){
        val queue = Volley.newRequestQueue(this)
        val url = "SomeAPI" + tempchar
        val jsonObjectRequest = JsonObjectRequest(
            Request.Method.GET, url, null,
            { response ->
                var tempcomparray = response.getJSONArray("companies")
                var objlength = tempcomparray.length()
                adapter?.clear()
                for (i in 0 until objlength)
                {
                    var tempcompobj = tempcomparray.getJSONObject(i)
                    mycompanies.add(tempcompobj.getString("name"))


                }
                Log.d("checktheobj", mycompanies.toString())

            },
            { error ->
                Log.d("check", "Something is Wrong ")
            }
        )
        queue.add(jsonObjectRequest)
        return
    }

I am using volly library to call an API.

Please guide me how to do so.

Thankyou in Advance..

3 Answers

Best way to use coroutines for such a case in kotlin. If you don't know coroutines or don't want to use it. Then you can use Timer.

 private Timer timer = new Timer();
 private final long DELAY = 500; // Milliseconds

override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int){
        timer.cancel();
        timer = new Timer();
        timer.schedule(
            new TimerTask() {
                @Override
                public void run() {
                    // TODO: Do what you need here.
                    val lengthoftext = searchCompany.text.length
                    mycompanies.clear()
                    callforapi(s.toString())
                }
            },
            DELAY
        );
    }
  }
}

You can keep a property reference to your last made request and to your handler, and use them to cancel any existing request and any pending handler messages each time a letter is typed. So when a letter is typed, any API call that has already started can be cancelled, and any posted runnable that hasn't finished waiting for its delay yet will also be cancelled before it even starts.

Note, you should eventually split the API call out into a ViewModel for proper encapsulation and behavior when rotating the screen, but I'll leave that for you because ViewModels are a big topic to learn and too much to explain here.

// Class properties:
val apiCallHandler = Handler(Looper.getMainLooper())
val lastApiCallRequest: Request<*>? = null

// In listener:
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
    apiCallHandler.removeCallbacksAndMessages()
    apiCallHandler.postDelayed({
        val lengthoftext = searchCompany.text.length
        mycompanies.clear()
        callforapi(s.toString())
    }, 500)
}

// ...
fun callforapi(tempchar: String){
    // your existing code
    // then right before returning:
    lastApiCallRequest?.cancel()
    lastApiCallRequest = jsonObjectRequest
}

You could launch yout api call in a coroutine with delay. Save it's reference to attribute, and cancel it on each call of the onTextChanged. If the Job is not canceled before delay, api call is realized.

private var textChangeJob: Job? = null

override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
    // job is cancelled on each character, if no character added in specified delay
    // job won't be canceled, hence api will be called
    textChangeJob?.cancel()
    // You could also use lifecycleScope or viewModelScope, depends if you want to make the call
    // to api if fragment is destroyed
    textChangeJob = GlobalScope.launch {
        delay(300) // how long to wait for next character
        // call your code here
        callforapi(s.toString())
    }
}
Related