I've a following problem. I have MediaPlayer and I use Handler to send messages about MediaPlayer's progress during it plays. And I want to be able change Handler's callback in runtime. When i change the playing file in MediaPlayer i use the following code:
player.setDataSource(filename);
player.prepare();
final int duration = player.getDuration();
player.start();
// removing previous callback
durationHandler.removeCallbacksAndMessages(null);
// setting new callback
durationHandler.postDelayed(
new Runnable() {
#Override
public void run() {
int progress = player.getCurrentPosition();
demonstrator.setProgress(progress, duration);
durationHandler.postDelayed(this, REPEAT_TIME);
}
}, 400);
I expect the old callback will be replaced by new one. But it doesn't work! Both of callbacks are running! Why? And how can i avoid it?
Related
I have used a service which posts code to a handler at an interval of 3 seconds to keep generating a simple touch event (a button press).
This is the service code:
public class BackgroundTouchService extends IntentService
{
public BackgroundTouchService() {
super("BackgroundTouchService");
}
#Override
protected void onHandleIntent(Intent intent)
{
final MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
MotionEvent.ACTION_BUTTON_PRESS, 400, 400, 0);
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run()
{
View v = new View(getApplication());
v.onTouchEvent(event);
handler.postDelayed(this, 3000);
}
});
}
}
In my UI, I have covered the screen with buttons (with appropriate listeners) so that I can easily spot which button has been pressed. However, the main activity loads up and then nothing happens. Why is this?
EDIT:
As Vojtěch Sáze correctly pointed out, the handler in the above code is not associated with the main thread, and hence cannot be used to modify the UI by generating touch events. Hence, I wrote the code for the handler in the main activity itself:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startTouch();
}
private void startTouch()
{
final MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
MotionEvent.ACTION_BUTTON_PRESS,400.0f, 400.0f, 0);
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run()
{
onTouchEvent(event);
handler.postDelayed(this, 3000);
}
});
}
}
However, this still does not do anything. Any ideas?
SOLUTION:
Okay, so in the event type parameter of the MotionEvent.obtain method I had originally specified ACTION_BUTTON_PRESS, however, when I used to separate events to specify ACTION_DOWN and ACTION_UP separately, this seemed to work.
Any explanations as to why this happened are welcome.
In case someone needs this, here is the code:
final MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
MotionEvent.ACTION_DOWN,400.0f, 400.0f, 0);
final MotionEvent event2 = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
MotionEvent.ACTION_UP, 400.0f, 400.0f, 0);
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run()
{
dispatchTouchEvent(event);
dispatchTouchEvent(event2);
handler.postDelayed(this, 3000);
}
});
I think you need to use dispatchTouchEvent method to send the touch event to the view.
I'm not sure if this will help. But definitely there is problem with
final Handler handler = new Handler();
From documentation:
Default constructor associates this handler with the Looper for the current thread. If this thread does not have a looper, this handler won't be able to receive messages so an exception is thrown.
But you don't have main thread in onHandleIntent(). You'll need to create the Handler in the main thread.
Just use code below. Using Android Handler, you need also looper for posting action.
final Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run()
{
onTouchEvent(event);
handler.postDelayed(this, 3000);
}
});
I'm playing around with some basic android, and I'm trying to write a metronome app. The basic idea is that I'm using a runnable in order to trigger a sound after a time period (msPeriod). I've tried to use SoundPool, but it will just log 'sample not loaded', and trying to ititialise a MediaPlayer causes the app to crash on opening. Could you explain to me where I'm going wrong please? Below is my code with MediaPlayer.
//I create the media player first thing inside MainActivity
private Handler handler = new Handler();
int msPeriod = 1000;
MediaPlayer mpHigh = MediaPlayer.create(this, R.raw.hightick);
MediaPlayer mpLow = MediaPlayer.create(this, R.raw.lowtick);
//within an onClick Listener
onClick(View v) { handler.postDelayed(startMetron, msPeriod); }
//the runnable that starts the metronome
private Runnable startMetron = new Runnable( ) {
#Override
public void run() {
if(isRunning){
if (count == 4) {
count = 1;
mphigh.start();
} else {
count++;
mplow.start();
}
}
textCount.setText(String.valueOf(count));
//triggering the next run
handler.postDelayed(this, msPeriod);
}
};
Thanks so much for bearing with me!
You are running a separate thread . The UI element must be updated form the main thread... so..
runOnUiThread(new Runnable(){
public void run() {
textCount.setText(String.valueOf(count));
}
});
This makes sure that the textview is update from the UI thread and your app will not crash.
I'm fairly new to Android programming.
Simple Explanation for my problem:
I have an async task to collect JSON based data after every 20 seconds based on this runnable:
Handler handler = new Handler();
final Runnable r = new Runnable() {
public void run() {
mTask = new JSONParse();
mTask.execute();
handler.postDelayed(this, 20000);
}
};
How do I stop it when I want to?
Detailed Explanation for my Problem:
Within this Async Task, I check if the data is available, and if not available, I go back to the mainscreen by first invoking
mTask.cancel(true);
and then in the onCancelled() method as follows:
protected void onCancelled() {
// TODO Auto-generated method stub
super.onCancelled();
mTask.cancel(false);
pDialog.dismiss();
displayWrongPhoneToast();
}
where displayWrongPhoneToast() is a simple function as follows:
public void displayWrongPhoneToast() {
Toast.makeText(getApplicationContext(),
"Sorry! Enter at least one field to continue.",
Toast.LENGTH_SHORT).show();
Intent intent = new Intent(TabActivity.this, MainActivity.class);
startActivity(intent);
}
The problem I have is, the handler is causing the runnable to execute in the background, which is making the application request data over and over again and causing the displayWrongPhoneToast() to execute over and over again.
I tried some methods I found online to stop the runnable, but it refuses to. Any help is appreciated. Thanks :)
You have to call
handler.removeCallbacks(r).
I set up event listener, for example: setOnClickListener like this
Button stopBtn = (Button)findViewById(R.id.stop);
stopBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
doMagic();
}
});
I would like to set this listener a timeout event on 10s if button is not pressed. Use case: i have button1 that activates this stopBtn listener for 10s and if timeout comes it becomes deactivated and i need to press button1 to make stopBtn active again.
Im probably doing it wrong:
final Handler myHandler = new Handler();
startBtn = (Button)findViewById(R.id.start);
myHandler.postDelayed(new Runnable() {
public void run() {
startBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.i(TAG,"runned");
}
});
}
}, 10000);
After 10s im still able to click it and that is probably cos event listener is still attached. How can i detach it even if i don't know if its fired or not.
A delayed Runnable posted on a Handler could manage that:
myHandler.postDelayed(new Runnable() {
public void run() {
if(something happened) {
// magic work
} else {
// turn off the event
}
}
, 10000);
You can init the Handler as an instance variable by using this code:
final Handler myHandler = new Handler();
Delayed actions can be arranged by using a Handler. Specifically check the 2 methods: postAtTime(Runnable, long) and postDelayed(Runnable, long).
It is easy to create a Handler, just use its default constructor Handler handler = new Handler() within the Activity.onCreate(Bundle state). Then wrap your desired action into a Runnable and pass to the handler.
I've got an activity that keeps reading words to the user, and using onUtteranceCompleted with textTospeech to display something when the code is completed.
Inside onUtteranceCompleted I have this code to delay a function with a second:
Runnable task = new Runnable() {
public void run() {
//runs on ui
runOnUiThread(new Runnable() {
public void run() {
readWord();
}
});
}
};
worker.schedule(task, 1, TimeUnit.SECONDS);
This seems like it works well, but I think it is causing a problem.
When I rotate the screen of my phone (I guess this starts a new activity).
I hear some words being read in the background. I guess this is because of runOnUiThread() which makes the activity continue in the background.
How could I avoid 2 activities running ? I would prefer if I don't have to stop the screen from rotating on doing some weird patch!
Thank you
EDIT:
public void readWord() {
if (this.readingOnPause) {
return;
}
txtCurrentWord.setText(currentItem[1]);
this.hashAudio.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID,"word");
this.tts.setLanguage(Locale.US);
this.tts.speak(this.currentItem[1], TextToSpeech.QUEUE_FLUSH,this.hashAudio);
}
EDIT2:
instantiation of worker:
private static final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();
I would use a Handler instead of runOnUiThread().
For one thing, you're using a Thread that starts another Thread - why?
Secondly, if you create a simple Handler, it should kill itself on the rotate config change. IE:
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
// do your background or UI stuff
}
};
Then later use a Thread to call the handler, which will kick off whatever process you want to run on the UI thread:
new Thread() {
#Override
public void run() {
long timestamp = System.currentTimeMillis();
// thread blocks for your 1 second delay
while (System.currentTimeMillis() - timestamp <= 1000) {
// loop
}
handler.sendEmptyMessage(0);
}
}.start();
Ok so this is a fix I've come up with, if someone has a better solution, I'm listening.
I've added android:configChanges="keyboardHidden|orientation" inside the activity in the androidmanifest
2.
and then a function that is called when the screen is rotate:
#Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
setContentView(R.layout.streaming);
initializeUI(); //contains all the findViewByID etc...
}