Can't access resources in another thread on Android (crash) - android

This has been driving me insane for days, i am new to android and i can't seem to figure out how to access resources from other threads. I am trying to schedule a task to execute after a while, here is the relevant code:
public class TikTakBoom extends Activity {
private SensorManager mSensorManager;
public ToggleButton startButton;
private Bitmap mBombOn;
private Bitmap mBombOff;
/** Tik tak boom **/
protected MediaPlayer mediaPlayer; // play tik tak or boom
protected Timer boomTimer; // timer for explosion
// bomb button backgrounds
protected android.graphics.drawable.BitmapDrawable buttonBombOn;
protected android.graphics.drawable.BitmapDrawable buttonBombOff;
// random boom delay in ms
protected long boomDelay = 1500;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
// tell system to use the layout defined in our XML file
setContentView(R.layout.main);
Log.w(this.getClass().getName(), "SIS is null");
// configure toggle button (bomb on/off)
startButton = (ToggleButton)findViewById(R.id.toggleButton1);
startButton.setText("");
startButton.setTextOn("");
startButton.setTextOff("");
// scale bomb images to button size
mBombOff = BitmapFactory.decodeResource(this.getResources(), R.drawable.bomb_off);
mBombOff = Bitmap.createScaledBitmap(mBombOff, 120, 120, true);
mBombOn = BitmapFactory.decodeResource(this.getResources(), R.drawable.bomb_on);
mBombOn = Bitmap.createScaledBitmap(mBombOn, 120, 120, true);
buttonBombOn = new android.graphics.drawable.BitmapDrawable(mBombOn);
buttonBombOff = new android.graphics.drawable.BitmapDrawable(mBombOff);
startButton.setChecked(false);
boomTimer = new Timer();
// onclick listener for toggle bomb on/off
startButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// Perform action on clicks
if (startButton.isChecked()) {
mediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.tik_tak);
mediaPlayer.setLooping(true);
mediaPlayer.start();
// align boom delay with tik tak duration
boomDelay -= boomDelay%mediaPlayer.getDuration();
boomTimer.schedule(new BoomTimeTask(getApplicationContext()), boomDelay);
startButton.setBackgroundDrawable(buttonBombOn);
} else {
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
startButton.setBackgroundDrawable(buttonBombOff);
}
}
});
}
class BoomTimeTask extends TimerTask {
Context context;
public BoomTimeTask(Context context) {
super();
this.context = context;
}
public void run() {
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
mediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.sheldon);
mediaPlayer.setLooping(false);
mediaPlayer.start();
startButton = (ToggleButton)((Activity)context).findViewById(R.id.toggleButton1);
startButton.setChecked(false);
//startButton.setBackgroundDrawable(buttonBombOff);
}
}
App crashes on startButton.setChecked(false) in BoomTimeTask.run(), i am guessing NullPointerException.
Pls help, i am gonna go insane, i have tried everything! Nothing works.. Tried it without the context passed as a parameter, since the reference should be visible to BoomTimeTask thread as well, tried a dozen of other things, just can't get it to work.
What am i missing? Thx in advance, would really appreciate any kind of help. Cheers, Val.
SOLVED: how to change button text dynamically for every 3 sec in android?

You should use
runOnUiThread(new Runnable() {
public void run() {
// YOUR ACTIONS ON Graphic interface
}
});
It's because in android you can't modify GUI elements without being in the thread that deal with GUI.
http://developer.android.com/reference/android/app/Activity.html

Related

Pausing a Android countdowntimer with same button

I have found numerous examples of how to pause a countdowntimer in Android, but each of these examples utilises more than 1 button (pause, resume and cancel).
I want to have one button that when I press it the timer starts, then when I press it again it pauses (cancelling the original timer, capturing the timer value) and resumes when clicked again (taking the captured time from the pause to start off a new counterdowntimer).
Does anyone have an example of how to achieve this? I have tried if else loops in the onClick listener of the button. I have a very crude semi-working example;
if (gameOn == 1) {
if((clkOnTimerBtn % 2)==0) {
isPaused = true; // PAUSE COUNTDOWN TIMER
resumeCountDownTimer(view, "pause");
} else { // RESUME COUNTDOWN TIMER
resumeCountDownTimer(view, "resume");
}
The problem with the above is that this is carried out in the button onclick listener, so if a new CountDownTimer is created inside my resumeCountDownTimer its not possible to access the timer later on to cancel it (pause). I have also tried looking for a way to cancel all countdowntimers, messy if I could, but I couldn't find any examples or references to doing so as this would at least get the desired behaviour even if it isn't the most elegant way.
If I understood you correctly, something like this should work.
public class MainActivity extends AppCompatActivity {
CountDownTimer countDownTimer;
long duration = 100000; //This is the initial time,
long millisecondsLeft = 100000; // This is the time left. At the start it equales the duration.
boolean isCountDownTimerActive = false;
Button startButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start = (Button) findViewById(R.id.startStop);
final TextView timeLeft = (TextView) findViewById(R.id.timeLeft);
start.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (isCountDownTimerActive) {
if (countDownTimer != null)
countDownTimer.cancel();
isCountDownTimerActive = false;
} else {
countDownTimer = new CountDownTimer(millisecondsLeft, 1000) {
#Override
public void onTick(long l) {
millisecondsLeft = l;
timeLeft.setText(" " + l);
}
#Override
public void onFinish() {
}
};
isCountDownTimerActive = true;
countDownTimer.start();
}
}
});
}
}

