简体   繁体   English

ToneGenerator非常重视app

[英]ToneGenerator slows down app very heavily

I'm writing a timer app, with a service and beeping every 30 seconds (actually there's a drop down that changes that time). 我正在写一个计时器应用程序,每30秒发一次服务并发出哔哔声(实际上有一个下拉时间会改变那个时间)。

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. 然而,当我让应用程序发出哔哔声时,哔声持续很长时间并冻结应用程序,最终(大约5秒后)它完成然后计时器赶上。 Why is this happening? 为什么会这样? How do I fix this? 我该如何解决? Here is my code: 这是我的代码:

MainActivity.java: 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: 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: 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. 如有必要,我可以添加我的AndroidManifest。 On AndroidStudio on the debug it gives me the following information when it happens: 在调试的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.

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. 我要补充一点,我很肯定这是来自ToneGenerator ,我已经评论了所有声音部分,只是离开了振动器,当它运行时没有问题。 But the ToneGenerator and the Ringtone both caused this problem 但是ToneGeneratorRingtone都引发了这个问题

I actually answered my own question, the issue (in this case) was that I was calling beep() way too often. 我实际上回答了我自己的问题,问题(在这种情况下)是我经常调用beep() 方式

My code was: 我的代码是:

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

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). 我的方式导致beep() 100次(代码每10毫秒更新一次)。

Try this in your beep() method (run your code on a background Thread): 在你的beep()方法中尝试这个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 enables proper and easy use of the UI thread. AsyncTask可以正确,方便地使用UI线程。

Should I be doing the beep in the service or something? 我应该在服务中发出嘟嘟声吗?

As you have no UI code in beep() , it would be better to do it in LocalService.java as it is easier to manage. 由于beep()没有UI代码,因此更容易在LocalService.java执行,因为它更易于管理。

you can use Thread: 你可以使用Thread:

Thread thread = new Thread(new Runnable() {
    public void run() {
      // your beep method
    });

Because you use the same beep sound, you only need to use thread.start() to call the beep method: 因为你使用相同的蜂鸣声,你只需要使用thread.start()来调用beep方法:

// 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));


        }
    ...
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM