Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 219
user2386276 Avatar asked Jan 28 '17 03:01

user2386276


1 Answers

I actually answered my own question, the issue (in this case) was that I was calling beep() way too often.

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

like image 61
user2386276 Avatar answered Oct 04 '22 10:10

user2386276