Java UsbRequest.queue崩溃Android 3.1应用程序

Java UsbRequest.queue崩溃Android 3.1应用程序,java,android,crash,Java,Android,Crash,我正在开发一个Android 3.1应用程序,它使用USB主机模式通过USB上的MIDI与我的键盘(Korg M3)通信。这是在安装了Android 4.0.3的Xoom上运行的。我能够通过USB毫无问题地接收MIDI消息,但将音符数据发送回键盘的成功与否参差不齐,在半秒延迟后频繁崩溃 以下是我在点击操作栏上的按钮发送便笺时不断遇到的错误: E/dalvikvm(6422):JNI错误(应用程序错误):访问了过时的全局引用0x1da0020a(大小为130的表中的索引130) 我检查/试图找出原

我正在开发一个Android 3.1应用程序,它使用USB主机模式通过USB上的MIDI与我的键盘(Korg M3)通信。这是在安装了Android 4.0.3的Xoom上运行的。我能够通过USB毫无问题地接收MIDI消息,但将音符数据发送回键盘的成功与否参差不齐,在半秒延迟后频繁崩溃

以下是我在点击操作栏上的按钮发送便笺时不断遇到的错误:

E/dalvikvm(6422):JNI错误(应用程序错误):访问了过时的全局引用0x1da0020a(大小为130的表中的索引130)

我检查/试图找出原因的内容:

  • 由于代码是多线程的,所以我有Java
    synchronized
    块,用于访问输出请求池、输入请求池(根据Android文档中的),以及当前输出请求的自定义锁对象&关联的
    ByteBuffer
    对象引用。我已经构建了执行这些锁的代码,以最小化死锁发生的可能性
  • 从相关请求池检索可用的
    UsbRequest
    对象时,我将clientData引用设置为新的
    ByteBuffer
    对象,而不是重用以前关联的
    ByteBuffer
    对象并对其调用
    clear()
  • 我在代码中的关键点添加了大量的日志调用(针对logCat),以尝试跟踪失败的确切位置。我发现错误最终发生在以下几点(在此之前,此代码可以正常工作几次):

    public void sendMidiData()
    {
    已同步(_outputLock)
    {
    if(_currentOutputRequest==null | | | u outputBuffer.position()<2)
    返回;
    Log.d(_标记,“排队发送请求”);
    ////错误-发生在下一条语句中:
    _currentOutputRequest.queue(_outputBuffer,_maxPacketSize);
    d(_标记,“发送请求排队;重置引用…”);
    //初始化下一个数据包
    _currentOutputRequest=null;//GetAvailablesAndRequest();
    _outputBuffer=null;//(ByteBuffer)_currentOutputRequest.getClientData();
    d(_标记,“输出请求和缓冲区引用集”);
    }
    }
    
  • 我还尝试将
    \u currentOutputRequest
    \u outputBuffer
    引用设置为
    null
    ,以便仅在将下一个MIDI事件写入缓冲区时检索可用请求。如果
    null
    被原始调用替换(如注释所示),则立即检索下一个可用请求。这没有什么区别

是否知道是什么导致了这个问题?这可能是以前在Android中未发现的错误吗?搜索返回的错误不会带来太多结果;主要参考NDK编程(我没有这么做)


干杯。

好吧,我有了一点突破,发现了两个问题:

  • 调用
    \u currentOutputRequest.queue(\u outputBuffer,\u maxPacketSize)
    正在传递整个缓冲区容量
    \u maxPacketSize
    ,该容量的常量值为64(字节)。显然,这只适用于批量读取,其中最多可读取64个字节;批量发送请求需要指定发送的确切字节数
  • \u currentOutputRequest.queue()
    \u connection.requestWait()
    方法调用似乎不是线程安全的,特别是在
    UsbDeviceConnection
    的实现中(这是
    \u连接的类型)。我怀疑UsbRequest
    \u currentOutputRequest
    在排队发送请求时在内部使用了UsbConnection对象
我是如何解决这个问题的:

queue()
的调用更改为:

_currentOutputRequest.queue(_outputBuffer, _outputBuffer.position());
对于第二个问题,
queue()
语句已经使用
\u outputLock
对象出现在
synchronized
块中。在读卡器线程的
Run()
方法中,我必须使用
outputLock
将对
requestWait()
的调用包装在
同步的
块中:

UsbRequest request = null;
synchronized(_outputLock)
{
    // requestWait() and request.queue() appear not to be thread-safe.
    request = _connection.requestWait();
}
假设这发生在
循环中,而
循环和
requestWait
阻塞线程,我发现的一个主要问题是,在尝试对发送请求排队时,锁会导致饥饿。结果是,当应用程序及时接收并处理传入的MIDI数据时,输出的MIDI事件会显著延迟

