Before moving on with my project I thought what I should use to run tasks in background.
The Activity has to do following on button hold - play audio, record video, record user voice input, move graph(set new coordinates with each iteration).
At the moment I am using MediaPlayer built in ASync for playing audio. RunOnUiThread with Runnable for audio recording and Handler for moving graph and I haven't gotten to recording video yet. The UI should be updated after every 3 or 5 ms.
My question is what would be the best option to use in this situation.
This is my audio recording and pitch detection:
AudioDispatcher dispatcher =
AudioDispatcherFactory.fromDefaultMicrophone(44100,2048,0);
PitchDetectionHandler pdh = new PitchDetectionHandler() {
#Override
public void handlePitch(PitchDetectionResult res, AudioEvent e){
final float pitchInHz = res.getPitch();
runOnUiThread(new Runnable() {
#Override
public void run() {
noteText.setText(micInputResult.processPitch(pitchInHz));
pitchText.setText("" + pitchInHz);
gameView.setSingerArrowY(micInputResult.processPlayerCoordinate(pitchInHz));
gameView.invalidate();
}
});
}
};
AudioProcessor pitchProcessor = new PitchProcessor(PitchProcessor.PitchEstimationAlgorithm.FFT_YIN, 44100, 2048, pdh);
dispatcher.addAudioProcessor(pitchProcessor);
Thread audioThread = new Thread(dispatcher, "Audio Thread");
audioThread.start();
And moving graph:
playRecordButton.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionevent) {
final int action = motionevent.getAction();
if (action == MotionEvent.ACTION_DOWN) {
Log.i("repeatBtn", "MotionEvent.ACTION_DOWN");
playMedia(songIndex);
runnable = new Runnable() {
public void run() {
//Setting new coordinates for graph on each iteration
assa.setMinX(orderNr);
assa.setMaxX(orderNr+5);
graph.invalidate();
if(pitchesList.size() != orderNr){
orderNr++;
}
handler.postDelayed(this, 100);
}
};
handler.postDelayed(runnable, 100);
} else if (action == MotionEvent.ACTION_UP) {
Log.i("repeatBtn", "MotionEvent.ACTION_UP");
handler.removeCallbacks(runnable);
assa.setMinX(-2);
assa.setMaxX(2);
graph.invalidate();
orderNr = 0;
stopAudio();
}//end else
return false;
} //end onTouch
}); //end b my button
I am aware that they should ultimately be ran at once but that is the reason for asking because I have tried many different ways but I am not sure which I should use.
Related
I have a tune in my android app. I have added a feature that if user selects a time then the tune will repeat until the time ends. I have also added the feature of infinite time but when I run my app goes in ANR (not responding) mode.
if(tinydb.getString("timer").equals("infinity"))
{
boolean valid = true; //Here i want to play the tune for infinite time
while(valid)
{
water_player = MediaPlayer.create(MainActivity.this, R.raw.water);
water_player.start();
}
}
else
{
while(!timerText.equals("0h: 0m: 1s")) //Here i want to play the tune until the timer gets zero
{
water_player = MediaPlayer.create(MainActivity.this, R.raw.water);
water_player.start();
}
you can use setLooping for the first case
and for the second case
final Runnable r = new Runnable() {
public void run() {
water_player.stop();
}
};
handler.postDelayed(r, 1000); //your time in millisecond
with your code :
if(tinydb.getString("timer").equals("infinity"))
{
water_player = MediaPlayer.create(MainActivity.this, R.raw.water);
water_player.setLooping(true);
water_player.start();
}
else
{
water_player = MediaPlayer.create(MainActivity.this, R.raw.water);
water_player.start();
final Runnable r = new Runnable() {
public void run() {
water_player.stop();
}
};
handler.postDelayed(r, 1000); //your time in millisecond
}
Use countdownTimer to complete your goal in which you can set countdown timer till x seconds manually. when countdown finish process it will go to finish method and execute finish method code
CountDownTimer cntr_aCounter = new CountDownTimer(3000, 1000) {
public void onTick(long millisUntilFinished) {
mp_xmPlayer2.start();
}
public void onFinish() {
//code fire after finish
mp_xmPlayer2.stop();
}
};cntr_aCounter.start();
If you have only 2 cases, use a boolean.
boolean infinitely;
if(infinitely == true){
water_player = MediaPlayer.create(MainActivity.this, R.raw.water);
water_player.start();
}
else {
water_player = new MediaPlayer.create(MainActivity.this, R.raw.water);
water_player.start();
Handler handler = new Handler();
handler.postDelayed(new Runnable(){
water_player.stop();
}
}
, 2000);
The handler method will be executed when 2sec passed. (put your own desired time).
I'm trying to access three sensors simultaneously. I need the accelerometer data to detect if the person is running, walking or standing still and MIC and CAMERAMIC data as input to an adaptive filter.
I tried to use threads, one thread for each sensor, but they don't really work in parallel.
//declaring 3 threads
public class firstMicThread implements Runnable {
public void run() {
isRecording = true;
Log.i("Thread 1 ", "first thread started" + String.valueOf(isRecording));
startRecording(audiosource);
}
}
public class secondMicThread implements Runnable {
public void run() {
isRecording = true;
Log.i("Thread 2 ", "second thread started" + String.valueOf(isRecording));
startRecording(audiosource2);
}
}
public class accelerometerThread implements Runnable {
public void run() {
isRecording = true;
Log.i("Thread 3 ", "acceletometer started " + String.valueOf(isRecording));
// onResume();
sensorManager.registerListener( RecordandStore.this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL );
Log.i("Thread 3 ", "acceletometer listener registered" + String.valueOf(isRecording));
}
}
and
private View.OnClickListener btnClick = new View.OnClickListener() {
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_startrec: {
if (checkpermissionfromdevice()) {
Log.i("Start click:", "permissions true");
// first mic
Runnable firstmicR = new firstMicThread();
Thread firstmicthread = new Thread( firstmicR);
// second mic
Runnable secondmicR = new secondMicThread();
Thread secondmicthread = new Thread( secondmicR);
// accelerometer
Runnable acceletometerR = new accelerometerThread();
Thread accelerometerthread = new Thread(acceletometerR);
firstmicthread.start();
secondmicthread.start();
accelerometerthread.start();
startrec.setEnabled(false);
stoprec.setEnabled(true);}
else { // if check permissionfordevice == false
requestPermission();
}
break;}
I am playing MIDI notes using this library as follows:
handler.postDelayed(new Runnable() {
ShortMessage message;
#Override
public void run() {
{
MidiEvent event = midiEvents.get(index[0]);
if (index[0] < midiEvents.size() - 1) {
delta = midiEvents.get(index[0] + 1).getDelta();
time[0] = timeFactor * midiEvents.get(index[0] + 1).getDelta();
mTotalMidiTime += time[0];
int noteValue;
int NOTE_STATUS;
if (event instanceof NoteOn) {
noteValue = ((NoteOn) event).getNoteValue();
NOTE_STATUS = NOTE_ON;
if (index[0] != 0) {
if (delta != 0) {
Intent intent = new Intent();
intent.setAction(Constants.SCROLL_RECYCLERVIEW);
localBroadcastManager.sendBroadcast(intent);
}
}
} else {
noteValue = ((NoteOff) event).getNoteValue();
NOTE_STATUS = NOTE_OFF;
}
try {
message = new ShortMessage(NOTE_STATUS, 2, noteValue,
127);
} catch (Exception e) {
e.printStackTrace();
}
Intent intent = new Intent();
intent.setAction(Constants.ACTION_SEEK);
localBroadcastManager.sendBroadcast(intent);
if (message != null)
recv.send(message, -1);
index[0]++;
} else {
index[0] = 0;
time[0] = 1;
mTotalMidiTime = mMinimumTime;
delta = 0;
}
handler.postDelayed(this, time[0]);
}
}
}, 0);
With each NoteOn event I am smooth scrolling a RecyclerView and updating a Seekbar using a LocalBroadcastManager
My problem is that playback is fine when UI operations are not performed but playback and UI get completely out of sync as soon as multiple MIDI notes (chords) are played in very quick succession. It would be appreciated if any performance improvements are suggested for the same. I have already tried performing the UI operations in runOnUiThread and also launching a new Handler for UI operations.
My BroadcastReceiver is as follows:
BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String actionType = intent.getAction();
switch (actionType) {
case Constants.SCROLL_RECYCLERVIEW:
Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
mNotesRecycler.smoothScrollBy(pixels, 0);
}
});
break;
case Constants.ACTION_SEEK:
Handler seekHandler = new Handler();
seekHandler.post(new Runnable() {
#Override
public void run() {
mPinchSeekBar.setSelectedCentreValue(mTotalMidiTime);
mCurrentTime.setText(timeInMinutes((int) mTotalMidiTime));
}
});
break;
}
}
};
Besides the optimizations you can do, I think the problem itself is RecyclerView's smoothScroll is more a method you call sometimes to do a fancy scroll rather than bomb it with requests that cause it to constantly recompute the running animation.
One thing you can try is to write yourself a simple scroll handler that calls mRecyclerView.scrollTo() that does the scroll without animation but I guess it's going to be more reliable.
Try something like this
Handler mHandler = new Handler();
int mPosition, mTargetPosition;
#IntRange(from = 20, to = 100)
final int INTERVAL = 50; // try with 50 ms or a little lower
Runnable mTimerScroll = new Runnable() {
public void run() {
if (mPosition != mTargetPosition) {
if (mPosition < mTargetPosition) {
mPosition += mDelta;
if (mPosition > mTargetPosition) mPosition = mTargetPosition;
}
if (mPosition > mTargetPosition) {
mPosition -= mDelta;
if (mPosition < mTargetPosition) mPosition = mTargetPosition;
}
mRecyclerView.scrollTo(mPosition, 0);
}
// repeat every 50ms
mTimerScroll.post(mRunnable, INTERVAL);
}
}
And then you start it
void startTimer() {
stop(); // prevent double start
mHandler.post(mTimerScroll);
}
void stopTimer() {
mHandler.removeCallbacks(mTimerScroll);
}
void scroll(int target) {
mTargetPosition = target;
}
void scrollBy(int pixels) {
scroll(mPosition + pixels);
}
I don't know the specifics of your app but it might work.
About optimization, you are using a lot of stuff there that can or cannot be necessary depending on the specifics of your library, etc, but if there are no different threads or services involved you could avoid the LocalBroadcast thing and all the handler.post() as everything is already on the UI thread (handler.post just posts a runnable to the thread where the handler was created, that in your case is the UI thread, so it doesn't do anything)
I am using this code to play a sound
final MediaPlayer mp = MediaPlayer.create(this, R.raw.sound);
mp.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mp.release();
}
});
It works fine on its own, however there was a problem after I added an animation that extends ImageView, which refreshes(by calling handler.postDelayed) the image resource at an interval about 30ms to create animation. The problem is that when the animation starts, it terminates the the playing of the sound. Here is the code for the Runnable that refreshes the ImageView.
private Runnable runnable = new Runnable () {
public void run() {
String name = "frame_" + frameCount;
frameCount ++;
int resId = mContext.getResources().getIdentifier(name, "drawable", mContext.getPackageName());
imageView.setImageResource(resId);
if(frameCount < totalFrameCount) {
mHandler.postDelayed(runnable, interval);
}
}
};
I also tried to use a thread that calls the anmiationView.postInvalidate to do the animation, however it has the same problem. Please help. Thanks
Edit:
It looks like the problem is due to WHEN the animation is called. Previously I called it in the onActivityResult of the activity. Looks like this is not the right place to call. Now I put the animation view in a popupWindow and play it there, it works properly. Not sure exactly why.
in handler's comments :
"A Handler allows you to send and process {#link Message} and Runnable
objects associated with a thread's {#link MessageQueue}. Each Handler
instance is associated with a single thread and that thread's message
queue. When you create a new Handler, it is bound to the thread /
message queue of the thread that is creating it -- from that point on,
it will deliver messages and runnables to that message queue and execute
them as they come out of the message queue."
so, the problem may be caused by both of animation and media playing operations are in
the same message queue own by which thread create the handler (let's say the main thread).
if the animation loops for ever, then the media player will hardly get any chance to run.
you could take it a try with HandlerThread, the thread will contain a new looper for the
handler created from it, all the runnables added to that handler will be running in another
individual thread.
the animation thread and the media play thread should be running in the different threads not
scheduling in the same one.
hope, it helps.
the HandlerThread usage and some discuss looks like this :
How to create a Looper thread, then send it a message immediately?
maybe it is caused by your miss arranged codes, i take it a try on my nexus 4 with android version 4.4.2, even no any cache tech, the animation and music works like a charm...
here is the major codes :
public class MainActivity extends Activity implements View.OnClickListener {
protected static final String TAG = "test002" ;
protected static final int UPDATE_ANI = 0x0701;
protected static final int UPDATE_END = 0x0702;
protected static final int[] ANI_IMG_IDS = {R.raw.img1, R.raw.img2, R.raw.img3, R.raw.img4,
R.raw.img5, R.raw.img6, R.raw.img7};
protected static final int[] BTN_IDS = {R.id.btnStart, R.id.btnStop};
protected android.os.Handler aniHandler = null; // async update
protected boolean isAniRunning = false ;
protected int aniImgIndex = 0 ;
protected ImageView aniImgView = null ;
protected MediaPlayer mediaPly = null ;
// animation timer
class AniUpdateRunnable implements Runnable {
public void run() {
Message msg = null ;
while (!Thread.currentThread().isInterrupted() && isAniRunning) {
msg = new Message();
msg.what = UPDATE_ANI;
aniHandler.sendMessage(msg);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break ;
}
}
msg = new Message() ;
msg.what = UPDATE_END ;
aniHandler.sendMessage(msg) ;
}
}
protected void prepareMediaPlayer(MediaPlayer mp, int resource_id) {
AssetFileDescriptor afd = getResources().openRawResourceFd(resource_id);
try {
mp.reset();
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength());
afd.close();
mp.prepare();
} catch (IllegalArgumentException e) {
Log.d(TAG, "IlleagalArgumentException happened - " + e.toString()) ;
} catch(IllegalStateException e) {
Log.d(TAG, "IllegalStateException happened - " + e.toString()) ;
} catch(IOException e) {
Log.d(TAG, "IOException happened - " + e.toString()) ;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// init : buttons onclick callback
{
Button btn;
int i;
for (i = 0; i < BTN_IDS.length; i++) {
btn = (Button) findViewById(BTN_IDS[i]);
btn.setOnClickListener(this);
}
}
// init : update animation handler callback
{
aniHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_ANI:
updateAniImages();
break ;
case UPDATE_END:
updateAniEnd();
break ;
default:
break;
}
}
};
}
// init : prepare image view
{
aniImgView = (ImageView)findViewById(R.id.imgAni) ;
mediaPly = MediaPlayer.create(this, R.raw.happyny) ;
mediaPly.setLooping(true);
}
}
protected void updateAniImages() {
if(aniImgIndex >= ANI_IMG_IDS.length) {
aniImgIndex = 0 ;
}
InputStream is = getResources().openRawResource(ANI_IMG_IDS[aniImgIndex]) ;
Bitmap bmp = (Bitmap) BitmapFactory.decodeStream(is) ;
aniImgView.setImageBitmap(bmp);
aniImgIndex++ ;
}
protected void updateAniEnd() {
aniImgIndex = 0 ;
aniImgView.setImageBitmap(null);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnStart:
isAniRunning = true ;
// no re-enter protectiion, should not be used in real project
new Thread(new AniUpdateRunnable()).start();
mediaPly.start();
break;
case R.id.btnStop:
isAniRunning = false ;
mediaPly.stop();
prepareMediaPlayer(mediaPly, R.raw.happyny);
break;
default:
break;
}
}
}
the major project codes and test apk should be find here :
apk installer
source code
In my application, I have a button that starts an AsyncTask that downloads data with coordinates for google maps, then draws a marker on the map at the following coordinates. I want to run this every 10 seconds until the user presses the button again.
Here's my code for the handler:
class handleMap{
Handler mHandler = new Handler();
Runnable mTask = new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
while(btnRefreshPressed == false){
try{
new getGoogleMap().execute();
mHandler.postDelayed(mTask, INTERVAL);
Thread.sleep(INTERVAL);
} catch(Exception e){
System.out.println(e.toString());
}
}
}
};
public void starReapetingClass (){
hMap.starReapetingClass();
}
public void stopDoing(){
mHandler.stopDoing();
}
}
And in the menubutton where it is called:
case R.id.id_Refresh:
handleMap hMap = new handleMap();
if(btnRefreshPressed == true){
menuItem = item;
menuItem.setActionView(R.layout.progressbar);
menuItem.expandActionView();
fRun += 1;
btnRefreshPressed = false;
hMap.run();
}else if(btnRefreshPressed == false){
if(fRun > 0){
menuItem.collapseActionView();
menuItem.setActionView(null);
}
btnRefreshPressed = true;
hMap.stopHandler();
}
This currently causes the application to freeze, and the system outputs a dialog saying that the app isn't responding, and asking if I want to close or wait.
I suspect it has to with the while statement, but I don't get any errors in logcat.
Thanks in advance.
Just use:
private int mSampleDurationTime = 10000;
private boolean continueToRun = true;
mHandler.postDelayed(mRunnable, mSampleDurationTime);
where mRunnable is your task:
private final Runnable mRunnable = new Runnable() {
//...
public void run() {
...
if(continueToRun == true){
mHandler.postDelayed(mRunnable, mSampleDurationTime);
}
}
...
};
First time you call postDelayed and invoke new Runnable(). After, if you want to continue,
call the same method into run()