Android MIDI Threading InteruptedException - 触后消息

yun*_*yun 11 java midi multithreading android interrupted-exception

试图在我的Android应用程序上运行MIDI.我正在按照midisuite示例来配置我的应用程序,除了触后功能,它工作正常.每当我尝试触发触后时,我都会遇到线程异常类型 InteruptedException.我该如何防止这个线程问题?我对多线程的了解并不是最好的,否则我已经想到了这一点.我现在真正能说的是,消息发送得太快,线程还没有从睡眠呼叫中唤醒.

我用我的代码跟着github repo如下:

MidiReceiver 子类:

@TargetApi(Build.VERSION_CODES.M)
public class MidiEngine extends MidiReceiver {

    public  AudioActivity activity;
    private MidiEventScheduler eventScheduler;
    private MidiFramer midiFramer;
    private MidiReceiver midiReceiver = new MyReceiver();

    private Thread mThread;
    private boolean go;
    private int mProgram;

    public MidiEngine() {
        this(new AudioActivity());
    }

    public MidiEngine(AudioActivity activity) {
        this.activity = activity;
        midiReceiver = new MyReceiver();
        midiFramer = new MidiFramer(midiReceiver);
    }

    public AudioActivity getActivity() {
        return this.activity;
    }

    /* This will be called when MIDI data arrives. */
    @Override
    public void onSend(byte[] data, int offset, int count, long timestamp)
            throws IOException {
        if (eventScheduler != null) {
            if (!MidiConstants.isAllActiveSensing(data, offset, count)) {
                eventScheduler.getReceiver().send(data, offset, count,
                        timestamp);
            }
        }
    }

    // Custom Listener to send to correct methods
    private class MyReceiver extends MidiReceiver {
        @Override
        public void onSend(byte[] msg, int offset, int count, long timestamp) throws IOException {
            byte command    = (byte)(msg[0] & MidiConstants.STATUS_COMMAND_MASK);
            int channel     = (byte)(msg[0] & MidiConstants.STATUS_CHANNEL_MASK);

            switch (command) {
                case MidiConstants.STATUS_NOTE_ON:
                    activity.keyDown(i, msg[1], msg[2]);
                    break;

                case MidiConstants.STATUS_NOTE_OFF:
                    activity.keyUp(channel, msg[1]);
                    break;

                case MidiConstants.STATUS_POLYPHONIC_AFTERTOUCH:
                    activity.keyDown(channel, msg[1], msg[2]);
                    break;

                case MidiConstants.STATUS_PITCH_BEND:
                    activity.pitchBendAction(channel, (msg[2] << 7) + msg[1]);
                    break;

                case MidiConstants.STATUS_CONTROL_CHANGE:
                    activity.ccAction(channel, msg[1], msg[2]);
                    break;

                case MidiConstants.STATUS_PROGRAM_CHANGE:
                    mProgram = msg[1];
                    break;

                default:
                    break;
            }
        }
    }

