Android change button text takes a long time - android

For my app I want to disable/change a specific button that is pressed.
I have an onclick method called btnClicked which simplified looks like this:
Public class MainActivity extends Activity{
Button myBytton;
#Override
protected void onCreate(Bundle savedInstanceState) {
myBytton = (Button)findViewById(R.id.buttonCall);
}
public void btnClicked(View view)
{
myBytton.setText("loading");
myBytton.setEnabled(false);
myBytton.setClickable(false);
// Do a call to an external api
callApi();
}
public void callApi(){
// run querys
if(succesullyCalledApi){
vibrator.vibrate(500);
// I tried commenting out the below part,
// it is than visible that the phone vibrates before it
// has changed the text (atleast a quarter of a second).
myBytton.setText("search");
myBytton.setEnabled(true);
myBytton.setClickable(true);
}
}
}
In the callApi method is a vibrate method which vibrates after the function gets a result.
Also if there is a result in the callApi myButton will be enabled and the text changed to search.
What happens is the following:
I click on the button, the phone vibrates first and afterwards it changes its text.
my question.
Why did callApi / vibrate run before myBytton.setText ?

what NigelK said is true.
When you arrive in the btnClicked method all the instructions are made on the UI thread. Therefore when you ask the System to vibrate, it will be blocked for XX time depending on the time you passed to the method vibrator.vibrate(XX);.
In order to avoid this "freeze" you need to make the vibration on another Thread.
Here is what it will look like :
Public class MainActivity extends Activity
{
Button myBytton;
#Override
protected void onCreate(Bundle savedInstanceState)
{
myBytton = (Button)findViewById(R.id.buttonCall);
}
public void btnClicked(View view)
{
myBytton.setText("loading");
myBytton.setEnabled(false);
myBytton.setClickable(false);
// Do a call to an external api
callApi();
}
public void callApi()
{
// run querys
if(succesullyCalledApi)
{
// here you create and run the Thread.
// put anything you want to do inside the run method
new Thread(
new Runnable()
{
public void run()
{
// here you start the vibration
vibrator.vibrate(500);
}
}
).start();
// I tried commenting out the below part,
// it is than visible that the phone vibrates before it
// has changed the text (atleast a quarter of a second).
myBytton.setText("search");
myBytton.setEnabled(true);
myBytton.setClickable(true);
}
}
}
And that's it. It will launch another Thread that will handle the vibration and not freeze your UI thread.
EDIT
Here is the AsyncTask version :
The three elements asked when you extend AsyncTask are :
The type of the parameters you pass to the doInBackground() method
The Type of the elements that are passed in the onProgressUpdate() method.
The Type of the element returned by the doInBackground() method that is also the parameter of the onPostExecute() method.
This is what it looks like :
public class MyTask extends AsyncTask<Void, Integer, Boolean>
{
private Button mButton;
public MyTask(Button button)
{
mButton = button;
}
// Here everything will run on a background Thread
protected Boolean doInBackground(Void... voids)
{
boolean succesullyCalledApi = false;
// do your long querys here
// ...
return succesullyCalledApi;
}
// Here everything will run on the UI Thread
protected void onProgressUpdate(Integer... progress) {
// here you can make some update to the UI like updating a
// progress bar
}
// Here everything will run on the UI Thread
protected void onPostExecute(Boolean succesullyCalledApi)
{
if(succesullyCalledApi)
{
mButton.setText("search");
mButton.setEnabled(true);
mButton.setClickable(true);
// here you start the vibration
vibrator.vibrate(500);
}
}
}
And in your callApi() method you only have to to this :
public void callApi()
{
new MyTask(myButton).execute();
}
EDIT 2
In order to retrieve the query back to your main Thread (or UI Thread) all you have to do is ... nothing.
You are in the UI Thread when the onPostExecute() method is called.
But I assume that you want to retrieve the query back to your MainActivity. To do so :
Pass MainActivity in parameter of MyTask constructor,
Create a method in MainActivity named processQuery() (or whatever you want),
Finally call this method in the onPostExecute() method.
Here are some snippets :
Public class MainActivity extends Activity
{
Button myBytton;
...
public void callApi()
{
// add this to the constructor
new MyTask(this, myButton).execute();
}
// I put String here but adapt it to your query Type.
public void processQuery(String query)
{
// process your query here.
}
}
public class MyTask extends AsyncTask<Void, Integer, Boolean>
{
private Button mButton;
private MainActivity mMainActivity;
public MyTask(MainActivity mainActivity, Button button)
{
mButton = button;
mMainActivity = mainActivity;
}
...
// Here everything will run on the UI Thread
protected void onPostExecute(Boolean succesullyCalledApi)
{
if(succesullyCalledApi)
{
// process your query
mMainActivity.processQuery("THE QUERY YOUR WANT TO PROCESS");
mButton.setText("search");
mButton.setEnabled(true);
mButton.setClickable(true);
// here you start the vibration
vibrator.vibrate(500);
}
}
}
There probably is a better way to do this but this one is simple and work :)
Hope it helps.
Cheers

