I download a RSS Feed and then parse it in a class, the rssfeed is saved as a custom object; however the code blocks as the feed is relatively big and the Android typically works only on EDGE.
I want to put the downloading of the file into an AsyncTask with an indefinite progress dialog.
I also want to be able to access the rssfeed object after its downloaded in the ASynctask from within the Main Thread. How do I reference it?
When you build an AsyncTask, the third generic argument is the Result, and when you execute the asyn task, you can call get to retrieve the Result object. Depending on what you need to do with the object on the main thread, you can also override the AsyncTask's onPostExecute method which will be run on the main thread after doInBackground completes. This is probably the best bet, to override onPostExecute on the AsyncTask.
Related
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.
I have an activity which gets called every time a Call is ended. This activity has below AsyncTask.
UploadRecordings uploadRecordings = new UploadRecordings();
uploadRecordings.execute(context);
Now when I get many Calls one after another, everytime new AysncTask is created. But Android limits the number of AsyncTask to 5. So problem is I want to check if a AsyncTask is already running, and if found running, don't create a new AsyncTask. I want to create a new AsyncTask if there is no AsyncTask running.
Any Help be Appreciated.
Use getStatus() to get the status of your AsyncTask. If status is AsyncTask.Status.RUNNING then your task is running.
check this way
if(uploadRecordings.getStatus() == AsyncTask.Status.RUNNING){
// My AsyncTask is currently doing work in doInBackground()
}
For More Detail Read : Android, AsyncTask, check status?
You can use getStatus ()
Returns the current status of this task.
if(YourAsyncTaskOBJ.getStatus() == AsyncTask.Status.RUNNING)
{
// AsyncTask Running
}
Read How to check if Async Task is already running
Override onPostExecute() method of AsyncTask, which is executed whenever a call is completed. Set some flags in onPostExecute() and proceed accordingly.
Dealing with AsyncTask
Put the AsyncTask in a Fragment.
Using fragments probably is the cleanest way to handle configuration changes. By default, Android destroys and recreates the fragments just like activities, however, fragments have the ability to retain their instances, simply by calling: setRetainInstance(true), in one of its callback methods, for example in the onCreate().
please find full implementation and description to deal with AsyncTask.
Handle Android AsyncTask
If you want to create a single AsyncTask when nothing is already running, you can do something like:
if(uploadRecordings == null || uploadRecordings.getStatus() != AsyncTask.Status.RUNNING){
uploadRecordings = new UploadRecordings();
uploadRecordings.execute(context);
}
This assumes that your uploadRecordings is a member variable. e.g.
private UploadRecordings uploadRecordings = null;
OK so I have an Android app which has a listview on a fragment being loaded on the mainactivity. The list view contains an imageview which is loaded with an image from the devices external storage via an adapter which in turn calls an AsyncTask object called BackgroundImageLoader.
At this point if I run my app everything work great and the images show nearly instantaneously.
To give more detail about that process....
On the listview's adapter's bindView method I call a method which invokes the following:
BackgroundImageLoader loader = new BackgroundImageLoader(photoID, imageView);
loader.execute();
Now after I got the above code working perfectly I wrote some logic to "purge old photos". This logic was put in an AsyncTask object named AutoPurgePhotos_Task. Basically I want to run the task once on startup of the app but I just want it to run in the background so as not to interfere with the UI. I have tried launching it from the tail-end of the Applications onCreate() method and I have tried launching it from the MainActivity's onCreate() method. The results are such that the purging logic runs and works perfectly. And while it is running in the background my UI seems to be working as well with EVERYTHING EXCEPT the BackgroundImageLoader AsyncTask. None of the photos will begin to show until the AutoPurge task completes. Even to prove it has nothing to do with what I am doing in the task, I commented out all of my business logic and just have the task sleeping.
public class AutoPurgePhotos_Task extends AsyncTask<Void, Void, Void> {
public AutoPurgePhotos_Task() {
super();
}
#Override
protected Void doInBackground(Void... params) {
SystemClock.sleep(10000);
return null ;
}
}
As a side note I have other asynctask in my app that will not run either until this initial asynctask finishes. Its as if only one asynctask will run at a time. Again all other code that is in my UI thread appears to be running and working just fine while the asynctask is running. It just seems that only one asynctask will run at a time.
Here is how I launch the autopurge task...
AutoPurgePhotos_Task task = new AutoPurgePhotos_Task();
task.execute();
again I have tried launching it from several different areas and no matter where/how I launch it the other asynctask will wait till that one is done before they will run.
Thanks for any help you can give me.
Use executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) instead of execute(). Quoting the AsyncTask documentation:
If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR.
So my problem is as follows, I have an AsyncTask Class that is called from a preference file ie. The intent is called from within an xml file. This AsyncTask Class is continuously running as I am doing a real time FFT of the sound within a room and will only be cancelled if I hit the return button. I cant check for keys being pressed as this Class extends AsyncTask and therefore I cant end the thread properly so that it reaches its onCancelled() state. The only other way I can think about ending this is to make a standby class which is called from the xml file which in turn calls this class and calls the .cancel(true) on the class object.. Any other ideas around this?
Can you post some code?
In your calling method (in which you might be able to check for keys), you can call myASyncTask.cancel() and then in the doInBackground() method of your AsyncTask, you can check for isCancelled() and then break your loop and return to your overridden onCancelled() method of your AsyncTask.
http://developer.android.com/reference/android/os/AsyncTask.html#cancel(boolean)
Does that help?
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.