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上进行debugging时,它给我提供了以下信息:

 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 ,我已经评论了所有的声音部分,只是离开了振动器,当它运行时没有问题。 但是ToneGeneratorRingtone都造成了这个问题

Solutions Collecting From Web of "ToneGenerator非常沉重地降低了应用程序的速度"

我实际上回答了我自己的问题,这个问题(在这种情况下)是我经常打电话beep()

我的代码是:

 if ((secs % beepTime == 0) && beep) beep(); 

但我真正想要的是在毫秒内进行计算。 我的方式导致调用beep() 100次(代码更新每10毫秒)。

你可以使用Thread:

 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()没有UI代码,最好在LocalService.java执行,因为它更容易pipe理。