Failed stop, Android app with TarsosDSP

I want to implement an android app using TarsosDSP package on this link:
https://github.com/srubin/TarsosDSP/releases/tag/TarsosDSPAndroid-v3-MAD
I created two buttons for startRecording and stopRecording, my app does "start" but doesnn't "stop". My MainActivity is this:
public class MainActivity extends ActionBarActivity {
private Button start;
private Button stop;
MicrophoneAudioDispatcher audioDispatcher = new MicrophoneAudioDispatcher(44100, 2048, 1024);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setButtonHandlers();
start = (Button) findViewById(R.id.btStart);
stop = (Button) findViewById(R.id.btStop);
}
private void setButtonHandlers() {
((Button)findViewById(R.id.btStart)).setOnClickListener(btnClick);
((Button)findViewById(R.id.btStop)).setOnClickListener(btnClick);
}
private void startRecording() {
audioDispatcher.run();
}
private void stopRecording() {
audioDispatcher.stop();
}
private View.OnClickListener btnClick = new View.OnClickListener() {
public void onClick(View v) {
switch(v.getId()){
case R.id.btStart:{
startRecording();
break;
}
case R.id.btStop:{
stopRecording();
break;
}
}
};
};
}
The class "MicrophoneAudioDispatcher" is on this link:
https://github.com/srubin/TarsosDSP/blob/master/src/be/hogent/tarsos/dsp/MicrophoneAudioDispatcher.java
I don't understand this error. Can someone help me?
Thanks in advance.
You have to initial a thread for AudioDispatcher instead to call AudioDispatcher.run() directly.
dispatcher = AudioDispatcherFactory.fromDefaultMicrophone(22050, 1024, 0);
dispatcher.addAudioProcessor(new PitchProcessor(PitchProcessor.PitchEstimationAlgorithm.FFT_YIN, 22050, 1024, new PitchDetectionHandler() {
#Override
public void handlePitch(PitchDetectionResult pitchDetectionResult,
AudioEvent audioEvent) {
final float pitchInHz = pitchDetectionResult.getPitch();
runOnUiThread(new Runnable() {
#Override
public void run() {
mFrequencyView.setText(String.valueOf(pitchInHz));
}
});
}
}));
new Thread(dispatcher, "Audio Dispatcher").start();
The thread will keep running until you call AudioDispatcher.stop(). It is important to avoid the thread remains on background.
#Override
protected void onPause() {
super.onPause();
dispatcher.stop();
}
First of all, the link you mentioned (TarsosDSPAndroid-v3-MAD) is a TarsosDSP fork that is compatible with Android. Since some time it isn't needed anymore, as described on official site:
the fork of TarsosDSP kindly provided by GitHub user srubin, created for a programming assignment at UC Berkley, is not needed any more.
And what relates to your main question, you shouldn't manually call run() of AudioDispatcher, it should be called from separate thread like mentioned in the link above, something like this:
new Thread(dispatcher,"Audio Dispatcher").start();

state errors with Android MediaPlayer

