I have a handler that I am using as follows:
handler.postDelayed(Play, 1000);
when my application onPause() is called before this is done, I need to pause it and tell it not to perform the "postDelayed" until I resume.
is this possible, or is there an alternative way?
My problem is that when onPause() is called I pause the audio (SoundManager), but if this handler.postDelayed is called after that, the audio will not be paused and will continue to play with my application in the background.
#Override
public void onPause()
{
Soundmanager.autoPause()
}
but then the postDelayed after 1000ms starts the audio playing again.
You need to subclass Handler and implement pause/resume methods as follows (then just call handler.pause() when you want to pause message handling, and call handler.resume() when you want to restart it):
class MyHandler extends Handler {
Stack<Message> s = new Stack<Message>();
boolean is_paused = false;
public synchronized void pause() {
is_paused = true;
}
public synchronized void resume() {
is_paused = false;
while (!s.empty()) {
sendMessageAtFrontOfQueue(s.pop());
}
}
#Override
public void handleMessage(Message msg) {
if (is_paused) {
s.push(Message.obtain(msg));
return;
}else{
super.handleMessage(msg);
// otherwise handle message as normal
// ...
}
}
//...
}
Have you tried with:
#Override
public void onPause()
{
handler.removeCallbacks(Play);
Soundmanager.autoPause()
}
Ger
Modifying the answer given by CpcCrunch. There handleMessage not worked for me, so instead of it using dispatchMessage. Note: Below code is written in Kotlin:
class CustomHandler: Handler() {
var s = Stack<Message>()
var is_paused = false
#Synchronized
fun pause() {
is_paused = true
}
#Synchronized
fun resume() {
is_paused = false
while (!s.empty()) {
sendMessageAtFrontOfQueue(s.pop())
}
}
override fun dispatchMessage(msg: Message?) {
if (is_paused) {
s.push(Message.obtain(msg))
return
} else {
super.dispatchMessage(msg)
}
}
}
public class YourActivity extends AppCompatActivity {
private static boolean handlerflag=false;
private Handler handler;
private Runnable runnable;
private int myind=0,index=0,count=0;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_activtiy);
//oncreate exe only
handlerflag=true;
handler = new Handler();
startyourtime(0);
}
private void startyourtime(int a) {
myind=0;
for (index=a; index<10 ;index++) {
myind++;
runnable=new Runnable() {
count++;
#Override
public void run() {
//your code here
}
};handler.postDelayed(runnable, Constants.TIME_LIMIT * myind);
}
#Override
protected void onPause() {
super.onPause();
handlerflag=false;
handler.removeCallbacksAndMessages(null);
}
#Override
protected void onResume() {
super.onResume();
if(!handlerflag)
{
startyourtime(count);
}
}
}
I came up with an alternative to CpnCrunch when wanting to pause/resume Runnables in a queue. To have methods that has been called whilst still connecting and is offline, once online, resume the queue and all runnables are executed.
Instead of using Handler, use ExecutorService:
public class ExecutorQueueService extends ThreadPoolExecutor {
private Stack<Runnable> runnables = new Stack<>();
private boolean paused = false;
public ExecutorQueueService() {
super(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
public synchronized void pause() {
paused = true;
}
public synchronized void resume() {
paused = false;
while (!runnables.empty()) {
execute(runnables.pop());
}
}
public synchronized boolean isPaused() {
return paused;
}
#Override
public void execute(Runnable runnable) {
if (paused) {
runnables.push(runnable);
} else {
super.execute(runnable);
}
}
}
Using it is similar to Handler, but instead of post(runnable), use execute(runnable)
Related
I'm trying to implement a callback method to be called whenever a Thread is is done It's work.
I'm using the interface approach and not the Handler approach.
I have a main UI Thread which is the onCreate(Bundle) method and a Thread i call from within the onCreate(Bundle) method.
(Only relevant code posted).
MainActivity.java:
public class MainActivity extends AppCompatActivity implements GetDataFromTheWebThreadCallback
{
public static GetDataFromTheWebThread getDataFromTheWebThread;
private GetDataFromTheWebEventNotifier eventNotifier;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.eventNotifier = new GetDataFromTheWebEventNotifier(MainActivity.this);
// The thread that will search the web for data
this.getDataFromTheWebThread = new GetDataFromTheWebThread();
getDataFromTheWebThread.start();
}
#Override
public void finishParsing() // The callback method that never called
{
Toast.makeText(MainActivity.this,"Callback Method Called",Toast.LENGTH_LONG).show();
Log.d("Callback:", "Callback Method Called");
}
}
GetDataFromTheWebEventNotifier.java:
public class GetDataFromTheWebEventNotifier
{
private GetDataFromTheWebThreadCallback callbackInterface;
public GetDataFromTheWebEventNotifier(GetDataFromTheWebThreadCallback callbackInterface)
{
this.callbackInterface = callbackInterface;
}
public void onEvent()
{
this.callbackInterface.finishParsing();
}
}
GetDataFromTheWebThreadCallback.java:
public interface GetDataFromTheWebThreadCallback
{
void finishParsing(); // The method i wish to invoke when certain event will happen
}
GetDataFromTheWebThread.java:
public class GetDataFromTheWebThread extends Thread
{
public static boolean isFinished = false; // False - the thread is still running. True - the thread is dead
#Override
public void run()
{
GetDataFromTheWebThread.isFinished = false;
try
{
// Some internet computations...
Thread.sleep(100);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
GetDataFromTheWebThread.isFinished = true;
}
}
So what's wrong with my callback?
As for your ThreadClass, have a constructor with the callback :
public class GetDataFromTheWebThread extends Thread {
public static boolean isFinished = false; // False - the thread is still running. True - the thread is dead
private GetDataFromTheWebThreadCallback mCallback;
public GetDataFromTheWebThread(GetDataFromTheWebThreadCallback c) {
mCallback = c;
}
#Override
public void run() {
GetDataFromTheWebThread.isFinished = false;
try {
// Some internet computations...
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
GetDataFromTheWebThread.isFinished = true;
if (mCallback !- null) {
mCallback.finishParsing();
}
}
}
As for your Activity, simply pass the callback when creating your Thread :
this.getDataFromTheWebThread = new GetDataFromTheWebThread(this);
As well as :
#Override
public void finishParsing() {
// You know that this function is called from a background Thread.
// Therefore from here, run what you have to do on the UI Thread
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this,"Callback Method Called",Toast.LENGTH_LONG).show();
Log.d("Callback:", "Callback Method Called");
}});
}
You never call onEvent(). Is your notifier supposed to be watching the isFinished variable or something?
Actually you didn't call onEvent(). And check AsyncTask.
My Timer doesn't stop running if I cancel it!
The Timer only stops if I shut down the whole app!
I don't know why the Timer is not cancelled. If I print out every try on cancelling the Timer I get hundrets of lines but the Timer does not stop!
My Class:
public class PlayActivity extends AppCompatActivity
implements View.OnClickListener, SeekBar.OnSeekBarChangeListener, MediaplayerEvent {
//region Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Initialize_Layout();
Initialize_Objects();
}
#Override
protected void onResume() {
super.onResume();
MusicService.setMediaPlayerEvent(this);
txvSongtitle.setText(serviceInterface.MP_getActualSong().getTitle());
Start_Timer();
}
#Override
protected void onPause() {
timer.cancel();
MusicService.clearMediaPlayerEvent();
super.onPause();
}
#Override
public boolean onSupportNavigateUp() {
finish();
return super.onSupportNavigateUp();
}
//endregion
//region Methods
private void Start_Timer() {
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
if (serviceInterface.MP_isPlaying()) {
runOnUiThread(new Runnable() {
#Override
public void run() {
seekBar.setMax(serviceInterface.MP_getDuration());
seekBar.setProgress(serviceInterface.MP_getCurrentPosition());
}
});
}
else {
runOnUiThread(new Runnable() {
#Override
public void run() {
timer.cancel();
}
});
}
}
}, 0, 200);
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
serviceInterface.MP_seekTo(progress);
Start_Timer();
}
}
//endregion
}
I hope you can help me!
Thanks!
I would suggest using a Thread instead of a Timer. Your Start_Timer()code would change to something like the following:
private Thread mTimerThread;
...
private void Start_Timer() {
mTimerThread = new Thread() {
#Override
public void run() {
try {
while (!isInterrupted()) {
if (serviceInterface.MP_isPlaying()) {
runOnUiThread(new Runnable() {
#Override
public void run() {
seekBar.setMax(serviceInterface.MP_getDuration());
seekBar.setProgress(serviceInterface.MP_getCurrentPosition());
}
});
} else {
interrupt();
}
Thread.sleep(200);
}
} catch (InterruptedException e) {
}
}
}
mTimerThread.start();
}
Threads are more efficient and lightweight and perfect for your needs. Plus, by setting the Thread to a global variable, you can make sure to call mTimerThread.interrupt(); during Android lifecycle events, such as onPause().
I hope this fixes your issue. Remember, the Java Thread is your friend!
You're creating and starting a new timer the user moves the seekbar (in onProgressChanged()). That also means you lose the reference to the old one. When isPlaying turns false, all the timers will try to cancel timer -- which only references the most recent one.
EDIT: I've found that what I'm describing below only occurs on my emulated device (Nexus 5, target api 19, 4.4.2 with Intel Atom (x86) cpu), but NOT on my physical device (HTC One)....
EDIT2: Edit1 was due to an IllegalStateException that I didnt catch. Added some code to check if the thread was already running before trying to start it. This combined with the accepted answer resolved my issue.
I have implemented an activty that starts a new thread in the activity's onCreate method, like this:
...
private boolean running;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
running = true;
new Thread(null, work, "myThread").start();
}
Runnable work = new Runnable() {
#Override
public void run() {
while (running) {
//Doing work
}
}
};
I'm "pausing" my thread with my activity's onPause method, like this:
#Override
protected void onPause() {
running = false;
super.onPause();
}
So I thought that resuming it would be just as easy...ยจ
#Override
protected void onResume(){
running = true;
super.onResume();
}
but my thread isn't resuming. Any ideas why? Thankful for any help.
Marcus
All of the answers i think have some issues about your running variable because you can not write and read a variable from two different Threads without synchronized block so i post my own answer:
package com.example.threadandtoast;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity {
public class MonitorObject{
public boolean running = true;
public String message = "";
public boolean mustBePost = true;
}
Thread t;
int threadNameCounter = 0; // i use this variable to make sure that old thread is deleted
// when i pause, you can see it and track it in DDMS
Runnable work = new Runnable() {
boolean myRunning;
#Override
public void run() {
synchronized(mSync) {
myRunning = mSync.running;
}
while (myRunning) {
runOnUiThread(new Runnable() { // in order to update the UI (create Toast)
#Override // we must switch to main thread
public void run() {
// i want to read the message so i must use synchronized block
synchronized(mSync) {
// i use this variable to post a message just for one time because i am in an infinite loop
// if i do not set a limit on the toast i create it infinite times
if(mSync.mustBePost){
Toast.makeText(MainActivity.this, mSync.message, Toast.LENGTH_SHORT).show();
// the message post so i must set it to false
mSync.mustBePost = false;
// if i am going to pause set mSync.running to false so at the end of infinite loop
//of thread he reads it and leaves the loop
if(mSync.message.equals("Main Activity is going to pause")){
mSync.running=false;
}
}
}
}
});
synchronized(mSync) {
myRunning = mSync.running;
}
}
}
};
final MonitorObject mSync = new MonitorObject();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onPause() {
super.onPause();
synchronized(mSync) {
// mSync.running = false; you can not set it here because
// it is possible for the thread to read it and exit the loop before he posts your message
mSync.mustBePost=true;
mSync.message = "Main Activity is going to pause";
}
}
#Override
protected void onResume(){
super.onResume();
threadNameCounter++;
synchronized(mSync) {
mSync.running = true;
mSync.mustBePost=true;
mSync.message = "Main Activity is going to resume";
}
t = new Thread(work,"My Name is " + String.valueOf(threadNameCounter));
t.start();
}
}
Or you can use this code:
public class MainActivity extends ActionBarActivity {
Thread t;
int threadNameCounter = 0; // i use this variable to make sure that old thread is deleted
// when i pause, you can see it in DDMS
String message = "";
boolean isPost = false;
Runnable work = new Runnable() {
#Override
public void run() {
while (true) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if(!isPost){
Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
isPost = true;
if( message.equals("Main Activity is going to pause")){
t.interrupt();
}
}
}
});
if(Thread.currentThread().isInterrupted()){
break;
}
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onPause() {
super.onPause();
message = "Main Activity is going to pause";
isPost = false;
}
#Override
protected void onResume(){
super.onResume();
message = "Main Activity is going to resume";
isPost = false;
threadNameCounter++;
t = new Thread(work,"My Name is " + String.valueOf(threadNameCounter));
t.start();
}
}
you can also use semaphore or wait-notify approach.
i put public String message = ""; and public boolean mustBePost = true; in to mSync object but it is
not necessary because only main thread have an access to them.
if you have any problem please ask.
The statement running = false; will stop execution of the Thread, instead of pausing it. Use two variables: One for stopping current Thread, and another for pausing and resuming the Thread, as follow:
boolean isThreadPause=false;
Runnable work = new Runnable() {
#Override
public void run() {
while (running) {
if (!isThreadPause) {
// Doing work
}
}
}
};
In the onPause event of the Activity, set isThreadPause to true, and in the onResume event, set isThreadPause to false.
This is because your Runnable object stops when the while loop stops. You could try this:
Runnable work = new Runnable() {
#Override
public void run() {
while () {
if(running){
//Doing work
}
}
}
};
I have got a problem
When hitting the home button, my game thread class gets paused. But when re-entering the app and the onResume() method gets called, it should resume the game thread... everythin i get is a force close.
Here is my MainActivity:
public class MainActivity extends Activity {
private RelativeLayout relativeLayout;
private GameView gameView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
gameView = new GameView(this, this);
setContentView(R.layout.activity_main);
relativeLayout = (RelativeLayout)findViewById(R.id.mainView);
relativeLayout.addView(gameView);
}
#Override
protected void onPause() {
super.onPause();
gameView.pause();
}
#Override
protected void onResume() {
super.onResume();
if(gameView.getGameThread() != null) {
if(gameView.getGameThread().isRunning == false) {
gameView.resume();
}
}
}
}
gameView is following class, a surfaceView getting rendered by GameThread:
public class GameView extends SurfaceView{
private GameThread gameThread;
public GameView(Context context, Activity activity) {
super(context);
gameThread = new GameThread(this);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
gameThread.setRunning(true);
gameThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
});
}
public GameThread getGameThread() {
return gameThread;
}
public void update() {
}
public void pause() {
gameThread.pause();
}
public void resume() {
gameThread.resumeThread();
}
}
This is the GameThread class:
public class GameThread extends Thread{
private GameView view;
public boolean isRunning = false;
public GameThread(GameView view) {
this.view = view;
}
public void setRunning(boolean setRunning) {
isRunning = setRunning;
}
public void stopThread() {
isRunning = false;
}
public void run() {
while(isRunning) {
Canvas c = null;
view.update();
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.draw(c);
}
}finally {
if(c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
}
}
public void startThread() {
isRunning = true;
}
public void pause() {
isRunning = false;
}
public void resumeThread() {
isRunning = true;
}
}
I removed the irrelevant parts of the code.
When re-entering the app the android.view.Choreographer class opens
Any ideas?
Actually you're not pausing your thread (to be strict, there's no such thing as pausing a thread). You're ending your thread.
When you call pause() you set the isRunning flag to false, that makes the thread exit the loop and the thread ends. When you set your 'isRunning' to true again, the thread won't restart.
Actually you can't reuse threads in java, you have to create a new one, because once the thread loop ends, it's dead.
I have a Service which is performing a data update. I have an activity which attaches a listener to the service (via a local binding). The listener receives progress updates. Upon receiving a progress update, it schedules a runnable to be run on the UI thread. Here's the code (updated to show the full listing):
public class MyActivity extends Activity {
static final int UPDATE_DIALOG = 0;
ProgressDialog updateDialog;
private TaskService taskService;
private ServiceConnection taskServiceConnection = new ServiceConnection() {
private final TaskServiceObserver taskServiceObserver = new TaskServiceObserver() {
public void updateProgress(final int progress, final int total) {
runOnUiThread(new Runnable() {
public void run() {
if (updateDialog == null || !updateDialog.isShowing()) {
showDialog(UPDATE_DIALOG);
}
updateDialog.setProgress(progress);
}
});
}
public void updateCompleted() {
runOnUiThread(new Runnable() {
public void run() {
dismissDialog(UPDATE_DIALOG);
startNextActivity();
}
});
}
};
public void onServiceConnected(ComponentName name, IBinder binder) {
taskService = ((LocalBinder) binder).getService();
taskService.addObserver(taskServiceObserver);
}
public void onServiceDisconnected(ComponentName name) {
taskService.removeObserver(taskServiceObserver);
taskService = null;
}
};
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, TaskService.class);
startService(intent);
bindService(intent, taskServiceConnection, Context.BIND_AUTO_CREATE);
}
protected void onStop() {
super.onStop();
if (taskService != null) {
unbindService(taskServiceConnection);
}
}
protected Dialog onCreateDialog(int id) {
switch (id) {
case UPDATE_DIALOG:
updateDialog = new ProgressDialog(this);
updateDialog.setTitle("My App");
updateDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
updateDialog.setMessage("Preparing to run for the first time...");
return updateDialog;
default:
return null;
}
}
}
If I tap the home button while the dialog is showing, then return to the app, I get a crash on the showDialog line. With the debugger I was able to determine that the activity is in the finished state.
What would be an appropriate check to put in my runnable which would determine whether it is safe to call showDialog?
I would personnally dismiss the progress dialog when the activity goes to pause (override onPause) and recreate it if necessary when the activity is resumed (override onResume). You could be leaking memory by keeping references to your activity in other separate objects (your dialog)
You should detach the listener in the onPause method so that since your activity is going into the background, the listener won't fire and try to update the UI.
The solution I ended up going with was to create a flag taskServiceBound which was set to true after binding to the service in onStart and set to false before unbinding from the service in onStop. Because the flag is updated on the UI thread, I can use it to gate the Runnables in updateProgress and updateCompleted. For example:
public void updateCompleted() {
runOnUiThread(new Runnable() {
public void run() {
if (taskServiceBound) {
dismissDialog(UPDATE_DIALOG);
startNextActivity();
}
}
});
}