This is because your call to the API is being done on the UI thread. Even though you have made changes to the UI, the screen won't refresh until the processing invoked from the button clicked event completes. Call your API on a new thread or via an Async Task to get the behaviour you want.

Because you are doing all stuff at the UI Thread. You must use an AsyncTask for your long running operations.
Try below implementation:
public void callApi() {
MyTask myTask = new MyTask();
myTask.execute();
}
private class MyTask extends AsyncTask<Void, Void, Boolean> {
protected void doInBackground(Void... params) {
// This runs on a separate background thread
boolean succesullyCalledApi = false;
// run querys
// do your long running query here and return its result.
return succesullyCalledApi;
}
protected void onPostExecute(Boolean succesullyCalledApi) {
// this runs on UI Thread
if(succesullyCalledApi){
vibrator.vibrate(500);
myBytton.setText("search");
myBytton.setEnabled(true);
myBytton.setClickable(true);
} else {
// You should better think this part also. what will happen if result is false?
}
}
}

Related

How can i know when the async task has returned

I have an async task that loads image urls from server.After loading urls than i load the images one by one through another asynctask.
On the click of a button i start the first asynctask
public void getit(View v)
{
new getdata().execute("http://10.0.2.2/geturls.php");
// line no 2
}
After i get the urls i use another async task to load images.
How can i find out when the image urls have been loaded and i can call the second async task at line no 2.
if i use a boolean variable which i toggle in the onpostexecute
#Override
protected void onPostExecute() {
urlgot=true;
}
then i shall have to use some repeating loop inside getit method at line no 2 to check the status of this variable urlgot. but it may take more time than allowed for ui thread.
Can there be a more cleaner method to do this check.
thanks
There are two solutions I can think of:
1) You create one AsyncTask that does everything (getting the urls, and downloading all images). Than you know exactly when to start downloading the images.
2) You start the next AsyncTask from the onPostExecute() of the first AsyncTask.
You won't be able to do your next piece of work in //line no 2 without defeating the purpose of AsyncTask. If you're doing network activity, you need to be doing it asynchronously, so that's not an option.
Instead, in onPostExecute() you can call another method in your activity that does what you would have done in //line no 2. This is safe to do, because onPostExecute() happens on the UI thread.
But depending on your design, it might make more sense to do all the //line no 2 stuff in your original AysncTask in onPostExecute, so you only have one task doing all of the work.
Use a Handler. In the method onPostExecute of your AsyncTask you can send a message informing the Handler to start another AsyncTask.
Something like this:
#Override
protected void onPostExecute(Void res) {
MyHandlerHandler handler = new MyHandlerHandler();
Message msg = new Message();
msg.what = MyHandler.TASK_FINISHED;
handler.sendMessage(msg);
}
And in your Handler class:
public class MyHandlerHandler extends Handler {
public static final int TASK_FINISHED = 2;
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case TASK_FINISHED:
new MyAsyncTask().execute();
break;
}
}
}
instead of putting line 2 in getIt, put it in onPostExecute like below :
public void getit(View v)
{
new getdata().execute("http://10.0.2.2/geturls.php");
}
#Override
protected void onPostExecute() {
// line 2
}
I use a custom interface to do stuff after execution.
public Interface OnDataReceived
{
public void onReceive( Object result);
}
and on MyASyncTask
public class MyAsyncTask extends ASyncTask<Object,Object,Object>
{
OnDataReceived receiver;
public MyAsyncTask( OnDataReceived receiver )
{
this.receiver = receiver;
}
...
protected void onPostExecute( Object result)
{
receiver.onreceive( result );
}
}
and let my main class implement OnDataReceived
public class Main implements OnDataReceived
{
....
public void getit(View v)
{
new MyAsyncTask(this).execute("http://10.0.2.2/geturls.php");
}
#override
public void onReceive( Object result)
{
// do whatever
}
}
EDIT
Even for more control you can add onFailed and rename your interface to OnResponse
public Interface OnResponse
{
public void onReceive( Object result);
public void onFailed( Object errcode);
}