I'm new to Android programming and am building a simple app that plays a sound from a local file when you flip the phone over, using a SensorListener for the accelerometer. Additionally, there's an image with a click listener that also plays the same sound when touched.
My code has the following sequence:
onCreate and onRestart - call MediaPlayer.create() for the local file.
onDestroy - call MediaPlayer.release and set the reference to null.
I'm debugging the app on my phone since the emulator doesn't support accelerometers.
When I click the image, I get a start called in state 64 error in Logcat, and when I flip the phone, the app FCs with a NullPointerException when mp.start() is called.
My understanding of how it works, from the MediaPlayer documentation is that you have to call create(), which in turn calls prepare() before you can start or stop the player. Is there a problem with the sequence of steps?
I've also tried releasing the media player in onStop, to no avail.
I have the following sensor listener for the accelerometer (mp is the MediaPlayer object) -
private final SensorEventListener accelerometerListener = new SensorEventListener() {
#Override
public void onSensorChanged(SensorEvent arg0) {
float z_value = arg0.values[2];
if (z_value < 0 && playerReady) {
mp.start(); // <-- NullPointerException thrown here.
} else {
mp.stop();
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
};
..and the following click listener for the button -
private final View.OnClickListener onClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if (playerReady) {
mp.start();
}
}
};
I also wrote an onPreparedListener for the media player (which sets the playerReady seen above)
private final OnPreparedListener opl = new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
playerReady = true;
}
};
Edit Here's onCreate() and onRestart() -
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mp = MediaPlayer.create(Moo.this, R.raw.moo);
mp.setOnPreparedListener(opl);
ImageView im = (ImageView) findViewById(R.id.imageView1);
im.setOnClickListener(onClickListener);
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
List<Sensor> sensorList = sensorManager
.getSensorList(Sensor.TYPE_ACCELEROMETER);
if (sensorList.size() > 0) {
accelerometerPresent = true;
accelerometerSensor = sensorList.get(0);
sensorManager.registerListener(accelerometerListener,
accelerometerSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
}
#Override
protected void onRestart() {
super.onRestart();
mp = MediaPlayer.create(Moo.this, R.raw.moo);
}

Media Player start stop start

I am making a new android sound application. I made a clickable button to play sound when I click on it. But I also want it to stop playing sound when I click for the second time. That part works fine now here is the problem, when I click again on button to play sound again, it doesn't play it, Media player is completely stopped. I was looking on forums but I can't seem to find an answer that could help me.
Here is my Activity:
MediaPlayer mpButtonClick1;
MediaPlayer mpButtonClick2;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.prvi);
final MediaPlayer mpButtonClick1 = MediaPlayer.create(this, R.raw.spalshm);
final MediaPlayer mpButtonClick2 = MediaPlayer.create(this, R.raw.splashs);
Button dugme = (Button) findViewById(R.id.dugme);
dugme.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mpButtonClick1.isPlaying()) {
mpButtonClick1.stop();
mpButtonClick1.reset();
}
else {
mpButtonClick1.start();
}
}
});
When I try to write mpButtonClick1.prepare(); I get error Unhandled Exception Type IOE exception
Try to use pause instead of stop.
Reason: if you pause the MediaPlayer, then you can resume it later. However, if you use stop, almost any other method won't work and you will have to prepare the MediaPlayer again (or create a new one).
More info: here and here
PS: don't forget to release the memory when you finish using the resources.
Try this:
You should use only one mediaplayer object
public class PlayaudioActivity extends Activity {
private MediaPlayer mp;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button b = (Button) findViewById(R.id.button1);
Button b2 = (Button) findViewById(R.id.button2);
final TextView t = (TextView) findViewById(R.id.textView1);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
stopPlaying();
mp = MediaPlayer.create(PlayaudioActivity.this, R.raw.far);
mp.start();
}
});
b2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
stopPlaying();
mp = MediaPlayer.create(PlayaudioActivity.this, R.raw.beet);
mp.start();
}
});
}
private void stopPlaying() {
if (mp != null) {
mp.stop();
mp.release();
mp = null;
}
}
}
Change your class with below code:
remove reset();.
init well all components:
MediaPlayer mpButtonClick1;
MediaPlayer mpButtonClick2;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.prvi);
mpButtonClick1 = MediaPlayer.create(this, R.raw.spalshm);
mpButtonClick2 = MediaPlayer.create(this, R.raw.splashs);
Button dugme = (Button) findViewById(R.id.dugme);
dugme.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mpButtonClick1.isPlaying()) {
mpButtonClick1.stop();
}
else {
mpButtonClick1.start();
}
}
});
You're calling mpButtonClick1.reset() after mpButtonClick1.stop() - don't do that:
if (mpButtonClick1.isPlaying()) {
mpButtonClick1.stop();
mpButtonClick1.reset(); //<--------- calling reset(), remove this line
}
The docs for reset() say:
Resets the MediaPlayer to its uninitialized state. After calling this method, you will have to initialize it again by setting the data source and calling prepare().
Remove mpButtonClick1.reset() and it should work.
Keep in mind that MediaPlayer works as a state machine, which means that if you call methods in the wrong order, you'll get problems. Please read about MediaPlayer here and here.
Hey please use following
for stop -> media player
mp.seekTo(0);
mp.pause();
again for start just call
mp.start();
In my experience when I need to play multiple times and I may need to stop one play to start another play, (like in the case of multiple buttons), I just create another player, making sure that I release the resources for the previous one. To stop just use
mediaPlayer.stop();
But for play use something like this (adapt the logging to your specific needs) to create/recreate your player:
private boolean createMediaPlayer()
{
if (mediaPlayer!=null)
{
if(mediaPlayer.isPlaying())
{
mediaPlayer.stop();
mediaPlayer.reset();
mediaPlayer.release();
mediaPlayer=null;
}
}
mediaPlayer = new MediaPlayer();
mediaPlayer.setVolume(1f, 1f);
try
{
mediaPlayer.setAudioStreamType(Interop.PRIMARY_STREAM);
mediaPlayer.setDataSource(m_soundFile);
mediaPlayer.prepare();
return true;
// Interop.logDebug(TAG + "-loadAudio: SUCCESS" + m_soundFile);
} catch (Exception e)
{
Interop.logError(TAG + "-LoadAudio for Clic Sound: audioPlayer prepare failed for current file: " + m_soundFile);
Interop.logError(TAG + "-Exception: " , e);
return false;
}
}
and than use
if (createMediaPlayer())
mediaPlayer.start();
this will ensure proper release of the resources used by the media player.
A simple solution is to Use pause instead of stop and the seek to the beginning of the song.
I know that this question is quite old but recently while learning Android, I also got stuck at this point and found a very simple solution which I'd like to share with everyone.
Instead of trying to stop or reset the media, you can just seek back to the starting position.
mediaPlayer.seekTo(0);
For reference, I am also posting my code below:
public class MainActivity extends AppCompatActivity {
MediaPlayer mp;
public void play(View view) {
mp.start();
}
public void pause(View view) {
mp.pause();
}
public void stop(View view) {
// this seeks to the beginning of the file
mp.seekTo(0);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mp = MediaPlayer.create(this, R.raw.sample_audio);
}
}

