When I do some processing in the background thread and wants to update the UI using runOnUiThread(), the code inside the function is running successfully without any error, but the UI gets rendered only if the user touches the screen. Otherwise, it's rendering consistently.
Why it's rendering only after touching the screen ?
It is possible if your screen gets overlapped by another screen. This causing Activity to move to paused state (.onPause()) method. when you touch it again, it become foreground again so can receive UI update events
It's possible that an operation is blocking (for example notifying an adapter too often) your ui thread and gets unblocked with an interrupt occurs. Touching the screen is an interrupt in your scenario.
If you paste your code maybe we can find an exact solution.
Edit: Debounce example code with RxJava
yourSearchObservable
.throttleLast(100, TimeUnit.MILLISECONDS)
.debounce(200, TimeUnit.MILLISECONDS)
.onBackpressureLatest()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
searchResults -> {
// Your success
} ,
() -> {
// Error
}
));
Try using callbacks or android.os.Handler.post() to interact with the Main Thread
Related
I have this code in an activity SignInActivity:
signInButton.setOnClickListener{
val query: HashMap<String, String> = HashMap()
query["email"] = signInEmail.text.toString()
query["password"] = signInPassword.text.toString()
signInViewModel.getAuthToken(query)
signInViewModel.signInResponse.observe(this, {
response-> when(response){
is NetworkResult.Success ->{
response.data?.let { Toast.makeText(this, it.access, Toast.LENGTH_SHORT).show()}
}
is NetworkResult.Error ->{
Toast.makeText(this, response.message.toString(), Toast.LENGTH_SHORT).show()
}
is NetworkResult.Loading -> {
}
}
})
}
Let's suppose in the first try I wrote my password wrong and it only runs once, but then after that if I click it again it runs multiple time by creating multiple toasts in this example.
Like #gpunto says, you're adding a new Observer every click, so they're stacking up and each one fires when the LiveData updates.
But really, the observer doesn't have anything to do with the actual click anyway, it just receives updates to signInResponse and displays a thing. The click just calls getAuthToken with the current query. If doing that happens to cause a signInResponse update, then you have everything wired up to react to that event. But the Activity doesn't need to know how all that stuff works, or be written so one thing follows another.
That's a reactive pattern, where your UI is really just sending events (like getAuthToken when there's a click) and then reacting to other events so it can display them. By separating these things, you get a simple system that Just Works, and can react to updates no matter what caused them (e.g. a click, or restoring state) without having to write code to handle each case.
That said, this is a slightly tricky case because you have an event you want to consume. If you just set up that observer on signInResponse, it will fire every time you get a value for that LiveData. And that includes when the Activity is recreated (e.g. on rotation), observes the LiveData, and gets the current (last-set) value. Basically, if you show a Toast, the same Toast will appear every time the Activity is recreated. That would be fine for setting the current value on a TextView, but it's bad for a popup that should only appear once.
This is the current official recommendation for handling this situation. They're creating a UI state, which basically holds everything that needs to be displayed, including any popup messages (which acts like a queue, which is useful!). When the UI displays a message, it basically tells the ViewModel it's done so, and that handles removing the message from the state.
You could just implement this your own way, even if it's something simple like a clearResponse() function in your VM that clears the current value when you've seen it. It really depends on your app and what state you need to maintain. Here's some other examples from the Android devs - but like it says at the top, this advice is deprecated following the recommendations I linked earlier
I have a video playing application. I have implemented Vertical Viewpager2 with fragment. The fragment contains fullscreen exoplayer where I load the video and have a button to open the profile of the user who have posted the video. I make all the API calls in viewmodel as
viewmodelScope.launch(Dispatchers.IO){
... //making api call and updating the livedata
...
}
I am also using
lifecycleScope.launch(Dispatchers.IO){
... //start caching the video
}
The Problem is :
When I scroll like 40 - 45+ items and launch any coroutine on Dispatchers.IO, the launch block is never executed hence the api calls are never made and app stops working.
Debugging results :
I tried to debug why the launch block is not executing with debug break points. In the LimitingDispatcher class, there is variable called inFlightTasks which is used to schedule the task if the parallelism limit is reached (which is 64 by default). So, my current task is added to ConcurrentLinkedQueue<Runnable>. I waited for an hour but after scheduling the task it never resumed the task again. I checked the queue for the tasks and at the time of testing it had around 150-170 tasks but none of them resumed.
Any idea how to solve this problem.
Note : I cannot remove the caching of video(which is am using in onStart() of the fragment).
EDIT
I tried with lifecycleScope.launch{ } and withContext(Dispatchers.IO){ }, but I am still getting the same issue.
In viewModelScope
viewModelScope.launch(Dispatchers.IO){
val response = repository.call()
when(repository){
is Resource.Sucess -> livedata.postValue(response.data)
is Resource.Error -> livedata.postValue(response.error)
}
}
In lifecycleScope
lifecycleScope.launch(Dispatcher.IO){
try{
startVideoCaching()
}catch(e : Exception){
e.printStackTrace()
}
}
And both the functions in launch are purely suspend functions.
Edit 2
I am adding the screen shots of my functions and the Dispatchers.IO's queue data.
Debug sscreenshot.
The functions named in the debug screenshot.
In the sendAnalytics I am using lifecycleScope only
cacheVideo() is called from onStart().
markVideoView() is called from onResume()
I tried with yield() but still no change in the behavior.
I am using all this methods in Fragment which is a part of ViewPager2 items.
If it reached the parallelism limit, and the other ones are put in the queue, the only
logical reason they're not resuming and executing is that those previously called
are not suspending/returning to free up the threads for the queued ones to run.
What you should do is make your functions cancellable (cooperatively cancellable using
isActive) and those coroutines that are fired for the items that are no longer visible
on the screen (scrolled off the screen) should be cancelled, since there is no point of
calculating something not visible on the screen. By cancelling those coroutines you
should always have enough free threads to run those coroutines for the items currently visible on the screen.
In case you don't want to cancel coroutines fired for the items not currently on the screen, you can call yield() inside your functions to deliberately yield thread to other coroutines that run on the same thread pool.
I am trying to make an application that constantly hits the google API to fetch the distance between two points and then process the distance and add it in db inside a loop.
I was previously using the volley but it does not waits and my function proceeds with the rest of lines of code. I need help so that the line of code waits for that method to return only if some result is received once I hit google API, then use that result in next line of code.
I'd recommend looking into coroutines. More specifically take a look at the async launcher which should return the value to an awaiting variable. Your code runs asynchronously (starting a new thread so your main UI thread doesn't block and the user can freely do as they wish) and when the call returns your code is called once again at the point where the variable will be awaiting. You could set a timer right before starting the async call and stop it after await.
You could also use withContext{} to await for a result. The code will be continued in the calling function when the coroutine returns a result.
startTimer()
val job = coroutineScope{
async{
networkCall()
}
}
job.await()
stopTimer()
or
startTimer()
coroutineScope{
launch{
withContext{
networkCall()
}
//code will continue when networkCall() has returned a result
stopTimer() //be careful, this may be called through a background thread.
}
}
Coroutines are thread-safe and lightweight so you can start as many as you want without any problems. Also, by using coroutineScope you will avoid lifecycle problems like your ui being rotated which would normally cause a network call to be resent. With coroutineScope your call will have the life span of the calling activity/fragment thus it will be cancelled or killed if your app unexpectedly decides to stop and not care for the result any longer.
Hope this helped, Panos.
If you are beginner use retrofit library to make API calls in there is option enque will call API background for you.
https://www.journaldev.com/13639/retrofit-android-example-tutorial
If you are pro go for Rxjava with Retrofit.
https://medium.com/3xplore/handling-api-calls-using-retrofit-2-and-rxjava-2-1871c891b6ae
You cannot do synchronous calls on the main thread. Blocking the UI thread on a network call for more than a specified period of time would trigger an ANR.
A couple of options would be to use an AysncTask or AsyncTaskLoader. Blocking the main thread is definitely not recommended.
AsyncTasks create a separate thread of execution while the UI thread can continue with its work.
Android applications usually start with a single thread of execution so if you block this thread then an ANR would result.
Take a look here
If you don't mind the ANRs which will not be acceptable to a user then go with using a simple HttpURLConnection but this is not recommended.
If you do not prefer the AsyncTask approach you can create a Runnable to do the background processing and then update the UI thread.
More here
You can implement a jobservice to get distance in an interval.You can view the implementation .here
If I want something to run and finish regardless of a lifecycle and subscribers, is following the correct way to do it?
I create a singleton and run a "Job" from it like following:
public void clearImageCache() {
Single.fromCallable(() -> {
ImageManager.clearCacheFromBackground();
return true;
}
)
.observeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(ignored -> {
// finished
}, error -> {
// error
});
}
Is the above way of using RxJava for background work that is independent from activities and lifecycles fine? I want it to finish in any case. I think this should be save and should finish, if the application is not destroyed. Is it ok to do it like this or am I missing something here that I should take care of?
I'm not talking care of the returned Disposable but I think that's fine as I never want to unsubscribe.
This will work fine until your app is not killed from the memory i.e. swiped from recent tasks, forced stop or killed by os. Also, you should subscribeOn on Schedulers.io() as it will take load of main thread and keep the UI smooth.
Well I just want to press a button and a countdown appears in it however when I press the button the program stops and finally shows the number 1 but doesn't show 3 or 2.
btnTurno.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v) {
btnTurno.setText("3");
SystemClock.sleep(1000);
btnTurno.setText("2");
SystemClock.sleep(1000);
btnTurno.setText("1");
}
});
What I'm doing wrong?
First of all, Sleep should be called as Thread.sleep(x);
But on the other hand , sleep is NOT recommended since it will block the user interaction with the application.
If you want to make a countdown ( as it looks like what you are doing ), you should look at this
http://developer.android.com/reference/android/os/CountDownTimer.html
onClick is being executed in single handler message on GUI thread, so each time you change text, it overwrites previous value, so system is not able to redraw view.
Also you should not call sleep() on main gui thread, that will cause ANR - application is notresponding
Setting the text and drawing the text are separate operations that both happen on the same thread. You're setting the text three times before the framework gets the chance to draw anything, so all you see is the last change.
For this to work, you'd need to do your text updates and sleeps on a separate thread. However, you can't make UI calls directly on a different thread, so you need to post your messages.
One approach would be to create an AsyncTask, and use your code above but call publishProgress() where you're currently calling setText(). The publishProgress() call in the async thread results in onProgressUpdate() running in the UI thread; from there, you can call setText() to update the UI.
Edit: the CountDownTimer approach mentioned in a different answer is better.