I've built a simple music player in Android. The view for each song contains a SeekBar, implemented like this:
public class Song extends Activity implements OnClickListener,Runnable {
private SeekBar progress;
private MediaPlayer mp;
// ...
private ServiceConnection onService = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder rawBinder) {
appService = ((MPService.LocalBinder)rawBinder).getService(); // service that handles the MediaPlayer
progress.setVisibility(SeekBar.VISIBLE);
progress.setProgress(0);
mp = appService.getMP();
appService.playSong(title);
progress.setMax(mp.getDuration());
new Thread(Song.this).start();
}
public void onServiceDisconnected(ComponentName classname) {
appService = null;
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.song);
// ...
progress = (SeekBar) findViewById(R.id.progress);
// ...
}
public void run() {
int pos = 0;
int total = mp.getDuration();
while (mp != null && pos<total) {
try {
Thread.sleep(1000);
pos = appService.getSongPosition();
} catch (InterruptedException e) {
return;
} catch (Exception e) {
return;
}
progress.setProgress(pos);
}
}
This works fine. Now I want a timer counting the seconds/minutes of the progress of the song. So I put a TextView in the layout, get it with findViewById() in onCreate(), and put this in run() after progress.setProgress(pos):
String time = String.format("%d:%d",
TimeUnit.MILLISECONDS.toMinutes(pos),
TimeUnit.MILLISECONDS.toSeconds(pos),
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(
pos))
);
currentTime.setText(time); // currentTime = (TextView) findViewById(R.id.current_time);
But that last line gives me the exception:
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Yet I'm doing basically the same thing here as I'm doing with the SeekBar - creating the view in onCreate, then touching it in run() - and it doesn't give me this complaint.
You have to move the portion of the background task that updates the UI onto the main thread. There is a simple piece of code for this:
runOnUiThread(new Runnable() {
#Override
public void run() {
// Stuff that updates the UI
}
});
Documentation for Activity.runOnUiThread.
Just nest this inside the method that is running in the background, and then copy paste the code that implements any updates in the middle of the block. Include only the smallest amount of code possible, otherwise you start to defeat the purpose of the background thread.
I solved this by putting runOnUiThread( new Runnable(){ .. inside run():
thread = new Thread(){
#Override
public void run() {
try {
synchronized (this) {
wait(5000);
runOnUiThread(new Runnable() {
#Override
public void run() {
dbloadingInfo.setVisibility(View.VISIBLE);
bar.setVisibility(View.INVISIBLE);
loadingText.setVisibility(View.INVISIBLE);
}
});
}
} catch (InterruptedException e) {
e.printStackTrace();
}
Intent mainActivity = new Intent(getApplicationContext(),MainActivity.class);
startActivity(mainActivity);
};
};
thread.start();
My solution to this:
private void setText(final TextView text,final String value){
runOnUiThread(new Runnable() {
#Override
public void run() {
text.setText(value);
}
});
}
Call this method on a background thread.
Kotlin coroutines can make your code more concise and readable like this:
MainScope().launch {
withContext(Dispatchers.Default) {
//TODO("Background processing...")
}
TODO("Update UI here!")
}
Or vice versa:
GlobalScope.launch {
//TODO("Background processing...")
withContext(Dispatchers.Main) {
// TODO("Update UI here!")
}
TODO("Continue background processing...")
}
Usually, any action involving the user interface must be done in the main or UI thread, that is the one in which onCreate() and event handling are executed. One way to be sure of that is using runOnUiThread(), another is using Handlers.
ProgressBar.setProgress() has a mechanism for which it will always execute on the main thread, so that's why it worked.
See Painless Threading.
You can use Handler to Delete View without disturbing the main UI Thread.
Here is example code
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
//do stuff like remove view etc
adapter.remove(selecteditem);
}
});
Kotlin Answer
We have to use UI Thread for the job with true way. We can use UI Thread in Kotlin, such as:
runOnUiThread(Runnable {
//TODO: Your job is here..!
})
I've been in this situation, but I found a solution with the Handler Object.
In my case, I want to update a ProgressDialog with the observer pattern.
My view implements observer and overrides the update method.
So, my main thread create the view and another thread call the update method that update the ProgressDialop and....:
Only the original thread that created a view hierarchy can touch its
views.
It's possible to solve the problem with the Handler Object.
Below, different parts of my code:
public class ViewExecution extends Activity implements Observer{
static final int PROGRESS_DIALOG = 0;
ProgressDialog progressDialog;
int currentNumber;
public void onCreate(Bundle savedInstanceState) {
currentNumber = 0;
final Button launchPolicyButton = ((Button) this.findViewById(R.id.launchButton));
launchPolicyButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
showDialog(PROGRESS_DIALOG);
}
});
}
#Override
protected Dialog onCreateDialog(int id) {
switch(id) {
case PROGRESS_DIALOG:
progressDialog = new ProgressDialog(this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage("Loading");
progressDialog.setCancelable(true);
return progressDialog;
default:
return null;
}
}
#Override
protected void onPrepareDialog(int id, Dialog dialog) {
switch(id) {
case PROGRESS_DIALOG:
progressDialog.setProgress(0);
}
}
// Define the Handler that receives messages from the thread and update the progress
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
int current = msg.arg1;
progressDialog.setProgress(current);
if (current >= 100){
removeDialog (PROGRESS_DIALOG);
}
}
};
// The method called by the observer (the second thread)
#Override
public void update(Observable obs, Object arg1) {
Message msg = handler.obtainMessage();
msg.arg1 = ++currentPluginNumber;
handler.sendMessage(msg);
}
}
This explanation can be found on this page, and you must read the "Example ProgressDialog with a second thread".
I see that you have accepted #providence's answer. Just in case, you can also use the handler too! First, do the int fields.
private static final int SHOW_LOG = 1;
private static final int HIDE_LOG = 0;
Next, make a handler instance as a field.
//TODO __________[ Handler ]__________
#SuppressLint("HandlerLeak")
protected Handler handler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
// Put code here...
// Set a switch statement to toggle it on or off.
switch(msg.what)
{
case SHOW_LOG:
{
ads.setVisibility(View.VISIBLE);
break;
}
case HIDE_LOG:
{
ads.setVisibility(View.GONE);
break;
}
}
}
};
Make a method.
//TODO __________[ Callbacks ]__________
#Override
public void showHandler(boolean show)
{
handler.sendEmptyMessage(show ? SHOW_LOG : HIDE_LOG);
}
Finally, put this at onCreate() method.
showHandler(true);
Use this code, and no need to runOnUiThread function:
private Handler handler;
private Runnable handlerTask;
void StartTimer(){
handler = new Handler();
handlerTask = new Runnable()
{
#Override
public void run() {
// do something
textView.setText("some text");
handler.postDelayed(handlerTask, 1000);
}
};
handlerTask.run();
}
I had a similar issue, and my solution is ugly, but it works:
void showCode() {
hideRegisterMessage(); // Hides view
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
showRegisterMessage(); // Shows view
}
}, 3000); // After 3 seconds
}
I was facing a similar problem and none of the methods mentioned above worked for me. In the end, this did the trick for me:
Device.BeginInvokeOnMainThread(() =>
{
myMethod();
});
I found this gem here.
I use Handler with Looper.getMainLooper(). It worked fine for me.
Handler handler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
// Any UI task, example
textView.setText("your text");
}
};
handler.sendEmptyMessage(1);
This is explicitly throwing an error. It says whichever thread created a view, only that can touch its views. It is because the created view is inside that thread's space. The view creation (GUI) happens in the UI (main) thread. So, you always use the UI thread to access those methods.
In the above picture, the progress variable is inside the space of the UI thread. So, only the UI thread can access this variable. Here, you're accessing progress via new Thread(), and that's why you got an error.
For a one-liner version of the runOnUiThread() approach, you can use a lambda function, i.e.:
runOnUiThread(() -> doStuff(Object, myValue));
where doStuff() can represents some method used to modify the value of some UI Object (setting text, changing colors, etc.).
I find this to be much neater when trying to update several UI objects without the need for a 6 line Runnable definition at each as mentioned in the most upvoted answer, which is by no means incorrect, it just takes up a lot more space and I find to be less readable.
So this:
runOnUiThread(new Runnable() {
#Override
public void run() {
doStuff(myTextView, "myNewText");
}
});
can become this:
runOnUiThread(() -> doStuff(myTextView, "myNewText"));
where the definition of doStuff lies elsewhere.
Or if you don't need to be so generalizable, and just need to set the text of a TextView object:
runOnUiThread(() -> myTextView.setText("myNewText"));
For anyone using fragment:
(context as Activity).runOnUiThread {
//TODO
}
This happened to my when I called for an UI change from a doInBackground from Asynctask instead of using onPostExecute.
Dealing with the UI in onPostExecute solved my problem.
I was working with a class that did not contain a reference to the context. So it was not possible for me to use runOnUIThread(); I used view.post(); and it was solved.
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
final int currentPosition = mediaPlayer.getCurrentPosition();
audioMessage.seekBar.setProgress(currentPosition / 1000);
audioMessage.tvPlayDuration.post(new Runnable() {
#Override
public void run() {
audioMessage.tvPlayDuration.setText(ChatDateTimeFormatter.getDuration(currentPosition));
}
});
}
}, 0, 1000);
When using AsyncTask Update the UI in onPostExecute method
#Override
protected void onPostExecute(String s) {
// Update UI here
}
This is the stack trace of mentioned exception
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6149)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:843)
at android.view.View.requestLayout(View.java:16474)
at android.view.View.requestLayout(View.java:16474)
at android.view.View.requestLayout(View.java:16474)
at android.view.View.requestLayout(View.java:16474)
at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352)
at android.view.View.requestLayout(View.java:16474)
at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352)
at android.view.View.setFlags(View.java:8938)
at android.view.View.setVisibility(View.java:6066)
So if you go and dig then you come to know
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
Where mThread is initialize in constructor like below
mThread = Thread.currentThread();
All I mean to say that when we created particular view we created it on UI Thread and later try to modifying in a Worker Thread.
We can verify it via below code snippet
Thread.currentThread().getName()
when we inflate layout and later where you are getting exception.
If you do not want to use runOnUiThread API, you can in fact implement AsynTask for the operations that takes some seconds to complete. But in that case, also after processing your work in doinBackground(), you need to return the finished view in onPostExecute(). The Android implementation allows only main UI thread to interact with views.
If you simply want to invalidate (call repaint/redraw function) from your non UI Thread, use postInvalidate()
myView.postInvalidate();
This will post an invalidate request on the UI-thread.
For more information : what-does-postinvalidate-do
Well, You can do it like this.
https://developer.android.com/reference/android/view/View#post(java.lang.Runnable)
A simple approach
currentTime.post(new Runnable(){
#Override
public void run() {
currentTime.setText(time);
}
}
it also provides delay
https://developer.android.com/reference/android/view/View#postDelayed(java.lang.Runnable,%20long)
For me the issue was that I was calling onProgressUpdate() explicitly from my code. This shouldn't be done. I called publishProgress() instead and that resolved the error.
In my case,
I have EditText in Adaptor, and it's already in the UI thread. However, when this Activity loads, it's crashes with this error.
My solution is I need to remove <requestFocus /> out from EditText in XML.
For the people struggling in Kotlin, it works like this:
lateinit var runnable: Runnable //global variable
runOnUiThread { //Lambda
runnable = Runnable {
//do something here
runDelayedHandler(5000)
}
}
runnable.run()
//you need to keep the handler outside the runnable body to work in kotlin
fun runDelayedHandler(timeToWait: Long) {
//Keep it running
val handler = Handler()
handler.postDelayed(runnable, timeToWait)
}
If you couldn't find a UIThread you can use this way .
yourcurrentcontext mean, you need to parse Current Context
new Thread(new Runnable() {
public void run() {
while (true) {
(Activity) yourcurrentcontext).runOnUiThread(new Runnable() {
public void run() {
Log.d("Thread Log","I am from UI Thread");
}
});
try {
Thread.sleep(1000);
} catch (Exception ex) {
}
}
}
}).start();
In Kotlin simply put your code in runOnUiThread activity method
runOnUiThread{
// write your code here, for example
val task = Runnable {
Handler().postDelayed({
var smzHtcList = mDb?.smzHtcReferralDao()?.getAll()
tv_showSmzHtcList.text = smzHtcList.toString()
}, 10)
}
mDbWorkerThread.postTask(task)
}
If you are within a fragment, then you also need to get the activity object as runOnUIThread is a method on the activity.
An example in Kotlin with some surrounding context to make it clearer - this example is navigating from a camera fragment to a gallery fragment:
// Setup image capture listener which is triggered after photo has been taken
imageCapture.takePicture(
outputOptions, cameraExecutor, object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val savedUri = output.savedUri ?: Uri.fromFile(photoFile)
Log.d(TAG, "Photo capture succeeded: $savedUri")
//Do whatever work you do when image is saved
//Now ask navigator to move to new tab - as this
//updates UI do on the UI thread
activity?.runOnUiThread( {
Navigation.findNavController(
requireActivity(), R.id.fragment_container
).navigate(CameraFragmentDirections
.actionCameraToGallery(outputDirectory.absolutePath))
})
Solved : Just put this method in doInBackround Class... and pass the message
public void setProgressText(final String progressText){
Handler handler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
// Any UI task, example
progressDialog.setMessage(progressText);
}
};
handler.sendEmptyMessage(1);
}
I have an asynctask named myAsync that performs some network operations (fetching data from server, and parsing the json).
I also have a handler created once the activity runs.
I also have a runnable in which i run the asynctask. The reason I am using runnable is because I will be using it inside a Handler's postdelayed method as I want this to be repeated every 1 minute.
Runnable runnable = new Runnable()
{
public void run()
{
new myAsync ().execute();
}
};
Then I am using the above runnable inside my onResume;
#Override
protected void onResume()
{
super.onResume();
handler.postDelayed(runnable, 60000);
}
Whenever I leave the activity, I want the check to stop, so I am calling,
handler.removeCallbacks(runnable);
However, the asynctask keeps on running non stop.
What shall I do ?
The whole point of asynctask is to run a thread on the main thread.
So it does not make sense to run it in Runnable()
What you can do is skip the Runnable and Handler...definitely not needed here. Assuming the AsyncTask is an inner class of your Activity, you can set a member boolean variable and check that in your doInBackground()
public Void doInBackground(Void...params)
{
// this is a boolean variable, declared as an
//Activity member variable, that you set to true when starting the task
while (flag)
{
// run your code
Thread.sleep(60000);
}
return null; // here you can return control to onPostExecute()
// if you need to do anything there
}
This will make the AsyncTask sleep for a minute before running the code again. Then in onPause() or wherever you want you set the flag to false. If you need to update the UI then call publishProgress() inside your loop and put the UI code in onProgressUpdate()
You can remove the AsyncTask and just do the proccess with Runnable in this way you can make the repetitions that you need. If this does not work you can set a flag to stop the proccess like said codeMagic.
runable = new Runnable() {
public void run() {
try {
//Proccess
while (flag)
{
//Proccess
handler.postDelayed(this, 3000);
}
}catch(Exception e)
{
Log.i("Log","Error: "+e);
}
};
handler.postDelayed(runable, 3000);
#Override
public void onPause() {
super.onPause();
flag=false;
handler.removeCallbacks(runnable);
}
#Override
public void onResume() {
super.onResume();
flag=true;
handler.postDelayed(runable, 3000);
}
I hope this help.
I have a problem with unit tests in Android.
My object MyObject has a method start() like this :
public void start() {
final Handler onStartHandler = new Handler();
new Thread() {
#Override
public void run() {
super.run();
onStartHandler.post(new Runnable() {
#Override
public void run() {
mIsRunning = true;
onStart();
}
});
}
}.start();
}
And I want to test that onStart() is called.
So I tried something like that :
public void testOnStartIsCalled() {
assertFalse("onStart() should not be called", mMyObject.isRunning());
mMyObject.start();
assertTrue("onStart() should be called", mMyObject.isRunning());
mMyObject.stop();
assertFalse("onStop() should be called", mMyObject.isRunning());
}
But it doesn't work, I guess it's because it's in a Handler and a new Thread.
My test class extends AndroidTestCase.
What should I do ? What is the best practice for this case ?
Regards.
When I deal with testing some multi-threaded code I try to let the program take as much of its natural flow as possible. Additionally, I avoid the use of sleep statements since you don't get any guarantees that the sleep interval you've chosen is enough to allow the subject of your test to finish what it's doing; you often end up having to choose sleep intervals that are too large and it forces a much slower execution of your test cases.
I would recommend that you try to add some code into the class you're testing, in this case MyObject, which call a listener whenever something happens. It seems that you already have callback methods for onStart() and onStop()(if those are events/callbacks), so those should be getting invoked and you should use them to control the flow of your test. When you get an onStart() event, you should then call stop() and wait for an onStop() event.
Update
First and foremost, you have redundant code:
public void start() {
final Handler onStartHandler = new Handler();
new Thread() {
#Override
public void run() {
super.run();
onStartHandler.post(new Runnable() {
#Override
public void run() {
mIsRunning = true;
onStart();
}
});
}
}.start();
}
Either start a new thread to call onStart() or schedule the runnable on the Handler's thread queue.
Version 1- remove the handler and just let the code be executed in a new thread:
public void start() {
new Thread() {
#Override
public void run() {
super.run();
mIsRunning = true;
onStart();
}
}.start();
}
Version 2- only use the handler to asynchronously execute the callback:
public void start() {
final Handler onStartHandler = new Handler();
onStartHandler.post(new Runnable() {
#Override
public void run() {
mIsRunning = true;
onStart();
}
});
}
And second: I noticed is that if you don't have a Looper, then whatever you post with the Handler will be ignored (thus it will never be called). For more information on the Looper-Handler pattern see the article: Android Guts: Intro to Loopers and Handlers. The Looper and the Handler are supposed to be attached to the same thread (usually the main thread). Additionally, if you're creating the Handler on a separate thread as your Looper, then you'll run into the same problem: anything you post with the Handler will be ignored.
Here are a few more good questions and articles on loopers and handlers:
Just do IT: looper and handler in android
Handler-Looper implementation in Android
The relationships between Looper, Handler and MessageQueue is shown below:
The problem here is that you are calling onStart() which invokes a new thread, and then immediately ask if it is started. There is startup time for the new thread and while that is happening, your test is asking if it is started -- it's not YET.
I bet if you waited by using Thread.sleep(), or a loop, you'd find it is started "eventually".
What is it you're actually trying to test?
If you need the new thread, you might want to read up on threads, synchronize, etc.
http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html
I have a Service that launches a Thread and a Runnable like so.
t = new Thread(new Runnable() {
public void run() {
doSomething();
}
});
t.start();
The reason for the thread is to perform an Async task doSomething(). For now lets not worry about the other class AsyncTask. I have tried it and it does not work for my case. Edit: I can't use AsyncTask because it is meant for the UI thread only. This piece of code has to operate inside a Service, so nope, no AsyncTask :(
doSomething() contains some external libs so the issue I am having is that it can potentially be hung at one of the commands, without return any value (hence no error checking can even be done)
To work around this, I will want to, at some point of time, destroy the Service.
stopService(new Intent("net.MyService.intent));
This works fine and is easily verified on the phone. However, the Thread which was created above will continue to run even when the Service that spawned it is destroyed.
I am thus looking for the correct commands to insert in the Service's onDestroy() which will clean up the Thread for me.
t.destroy();
t.stop();
are both depreciated and cause application crashes.
I took this code from somewhere
#Override
public void onDestroy() {
Thread th = t;
t = null;
th.interrupt();
super.onDestroy();
}
but it still does not work, the thread continues to run. Any help guys?
The thread destroy and stop methods are inherently deadlock prone and not safe. Their existence also gives the illusion that there might be some way of halting another thread immediately when something else tells it to.
I understand your thinking, from your point of view their is one main thread, and when this thread hasn't received a response from it's worker thread in a while you'd like to kill it and restart it, without caring what it's up to. But the reason those methods are deprecated is you should care what the thread is up to. A lot.
What if the thread has a lock around a variable you need to use later? What if a thread has a file handle open? In all these cases, and many more, simply stopping the thread at it's current operation would leave things in mess -- quite likely your application would just crash further down the line.
So in order for a thread to be interruptible or cancel-able or stoppable, it has to manage this itself. If a thread or operation provides no way for itself to be interrupted, then you cannot interrupt it - it is assumed to do so would be unsafe.
If you runnable is literally
public void run() {
doSomething();
}
then there is no way to interrupt it. One would hope that if doSomething were a long operation that there might be a way to either interact with it incrementally with something like
public void run() {
while (running) {
MyParser.parseNext();
}
}
or to be able to pass in a variable by reference which indicates whether the thread is interrupted or not, and hopefully the method would interrupt itself at suitable location.
Remember a blocking operation is blocking. There is no way to get around that, you cannot cancel it part way through.
Alternative answer
Use the following code:
MyThread thread; // class field
Create and start the thread as you do it right now.
thread = new MyThread();
thread.start();
When the service is destroyed, "signal" the thread to quit
public void onDestroy() {
// Stop the thread
thread.abort = true;
thread.interrupt();
}
Here is thread implementation
//another class or maybe an inner class
class MyThread extends Thread {
syncronized boolean abort = false;
//ugly, I know
public void run() {
try {
if(!abort) doA();
if(!abort) doB();
if(!abort) doC();
if(!abort) doD();
} catch (InterruptedException ex) {
Log.w("tag", "Interrupted!");
}
}
}
You might want to read the following:
How do you kill a thread in Java?
Thread Primitive Deprecation as already pointed by Claszen
http://www.devx.com/tips/Tip/31728 - based my code from here, but there are some issues with the code!
I think that you could rely on catching the exception and not check abort but I decided to keep it that way.
UPDATE
I've seen this sample in codeguru:
public class Worker implements Runnable {
private String result;
public run() {
result = blockingMethodCall();
}
public String getResult() {
return result;
}
}
public class MainProgram {
public void mainMethod() {
...
Worker worker = new Worker();
Thread thread = new Thread(worker);
thread.start();
// Returns when finished executing, or after maximum TIME_OUT time
thread.join(TIME_OUT);
if (thread.isAlive()) {
// If the thread is still alive, it's still blocking on the methodcall, try stopping it
thread.interrupt();
return null;
} else {
// The thread is finished, get the result
return worker.getResult();
}
}
}
Did you check the Java Thread Primitive Deprecation Documentation which is referenced in the Thread API JavaDoc. You will find some hints to handle your problem.
why don't you use an AsyncTask?
A task can be cancelled at any time by
invoking cancel(boolean). Invoking
this method will cause subsequent
calls to isCancelled() to return true.
After invoking this method,
onCancelled(Object), instead of
onPostExecute(Object) will be invoked
after doInBackground(Object[])
returns. To ensure that a task is
cancelled as quickly as possible, you
should always check the return value
of isCancelled() periodically from
doInBackground(Object[]), if possible
(inside a loop for instance.)
I like to take the following approach:
class MyHandler extends Handler {
final Semaphore stopEvent = new Semaphore(0);
#Override
public void handleMessage(Message msg) {
try {
while (!stopEvent.tryAcquire(0, TimeUnit.SECONDS)) {
doSomething();
if (stopEvent.tryAcquire(SLEEP_TIME, TimeUnit.MILLISECONDS)) {
break;
}
}
} catch (InterruptedException ignored) {
}
stopSelf();
}
}
On service onDestroy just release the stopEvent:
#Override
public void onDestroy() {
myHandler.stopEvent.release();
myHandler = null;
super.onDestroy();
}
Better to use global variable stopThread, stop thread once variable changed to true.
btnStop.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0){
stopThread = true;
}
});
public void run() {
while (!stopThread) {
//do something
}
}
I think the best way to create and communicate with another thread is to use an AsyncTask. Heres an example of one:
public class Task extends AsyncTask<Void, Void, Void> {
private static final String TAG = "Task";
private boolean mPaused;
private Runnable mRunnable;
public Task(Runnable runnable) {
mRunnable = runnable;
play();
}
#Override
protected Void doInBackground(Void... params) {
while (!isCancelled()) {
if (!mPaused) {
mRunnable.run();
sleep();
}
}
return null;
}
private void sleep() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Log.w(TAG, e.getMessage());
}
}
public void play() {
mPaused = false;
}
public void pause() {
mPaused = true;
}
public void stop() {
pause();
cancel(true);
}
public boolean isPaused() {
return mPaused;
}
}
You can now easily use this class, and start the thread by writing:
Task task = new Task(myRunnable);
task.execute((Void) null);
Along with this you can easily pause or stop the thread from looping:
Example of pausing and playing the thread:
mButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (task.isPaused()) {
task.play();
} else {
task.pause();
}
}
});
Example of stopping and starting the thread:
mButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (task.isCancelled()) {
task = new Task(myRunnable);
task.execute((Void) null);
} else {
task.stop();
}
}
});