Add waiting time to android asynchronous task - android

I have a Forgot Password screen. The user fills in his email, clicks on submit and an HTML request is submitted. It either returns the string "true" or "false". If it returns true, I would change the text on the screen, wait for 2 seconds and send the user to another page. Here's how I'm doing it
StringRequest stringRequest = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
if ("true".equals(response)) {
//make one TextView invisible and the other visible.
findViewById(R.id.pre_password_reset_text).setVisibility(View.INVISIBLE)
findViewById(R.id.post_password_reset_text).setVisibility(View.VISIBLE);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace;
}
startActivity(new Intent(ForgotPassword.this, Login.class));
} else {
//show error message
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
//show error message
}
});
requestQueue.add(stringRequest);
The problem is, the thread goes to sleep for 2 seconds and after that the text-change happens. But, in the code I've written the text-change to happen first and then Thread.sleep.
What am I missing here?

Using Volley, the onResponse() code is called from the main thread, so calling Thread.sleep is definitelly forbidden because it would block the UI.
If you want to delay the call to the next activity, you need to defer it to a runnable, and delay it :
#Override
public void onResponse(String response) {
if ("true".equals(response)) {
//make one TextView invisible and the other visible.
TextView passwordView = (TextView) findViewById(R.id.pre_password_reset_text).setVisibility(View.INVISIBLE);
findViewById(R.id.post_password_reset_text).setVisibility(View.VISIBLE);
passwordView.postDelayed(new Runnable() {
public void run() {
startActivity(new Intent(ForgotPassword.this, Login.class));
}
}, 2000);
} else {
//show error message
}
}
A cleaner way would be to create a Handler in your Activity/Fragment and post the runnable on it, but it is simpler to directly use your password view.

Thread.sleep in main thread of your app isn't a good idea. You could use Android handlers to schedule a task in the future. Handlers will behave in an asynchronous message passing manner, so you don't freeze up your whole app during the wait period.
handler.postDelayed(new Runnable(){
#Override
public void run(){
// Start your new activity here!
}
}, 2000);
Note: All views and their derivations come with a built-in handler, so you wouldn't need to define a bare handler for this purpose.

Related

Best practice to create a thread that runs every hour in Android?

So I'm attempting to create background task that needs to be run every hour in an Android app. Its a rather heavy task that takes around 5 - 10 minutes to finish, and right now it runs on the UI thread which of course isn't good, because it hangs the whole application. I've attempted the following in my MainActivity onCreate:
new Thread(new Runnable() {
private Handler HeavyTaskHandler = new Handler(Looper.getMainLooper());
public void run(){
final TextView updatedTxt = findViewById(R.id.txt);
updatedTxt.post(new Runnable() {
#Override
public void run() {
updatedTxt.setText("Performing cleanup..");
}
});
HeavyTask(); // <-- This method runs for 5 - 10 minutes
updatedTxt.post(new Runnable() {
#Override
public void run() {
updatedTxt.setText("Done..");
}
});
HeavyTaskHandler.postDelayed(this, HeavyTaskCycle);
}
}).start();
I have two issues with the above
It works fine the first time, and the task is performed in the background well without hanging the UI thread. However, after this first time and the next time(s) it is run, the UI thread hangs again when it is run. What am I missing?
Notice that before the HeavyTask() method is called i try to set a TextViews text to "Performing cleanup.." .. This never shows, only the "Done.." which happens after the HeavyTask() method is done. How can i ensure that the message also appears before?
I ended up doing the following from MainActivity which doesn't hang the application
private void CreateCleanUpThread()
{
CleanUpThread = new Thread(new Runnable() {
public void run(){
try {
while(true) {
performingCleanup = true;
final TextView updatedTxt = findViewById(R.id.updated_txt);
runOnUiThread(new Runnable() {
#Override
public void run() {
updatedTxt.setText("Performing database history cleanup..");
}
});
HeavyTask(); // <-- This method runs for 5 - 10 minutes
runOnUiThread(new Runnable() {
#Override
public void run() {
updatedTxt.setText("Done..");
}
});
performingCleanup = false;
Thread.sleep(CleanUpCycle); // 1 hour wait time
}
} catch(Exception ex) {
System.out.println("Error in CreateCleanUpThread : " + ex.getMessage());
}
}
});
}
// onCreate in MainActivity
...
CleanUpThread.start();
Certainly not the best way, but it works and will do for now. Should be moved to a service instead i think.