android asynchronous get foreground activity

Is there a (thread safe) method I can call from Java on Android which
will give me a handle to the foreground activity from a background task?
I would like to be able to do so in order to safely post toasts on top
of the top window.
Thanks.
You wouldn't necessarily have to get a handle to the foreground UI activity to show a toast message. You can do this from a background thread like this:
runOnUiThread(new Runnable() {
public void run() {
// make toast, show toast
}
});
Create your own class that extends AsyncTask and pass the Activity as one of the parameters:
public class MyAsyncTask extends AsyncTask<String, String, String>{
Activity mActivity;
public MyAsyncTask (Activity mActivity){
this.mActivity = mActivity;
}
#Override
protected String doInBackground(String... urls) {
return null;
}
#Override
protected void onPostExecute(String result) {
}
}
Doing that should allow you to access the Activity (assuming it's still the foreground throughout; which may not necessarily be the case).
If there's a chance that it's not the foreground Activity, you may want to just use the:
runOnUiThread(new Runnable() {
public void run() {
// Do UI stuff here
}
});
However, that isn't necessarily thread-safe

Android AsyncTask onPostExecute off of main ui thread

I'm having an issue with AsyncTask and onPostExecute. I am finding that onPostExecute is executing on a different thread than the main ui thread, which is causing a CalledFromWrongThreadException to happen when I modify any views.
I put in some logging to see what threads onPreExecute, doInBackground, and onPostExecute are running on. I would see a result like this...
onPreExecute ThreadId: 1
doInBackground ThreadId: 25
onPostExecute ThreadId: 18
I believe the main ui thread id is 1 and I would expect both onPre and onPost to both execute on thread 1. I am making sure to create and also call the execute method from the ui thread (for example in onCreate of an Activity).
Another thing to note that I have noticed is that later async tasks will run their onPostExecute method on the same thread as previous async task onPostExecute methods (in this case thread 18).
Right now in order to get around this I am wrapping the code in my onPostExecute methods in a call to runOnUiThread, but I think this is hacky and would like to get to the real issue.
I am out of ideas! Any one have any insight? I'm happy to answer any questions that could helper with further investigation!
EDIT:
There are two ways that async tasks are being run in the code. I am wondering if the latter in these examples is causing something weird to happen?
public class SomeActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
new SomeAsyncTask().execute();
}
private class SomeAsyncTask extends AsyncTask<String, Void, Integer> {
#Override
public void onPreExecute() {
Thread.currentThread().getId() // 1
//Show a dialog
}
#Override
public Integer doInBackground(String... params) {
Thread.currentThread().getId() // 25
return 0;
}
#Override
public void onPostExecute(Integer result) {
Thread.currentThread().getId() // 18
//hide dialog
//update text view -> CalledFromWrongThreadException!!!
}
}
}
The above seems like a vanilla use of AsyncTask, but I still see this issue occurring even in simple cases like this. The next example uses an async task to run other async tasks. Maybe there is something I don't know about what happens when an async task gets constructed that is causing some weird behavior?
public class SomeActivity extends Activity implements TaskRunner.OnFinishListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
TaskRunner taskRunner = new TaskRunner();
taskRunner.setOnFinishListener(this);
taskRunner.addTask(new SingleTask());
taskRunner.addTask(new SingleTask());
taskRunner.execute();
}
#Override
public void onTaskFinish(List<Integer> results) {
//Thread id is 18 when it should be 1
//do something to a view - CalledFromWrongThreadException!!
}
}
//In a different file
public class SingleTask extends AsyncTask<String, Void, Integer> {
//This is a an async task so we can run it separately as an asynctask
//Or run it on whatever thread runnerExecute is called on
#Override
public Integer doInBackground(String... params) {
return runnerExecute(params);
}
//Can be called outside of doInBackground
public Integer runnerExecute(String... params) {
//some long running task
return 0;
}
}
//In a different file
public class TaskRunner {
private List<SingleTask> tasks;
private OnFinishListener onFinishListener;
public interface OnFinishListener {
public void onTaskFinish(List<Integer> results);
}
public TaskRunner() {
this.tasks = new ArrayList<SingleTask>();
}
public void setOnFinishListener(OnFinishListener listener) {
this.onFinishListener = listener;
}
public void addTask(SingleTask task) {
tasks.add(task);
}
public void executeTasks() {
new RunnerTask().execute((SingleTask[]) tasks.toArray());
}
//Calls the runnerExecute method on each SingleTask
private class RunnerTask extends AsyncTask<SingleTask, Integer, List<Integer>> {
#Override
public void onPreExecute() {
//Runs on thread 1
}
#Override
public List<Integer> doInBackground(SingleTask... params) {
//Runs on arbitrary thread
List<Integer> results = new ArrayList<Integer>();
for(SingleTask task : params) {
int result =task.runnerExecute(task.getParams());
results.add(result);
}
return results;
}
#Override
public void onPostExecute(List<Integer> results) {
//Runs on thread 18
onFinishListener.onTaskFinish(results);
}
}
}
Maybe what is going on here is just super weird, and not at all how async tasks are meant to be used, either way it would be nice to get to the bottom of the issue.
Let me know if you need any more context.
I have been experiencing the same problem and it turned out the the issue was using Flurry 3.2.1. However, the issue is not limited to the Flurry library.
The issue behind the scenes is having the first ever (when the app is loaded for the first time) AsyncTask call from a looper thread which is not the Main UI thread. This call initializes a sHandler static variable in AsyncTask to the wrong thread id, and this id is then used in all subsequent AsyncTask$onPostExecute() calls.
To solve the problem, I call an empty (do-nothing) AsyncTask on first app load, just to initialize AsyncTask correctly.
try using:
getBaseContext().runOnUiThread(new Runnable()
{
#override
public void run()
{
}
});
and write your code inside the run function
The AsyncTask is designed to be used from the main thread. Your problem is the second case, and is that you call execute on the SingleTask from a background thread. You call it in the doInBackground method of RunnerTask. The onPostExecute is then run from the backgroundthread of RunnerTask
Two options for you.
1: Trash RunnerTask, and execute the SingleTasks from you main thread, they'll all run in parallell and you won't know which finishes first, but onPreExecute and onPostExecute is called on the main thread
2: Trash the SingleTask and define them as Runnables instead, then you can run them in sequence in the RunnerTask's doInBackground. They'll all run in the background thread of RunnerTask, in the order you call Run. When it is finished, the onPostExecute of RunnerTask is run on the main thread.
i just tried your code and onPreExecute and onPostExecute does run on the same thread, how do you output the thread id ? try:
Log.d("THREADTEST","PRE"+Long.toString(Thread.currentThread().getId()));
Log.d("THREADTEST","BACKGROUND"+Long.toString(Thread.currentThread().getId()));
Log.d("THREADTEST","POST"+Long.toString(Thread.currentThread().getId()));
P.S. it should be:
new SomeAsyncTask().execute();
and
private class SomeAsyncTask extends AsyncTask<String, Void, Integer> { ... }
you are actually executing the SingleTask from RunnerTask's doinbackground method which is incorrect as asynctask should be executed from a main thread only. You need to relook into the logic which runs the set of SingleTasks from RunnerTask.

