If I understand your problem correctly, you have an independent source of events working on your own thread ( PitchDetectionHandler ) and SurfaceView , which you want to repaint into your own thread when an event occurs from the source. If so, then I think the whole idea with sleep(1000) is incorrect. You should keep track of actual events and respond to them, not sleep, waiting for them. And it seems that on Android, the easiest solution is to use the HandlerThread / Looper / Handler infrastructure:
Beware of errors in the following code; not only did not try, but did not even compile it.
import android.graphics.Canvas; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.view.SurfaceHolder; public class SurfacePitchDrawingHelper implements Handler.Callback, SurfaceHolder.Callback2 { private static final int MSG_DRAW = 100; private static final int MSG_FORCE_REDRAW = 101; private final Object _lock = new Object(); private SurfaceHolder _surfaceHolder; private HandlerThread _drawingThread; private Handler _handler; private float _lastDrawnCent; private volatile float _lastCent; private final boolean _processOnlyLast = true; @Override public void surfaceCreated(SurfaceHolder holder) { synchronized (_lock) { _surfaceHolder = holder; _drawingThread = new HandlerThread("SurfaceDrawingThread") { @Override protected void onLooperPrepared() { super.onLooperPrepared(); } }; _drawingThread.start(); _handler = new Handler(_drawingThread.getLooper(), this);
And then in your activity class you will do something like
private SurfaceView surfaceView; private SurfacePitchDrawingHelper surfacePitchDrawingHelper = new SurfacePitchDrawingHelper(); ... @Override protected void onCreate(Bundle savedInstanceState) { ... surfaceView = (SurfaceView) findViewById(R.id.surfaceView); surfaceView.getHolder().addCallback(surfacePitchDrawingHelper); int sr = 44100;
Note that SurfacePitchDrawingHelper encapsulates most of the drawing-related logic, and there is no need for your MySurfaceView subclass (which I think is a bad idea).
The basic idea is that SurfacePitchDrawingHelper creates a dedicated HandlerThread when creating a new Surface . HandlerThread + Looper + Handler provide a useful infrastructure for efficiently (effectively) starting an endless loop in a separate thread that waits for incoming messages and processes them one by one. Thus, its efficient open API, besides SurfaceHolder.Callback2 consists of one postRedraw method, which can be used to ask the drawing stream to perform another redraw, and this is exactly what the custom PitchDetectionHandler . A “request” is made by placing a message in a queue that will be processed by the drawing stream (more specifically, our custom Handler in this stream). I did not bother to reduce the real public API to "efficient" because it makes the code a little more complicated and I'm too lazy. But, of course, both "tools" can be transferred to the inner classes.
You have to make one important decision: whether the thread thread should be each incoming message (all cent values) so that it arrives or only the last one at the time of drawing creation. This can be especially important if the PitchDetectionHandler events much faster than the drawing stream can update the Surface . I believe that in most cases it’s normal to process only the last value from the PitchDetectionHandler , but I left both versions in the code for illustration. This difference is currently implemented in code using the _processOnlyLast field. Most likely, you should make this decision and just get rid of this almost constant field and code in irrelevant branches.
And of course, don't forget to put your actual drawing logic inside doRedraw
Refresh (why the back button does not work)
TL version; DR
Insult line
d.run();
Just comment on this!
Longer version
In our example, we can see that d is an AudioDispatcher that implements Runnable and, therefore, the run method is a method that will be called in a new thread. You may notice that this is important because several I / O operations are performed inside this method and the thread it is running on is blocked. Therefore, in your case, it blocked the main thread of the user interface. Few lines down you
new Thread(d, "Audio Dispatcher").start();
and this, apparently, is the right way to use AudioDispatcher
This is easy to see from the stack traces I asked in the comments for.