Android: Multiple handlers in one Activity

I have a single handler instance, and I try to post two Runnables. But what I observe is only the latest Toast is getting printed on the device.
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Toast.makeText(getApplicationContext(),"Showing from main activity",
Toast.LENGTH_SHORT).show();
}
});
handler.post(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Toast.makeText(getApplicationContext(),"Showing from main activity new",
Toast.LENGTH_SHORT).show();
}
});
As per the explanation of Handler, it enqueues the runnables into the messages queue of the thread to which it it associated. Which means Both the toast should be displays in the order in which they are enqueued.
Can someone please explain this?
When you make a Handler associated with the main looper, you should keep in mind that it's associated with the main thread. So calling Thread.sleep in the main thread is absolutely discouraged and should be avoided.
Toasts use the UI thread as well, but you prevent it from appearing by freezing this thread. The steps happening in your code are as follow:
The action to show first Toast is enqueued
The action to show second Toast is enqueued
// First action execution
Make the thread sleep for 3
seconds
Showing first toast is enqueued
// Here first Toast should appear, but it doesn't happen right at the moment you called the method. Treat it as another message enqueued in the main looper
Make the thread sleep for 3 seconds
Showing second toast is enqueued
First toast is shown
Second toast is shown
In the end both toasts are shown, but you can see only the last one, because it's shown after the first and covers it. If you want to show two toasts with a short delay, use post delayed method, or something like:
final Handler handler = new Handler(Looper.getMainLooper());
final Context context = getApplicationContext();
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(context, "Showing from main activity",
Toast.LENGTH_SHORT).show();
handler.postDelayed(new Runnable() {
#Override
public void run() {
Toast.makeText(context, "Showing from main activity new",
Toast.LENGTH_SHORT).show();
}
}, 3000);
}
});

Updating Content thru Volley at Time Intervals

I know there are multiple post on doing this, but with limited Android experience I am little confused as to who to believe. I have an app that loads content on start up from my server thru Volley request. After a period of time I want to make a Volley request back to update the content that is displayed to the user. When the app first loads, I determine the number of seconds from the half hour which I pass to the following
public static void refreshAllContent(final long timetoupdate) {
new CountDownTimer(timetoupdate, 1000) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
Log.i("SCROLLS ", "UPDATE CONTENT HERE ");
resetContent();
}
}.start();
}
On the finish it calls the refreshAllContent which is where I would make the Volley request and reset the count for the next update, I have something like
public static void resetContent(){
Handler handler= new Handler();
Runnable runnable= new Runnable() {
#Override
public void run() {
//PUT VOLLEY REQUEST HERE
refreshAllContent(Times. initialTime());
}
};
}
I guess I am stuck as to exactly how to make the Volley request, meaning what do I have to worry do this. Like I said not a lot of experience so not sure if I run the request in a special runnable or task. Any direction appreciated.
EDIT: I reworked this some, instead of going back to the refreshAllContent, I replaced this with
private static void resetContent(){
Log.i("SCROLLS ", "ENTER resetContent");
final Handler handler= new Handler();
Runnable runnableCode = new Runnable() {
#Override
public void run() {
refreshData(); // Volley Request
handler.postDelayed(runnableCode, 20000);
}
};
handler.post(runnableCode);
}
Logic is now on the initial run, the first timetoupdate is created and passed to the refreshAllContent. Once the countdown is complete, the resetContent() will run which makes the Volley Request in the refreshData(). Now I am getting an error stating the runnableCode needs to be declared final since it's accessed from an inner class, same for the handler. Well adding final to the
final Runnable runnableCode=new Runnable(){
line doesn't fix the error, I still have an error telling me the runnableCode has not been initialized. Can I get little help on this.
You don't create a Runnable to run Volly. Volly runs network calls on a background thread by default.
Here is a simple Volly code:
public void volleyProcess(){
RequestQueue requestQueue = Volley.newRequestQueue(this);
StringRequest request = new StringRequest(Request.Method.GET, "https://api.myjson.com/bins/753rt", new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.d(TAG, response);
refreshAllContent(30000);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, error.toString());
}
});
requestQueue.add(request);
}
onResponse() is called when a response is successfully returned by the call. This method is called on the main thread therefore you can run your refreshAllContent() method here. and the parameter 'response' is the data returned, do what ever you want with it here(i am simply printing it to the Logcat).
Now to make this code run after the desired interval, just call it in onFinish() of the countdownTimer.
public static void refreshAllContent(final long timetoupdate) {
new CountDownTimer(timetoupdate, 1000) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
Log.i("SCROLLS ", "UPDATE CONTENT HERE ");
volleyProcess();
}
}.start();
}
Hope this helped

