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.
Related
I created one handler to repeat a task repeatedly and I also want to destroy it within that handler once a condition has been met.
pinHandler = new Handler();
Now I created two functions separately to start and stop the task.
void startRepeatingPins() {
mPinSetter.run();
}
Runnable mPinSetter = new Runnable() {
#Override
public void run() {
try{
System.out.println("PinIndwx count is :"+pinIndexCount);
if(pinIndexCount==(plist.size()-1))
{
stopUpdatingPins();
pinIndexCount=0;
//pinHandler.removeCallbacks(mPinSetter);
System.out.println("Handler stopped by itself.");
}
else
{
updatePoint(plist.get(pinIndexCount));
pinIndexCount++;
}
}
finally {
pinHandler.postDelayed(mPinSetter, pinInterval);
}
}
};
private void stopUpdatingPins()
{
pinIndexCount=0;
pinHandler.removeCallbacks(mPinSetter);
System.out.println("Called the stop function.");
}
Now, the issue is that, if I call the stopUpdatingPins function , the handler stops but when I try to stop it automatically from within the handler, it just doesn't stop. Although the stopUpdatingPins function does get called.
Change You startRepeatingPins() like this, You should not directly call the run. If your run like this then there is no point of removing this from Handler. So attach Runnable with Handler.
void startRepeatingPins() {
pinHandler.post(mPinSetter);
}
You added post delay in finally that means you are stopping at first if loop and starting again in finally, So it's never stopping. So Change your runnable like this,
Runnable mPinSetter = new Runnable() {
#Override
public void run() {
System.out.println("PinIndwx count is :"+pinIndexCount);
if(pinIndexCount==(plist.size()-1))
{
stopUpdatingPins();
pinIndexCount=0;
//pinHandler.removeCallbacks(mPinSetter);
System.out.println("Handler stopped by itself.");
}
else
{
updatePoint(plist.get(pinIndexCount));
pinIndexCount++;
pinHandler.postDelayed(mPinSetter, pinInterval);
}
}
};
Following code is within an android activity class.
The project required a non-stop thread when apps is active and stop when apps is inActive/closed.
I predicted to see a non-stop "Hello World" message display in logCat.However I only saw one-times Hello World messgae .
What wrong of my code,so that I able to see a non-stop "Hello World"
Hope someone advice. Thanks
#Override
protected void onStart(){
super.onStart();
Log.e("onStart","beforeStart");
new HttpRequestTask().execute();
Log.e("onStart","Start");
this.pickButtonThread();
}
private void pickButtonThread(){
new Thread() {
#Override
public void run() {
try {
// code runs in a thread
PickerItemActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
// code runs in a UI(main) thread
//isPickButtonEnableDisable();
//new HttpRequestTask().execute();
Log.e("pickButtonThread", "Hello World");
}
});
} catch (final Exception ex) {
}
}
}.start();
}
That's because you don't have any loop inside your run() method, thus it's run just once and it exits. However, declaring an endless loop is not considered a good idea as Android OS might kill it if there's lack of memory.
You could use a Handler using the .postDelayed() method to post messages every X seconds.
private Handler mHandler = new Handler();
private Runnable mUpdateTimeTask = new Runnable() {
public void run() {
Log.e("pickButtonThread", "Hello World");
mHandler.postDelayed(this, 1000); // Every second
}
};
I have an app that runs 2 threads in loops. 1st one is updating a graph in 1s interval and the second one is updating another graph at 60s interval. The second task is taking a long time since it is quering some server in the internet 3 times that might not always be available and even if it is it will take up to 5-7s to execute.
What is happening is when I launch the second thread it will pause execution of the first one and that is not what I want, I wish both run concurrently. Here in the Youtube video you can see the results of the app running. http://youtu.be/l7K5zSWzlxI
"thread_updater1s" is running a green graph, large readout, and a timer in the corner so you clearly see it stalls for 11 seconds.
1)First of all why is that happening? how to fix it?
2)I'm aware that I might not launch the threads properly at all. I had hard time understanding how to make something to run in a interval loop in Java and my code worked fine for one graph/tread. Now when I have 2 loops in separate threads I don't know why they are not executing concurrently.
Here is the code:
public class LoopExampleActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
thread_updater1s.start();
thread_updater2.start();
}// end of onCreate
final Runnable r1s = new Runnable() {
public void run() {
do_1s_updates(); // those are very quick http calls to the local API server
} // to get data nessessary for some plot.
// They have 1s timeout as well but rarely timeout
};
final Runnable r2 = new Runnable() {
public void run() {
do_large_updates(); //This makes 7 long call over the Internet to the slow https
//server once every 60s. Has 10s timeout and sometimes takes as much as
//7s to execute
}
};
Thread thread_updater1s = new Thread() {
#Override
public void run() {
try {
while (true) {
handler.post(r1s);
sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread_updater2 = new Thread() {
#Override
public void run() {
try {
while (true) {
handler2.post(r2);
sleep(60000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}
PS. please be forgiving and informative I only code Java for 15 days so far with absolutely no prior experince or lesson.
You need to make the http requests in the threads (not the posted runnables). Then, when you have the data downloaded, you create a runnable with that data that will update the graph and post that runnable to be executed by the UI thread. Here is an example:
public class LoopExampleActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
thread_updater1s.start();
thread_updater2.start();
}// end of onCreate
Thread thread_updater1s = new Thread() {
#Override
public void run() {
try {
while (true) {
final Object data = getDataFromServer1();
handler.post(new Runnable() {
#Override
public void run() {
updateGraph1(data);
}
);
sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread_updater2 = new Thread() {
#Override
public void run() {
try {
while (true) {
final Object data = getDataFromServer2();
handler.post(new Runnable() {
#Override
public void run() {
updateGraph2(data);
}
);
sleep(60000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Obviously, change that final Object data by the appropriate class that represents your data downloaded.
handler.post pushes the runnable onto the main (UI) thread's message queue for execution on the main thread.
So what you're doing is every sleep interval, you're sending a message to the main thread to run the function. Clearly, the main thread can't run 2 things at once, so that's why one runnable is delayed for the next one.
You probably want to do the work of the runnable in the separate threads - why did you start using a handler? What happens if you call do_1s_updates and do_large_updates directly instead of through the handler & runnable?
Hello I am trying to update the textview at every certain time, but it only updates the first and then it force closes, here is the code:
try {
timer = new Timer();
timerTask = new TimerTask() {
#Override
public void run() {
//Download file here and refresh
updateRecSMSCount(count);
}
};
timer.schedule(timerTask,0, 3000);
} catch (IllegalStateException e){
}
void updateRecSMSCount(Integer count)
{
TextView numRecSMS=(TextView)findViewById(R.id.numRecSMS);
numRecSMS.setText(count.toString());
}
Can someone help please?
You can't access the UI from other thread than the thread that created the UI. The timer task runs in different thread. You can find solutions here: Updating the UI from a Timer
The most simple repair is:
void updateRecSMSCount(Integer count)
{
final TextView numRecSMS=(TextView)findViewById(R.id.numRecSMS);
numRecSMS.post(new Runnable()
{
#Override
public void run()
{
numRecSMS.setText(count.toString());
}
});
}
You can't update the contents of the view from another thread. You need to look at the Handler-class. See this and this.
I have an app that shows a disclaimer at the beginning of the program. I want a button to remain invisible for a set amount of time, and then become visible.
I set up a thread that sleeps for 5 seconds, and then tries to make the button visible. However ,I get this error when I execute my code:
08-02 21:34:07.868: ERROR/AndroidRuntime(1401): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
How can I count 5 seconds, and then make the button visible?
THanks.
Thread splashTread = new Thread() {
#Override
public void run() {
try {
int waited = 0;
while(_active && (!_ok2)) {
sleep(100);
if(_active) {
waited += 100;
if(waited >= _splashTime)
{
turnButtonOn();
}
}
}
} catch(InterruptedException e) {
// do nothing
} finally {
finish();
startActivity(new Intent("com.lba.mixer.Choose"));
}
};
splashTread.start();
public static void turnButtonOn() {
okButton.setVisibility(View.VISIBLE);
}
The problem is that you're not in the UI thread when you call okButton.setVisibility(View.VISIBLE);, since you create and run your own thread. What you have to do is get your button's handler and set the visibility through the UI thread that you get via the handler.
So instead of
okButton.setVisibility(View.VISIBLE)
you should do
okButton.getHandler().post(new Runnable() {
public void run() {
okButton.setVisibility(View.VISIBLE);
}
});
I found this to be a much simpler solution. Visibility on 7 second delay
continuebutton.setVisibility(View.INVISIBLE);
continuebutton.postDelayed(new Runnable() {
public void run() {
continuebutton.setVisibility(View.VISIBLE);
}
}, 7000);
I found this a Better solution to the problem
(button id = but_resend)
define handler
private Handler handler;
call function in extend class
showButtons();
define after class
private void showButtons() {
handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
((Button) findViewById(R.id.but_resend)).setVisibility(View.VISIBLE);
}
}, 20000); // produce 20 sec delay in button visibility
}
and keep in mind to hide the visibility in the.xml file by
android:visibility="invisible"