I got an AsyncTask for URL connection. Now I want to have a loading spinner everytime I do the URL connection. I display the loading spinner in onPreExecute() and dismiss it in onPostExecute.
I tested this with an endless while loop in doInBackgroung().
The big problem is the GUI is freezing and the loading spinner is not shown.
In my opinion the reason is URLconnection.execute().get(). But I need the get() because the activity need the result to working with it.
My question is now: What is the best way to do this to achieve my wishes? (By the way it is not important to get a solution with an "AsyncTask solution" because there are maybe better solutions and AsyncTask will be deprecated with SDK version 30)
Thank you very much and stay healthy!
As you said AsyncTask will be deprecated.
So it is better to go for an alternative.
Since you mentioned you are not relying on AsyncTask, I will present to you another approach.
Let me introduce you to coroutines and convince you that they will solve your problem and "get the job done".
When I got to know about coroutines, this video was one of the first example that has demonstrated to me the potential of using coroutines in my app. At that point I was still using 100% Java, probably like you are right now.
The good part is: getting started with Kotlin is really easy! Not only you can call Kotlin functions from Java Code, you can also call Java functions from Kotlin code.
To "do something in background" in Kotlin, all you need to do is to launch a coroutine (on a background thread).
Do you have a ViewModel to fetch your data? If it is an option to transfer this file to kotlin, then starting (and scoping) a coroutine becomes as simple as this.
For fragments or activitities you could use other copes as well. However, using the global scope is usually discouraged.
Executing coroutines is as simple as that:
class MyViewModel: ViewModel() {
fun loadDataInBackgroundAndShowSpinner {
viewModelScope.launch {
// Coroutine that will be canceled when the ViewModel is cleared.
// start your spinning
// do all the heave data work on a background thread
doInBackground()
// end your spinning here
}
}
suspend fun doInBackground(inputURL: String) {
withContext(Dispatchers.IO) {
// Execute all your data fetching here
...
// Assign your data to your viewModel variables, post it to a LiveData object, etc.
}
}
}
You do not need any loops in the main thread or anything. By using withContext on a background thread you can achieve main-safety.
Within a launched coroutine, everything (by default) gets executed in order.
Still you will not block the Main Thread. How did you achieve that?
The key here is that your doInBackground function has the suspend keyword. Therefore your loadDataInBackgroundAndShowSpinner on the main thread will "suspend" your doInBackground function and the main thread is able to do whatever you want (i.e. nothing freezes). Then, once your doInBackground is finished, it will resume execution and you can just dismiss your spinner again on the main thread.
Kotlin coroutines make it so much easier to do something in the background and I really want to encourage you to give it a try! It will definitely solve your problem and I can not think of a more easy way.
Google also tried to make it as easy as possible to get you started when coming from Java.
Related
im getting a string from callback and storing in a variable call channel. it's fine now but when accessing the value outside the callback method it returns null why?
it's not accessable outside of that method.
callback is working fine while inside of callback method
but i
please help me
ch = new Channel(fuser.getUid(),userid);
ch.getChann();
ch.Back(new Channel.MyCallback() {
#Override
public void onCallback(String value) {
channel = value;
Log.e("Channel",channel);
}
});
channel+="1";
Log.e("Channel1",channel);
unfortunately, there's not all text visible on your screenshot. On Stackoverflow, it's generally preferred if you paste the source code directly into your question rather than sharing a screenshot. It's a bit more effort, at the same time, please consider that the people answering your question also take effort to help you - and by posting the source code as text, you make it easier for everyone to help you.
For your question, there's a couple of things that might happen:
the callback is never called - and therefore there's nothing written into that variable
the callback is not called immediately, but later (maybe by another thread). Therefore, you will find that the variable will not be set immediately after you pass that callback to the Back method, but maybe a few seconds or milliseconds later. That's a long time for a computer, so when you access the variable later, you might just be too early.
if there's threads involved, your code is not thread save. That means - it might also happen that channel=value and channel+="1" might happen at the same time, which could give you unpredictable results.
To solv the problem, you will need to trigger whatever action should happen after your callback at a time when you know that the callback has been called. I am no expert on Android; there might be listeners available for that. If not, then the simplest way would be to call the code that should happen after the callback was called from the callback itself (be aware, that this is most likely a bad practise in android as it might make the UI become unresponsive. To solve that, you will need to execute your code on a different thread than the UI thread)
I just try to show a loading-animation while I access an ERP in my code like this:
protected void submit()
{
messageField.getStyleClass().add("smallLoading");
submitImpl();
messageField.getStyleClass().remove("smallLoading");
}
Sadly the animation is never shown... Just the result as before. I tried using Platform.runLater, which yielded the same result. I also transfered the last 2 lines in a Thread, which worked (the animation was shown), but lead to the error "Not on FX application thread", when the Submitter tried to write to my message-field. When I passed the Thread to Platform.runLater it did not show the animation... I googled a little bit, but could not find a solution. Maybe I'm missing something important...
I appreciate any help. Thank you!
It seems like you don't really fully understand how the UI thread works.
The code you've posted is single-threaded. It all operates on the UI thread. You add a style class, do some work, then remove it. The problem is that this sequence of operations is effectively "atomic": the UI doesn't actually update anything until its all done. This is why you don't see the loading symbol change.
When you put all of this within runLater the result is the same. It's still all on the UI thread. The only difference here is that rather than running the code now, you're deferring it until some point "later" (probably actually very soon).
When you try to put the last two lines in a separate thread, the issue is that you're trying to make UI changes on a non-UI thread. That's not allowed.
What you want to do is run everything on a non-UI thread, and push back the UI operations to the UI thread with runLater. Something like this:
new Thread(() -> {
Platform.runLater(()-> messageField.getStyleClass().add("smallLoading"));
submitImpl();
Platform.runLater(()-> messageField.getStyleClass().remove("smallLoading"));
}).start();
I am using volley library to perform network operations. On Application launch, I hit the service, I want to stop all the operations until i get the response from the service.
So i want to perform this synchronously. As I am using Volley which by default works in a separate thread. So how can i do this.
I have created custom Interface/listener to handle this, but does Android provide some way to achieve this.
I have done following.
Splash Activity implements an interface, and it goes to Main Activity after data is loaded
#Override
public void onContainerLoaded() {
//startActivity(MainActivity)
}
Even if you want to, you should definitely never EVER run any network-related task synchronously.
What you can do instead is starting your activity normally, and replace your layout with a progressbar logo, that is set to visibility.gone when your task is completed.
EDIT : By the way, if you are just starting your app and you haven't done anything concrete yet, I would recommend you to use an AsyncTask instead of Volley, which is often causing layer-coupling mistakes.
Use some event bus such as Otto
Create an event, make your main activity subscribe to the event using the event bus, start your operation, display a "Loading..." or something ProgressDialog in your main activity. From your worker thread when it completes send an event to your main activity. Make your main activity close the "Loading" dialog when it receives the event,
I guess a better question would be why you want to force it on the main thread?
As far as I know, volley won't let you do that but you might be able to if you make your own network operation. After Honeycomb, you will get a NetworkOnMainThreadException so you will need to override the policies.
At runtime, I am using a callback pattern to have generic code run and use the callback when finished to execute code on the GUI. So in my handler I use the runOnUiThread function against the callback.
These are all async http calls. At runtime if I interrupt and use the back button and go to another fragment for example, the system will swap out the fragments and run both callbacks (the new fragment one on the correct callback, and the old callback that should have ran on the old fragment on the new current fragment). This is wrong, the new fragment gets both callbacks but when it was initialized it was assigned to the other fragment...
This makes no sense and you can only observe the behavior by switching fragments at runtime before an async call finishes. I don't see how it's possible, in the code I check if callback is null so it should have been garbage collected and how it runs on the new callback I don't know how it's possible....there's no additional assignment happening to change this. It's almost like it's just looking in memory for the function signature and running it even though when it does it's on the wrong object.
Does anyone have any clue what's going on? I've surpressed it with an ugly piece code, I just don't know how this is possible?
I understand the obvious candidates are if I assigned it again somewhere, but the answer is no. When the fragment is created it creates an object, assigns itself as the callback, and processing begins so when the fragment is destroyed it should be too. But the async task is still executing which is fine, why it does a callback on the new thread on the main gui I guess is because of runOnUiThread, so that function is somehow changing what the callback object points to in memory?
Its your code :-)
There is no magic happening 'behind the scenes' in Android and no references are changed.
Many have problems like yours, and its always because they assume that the Android system is somehow cleaning up when a fragment or activity instance is replaced or removed. It isn't, and therefore the callbacks are still executed, on a obsolete fragment or activity.
runOnUiThread takes a Runnable and runs it when the UI thread have spare time (after invoking lifecycle methods and finished rendering). Its not magic either :-)
I'm using an AsyncTask class to add, remove and clear items from my ArrayAdapter. Unfortunately, when any of those tasks are run, the ArrayAdapter wants to notifyDataSetChanged(), which requires that the thread be run on the uiThread.
If I use runOnUiThread(new Runnable(){...}); in order to update an ArrayAdapter Asynchronously, doesn't that defeat the purpose of trying to update it in a new Thread to begin with?
What is the best approach here?
Do .notifyDataSetChanged() insde onPostExecute() method. onPostExecute() method runs on Ui-Thread. If your app requires to periodically update during execution of doInBackground() method then use publishProgress() (which will envoke onProgressUpdate() method) and call notifyDataSetChanged() inside onProgressUpdate() method, which also runs on Ui-thread.
Just to add something to what everyone else already said, after your task is done running, you might want to set the adapter (or a new one) again for the view you are working with.
AFAIK depending on your code, after notifyDataSetChanged or after refreshing the cursor, the view might not update right away if you are not working with a reference.
If you leave your activity and when you come back, the view is updated, you probably need to set the adapter again.
To answer your first question, calling runOnUiThread doesn't defeat the purpose as long as the only thing you call on the UI thread is notifyDataSetChanged(). Do all your actual, potential long running, tasks in the AsycnAdpater and then when you're ready to tell the activity things have changed, call runOnUiThread(). It will only run the commands you specify in the Runnable you pass to it.
That said, why not instead of using an AsyncTask, try using an AsyncTaskLoader. AsycnTaskLoaders were designed almost specifically for populating lists asynchronously. They weren't introduced until API level 10, but you can still access them from older API levels by using the android Support Package.