    class MyRunnable implements Runnable {
        @Override
        public void run() {
            do {
                try {
                    activity.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                processMidiEvents();
                            }
                            catch (Exception e) {
                                Log.e("Java", "SynthEngine background thread exception.", e);
                            }
                        }
                    });
                    Thread.sleep(100);
                }
                catch (InterruptedException e) {
                    Log.e("Java", "Threading exception", e);
                }
            }
            while (go);
        }
    }

    /**
     * @throws IOException
     *
     */
    private void processMidiEvents() throws IOException {
        long now = System.nanoTime();
        MidiEventScheduler.MidiEvent event = (MidiEventScheduler.MidiEvent) eventScheduler.getNextEvent(now);
        while (event != null) {
            midiFramer.send(event.data, 0, event.count, event.getTimestamp());
            eventScheduler.addEventToPool(event);
            event = (MidiEventScheduler.MidiEvent) eventScheduler.getNextEvent(now);
        }
    }

    public void start() {
        stop();
        go = true;
        mThread = new Thread(new MyRunnable());
        mThread.setPriority(6);
        eventScheduler = new MidiEventScheduler();
        mThread.start();
    }

    public void stop() {
        go = false;
        if (mThread != null) {
            try {
                mThread.interrupt();
                mThread.join(500);
            }
            catch (Exception e) {

            }
            mThread = null;
            eventScheduler = null;
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

堆栈跟踪错误(第154行指的Thread.sleep是我的自定义Runnable类中的部分):

Java: Threading exception
      java.lang.InterruptedException
          at java.lang.Thread.sleep(Native Method)
          at java.lang.Thread.sleep(Thread.java:1031)
          at java.lang.Thread.sleep(Thread.java:985)
          at com.rfoo.midiapp.communication.MidiEngineInput$MyRunnable.run(MidiEngineInput.java:154)
                                                                     at java.lang.Thread.run(Thread.java:818)
Run Code Online (Sandbox Code Playgroud)

谢谢!

编辑:线程启动

Midi Device Service子类(线程将在设备连接或断开时启动).

@TargetApi(Build.VERSION_CODES.M)
public class MidiSynthDeviceService extends MidiDeviceService {
    private static final String TAG = "MidiSynthDeviceService";
    private boolean midiStarted = false;

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        AudioActivity.midiEngine.stop();
        super.onDestroy();
    }

    @Override
    // Declare the receivers associated with your input ports.
    public MidiReceiver[] onGetInputPortReceivers() {
        return new MidiReceiver[] { AudioActivity.midiEngine };
    }

    /**
     * This will get called when clients connect or disconnect.
     * You can use it to turn on your synth only when needed.
     */
    @Override
    public void onDeviceStatusChanged(MidiDeviceStatus status) {

        if (status.isInputPortOpen(0) && !midiStarted) {
            AudioActivity.midiEngine.start();
            midiStarted = true;
        } else if (!status.isInputPortOpen(0) && midiStarted){
            AudioActivity.midiEngine.stop();
            midiStarted = false;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

活动类:

public class AudioActivity extends AppCompatActivity {
    private Thread thread;
    public static MidiEngine midiEngine;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Layout inits
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);

        // Setup MIDI:
        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI)) {
            Toast.makeText(this, "MIDI not supported!", Toast.LENGTH_LONG).show();
        }
        else {
            midiEngine = new MidiEngine(this);
            setupMidi();
        }

        // Setup audio thread:
        if (thread == null) {
            thread = new Thread() {
                public void run() {
                    setPriority(Thread.MAX_PRIORITY);
                    // Runs an Open SL audio thread (C++)
                    // This generates a waveform. 
                    // AudioEngine is a wrapper class connecting C++ to Java
                    AudioEngine.runProcess();
                }
            }
        }
    }

    public void setupMidi() {
        if (activity == null) activity = (AudioActivity) getContext();

        mMidiManager = (MidiManager) activity.getSystemService(AudioActivity.MIDI_SERVICE);
        if (mMidiManager == null) {
            Toast.makeText(activity, "MidiManager is null!", Toast.LENGTH_LONG).show();
            return;
        }
        // Get Device Info
        MidiDeviceInfo deviceInfo = MidiTools.findDevice(mMidiManager, "RFOO", "AudioApp");

        // MIDI Input
        portIndex = 0;
        inputPortSelector = new MidiOutputPortConnectionSelector(mMidiManager, activity, R.id
                .inputListView, deviceInfo, portIndex);
        inputPortSelector.setConnectedListener(new MyPortsConnectedListener());

        midi_ch_input = 0;
        midi_ch_output = 0;
    }

    // Bunch of UI code here....
}
Run Code Online (Sandbox Code Playgroud)

Ren*_*tik 0

这确实是旧帖子,但我还是会回复:

当我调试 mainfest 中定义的 MidiDeviceService 中使用的 MidiReceiver 的 onSend 时,我可以清楚地看到它正在某个线程上运行,而不是 UI 线程。当我看到这段代码时,我可以清楚地看到多个相关问题。

我通过以下方式检查线程:

val Thread.isMain get() = Looper.getMainLooper().thread == this
Run Code Online (Sandbox Code Playgroud)

或者

val Thread.isMain2 get() = Looper.myLooper() == Looper.getMainLooper()
Run Code Online (Sandbox Code Playgroud)

是什么给了我同样的结果。我在文档中找不到任何疯狂的地方,但我的调试给了我明确的答案。我在这段代码中看不到任何同步或任何内容,因为我看不到 MidiEventScheduler 的代码,并且这不是 android api 的一部分,这是否是线程安全的?

我怀疑是这样。