ToneGenerator очень сильно тормозит приложение
Я пишу приложение с таймером, со службой и каждые 30 секунд подает звуковой сигнал (на самом деле есть выпадающий список, который меняет это время)
Однако, когда я издаю звуковой сигнал приложения, звуковой сигнал длится очень долго и останавливает приложение, в конце концов (примерно через 5 секунд) он заканчивается, а затем таймер срабатывает. Почему это происходит? Как это исправить? Вот мой код:
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>
Я могу добавить свой AndroidManifest при необходимости. На AndroidStudio при отладке он дает мне следующую информацию, когда это происходит:
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.
Должен ли я делать сигнал в службе или что-то?
Я добавлю, что я уверен, что это из ToneGenerator
Я закомментировал все звуковые части и просто покинул вибратор, и когда он работает, проблем нет. Но ToneGenerator
и Ringtone
оба вызвали эту проблему
3 ответа
Я на самом деле ответил на свой вопрос, проблема (в данном случае) была в том, что я звонил beep()
слишком часто.
Мой код был:
if ((secs % beepTime == 0) && beep)
beep();
но то, что я действительно хотел, было делать вычисления за миллисекунды. То, как я привел в вызове beep()
100 раз (код обновляется каждые 10 мс).
Вы можете использовать тему:
Thread thread = new Thread(new Runnable() {
public void run() {
// your beep method
});
Поскольку вы используете один и тот же звуковой сигнал, вам нужно только использовать thread.start()
Чтобы вызвать метод звукового сигнала:
// make a new thread for beep only once.
Thread beepThread = new Thread(new Runnable() {
public void run() {
// call beep() method here
beep();
});
// move the beep method from BroadcastReceiver
public void beep() {
// your code implementation.
...
}
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)
// Call the thread here.
beepThread.start();
int millis = (int) (miliTime % 1000);
timerValue.setText("" + mins + " "
+ String.format("%02d", secs));
timerValueMils.setText(String.format("%02d", millis/10));
}
...
}
Попробуйте это в вашем beep()
метод (запустите ваш код в фоновом потоке):
AsyncTask.execute(new Runnable() {
@Override
public void run() {
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);
}
});
AsyncTask позволяет правильно и легко использовать поток пользовательского интерфейса.
Должен ли я делать сигнал в службе или что-то?
Поскольку у вас нет кода UI в beep()
было бы лучше сделать это в LocalService.java
как проще управлять.