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).