Android activity to handle multi threading

In my activity class i want to perform a series of long calculations that require around 5 sec to complete when i press a button. So in order to do that i have create a new class which does all the calculations in its run method(since it implements Runnable) and when finished i set a variable to true to indicate that. In the code that checks the if the button is pressed i start a new Thread passing my class in it and then cheking whether the run method has finished or not. If it finished i then print the data. The problem with this is that when i check if the calculations have finished they actually havent so it pass that line of code and never prints the data. I have tried to do the Async Class method but still i think it wont work. Is there a way to create the thread when i press the button and keep checking if had finished so i can print the data? Which piece of code in an Activity is actually get executed over and over again? Thanks for any information.
if(v.equals(this.button)) {
EditText param1 = (EditText)findViewById(R.id.param1);
EditText param2 = (EditText)findViewById(R.id.param2);
calculations = new MathCalc(param1.getText().toString(), param2.getText().toString());
new Thread(calculations).start();
while(!calculations.isReady());
Intent intent = new Intent(this,Show.class);
intent.putExtra("show1", calculations.getResult());
startActivity(intent);
}
This is want i want to achieve.
The AsyncTask is the right tool for this. The typical use case for the AsyncTask is that you want to do something small in the background and leave feedback through the UI before, during and/or after the task is done.
Be aware that running things in the background can get you in trouble if the user quits and restarts your activity a lot, since the background task will not end when the Activity is removed from screen.
An example activity is shown below. You could add the onPreExecute and onProgress methods to the AsynchTask to give the user feedback before and during the calculation.
public class CalcActivity extends Activity {
private Button button;
private TextView resultView;
public void onCreate() {
button = (Button) findViewById(R.id.my_button);
resultView = (TextView) findViewById(R.id.result);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
button.setEnabled(false);
AsyncCalculation calc = new AsyncCalculation();
calc.execute();
}
});
}
public class AsyncCalculation extends AsyncTask<Void, Void, Integer> {
#Override
protected Integer doInBackground(Void... params) {
int result = 0;
// Do some calculation
return result;
}
#Override
protected void onPostExecute(Integer result) {
// Set the result, start another activity or do something else
resultView.setText("The result was " + result);
button.setEnabled(true);
}
}
}
I don't see how this won't work with AsyncTask. You basically need to override two methods - doInBackground() and onPostExecute().
You're guaranteed that onPostExecute() will be invoked on the UI thread after the background computation finishes. You also don't have to worry how to update the UI Thread from another thread.
Here's a good example.
Use
Button someButton = (Button) findViewById(R.id.favouriteButton);
someButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
while(!isDone){
doAlotOfCalculations();
}
}
});
thread.start();
}
});
private void doAlotOfCalculations(){
...
if(whenDone){
isDone = true;
}
....
}
Which piece of code in an Activity is actually get executed over and
over again?
There is no such a thing.
It is just onResume which executes every time you start(restart) this activity