Stopping MP playback onTouch / Click in extended ListActivity

I'm displaying a ListView of files to be played, read from an array in Strings.xml.
Everything works fine, however I can't figure out how to implement an Event which would stop the playback on Click or Touch anywhere on the screen, so the audio file can be interrupted and there's no need to wait until the whole file is played.
Please advise.
public class ViewSounds extends ListActivity {
MediaPlayer mp;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
// Get Button Labels
String[] lex_names = getResources().getStringArray(R.array.lex_names);
// Get File Names
final String[] lex_files = getResources().getStringArray(R.array.lex_files);
setListAdapter(new ArrayAdapter<String>(this, R.layout.list_sounds, lex_names));
final ListView lv = getListView();
lv.setTextFilterEnabled(true);
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final int playFile = getResources().getIdentifier(lex_files[position], "raw", getPackageName());
mp = MediaPlayer.create(getApplicationContext(), playFile);
mp.start();
while (mp.isPlaying()) {
// ...
};
mp.release();
}
});
}
}
Instead of playing the media in the onItemClick(), create an AsyncTask to start it in the background, then flag the background thread to stop when the user indicates they want to stop (clicking a button say). Alternatively control the player through a Service and make calls to stop/start the player through that, as you really don't want the player to be running on the UI thread.
Here's an example using AsyncTask that will stop playing when the user clicks a button (R.id.stop_playing):
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final MediaPlayerAsyncTask player = new MediaPlayerAsyncTask(R.raw.my_media);
Button stopPlaying = (Button) findViewById(R.id.stop_playing);
stopPlaying.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
player.stopPlayer();
}
});
setVolumeControlStream(AudioManager.STREAM_MUSIC);
player.execute((Void []) null);
}
private class MediaPlayerAsyncTask extends AsyncTask<Void, Void, Void> {
private boolean stopPlayer;
MediaPlayer mp;
MediaPlayerAsyncTask(int playFile) {
mp = MediaPlayer.create(TestStuff.this, playFile);
}
#Override
protected Void doInBackground(Void... params) {
stopPlayer = false;
mp.start();
while (!stopPlayer && mp.isPlaying()) {
SystemClock.sleep(1000);
}
mp.stop();
return null;
}
public void stopPlayer() {
stopPlayer = true;
}
}

Categories

Resources