How to call two method one after another?

I'm building a simple Android Application. I want to parse JSON. I have everything working using Button. But I want call it without using Button. It need to be get the JSON first from url and then Parse it. I have function getJson() and parseJSON(). First I want getJSON to be called and after 5 second parseJSON(). here is my code:
Runnable[] methods = new Runnable[]{
new Runnable() {
#Override
public void run() {
Toast.makeText(SheduleActivity.this, "This is gettin JSON", Toast.LENGTH_LONG).show();
getJSON();
}
},
new Runnable() {
#Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},
new Runnable() {
#Override
public void run() {
Toast.makeText(SheduleActivity.this, "This is Parsing JSON", Toast.LENGTH_LONG).show();
parseJSON();
}
}
};
for(Runnable r : methods)
//r.run();
service.submit(r);
service.shutdown();
while (!service.isTerminated()) {}
Toast.makeText(SheduleActivity.this,"Finished all threads", Toast.LENGTH_LONG).show();
Everytime I run my app only getJSON() method calls and stops. Any suggetions?
I think you need to use AsyncTask. put getJson() in doInBackground block and parseJSON() on onPostExecute block. hope this help.
Android does not suggest to use sleep(5000) for delay. Use handler to do this. See the documentation of Handler.
For example in your code,
Handler handler = new Handler( /* --- */ );
handler.postDelayed(methods[i], 5000);
Also for getting json from web, it is suggested to use Asynctask to make the UI responsive. Best tutorial for Asynctask is here.

How to wait for webview.post to complete?

In the below code, outside is printed before inside. I want the order to be inside first and then outside. So how do I ensure that the Runnable is finished before the second Log is reached?
webView.post(new Runnable() {
#Override
public void run() {
Log.d("check", "inside");
}
});
// some code needed here
Log.d("check", "outside");
Some code must be inserted where to comment is to achieve this.
EDIT: I am doing all the work in a background service.
[P.S.: Those who are curious as to why I am doing this, it is so because unless I add the webview.post, I keep getting the following error: "All WebView methods must be called on the same thread.". Anyway, this shouldn't affect you from answering the question.]
You might try using a CountDownLatch:
final CountDownLatch latch = new CountDownLatch(1);
webView.post(new Runnable() {
#Override
public void run() {
Log.d("check", "inside");
latch.countDown();
}
});
// some code needed here
latch.await();
Log.d("check", "outside");
However, you wouldn't want to use latch.await() on the UI thread, as that's a blocking method. If you wanted to run this on the UI thread it would be best to replace latch.countDown() with a call to a callback method, which would in turn run Log.d("check", "outside").

Categories

Resources