作为对此的部分修复,我在
while
循环结束之前插入了
yield
语句,以保持UI线程的畅通。(UI线程暂时被用作通过按下按钮触发的便笺事件;这最终将使用单独的播放线程。)因此,它更好,但并不完美,因为在发送第一个输出便笺之前仍有相当长的延迟

更好的解决方案:

为了解决异步读写对互锁的需求,异步
queue()
requestWait()
方法仅用于读操作,它们保留在单独的“reader”线程上。因此,不需要
synchronized
块,因此可以将该段缩减为以下内容:

UsbRequest request = _connection.requestWait();
对于写/发送操作,其核心被移动到一个单独的线程,用于执行同步
bulkTransfer()
语句:

private class MidiSender extends Thread
{
    private boolean _raiseStop = false;
    private Object _sendLock = new Object();

    private LinkedList<ByteBuffer> _outputQueue = new LinkedList<ByteBuffer>();

    public void queue(ByteBuffer buffer)
    {
        synchronized(_sendLock)
        {
            _outputQueue.add(buffer);
            // Thread will most likely be paused (to save CPU); need to wake it
            _sendLock.notify();
        }
    }

    public void raiseStop()
    {
        synchronized (this)
        {
            _raiseStop = true;
        }

        //Thread may be blocked waiting for a send
        synchronized(_sendLock)
        {
            _sendLock.notify();
        }
    }

    public void run()
    {
        while (true)
        {
            synchronized (this)
            {
                if (_raiseStop) 
                    return;
            }

            ByteBuffer currentBuffer = null;
            synchronized(_sendLock)
            {
                if(!_outputQueue.isEmpty())
                    currentBuffer =_outputQueue.removeFirst(); 
            }

            while(currentBuffer != null)
            {
                // Here's the synchronous equivalent (timeout is a reasonable 0.1s):
                int transferred = _connection.bulkTransfer(_outPort, currentBuffer.array(), currentBuffer.position(), 100);

                if(transferred < 0)
                    Log.w(_tag, "Failed to send MIDI packet");

                //Process any remaining packets on the queue
                synchronized(_sendLock)
                {
                    if(!_outputQueue.isEmpty())
                        currentBuffer =_outputQueue.removeFirst();
                    else
                        currentBuffer = null;
                }
            }

            synchronized(_sendLock)
            {
                try
                {
                    //Sleep; save unnecessary processing
                    _sendLock.wait();
                }
                catch(InterruptedException e)
                {
                    //Don't care about being interrupted
                }
            }

        }           
    }
}
私有类扩展线程
{
私有布尔值_raiseStop=false;
私有对象_sendLock=新对象();
私有LinkedList_outputQueue=新LinkedList();
公共无效队列(ByteBuffer缓冲区)
{
已同步(_sendLock)
{
_添加(缓冲区);
//线程很可能会暂停(至sav
private class MidiSender extends Thread
{
    private boolean _raiseStop = false;
    private Object _sendLock = new Object();

    private LinkedList<ByteBuffer> _outputQueue = new LinkedList<ByteBuffer>();

    public void queue(ByteBuffer buffer)
    {
        synchronized(_sendLock)
        {
            _outputQueue.add(buffer);
            // Thread will most likely be paused (to save CPU); need to wake it
            _sendLock.notify();
        }
    }

    public void raiseStop()
    {
        synchronized (this)
        {
            _raiseStop = true;
        }

        //Thread may be blocked waiting for a send
        synchronized(_sendLock)
        {
            _sendLock.notify();
        }
    }

    public void run()
    {
        while (true)
        {
            synchronized (this)
            {
                if (_raiseStop) 
                    return;
            }

            ByteBuffer currentBuffer = null;
            synchronized(_sendLock)
            {
                if(!_outputQueue.isEmpty())
                    currentBuffer =_outputQueue.removeFirst(); 
            }

            while(currentBuffer != null)
            {
                // Here's the synchronous equivalent (timeout is a reasonable 0.1s):
                int transferred = _connection.bulkTransfer(_outPort, currentBuffer.array(), currentBuffer.position(), 100);

                if(transferred < 0)
                    Log.w(_tag, "Failed to send MIDI packet");

                //Process any remaining packets on the queue
                synchronized(_sendLock)
                {
                    if(!_outputQueue.isEmpty())
                        currentBuffer =_outputQueue.removeFirst();
                    else
                        currentBuffer = null;
                }
            }

            synchronized(_sendLock)
            {
                try
                {
                    //Sleep; save unnecessary processing
                    _sendLock.wait();
                }
                catch(InterruptedException e)
                {
                    //Don't care about being interrupted
                }
            }

        }           
    }
}
if (usb_request_queue(request)) {
    request->buffer = NULL;
    return false;
} else {
    // save a reference to ourselves so UsbDeviceConnection.waitRequest() can find us
    // we also need this to make sure our native buffer is not deallocated
    // while IO is active
    request->client_data = (void *)env->NewGlobalRef(thiz);
    return true;
}