I'm writing a timer app, with a service and beeping every 30 seconds (actually there's a drop down that changes that time).
However when I make the app beep the beep lasts very long and freezes the app, eventually (after about 5 seconds) it finishes and then the timer catches up. Why is this happening? How do I fix this? Here is my code:
MainActivity.java:
package com.example.servicetimer;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.media.ToneGenerator;
import android.net.Uri;
import android.os.Vibrator;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private Button startButton;
private Button pauseButton;
private Button resetButton;
private TextView timerValue;
private TextView timerValueMils;
private long miliTime;
private int beepTime = 0;
private boolean running = false;
private boolean beep = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
miliTime = 0L;
timerValue = (TextView) findViewById(R.id.timerValue);
timerValueMils = (TextView) findViewById(R.id.timerValueMils);
registerReceiver(uiUpdated, new IntentFilter("TIMER_UPDATED"));
startButton = (Button) findViewById(R.id.startButton);
startButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
if (running){
return;
}
Intent i = new Intent(MainActivity.this,LocalService.class);
i.putExtra("timer",miliTime);
startService(i);
running = true;
resetButton.setVisibility(View.GONE);
}
});
pauseButton = (Button) findViewById(R.id.pauseButton);
pauseButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
if(!running){
return;
}
running = false;
stopService(new Intent(MainActivity.this, LocalService.class));
resetButton.setVisibility(View.VISIBLE);
}
});
resetButton = (Button) findViewById(R.id.resetButton);
resetButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
stopService(new Intent(MainActivity.this, LocalService.class));
running = false;
miliTime = 0L;
((TextView) findViewById(R.id.timerValue)).setText(R.string.timerVal);
((TextView) findViewById(R.id.timerValueMils)).setText(R.string.timerValMils);
beep = false;
}
});
Spinner dropdown = (Spinner)findViewById(R.id.spinner1);
ArrayAdapter<CharSequence> adapter = ArrayAdapter
.createFromResource(this, R.array.times,
android.R.layout.simple_spinner_item);
dropdown.setAdapter(adapter);
dropdown.setSelection(1);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
dropdown.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
// On selecting a spinner item
String label = parent.getItemAtPosition(position).toString();
beepTime = Integer.parseInt(label);
}
public void onNothingSelected(AdapterView<?> parent) {
beepTime = 30;
}
});
}
private BroadcastReceiver uiUpdated = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//This is the part where I get the timer value from the service and I update it every second, because I send the data from the service every second. The coundtdownTimer is a MenuItem
miliTime = intent.getExtras().getLong("timer");
long secs = miliTime/1000;
int mins = (int) (secs/60);
secs = secs % 60;
if (secs > 0)
beep = true;
if ((secs % beepTime == 0) && beep)
beep();
int millis = (int) (miliTime % 1000);
timerValue.setText("" + mins + " "
+ String.format("%02d", secs));
timerValueMils.setText(String.format("%02d", millis/10));
}
public void beep(){
/*try {
Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification);
r.play();
} catch (Exception e) {
e.printStackTrace();
}*/
final ToneGenerator tg = new ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100);
tg.startTone(ToneGenerator.TONE_PROP_BEEP,100);
tg.stopTone();
tg.release();
/*ToneGenerator toneG = new ToneGenerator(AudioManager.STREAM_ALARM, 100);
toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200);*/
Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
// Vibrate for 500 milliseconds
v.vibrate(500);
}
};
}
LocalService.java:
package com.example.servicetimer;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;
import java.util.Timer;
import java.util.TimerTask;
public class LocalService extends Service
{
private static Timer timer;
private Context ctx;
private static long miliTime = 0;
public IBinder onBind(Intent arg0)
{
return null;
}
public void onCreate()
{
timer = new Timer();
super.onCreate();
ctx = this;
miliTime = 0;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
miliTime = intent.getExtras().getLong("timer");
timer = new Timer();
timer.scheduleAtFixedRate(new mainTask(), 0, 10);
return START_STICKY;
}
private class mainTask extends TimerTask
{
public void run()
{
miliTime += 10;
Intent i = new Intent("TIMER_UPDATED");
i.putExtra("timer",miliTime);
sendBroadcast(i);
}
}
public void onDestroy() {
super.onDestroy();
timer.cancel();
miliTime = 0L;
}
}
activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:background="@drawable/silver"
android:layout_height="match_parent" >
<TextView
android:id="@+id/timerValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/pauseButton"
android:layout_centerHorizontal="true"
android:layout_marginBottom="37dp"
android:textSize="40sp"
android:textColor="#000000"
android:text="@string/timerVal" />
<TextView
android:id="@+id/timerValueMils"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/timerValue"
android:layout_toEndOf="@+id/timerValue"
android:layout_above="@+id/pauseButton"
android:layout_centerHorizontal="true"
android:layout_marginBottom="45dp"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:textSize="20sp"
android:textColor="#000000"
android:text="@string/timerValMils" />
<Button
android:id="@+id/startButton"
android:layout_width="90dp"
android:layout_height="45dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginLeft="38dp"
android:layout_marginStart="38dp"
android:text="@string/startButtonLabel" />
<Button
android:id="@+id/pauseButton"
android:layout_width="90dp"
android:layout_height="45dp"
android:layout_alignBaseline="@+id/startButton"
android:layout_alignBottom="@+id/startButton"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="38dp"
android:layout_marginEnd="38dp"
android:text="@string/pauseButtonLabel" />
<RelativeLayout android:id="@+id/dropdown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/pauseButton"
android:layout_marginTop="37dp">
<TextView
android:id="@+id/secondsToBeep"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="37dp"
android:layout_marginStart="37dp"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp"
android:textSize="30sp"
android:textColor="#000000"
android:text="@string/beeps" />
<Spinner
android:id="@+id/spinner1"
android:dropDownWidth="80dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@android:drawable/btn_dropdown"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginEnd="40dp"
android:layout_marginRight="40dp"
android:layout_marginLeft="40dp"
android:layout_marginStart="40dp"
android:spinnerMode="dropdown"
android:popupBackground="@drawable/silver"/>
</RelativeLayout>
<Button
android:id="@+id/resetButton"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_below="@id/dropdown"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="50dp"
android:text="@string/resetButtonLabel"
android:visibility="gone"/>
</RelativeLayout>
I can add my AndroidManifest if necessary. On AndroidStudio on the debug it gives me the following information when it happens:
I/Choreographer: Skipped 35 frames! The application may be doing too much work on its main thread.
I/Choreographer: Skipped 175 frames! The application may be doing too much work on its main thread.
I/Choreographer: Skipped 44 frames! The application may be doing too much work on its main thread.
Should I be doing the beep in the service or something?
I'll add that I'm positive that this is from the ToneGenerator
, I've commented all the sound parts out and just left the Vibrator and when it runs there's no problem. But the ToneGenerator
and the Ringtone
both caused this problem
I actually answered my own question, the issue (in this case) was that I was calling
beep()
way too often.My code was:
but what I really wanted was to do the computation on the milliseconds. The way I have resulted in calling
beep()
100's of times (code updates every 10 ms).