How can I make use of a delay in the while loop without also delaying the UIThread?
My while loop should update the UI everytime the app run through the loop but there should also be the possibility to interact with a button. The problem I have with my current version is that the app pauses when the while loop is started until the while loop is finished and then updates the UI. I want it to update the UI with every pass through.
Do you have any ideas, maybe also an alternative way that is more efficient?
This is my current version:
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Button;
import android.widget.ProgressBar;
import android.view.View.OnClickListener;
import android.widget.Toast;
import java.util.regex.Pattern;
public class ReaderFragment extends Fragment
{
ProgressBar progress_in_main_thread;
private int progressBarStatus = 0;
TextView main_text;
Pattern p = Pattern.compile(" ");
String[] splitted_text;
Button start_button;
Button pause_button;
public boolean paused;
int index = 0;
long wait = 1000;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
//Layout des Fragments verknuepfen
{
View ReaderFragmentView = inflater.inflate(R.layout.reader_fgmt, container, false);
progress_in_main_thread = (ProgressBar) ReaderFragmentView.findViewById(R.id.reader_progress);
progress_in_main_thread.setProgress(0);
progress_in_main_thread.setVisibility(progress_in_main_thread.VISIBLE);
main_text = (TextView) ReaderFragmentView.findViewById(R.id.center_view);
start_button = (Button) ReaderFragmentView.findViewById(R.id.go);
start_button.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View view)
{
if(InputFragment.text.trim().length() == 0)
{
Toast.makeText(getActivity().getApplicationContext(), "Es wurde kein Text eingegeben!", Toast.LENGTH_SHORT).show();
}
else
{
paused = false;
splitted_text = p.split(InputFragment.text);
progress_in_main_thread.setMax(splitted_text.length-1);
mainThread();
}
}
});
pause_button = (Button) ReaderFragmentView.findViewById(R.id.pause);
pause_button.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View view)
{
paused = true;
}
});
return ReaderFragmentView;
}
private void mainThread()
{
new Thread(new Runnable()
{
#Override
public void run()
{
while(progressBarStatus < (splitted_text.length)-1)
{
progressBarStatus = index;
progress_in_main_thread.setProgress(progressBarStatus);
}
}
}).start();
getActivity().runOnUiThread(new Runnable()
{
public void run()
{
while(!paused)
{
main_text.setText(splitted_text[index]);
if(paused)
{
break;
}
else if(index == (splitted_text.length)-1)
{
Toast.makeText(getActivity().getApplicationContext(), "Ende", Toast.LENGTH_LONG).show();
break;
}
index++;
try
{
Thread.sleep(wait);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
});
}
}
Thank you all!
By definition, you cannot delay the UI thread without delaying the UI thread.
Fundamentally, you need to switch from a loop style of program to an event driven one, which in which the Android UI must be handled. Your program will not have a while loop - instead, the Android framework will call into your code to execute event functions, each of which must return as quickly as possible.
Android already does this out of the box to deliver user interaction events such as touches and button pushes, and lifecycle events. To add events for periodic evolution of your UI (what you were trying to do with the while loop and delay), you can create a Timer and have its TimerTask push some UI related work to do onto the UI Thread using RunOnUiThread; if what you need to do does not involve the UI, then you can just do it in the background thread where the TimerTask executes.
In your posted code, it looks like you might have attempted to create your own background thread which would run a loop with a sleep delay, and push work to the UI thread with RunOnUiThread; technically that is workable, but not really encouraged compared to the timer method. However, there are two problems with the way that you did it:
First, you named the launcher method for your background thread mainThread, which is a bit confusing as on Android the "main thread" and the "UI thread" are one and the same - it would be better to call it createBackgroundThread() or something.
Second, you have the while and sleep occurring within the code which it pushes to the UI thread for execution, which is unworkable. You will need to move the while loop and sleep out into the background thread and instead have it repeatedly use RunOnUiThread when you need to push onto the UI Thread small batches of work which can immediately complete, such as each actual visual update.
Also you may want to put some thought into if your implementation could end up creating multiple concurrent background threads as a result of repeated button pushes, or one of your threads it is still running when a significant Activity Lifecycle event occurs.
Related
my app is just at starting mode but does not load completely
the Logcat Message is :
"Skipped 33 frames! The application may be doing too much work on its main thread."
please help me ..
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("in on create", "before thread");
new handler().start();
}
private class handler extends Thread {
#Override
public synchronized void start() {
super.start();
Log.d("in handler ", "inside start");
}
}
}
If you really think that is doing all of the work on UI Thread. then just do create a Thread object and make your function calls in its run()
Thread t=new Thread();
t.start();
public void run(){
makeYourFunctionCallHere();
}
or you can use ASysncTask if doing some network operation.
Use Async Task to run the tasks in background.
Here is a example :
http://android-am.blogspot.in/2012/10/async-task-with-progress-dialog-in.html
Please let me know if you are doing some network operation, Bitmap downloading and setting on ImageViews iteratively. This consumes much memory to be done So this kind of issues raised.
In case of Bitmap downloading and setting to imageView you should resize images as per imageView (eg. list image-icons in ListView).
I Have searched for a few hours on google and on here. I have been unable to find a solution to my problem. I want the thread to stop when the user presses the back button. So for example the user will press the back button and the loop will stop flipping coins. Then the textviews will be populated with the number of heads and tails that the loop managed to complete before the user canceled the operation.
I know i have to set
dialog.setCancelable(false);
to
dialog.setCancelable(true);
and that i need to implement
progressDialog.setOnCancelListener(new OnCancelListener(){
public void onCancel(DialogInterface dialog) {
background.setRunning(false);
}});
But when i try to do this it ends up killing my entire app force closing it.
Can you please help. Im still kind of new to android programming and I am eager to learn more so if you notice any other things in the code i can improve it would be appreciated.
package com.michaelpeerman.probability;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.util.Random;
public class ProbabilityActivity extends Activity implements OnClickListener {
private Button submit;
ProgressDialog dialog;
int increment;
Thread background;
int heads = 0;
int tails = 0;
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
setContentView(R.layout.main);
submit = ((Button) findViewById(R.id.submit));
submit.setOnClickListener(this);
}
public void onClick(View view) {
increment = 1;
dialog = new ProgressDialog(this);
dialog.setCancelable(false);
dialog.setMessage("Flipping Coin...");
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setProgress(0);
EditText max = (EditText) findViewById(R.id.number);
int maximum = Integer.parseInt(max.getText().toString());
dialog.setMax(maximum);
dialog.show();
background = new Thread(new Runnable() {
public void run() {
for (int j = 0; j < dialog.getMax(); j++) {
int i = 1 + new Random().nextInt(2);
if (i == 1)
heads++;
if (i == 2)
tails++;
progressHandler.sendMessage(progressHandler.obtainMessage());
}
}
});
background.start();
}
Handler progressHandler = new Handler() {
public void handleMessage(Message msg) {
dialog.incrementProgressBy(increment);
if (dialog.getProgress() == dialog.getMax()) {
dialog.dismiss();
TextView result = (TextView) findViewById(R.id.result);
result.setText("heads : " + heads + "\ntails : " + tails);
}
}
};
}
I'm curious how your code compiles, since setRunning(boolean) is not part of the api for Thread.
Regardless of that, here's one way to exit a thread cleanly. Change your background thread definition to this:
background = new Thread(new Runnable() {
public void run() {
for (int j = 0; !Thread.interrupted() && j < dialog.getMax(); j++) {
int i = 1 + new Random().nextInt(2);
if (i == 1)
heads++;
if (i == 2)
tails++;
progressHandler.sendMessage(progressHandler.obtainMessage());
}
}
}
Then cancel your thread with:
background.interrupt();
You should not be forcibly closing threads in your app. This kind of thread management is generally a bad idea and usually completely unnecessary with the kind of niceties provided to you by the Android framework. Instead what you should be doing is using an AsyncTask that will update the UI (this also eliminates the use of that Messenger, which while in this example is pretty simple, can get much nastier!). Using an AsyncTask you can perform whatever work you need in the background and update the UI at the same time.. You can read about all of this in the painless threading Android tutorial.
To stop a thread you should use Thread.interrupt() method, it does not stop the thread immediately, but it notify android system that that particular thread needs to be stopped, and android os interrupts that thread when it is suitable to stop the thread.
I have this code. I don't know why postDelay make UI frozen in this case. I want the Runnable will run after 100 miliseconds deley and run in 4000 miliseconds.
package com.delaythread;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
public class MyNeedActivity extends Activity implements OnClickListener {
private ProgressBar progressBar;
private final Handler handler = new Handler() {
#Override
public void handleMessage(final Message msg) {
super.handleMessage(msg);
progressBar.setVisibility(ProgressBar.INVISIBLE);
}
};
#Override
public void onClick(final View v) {
if(v.getId() == R.id.button1) {
/* This call doesn't make ProgressBar frozen.
final Thread t = new Thread(new MyRunnable());
t.start();
progressBar.setVisibility(ProgressBar.VISIBLE);
*/
// This makes ProgressBar frozen in 4000 miliseconds.
final boolean b = handler.postDelayed(new MyRunnable(), 100);
if(b) {
progressBar.setVisibility(ProgressBar.VISIBLE);
}
}
}
/** Called when the activity is first created. */
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
((Button)findViewById(R.id.button1)).setOnClickListener(this);
progressBar = (ProgressBar)findViewById(R.id.progressBar1);
}
private class MyRunnable implements Runnable {
#Override
public void run() {
sleep();
}
private void sleep() {
try {
Thread.sleep(4000);
handler.sendEmptyMessage(0);
} catch (final InterruptedException e) {
e.printStackTrace();
}
}
}
}
Update: Actually what I want is AsyncTask executes after a delay time, so I do as this answer Java/android how to start an AsyncTask after 3 seconds of delay?. He said I should use Handler and Runnable.
The following should suit your need according to the post
private final Handler handler = new Handler() {
#Override
public void handleMessage(final Message msg) {
super.handleMessage(msg);
//start Asyntask here. progress show/hide should be done in asynctaswk itself.
}
};
#Override
public void onClick(final View v) {
if(v.getId() == R.id.button1) {
final boolean b = handler.postDelayed(new MyRunnable() , 1000);
}
}
private class MyRunnable implements Runnable {
#Override
public void run() {
handler.sendmessage(0);
}
}
}
You probably want to run your MyRunnable on other thread than main UI one, so you need to start a regular thread for this, like
new Thread(new MyRunnable()).start();
instead of using Handler for this, which queues your runnable to be executed on main UI thread.
BTW, for this purpose Timer with TimerTask would suit better.
The Android Reference for the class Handler points out:
"[...] When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it [...]"
So your Handler, created on instantiation of the Activity, should be running on the UI thread causing it to block when your Runnable is executed.
Try creating a new Thread class in which your Handler is instantiated. Then pass the Runnable to it from your onClick() method. To pass messages back (such as updating the progress bar) you can use another Handler that is running on the UI thread.
You could also save yourself a lot of pain by taking a look at the AsyncTask class.
PS:
Delaying the execution could be done in the AsyncTaskdoInBackground() via a Thread.sleep(100) call. To delay execution on UI thread level you could do the same in AsyncTask.onPreExecute().
As far as I understand it you ask your MyRunnable to run on the GUI thread (of which there is only one); but the only this it does is sleep, effectively causing the GUI thread to freeze waiting for it.
You shouldn't do complicated calcultaions (or, sleep) in the GUI thread.
You may want to read the documentation on threads and the UI thread for a more elaborate description.
Your progress bar isn't updating because you aren't updating it! Try using an AsyncTask, (it runs on a different thread but allows you to update UI elements) and setting the state of the progress bar from within the onProgress method in the Async task.
OR
Just follow this example on the Android Progress Bar page
Try this:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//Code to be executed after desired seconds
}
}, seconds*1000);
This would freeze the UI for given number of seconds and the execute the code inside the run()
Basically, I am trying to run a seconds counter and a levels counter. For every 10 seconds I want to ++level.
But that's not implemented as yet, so far I am just trying to get the seconds to display but I am getting runtime exceptions and a crash.
Googling I see that its because I am trying to update the UI from my thread and thats not allowed.
So I guess I am going to need asyncTask, but I have no idea how to do that with my simple little program. Please help or give me some alternatives...
package com.ryan1;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
public class main extends Activity {
int level = 1;
int seconds_running=0;
TextView the_seconds;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
the_seconds = (TextView) findViewById(R.id.textview_seconds);
Thread thread1 = new Thread(){
public void run(){
try {
sleep(1000); Log.d("RYAN", " RYAN ");
updated_secs();
} catch (Exception e) {
e.printStackTrace();
Log.d("RYAN", " "+e);
}
}
};
thread1.start();
}
public void updated_secs(){
seconds_running++;
the_seconds.setText(" "+seconds_running);
}
}
Create a Handler in your UI thread, then in the worker thread send a message to the handler (Handler.sendMessage(...)).
The message will be processed on your UI thread, so you can update the text widget correctly. Like this:
private Handler myUIHandler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
if (msg.what == some_id_you_created)
{
//Update UI here...
}
}
};
Then in your thread, to send a message to the handler you do this:
Message theMessage = myHandler.obtainMessage(some_id_you_created);
myUIHandler.sendMessage(theMessage);//Sends the message to the UI handler.
For this kind of thing, it is a waste to use another thread; it is just a waste and makes it so you have to dael with multithreading issues. Instead, just use Handler.sendDelayedMessage():
static final int MSG_DO_IT = 1;
static final long TIME_BETWEEN_MESSAGES = 10 * 1000; // 10 seconds
Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_IT: {
// Update your UI here...
Message newMsg = obtainMessage(MSG_DO_IT);
sendMessageDelayed(newMsg, TIME_BETWEEN_MESSAGES);
} break;
}
}
}
#Override
void onResume() {
super.onResume();
// Start the timer, executing first event immediately.
Message newMsg = mHandler.obtainMessage(MSG_DO_IT);
mHandler.sendMessage(newMsg);
}
#Override
void onPause() {
super.onPause();
// Stop the timer.
mHandler.removeMessages(MSG_DO_IT);
}
Note that this implementation will have some drift -- the actual time between messages is TIME_BETWEEN_MESSAGES + (time to dispatch message). If you need something that won't drift, you can do the timing yourself by using Hander.sendMessageAtTime() and incrementing the new time with a constant value each time, starting with an initial time you get with SystemClock.uptimeMillis() in onResume().
There is a great example of a Timer built using AsyncTask and a Handler with the postDelayed method.
You are correct that updating the UI from a background is a no-no.
The code that follows comes from p.58-61 of the book "Android Developer's Cookbook". The book introduces the code in the context of messages being a way to pass information between threads. It describes the code by saying: "The timer is run in a background thread so it does not block the UI thread, but it needs to update the UI whenever the time changes."
I'm confused because I don't see two threads. To me it seems that the main UI thread posts a runnable message to its own message queue (and that message then re-posts itself with a time-delay). Am I missing something?
package com.cookbook.background_timer;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class BackgroundTimer extends Activity {
//keep track of button presses, a main thread task
private int buttonPress=0;
TextView mButtonLabel;
//counter of time since app started, a background task
private long mStartTime = 0L;
private TextView mTimeLabel;
//Handler to handle the message to the timer task
private Handler mHandler = new Handler();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if (mStartTime == 0L) {
mStartTime = SystemClock.uptimeMillis();
mHandler.removeCallbacks(mUpdateTimeTask);
mHandler.postDelayed(mUpdateTimeTask, 100);
}
mTimeLabel = (TextView) findViewById(R.id.text);
mButtonLabel = (TextView) findViewById(R.id.trigger);
Button startButton = (Button) findViewById(R.id.trigger);
startButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view){
mButtonLabel.setText("Pressed " + ++buttonPress + " times");
}
});
}
private Runnable mUpdateTimeTask = new Runnable() {
public void run() {
final long start = mStartTime;
long millis = SystemClock.uptimeMillis() - start;
int seconds = (int) (millis / 1000);
int minutes = seconds / 60;
seconds = seconds % 60;
mTimeLabel.setText("" + minutes + ":" + String.format("%02d",seconds));
mHandler.postDelayed(this, 200);
}
};
#Override
protected void onPause() {
mHandler.removeCallbacks(mUpdateTimeTask);
super.onPause();
}
#Override
protected void onResume() {
super.onResume();
mHandler.postDelayed(mUpdateTimeTask, 100);
}
}
The second thread is kind of hidden. It's when you call postDelayed(mUpdateTImeTask,100) in onCreate(). The handler has a thread in it that counts down the delay time (100 milliseconds in this case) and then runs mUpdateTImeTask. Note that at the end of the run() method of mUpdateTimeTask, it puts itself back in the handler's timer thread by calling postDelayed() again.
The Android api has lots of classes like Handler and AsyncTask that make it easier to do multithreading. These classes hide a lot of the nuts and bolts of threading (which is what makes them nice to use). Unfortunately, that makes it hard to learn what's going on--you sort of have to know what's going on in order to learn it. :)
The Runnable class is essentially a class used in threading. The run() method will be invoked by the interface that calls it (the Handler) and - in this implementation - the application sets up the Handler to run mUpdateTimeTask 100ms after that line is executed. Which will then run everything in the run() method in your Runnable.
When onCreate() is called, your application gets the mTimeLabel object from the view and it is updated with the setText() method in your Runnable. That will update the time on your UI thread and then register itself to go off in another 200 milliseconds.
There is no second thread here! You can test easily by putting some expensive code in the runnable, which will block the UI thread. You have to make a new Thread(Runnable) and go from there.
This is something you need almost in every project. I had to add a Timer class in my open-source Aniqroid library which is get triggered in the UI thread and utilizes the Handler.postDelayed() feature without having to write all the boiler-plate code.
http://aniqroid.sileria.com/doc/api/ (Look for downloads at the bottom or use google code project to see more download options: http://code.google.com/p/aniqroid/downloads/list)
The class documentation is here: http://aniqroid.sileria.com/doc/api/com/sileria/android/Timer.html