Thread and mediaplayer problems, Timer with sound - android

I'm developing an Android application that is based on a timer that every second must play a sound that you selected before.
Here is the problem:
No objects can run Media Player in a Thread or a Runnable
I can not call methods that reproduce the sound
I can not run it on a timer task
Can you think of anything?
Thanks
thread:
public void run(){
while(!detenido) //Bolean for stop the thread
{
try {
Servicio servicios = new Servicio();
switch(segundosesperar){ //Int to select the ms to slep
case 0:
this.sleep(1500);
servicios.intento1(); //the metod with " mp.start()"
dormir = 1500;
break;
case 1:
this.sleep(1600);
servicios.intento1();
dormir = 1600;
break;
case 2:
this.sleep(1700);
servicios.intento1();
dormir = 1700;
break;
case 3:
this.sleep(1800);
servicios.intento1();
dormir = 1800;
break;
case 4:
this.sleep(1900);
servicios.intento1();
dormir = 1900;
break;
case 5:
this.sleep(2000);
servicios.intento1();
dormir = 2000;
break;
default:
this.sleep(1750);
servicios.intento1();
dormir = 1750;
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
piedras =piedras+1; //Counter cronometer +1
cron = piedras+"";
//Set the counter in the activity
handler.setHcron(cron);
handler.act();
cronoparaganar = cron;
if ( piedras == piedrasmodo){
tocarsirena = true;
}
}
}
Metod in the service to start a play sound:
public void intento1(){
mp= MediaPlayer.create(this, R.raw.censura);
mp.start();
}

You can run a MediaPlayer in an extra Service.
Check this out for information about Service.
Also, this link might be useful to handle the MediaPlayer with a Service.
You could then call the functions of your Service to handle the MediaPlayer.

Related

Issues with Android RFCOMM retransmissions

I am working with Android's RFCOMM Tutorial. I have it working, but I'm having an issue when I send large messages (this is intentional, and cannot be changed).
The issue is that I will see the same data transferred multiple times, but it stops after sending X bytes, even though N of those bytes were duplicates.
As an example, sending a chunk of 12800 bytes, my receiving side sees this data (cut down from the full log -- the numbers indicate the length of the string):
Reading: (990)yJv;ZU>h8JCfAEttQEA7V9BSkITgDn5350SdahFILcbwrYAfD7b_Oavhqr:DF
Reading: (2970)HQTZWMFv08G3qHiHn#4_f8KWrQX9v]gmV=tvqGFpP:c8yV^a0i17:ndDiOS
Reading: (990)ZdXZvCxK5sdLlKDnPrrP;2nuEItZ9<\29e7x7lNFafHU]R#Ap=lBF\M?1\W_O
Reading: (990)ZdXZvCxK5sdLlKDnPrrP;2nuEItZ9<\29e7x7lNFafHU]R#Ap=lBF\M?1\W_O
Reading: (990)ZdXZvCxK5sdLlKDnPrrP;2nuEItZ9<\29e7x7lNFafHU]R#Ap=lBF\M?1\W_O
Reading: (1980)ZdXZvCxK5sdLlKDnPrrP;2nuEItZ9<\29e7x7lNFafHU]R#Ap=lBF\M?1\W_
Reading: (990)TD?lyEvS;[PMj>xpg5RiAIK8di61QNngkOvpoMhl=0`I#32iUuScR1xiGiZ:
Reading: (990)Ahyxc2PMA?sub8l^:prr=D5vIaGw73#rS0EHTeT;XLcsq9k6li;MVw_IJ4QiSs
Reading: (990)YQe:OWv_Iv_6eL#Yu>KUnbjmsN9KsidM1Vx6:LSsgZ[sbG3V>0dp7kkvUhX1^n
Reading: (920)YQe:OWv_Iv_6eL#Yu>KUnbjmsN9KsidM1Vx6:LSsgZ[sbG3V>0dp7kkvUhX1^n
As you can see, there are 3 exact retransmissions, and one retransmission that had more data appended. However, the sum of all the lengths is 12800 -- the original size of the message.
In case you're wondering, presently the data is just a random string. So, no, it's not getting the same bits of data in there more than once. And I have confirmed this by looking at the sending side.
Now, I need the data to come through without any corruption, so I'm checking a hash on the other side. However, with the retransmissions, and the lost data, it isn't matching most of the time. And cutting out the retransmissions just leaves me with missing data.
So, the issue seems to be two-fold:
Retransmissions are happening even though the data is received successfully.
Even when I'm smart about ignoring the retransmissions, all the data doesn't come through. If N bytes are retransmitted, I'm left with N bytes of missing data.
All relevant code for this has come directly from Android's website (link above). Key points of interest however are:
Managing A Connection
And this block, which comes from the full code from the tutorial I linked to:
private final Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_STATE_CHANGE:
if(D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
switch (msg.arg1) {
case BluetoothChatService.STATE_CONNECTED:
mTitle.setText(R.string.title_connected_to);
mTitle.append(mConnectedDeviceName);
mConversationArrayAdapter.clear();
break;
case BluetoothChatService.STATE_CONNECTING:
mTitle.setText(R.string.title_connecting);
break;
case BluetoothChatService.STATE_LISTEN:
case BluetoothChatService.STATE_NONE:
mTitle.setText(R.string.title_not_connected);
break;
}
break;
case MESSAGE_WRITE:
byte[] writeBuf = (byte[]) msg.obj;
// construct a string from the buffer
String writeMessage = new String(writeBuf);
mConversationArrayAdapter.add("Me: " + writeMessage);
break;
case MESSAGE_READ:
byte[] readBuf = (byte[]) msg.obj;
// construct a string from the valid bytes in the buffer
String readMessage = new String(readBuf, 0, msg.arg1);
mConversationArrayAdapter.add(mConnectedDeviceName+": " + readMessage);
break;
case MESSAGE_DEVICE_NAME:
// save the connected device's name
mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
Toast.makeText(getApplicationContext(), "Connected to "
+ mConnectedDeviceName, Toast.LENGTH_SHORT).show();
break;
case MESSAGE_TOAST:
Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST),
Toast.LENGTH_SHORT).show();
break;
}
}
};

Recording video using MediaRecorder and FileOutputStream produces video file that can't be played

I am trying to implement the function where i can start and stop the video recording multiple times, and accumulate video data into a File.
This is how i prepare my media recorder:
private boolean prepareVideoRecorder(){
mMediaRecorder = new MediaRecorder();
//0 for landscape
//90 for portrait
//Check for available profile
CamcorderProfile profile = null;
if(CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_480P)){
Log.d(TAG, "480p");
profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P);
}else if(CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)){
Log.d(TAG, "720p");
profile = CamcorderProfile.get(CamcorderProfile.QUALITY_720P);
}else{
Log.d(TAG, "LOW");
profile = CamcorderProfile.get(CamcorderProfile.QUALITY_LOW);
}
// Step 1: Unlock and set camera to MediaRecorder
mCamera.unlock();
mMediaRecorder.setCamera(mCamera);
// Step 2: Set sources
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
// Step 3: Set profile
mMediaRecorder.setProfile(profile);
// Step 4: Set output file and pass media recorder the file descriptor
if(mStoreFile == null){
mStoreFile = MediaUtil.getOutputMediaFile(MediaUtil.MEDIA_TYPE_VIDEO);
try {
mStoreFile.createNewFile();
} catch (Exception e) {
e.printStackTrace();
}
}
try {
mOutputStream = new FileOutputStream(mStoreFile, true);
mMediaRecorder.setOutputFile(mOutputStream.getFD());
} catch (Exception e1) {
e1.printStackTrace();
}
mMediaRecorder.setMaxDuration(30000);
// Step 5: Set the preview output
mMediaRecorder.setPreviewDisplay(mPreviewSurface.getHolder().getSurface());
//Check orientation and set hint
switch(mOrientation){
case ORIENTATION_PORTRAIT_NORMAL:
mMediaRecorder.setOrientationHint(90);
break;
case ORIENTATION_PORTRAIT_INVERTED:
mMediaRecorder.setOrientationHint(270);
break;
case ORIENTATION_LANDSCAPE_NORMAL:
mMediaRecorder.setOrientationHint(0);
break;
case ORIENTATION_LANDSCAPE_INVERTED:
mMediaRecorder.setOrientationHint(180);
break;
}
// Step 6: Prepare configured MediaRecorder
try {
mMediaRecorder.prepare();
} catch (IllegalStateException e) {
Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage());
releaseMediaRecorder();
return false;
} catch (IOException e) {
Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage());
releaseMediaRecorder();
return false;
}
return true;
}
This is the button on click code:
#Override
public void onClick(View v) {
if (isRecording) {
try{
mOutputStream.close();
}catch(Exception ee){
}
// stop recording and release camera
mMediaRecorder.stop(); // stop the recording
mMediaRecorder.reset();
mCamera.lock(); // take camera access back from MediaRecorder
// inform the user that recording has stopped
mRecordButton.setImageResource(R.drawable.record_button);
isRecording = false;
} else {
// initialize video camera
if (prepareVideoRecorder()) {
// Camera is available and unlocked, MediaRecorder is prepared,
// now you can start recording
mMediaRecorder.start();
// inform the user that recording has started
mRecordButton.setImageResource(R.drawable.record_button_on);
isRecording = true;
} else {
// prepare didn't work, release the camera
releaseMediaRecorder();
// inform user
}
}
}
});
MediaUtil.getOutputMediaFile(MediaUtil.MEDIA_TYPE_VIDEO) will give me something like:
/storage/sdcard0/Pictures/Project/VID_2013.mp4
Current problem:
The way i test it at the moment is by:
Start recording
Stop Recording
Start recording
stop recording
Go to android's file manager on /Pictures/Project path
I can see the file that was created and "appended" with multiple segments of data. But it won't play. And it doesn't have a cover image like other video files.
Somewhere along the line, the file is corrupted? It doesn't work as well if i just record once and check the file in the storage. I am able to record video if i just define a File for the MediaRecorder in setOutputFile, but i'm trying append video data for multiple shoot. Is this possible?

thread start/stop does not work correct on android

I need some help with starting/stopping my monsters.
We generate like this:
for (int i = 0; i < 20; i++) {
boolean notFree = true;
int x = 0, y = 0;
// while not free change
// not determinstisch setting!
while (notFree) {
x = (int) (Math.random() * Config.XSIZE);
y = (int) (Math.random() * Config.YSIZE);
if (map.getMapPosition(x, y) == Config.EMPTYPOSITION) {
notFree = false;
}
}
switch ((int) Math.floor(Math.random() * 2)) {
case 0:
monsterVerktor.add(new Monster(context, x, y, this.map, 1,
this.charac));
break;
case 1:
monsterVerktor.add(new DeathReaper(context, x, y, this.map, 1,
this.charac));
break;
default:
break;
}
}
And I stop them like this: (Start is same just with Startmoving...)
public void stopAllMonsters() {
for (Monster monster : monsterVerktor) {
monster.getControl().stopMovingThread();
}
}
The thread.stopMovingThread works like this:
public void stopMovingThread() {
this.monster.setAlife(false);
running = false;
moveDone = true;
boolean retry = true;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
// try again shutting down the thread
}
}
}
To the run():
public void startMovementThread() {
running = true;
thread = new Thread() {
#Override
public void run() {
while (running) {
Log.d(TAG, "run Thread");
// get a little randomness in the movement ;)
try {
// up to 0.5s sleeping till next moving
sleep(new Random().nextInt(Config.RANDOMSLEEP));
} catch (InterruptedException e1) {}
while (monster.isAlife()) {// check if alife
moveDone = false; // resett movement Done
noMove = false;
// Charakter in near if one is in near he
// will move to it.
if (!checkCharInAggroRange()) {
noMove = rndMove(); // if we have no move
}
while (!moveDone) {
timeBegin = System.currentTimeMillis();
// if hes not done with the move
if (monster.moveToX == monster.positionX
&& monster.moveToY == monster.positionY) {
if (noMove) {
try {// sleep because no movement with a bit
// randomness
sleep(Config.MONSTERMOVINGTIME
+ (new Random()
.nextInt(Config.RANDOMSLEEP)));
} catch (InterruptedException e) {
Log.d(TAG,
"An error occured during sleep because"
+ " of no Animatino");
}
}
// false after sleep
moveDone = false;
break; // already at the right position!
// movetoX is left of position
} else if (monster.moveToX > monster.positionX) {
monster.positionX++;
// movetoX is left of it
} else if (monster.moveToX < monster.positionX) {
monster.positionX--;
// movetoY is left of position
} else if (monster.moveToY > monster.positionY) {
monster.positionY++;
// movetoY is left of position
} else if (monster.moveToY < monster.positionY) {
monster.positionY--;
}
// sleep if hes moving to fast!
timeDiff = System.currentTimeMillis() - timeBegin;
sleepTimer = (int) (Config.MONSTERTIMING - timeDiff);
if (sleepTimer > 0) { // if >0 we are fast enough
// and can sleep a bit ;)
try {
sleep(sleepTimer);
} catch (InterruptedException e) {
Log.d(TAG, "Gameloop thread cant sleep");
}
}
}
}
try {
sleep(Config.RESPAWNTIMER);
} catch (InterruptedException e) {
Log.d(TAG, "Monsterthread cant sleep");
}
// respawn it after a sleep :)
respawn();
}
}
};
thread.start();
}
Android stops working if we want to stop them and start them like this, and I dont get it why. We also stop/start our render like this. (Well it's a runable and this isn't)
Monster isAlife and etc:
public class Monster extends Drawable {
private static final String TAG = Monster.class.getSimpleName();
private int fullLife; // full life
private int curLife; // current life
public Context context;
private Character charac;
// ..
// Basic monsterstuff
private boolean alife, moveDone;
private int level;
public Status status;
private MonsterControl control;
// ..
public Monster(Context context, int mapPosX, int mapPosY, Map map,
int level, Character cha) {
this.context = context;
this.map = map;
this.setCharac(cha);
this.mapPosition[0] = mapPosX;
this.mapPosition[1] = mapPosY;
this.status = Status.IDLE;
// example for full life calculation
this.fullLife = level * 100 + ((int) (Math.random() * 10 * level)); // Examples
this.curLife = this.fullLife;
this.map.setMapPosition(mapPosX, mapPosY, Config.MONSTERSTATE);
// set monster position
// ..
// load the sprite bitmap
// ...
// Change this later!
alife = true;
Log.d(TAG, "Monster created");
// Starting the Controler
control = new MonsterControl(this, this.charac);
control.startMovementThread();
Log.d(TAG, "Monster start moving");
// exemplarisch cut of the sprite
this.monsterPicAnimation();
}
}
here the getter/setter for aLife
public boolean isAlife() {
return alife;
}
public void setAlife(boolean alife) {
this.alife = alife;
}
First off, your comment of "Android stops working", Are you implying a crash? Got a LogCat trace?
Other than that is this analysis correct?
1.Create a monster.
running = true;
isAlife = true; //or whatever monster.isAlife() checks
2.Thread starts.
while (running) {
...
while (monster.isAlife()) {
...
}
}
3.You try to stop the thread by (1) setting running to false, and (2) joining on the thread?
Assuming that is all true. The monster thread still runs until something kicks it from the nested while(monster.isAlife()) loop. Once that ends, the while(running) will evaluate false and the thread should terminate, moving on to the next monster in your collection. Without this ever terminating you'll be waiting on each monster to die while locking your main thread with join().
Apart from the logic of your code which I haven't looked at in details, there is a lack of synchronization around shared variables. In particular, as it is, your code provides no guarantee that your thread (in startMovementThread) will ever be able to observe the changes made to your flags from the main thread.
A good first step would be to make those variables volatile:
private volatile boolean alife, moveDone;
Note however that there might be other issues (depending on your requirements). For example, when those statements are executed in startMovementThread:
if (monster.moveToX == monster.positionX ...)
monster.positionX++;
any changes made in that thread might not be visible from the main thread and vice versa. If that can be an issue, you need to synchronize access to those variables too (and making them volatile will not be enough because, for example, monster.positionX++ is not atomic).
I solved it myself with and "good" idea.
The problem was, that i let them sleep at some points. If they sleep it cant stop/join the thread and even dont watch if its "running". So i solved it with a small methode that looks like this:
private void breakableSleep(int i, int intervall){
int x = 0;
while(running && x <= i){
try {
this.thread.sleep(intervall);
} catch (InterruptedException e) {
Log.d(TAG, "Monsterthread cant sleep");
}
x++;
}
}
it can be stoped at every intervall. Sure it will cost some Battery but did not finde any other way to stop the Monsters in a "fast" way because some sleep for around 3seconds if they died (respawning)
I also rewrote the whole startMovementThread because it did not look well in my opinion.
Best Regards

Android outofmemory error allocation

I've been playing around with Androids sample code Bluetooth Chat. I'm thinking about using it as a base for my own app but I keep running into outofmemory errors. Right now I have it set up to read tons of data from a bluetooth device at a pretty fast rate. The app changes the byte array it receives into a String but after a while I get the outofmemory error. Is the gc too slow in removing all the String objects it creates? How do I free up the memory of the String
Bluetooth Chat
private final Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_STATE_CHANGE:
if(D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
switch (msg.arg1) {
case BluetoothChatService.STATE_CONNECTED:
setStatus(getString(R.string.title_connected_to, mConnectedDeviceName));
mConversationArrayAdapter.clear();
break;
case BluetoothChatService.STATE_CONNECTING:
setStatus(R.string.title_connecting);
break;
case BluetoothChatService.STATE_LISTEN:
case BluetoothChatService.STATE_NONE:
setStatus(R.string.title_not_connected);
break;
}
break;
case MESSAGE_WRITE:
byte[] writeBuf = (byte[]) msg.obj;
// construct a string from the buffer
String writeMessage = new String(writeBuf);
mConversationArrayAdapter.add("Me: " + writeMessage);
break;
case MESSAGE_READ:
byte[] readBuf = (byte[]) msg.obj;
// construct a string from the valid bytes in the buffer
String readMessage = new String(readBuf, 0, msg.arg1);
mConversationArrayAdapter.add(mConnectedDeviceName+": " + readMessage);
break;
case MESSAGE_DEVICE_NAME:
// save the connected device's name
mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
Toast.makeText(getApplicationContext(), "Connected to "
+ mConnectedDeviceName, Toast.LENGTH_SHORT).show();
break;
case MESSAGE_TOAST:
Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST),
Toast.LENGTH_SHORT).show();
break;
}
}
};
The MESSAGE_READ case is the one that runs tons of times. Also I'm converting the String into a Hex String. Is there a way to change the byte[] directly to a hex String which can save the precious memory?
about out of memory errors: i have not the perfect solution but nullifying the variable after the last use might help the GC to speed up its process.
About byte[] to a string of hexes, you may try to iterate over the byte array and call Integer.toHexString(...) on each byte.

Bluetooth Chat - Pass the complete incoming message as string

I am using Bluetooth Chat sample app from Android website. I want to pass the complete incoming message, (message format included in the code below as this website is parsing the message for me which is my job in the code as well, to a function which removes the tags and extracts message name and value and uses it for further action. The readMessage string (in the switch case Message_Read) sends selected characters and omits few special characters. Following is the code from Bluetooth Chat app (from Android website).
I am not able to receive the complete message in the format which I have mentioned in the code. It gets displayed in multiple lines and many characters get deleted. Any suggestion why this is happening?
// The Handler that gets information back from the BluetoothChatService
private final Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_STATE_CHANGE:
if(D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
switch (msg.arg1) {
case BluetoothChatService.STATE_CONNECTED:
mTitle.setText(R.string.title_connected_to);
mTitle.append(mConnectedDeviceName);
mConversationArrayAdapter.clear();
break;
case BluetoothChatService.STATE_CONNECTING:
mTitle.setText(R.string.title_connecting);
break;
case BluetoothChatService.STATE_LISTEN:
case BluetoothChatService.STATE_NONE:
mTitle.setText(R.string.title_not_connected);
break;
}
break;
case MESSAGE_WRITE:
byte[] writeBuf = (byte[]) msg.obj;
// construct a string from the buffer
String writeMessage = new String(writeBuf);
mConversationArrayAdapter.add("Me: " + writeMessage);
break;
case MESSAGE_READ:
byte[] readBuf = (byte[]) msg.obj;
// construct a string from the valid bytes in the buffer
String readMessage = new String(readBuf, 0, msg.arg1);
mConversationArrayAdapter.add(mConnectedDeviceName+": " + readMessage);
//Make sure there is a received message. Extract it's name and value
//int msgLen = readMessage.length();
//if( msgLen!= 0)
// Message format is <MSG><N>shiftDirection<!N><V>1<!V><!MSG>
if (readBuf.equals("<MSG><N>.*<!N><V>.*<!V><!MSG>"))
extractData(readMessage);
else mTitle.setText(R.string.floor_it);
break;
case MESSAGE_DEVICE_NAME:
// save the connected device's name
mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
Toast.makeText(getApplicationContext(), "Connected to "
+ mConnectedDeviceName, Toast.LENGTH_SHORT).show();
break;
case MESSAGE_TOAST:
Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST),
Toast.LENGTH_SHORT).show();
break;
}
}
};
The part of the code which reads the incoming stream of bytes into a buffer and passes the buffer object for display.
public void run() {
Log.i(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
//String Incoming = new String(buffer);
//Pass "Incoming" instead of "buffer"
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
break;
}
}
}
I think the problem is, that case MESSAGE_READ: is not only called if a complete Packet is received, it is also called if a small fragment is received. So you should place the readMessage in a separate buffer (i.e. mNewBuf += readMessage) check the mNewBuf has a complete packet in it. If so, you parse your message and after that you clear your buffer. It worked for me.
if (readBuf.equals("<MSG><N>.*<!N><V>.*<!V><!MSG>"))
extractData(readMessage);
equals() doesn't apply regular expressions. If you want to match .*, use classes from java.util.regex to help you.
I was having the same issue, and I'm not 100% this will always work but I found this on the javadoc:
read(byte[] b, int off, int len)
Reads up to len bytes of data from the input stream into an array of bytes.
If you know how many bytes you are expecting then you could try using
bytes = mmInStream.read(buffer, 0, lengthYouWant);
However, this wouldn't work if your incoming data is variable in length.

Categories

Resources