Android: how to set button disable right after the method setEnabled(false)?

This question is not only limit for Button or this setter.
For example, here a button listener:
runButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
runButton.setEnabled(false);
runScript();//This function will take a long time!!
}
});
When this button is clicked, I want it be disabled immediately. However, practically, I see this button won't get disabled until all the methods finished, that is after the method runScript() finishes, the button actually turns grey.
So, could anyone tell me how to make the button disabled right after that line?
What happens is the runScript method blocks the UI thread. What you want to do inside your onClick method is disable the button then run the script in a different thread, like so
runButton.setEnabled(false);
new Thread(new Runnable() {
#Override
public void run() {
runScript();//This function will take a long time!!
}
}).start();
More information found here:
http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html
As a general rule of thumb, you want to avoid doing anything that may take a long time on the UI thread. What you can do in your click listener is spawn a new thread that does the long-running method in the background. The android AsyncTask class provides a lot of support methods for having background threads that can send updates back to the UI via progress events.
Try calling runButton.invalidate() immediately after setEnabled(false) method
Also
You should not have long running methods in the UI thread transfer runScript() to an asynch task. Button state will get updated faster
runButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
runButton.setEnabled(false);
MyTask asyncTask = new MyTask();
asyncTask.execute();
}
});
private class MyTask extends AsyncTask<void, void, String> {
protected String doInBackground(void) {
runScripts();
//set to something relevant to your app
String result ="";
return result;
}
protected void onPostExecute(String result) {
//do something
}
}
I

Categories

Resources