I want to wait for a specific operation to complete before continuing the code flow.
Something like this:
protected void onCreate(Bundle savedInstanceState) {
downloadSomeFiles(); // I want to wait for this operation to finish
Log.d("Download", "Download Complete") //This should run After downloading completed
}
However downloading takes some time and I always end up getting NullPointerException because downloading wasn't completed.
Assume we don't know how long we must wait.
Long running operations on the main thread are never a good idea, as they block the User Interface.
If you want to do this while the application is running, consider using methods from java.util.concurrent package (or coroutines if you want to switch to Kotlin). AsyncTask became deprecated.
Here is a guide: https://www.baeldung.com/java-concurrency
If you want your download to execute in the background even if your application is closed, consider using a Service: https://developer.android.com/guide/components/services
I could only suggest using Asyntask<>
Here's a sample approach for you to understand.
I just wanna comment this but I don't have enough reputation to do it.
AsyncTask<String, String, String> asyncTask = new AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
// ... Show a Progress Dialog or anything you want just to indicate that you
// ... are downloading ...
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
}
#Override
protected String doInBackground(String... strings) {
// ... Do Downloading or anything
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
// Post any codes to be followed here ...
Log.d("Download", "Download Complete")
}
}
asynTask.execute();
Create a response callback like so:
public interface ResponseCallback {
void onDownload();
}
Then in your method
downloadSomeFiles(ResponseCallback responsecallback) {
//Download files
responsecallback.onDownload(); //Trigger it
}
Then call it
protected void onCreate(Bundle savedInstanceState) {
downloadSomeFiles(new ResponseCallback() {
#Override
public void onDownload() {
Log.d("Download", "Download Complete");
}
});
Related
I defined this method, in MyActivity class that allow me to download in memory some data from a Firebase storage.
public void submitDownload() {
Task<byte[]> downloadTask=FirebaseStorage.getInstance.
getReference(DATA_PATH_TO_DOWNLOAD).getBytes(MAX_BYTES);
isTaskActive=true;
//remove eventually a previous callback from the handler
timeoutHandler.removeCallbacks(timeoutCallback);
downloadTask.addOnSuccessListener(MyActivity.this, onSuccessListener);
downloadTask.addOnFailureListener(MyActivity.this, onFailureListener);
timeoutHandler.postDelayed(timeoutCallback, 5000);
}
This is, instead, the onCreate() method:
protected void onCreate() {
super.onCreate();
onSuccessListener=new OnSuccessListener<byte[]>() {
public void onSuccess(byte[] bytes) {
if(isTaskActive) {
isTaskActive=false;
Log.d("DOWNLOAD_TASK", "SUCCESS");
}
}
};
onFailureListener=new OnFailureListener() {
public void onFailure(Exception e) {
if(isTaskActive) {
isTaskActive=false;
Log.d("DOWNLOAD_TASK", "FAILURE");
}
}
};
timeoutHandler=new Handler();
timeoutCallback=new Runnable() {
public voi run() {
if(isTaskActive) {
isTaskActive=false;
Log.d("DOWNLOAD_TASK", "TIMEOUT");
submitDownload(); //retry download
}
}
};
submitDownload();
}
Obviously, onSuccessListener, onFailureListener, timeoutHandler, timeoutCallback and isTaskActive are instance variable.
As you can see in the run() method defined in timeoutCallback, in addition to a log message, is also called the sumbitDownload(). Pratically, if a timeout occurs and the task is still active, a new download is started.
Now, imagine this scenario.
When Activity is created, a download task is started. Suppose that downloadTask doesn't complete, and neither onSuccessListener nor onFailureListener are called, but timeout occurs. So, from the run() method of timeoutCallback a new download is started.
Now, what happens to the previous downloadTask? Is it canceled? Is it replaced by the current task? or does it continue to be active and potentially could trigger its attached listeners?
If the latter question is true, how to remove the listeners from a Task<T> object?
Does the getResult() method, however, complete (i.e finish) the task?
I'm getting info from an URL and sending that info to multiple URLs. I'm running it on a "for" in AsyncTask, and at onPostExecute of first AsyncTask, I change a TextView and telling that "All is done" to user.
But the thing is even the children of AsyncTask is continuing to being executed, parent AsyncTask executes its onPostExecute. So I need to wait for them and I'm stuck about it.
So, I have two AsyncTask classes. That's the class I send to websites:
class SendTo extends AsyncTask <Object,Object,String>{
#Override
protected String doInBackground(Object... strings) {
....
}
protected void onPostExecute(String s) {
super.onPostExecute(s);
//update ui for gathered information
}
}
That's the task which I get from websites:
class GetFrom extends AsyncTask <Object,Object,String>{
#Override
protected String doInBackground(Object... strings) {
....
String param = anotherMagicalFunctionThatGivesParamToSend(strings[1]);
for(i = 1; i < websites.length; i++){
publishProgress(websites[i],param);
}
return "";
}
protected void onProgressUpdate(final Object... values) {
super.onProgressUpdate(values);
new SendTo().executeOnExecutor(THREAD_POOL_EXECUTOR, values[1], values[0]);
}
protected void onPostExecute(String s) {
super.onPostExecute(s);
//finish things
}
}
I would recommend use callback in this case.
Create an interface:
public interface MyCallback {
public void readycallback(int index_thread);
}
First class:
class SendTo extends AsyncTask <Object,Object,String>{
private MyCallback cb;
private int i;
public SendTo(int i, MyCallback cb) {
this.cb = cb;
this.i = i;
}
#Override
protected String doInBackground(Object... strings) {
....
}
protected void onPostExecute(String s) {
super.onPostExecute(s);
if (cb != null)
cb.readycallback(i);
//update ui for gathered information
}
}
Second class:
class GetFrom extends AsyncTask <Object,Object,String>{
private boolean[] array_of_completed_tasks = new boolean[websites.length - 1];
#Override
protected String doInBackground(Object... strings) {
....
String param = anotherMagicalFunctionThatGivesParamToSend(strings[1]);
for(i = 1; i < websites.length; i++){
publishProgress(websites[i],param);
}
while(!checkIfAllThreadAreCompleted()) {
//waiting all threads
//you can wait
try {
Thread.sleep(10L);// ten milliseconds
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return "";
}
protected void onProgressUpdate(final Object... values) {
super.onProgressUpdate(values);
new SendTo(i, new MyCallback() {
#Override
public void readycallback(int index_thread) {
//this is your ready callback
array_of_completed_tasks[index_thread] = true;
}
}).executeOnExecutor(THREAD_POOL_EXECUTOR, values[1], values[0]);
}
}
private boolean checkIfAllThreadAreCompleted() {
boolean ok = true;
for(i = 0; i < array_of_completed_tasks.length; i++){
ok = ok && array_of_completed_tasks[i];
}
return ok;
protected void onPostExecute(String s) {
super.onPostExecute(s);
//finish things
}
}
Or use Threads for much better coding style. But I really think that using AsyncTask is very ugly methods is real projects.
Run following for loop inside postExecute() of asyncTask as it's anyway not useful running thread inside the thread.
Maintain one global variable which will have count of completed asyncTasks and will be incremented by 1 when each asyncTask will completes it's execution. i.e it will come into postExecute.
In Every postExecute check if that count is equal to number of websites. If that number will be equal to number of websites that basically means all of your data is successfully synced else it's still in process.
It's probably not a good idea to have one AsyncTask simply wait for other AsyncTasks to complete. It makes the code more complex and prevents using that thread in the pool while it's waiting.
If you can, it would be simpler that you launch all AsyncTasks in parallel and have them all call the same method in onPostExecute(). That method would then check if all the results of all the AsyncTasks have been posted and show "All is done" in that case.
If you really want to have one AsyncTask wait in doInBackground() for one or more other AsyncTasks to complete (effectively pausing that background thread), you can use standard Java synchronization mechanisms. I recommend using CountDownLatch.
It's worth noting that synchronizing between background tasks is something that can be achieved quite easily with RxJava rather than using AsyncTask which has been designed for simple use cases.
You can pass you TextView in child asyncTask and update its value in onPost method of child asynchTask .
I have an async task. It runs a sql query.It should go to the onPostExecute automatically, but it does not always go there. THere is no exception.The logs are also fine.
class QueryDbTask extends AsyncTask<String, Void, Cursor> {
protected Cursor doInBackground(String... params) {
....
//sql query
try {
cur=activityObject.execSQL(Query);
}catch (Exception e) {
e.printstacktrace
}
return cur;}
protected void onPostExecute(Cursor result) {
}
Under what conditions onPostExecute would not be called?
i debugged and found that it was not reaching onPost Execute
Can two async task simulataneoulsy?
onPostExecute() would not be called if doInBackground() never finished or if the task was interrupted/cancelled in which case onCancelled() would be called.
You have to put an #Override annotation to onPostExecute() to be called
#Override
protected void onPostExecute(Cursor result) {
....
}
I have an AsyncTask that downloads files in doInBackground() method. I am testing my app before releasing it. When I manually force quit the app and relaunch it, the doInBackground() seems to be invoked, but not onProgressUpdate() and onPostExecute(). My onPostExecute() call methods that cause UI changes.
How can I make sure the onPostExecute and onProgressUpdate() get called? I also see the following warning:
Activity stop timeout for ActivityRecord or Activity destroy timeout for ActivityRecord or Activity idle timeout for ActivityRecord
Code:
public void OnCreate(){
new RefreshMyDashboardTask().execute();
}
private class RefreshMyDashboardTask extends AsyncTask<Void, Void, Long> {
#Override
protected void onPreExecute() {
}
#Override
protected void onProgressUpdate(Void... params) {
super.onProgressUpdate(params);
}
#Override
protected Long doInBackground(Void... params) {
// my server calls are called here.
}
#Override
protected void onPostExecute(Long result) {
}
}
If you kill your app, or if the system kills it, then AsyncTask threads for your app get killed. They're all part of your app's process; threads can't survive outside the process that started them.
Even an IntentService might not survive a force quit. That's why task killers are such a bad idea.
In short, what you're seeing is expected behavior.
i bascially have an activity
that calls an async task to set up the twitter classes, provided via twitter4j.
But i recieve an error regarding "cant create handler inside thread that has not called looper.prepare "
which is orginating from the TwitterApp class where there is a handler...
how can i get around this? successfully setting up the class no on the main UI thread as i used to have before (which worked perfectly but slowed down the app);
im basically doing:
new SetUpAsyncTaskt().execute();
within the asynctask all im doing is:
TwitterApp mTwitter;
postToTwitter = true;
String twitter_consumer_key="bllaalaa";
String twitter_secret_key="blaa"
private class SetUpAsyncTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
mTwitter = new TwitterApp(context, twitter_consumer_key,twitter_secret_key);
mTwitter.setListener(mTwLoginDialogListener);
return null;
}
#Override
protected void onPostExecute(Void result) {
if(!mTwitter.hasAccessToken()){
postToTwitter=false;
}
}
}
Thanks for any help!
UPDATE: After doing more testing, it seems the problem is due to the context, as if i remove all context based data within the class it works, but what i dont get is if i pass in the context from the UI thread, it still occurs ?? :S
UPDATE2: Found another way around it, thanks to all who replied.
Look here for the documentation: http://developer.android.com/reference/android/os/AsyncTask.html
some rules:
The task instance must be created on the UI thread.
execute(Params...) must be invoked on the UI thread.
My guess is you are executing the task on some different thread. To Execute it on UI thread create a Handler in onCreate and:
mHandler.post(new Runnable(){
//insert task creation & execution here
});
In this way the result that are in onPostExecute will be returned on the UI Thread too.
You can use runOnUiThread() to make the non-UI task run on the UI,
Try this,
#Override
protected Void doInBackground(Void... params) {
mTwitter = new TwitterApp(context, twitter_consumer_key,twitter_secret_key);
Activity_name.this.runOnUiThread(new Runnable() {
#Override
public void run() {
mTwitter.setListener(mTwLoginDialogListener);
}
});
return null;
}