我正在编写一个计时器应用程序,每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
都导致了这个问题
答案 0 :(得分:5)
我实际上回答了我自己的问题,问题(在这种情况下)是我经常调用beep()
方式。
我的代码是:
if ((secs % beepTime == 0) && beep)
beep();
但我真正想要的是在几毫秒内进行计算。我的方式导致调用beep()
100次(代码每10毫秒更新一次)。
答案 1 :(得分:0)
在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
中进行操作,因为它更易于管理。
答案 2 :(得分:0)
你可以使用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));
}
...
}