java – UsbRequest.queue崩溃Android 3.1应用程序
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了java – UsbRequest.queue崩溃Android 3.1应用程序,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含5080字,纯文字阅读大概需要8分钟。
内容图文
![java – UsbRequest.queue崩溃Android 3.1应用程序](/upload/InfoBanner/zyjiaocheng/697/473085a76c454f83975a895b6038784f.jpg)
我正在开发一个Android 3.1应用程序,它使用USB主机模式通过USB over USB与我的键盘(Korg M3)进行通信.这是在安装了Android 4.0.3的Xoom上运行的.我能够通过USB接收MIDI信息而没有任何问题,但将笔记数据发送回键盘却取得了不同的成功,在半秒延迟后频繁崩溃.
这是我在操作栏上点按按钮发送注释时遇到的错误:
E/dalvikvm(6422): JNI ERROR (app bug): accessed stale global reference 0x1da0020a (index 130 in a table of size 130)
我检查/试图追查原因:
>由于代码是多线程的,我有Java同步块,包括对输出请求池,输入请求池(根据Android文档中的ADB sample)的访问,以及当前输出请求的自定义锁定对象&关联的ByteBuffer对象引用.我已经构造了执行这些锁的代码,以最大限度地减少发生死锁的可能性.
>从相关请求池中检索可用的UsbRequest对象时,我将clientData引用设置为新的ByteBuffer对象,而不是重用先前关联的ByteBuffer对象并对其调用clear().
>我已经在代码的关键点添加了大量的日志记录调用(对于logCat),以尝试跟踪其确切失败的位置.我发现错误最终发生在以下几点(此代码在此之前可以正常运行几次):
public void sendMidiData()
{
synchronized(_outputLock)
{
if(_currentOutputRequest == null || _outputBuffer.position() < 2)
return;
Log.d(_tag, "Queuing Send request");
//// ERROR - happens in this next statement:
_currentOutputRequest.queue(_outputBuffer, _maxPacketSize);
Log.d(_tag, "Send request queued; resetting references...");
//Initialise for next packet
_currentOutputRequest = null; //getAvailableSendRequest();
_outputBuffer = null; //(ByteBuffer)_currentOutputRequest.getClientData();
Log.d(_tag, "Output request & buffer references set.");
}
}
>我还尝试将_currentOutputRequest和_outputBuffer引用设置为null,以便仅在将下一个MIDI事件写入缓冲区时才检索可用请求.如果null被原始调用替换(如注释所示),则立即检索下一个可用请求.这没有任何区别.
有什么想法会导致这个问题吗?这可能是Android中以前未被发现的错误吗?对返回的错误的搜索不会带来太多影响;主要参考NDK编程(我不做).
干杯.
解决方法:
好的,我有一点突破,发现了两个问题:
>调用_currentOutputRequest.queue(_outputBuffer,_maxPacketSize);正在传递整个缓冲区容量_maxPacketSize,其常量值为64(字节).显然,这仅适用于批量读取,最多可读取64个字节;批量发送请求需要指定发送的确切字节数.
> _currentOutputRequest.queue()和_connection.requestWait()方法调用似乎不是线程安全的,特别是在UsbDeviceConnection(这是_connection的类型)的实现中.我怀疑UsbRequest _currentOutputRequest在排队发送请求时在内部使用UsbConnection对象.
我是如何解决的:
对queue()的调用更改为:
_currentOutputRequest.queue(_outputBuffer, _outputBuffer.position());
对于第二个问题,queue()语句已经使用_outputLock对象发生在synchronized块内.在读者线程的Run()方法中,我不得不使用outputLock在同步块中包装对requestWait()的调用:
UsbRequest request = null;
synchronized(_outputLock)
{
// requestWait() and request.queue() appear not to be thread-safe.
request = _connection.requestWait();
}
鉴于这发生在while循环中并且requestWait阻塞了线程,我发现的一个主要问题是在尝试对Send请求进行排队时锁会导致饥饿.结果是,当应用程序及时接收并作用于传入的MIDI数据时,输出的MIDI事件显着延迟.
作为对此的部分修复,我在while循环结束之前插入了一个yield语句,以保持UI线程不被阻塞. (UI线程暂时被用作音符事件由按钮按下触发;这最终将使用单独的播放线程.)因此它更好,但不完美,因为在第一个输出音符之前仍有相当长的延迟已发送.
更好的解决方案:
为了解决异步读取和写入的相互锁定的需求,异步queue()和requestWait()方法仅用于读取操作,这些操作保留在单独的“读取器”线程上.因此,不需要同步块,因此可以将此段简化为以下内容:
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
}
}
}
}
}
起初我担心异步代码与上面的内容冲突(因为它们共享相同的UsbDeviceConnection),但这似乎不是问题,因为它们使用的是完全不同的UsbEndpoint实例.
好消息是应用程序运行更顺畅,并且在我弹奏键盘的同时发送笔记时不会崩溃.
所以一般情况下(对于不同端点上的双向USB通信),似乎异步方法最适合读/输入操作,我们不需要担心定义轮询行为(这是否是内部发生的事情) ),同步bulkTransfer()方法更好地服务于输出/发送操作.
内容总结
以上是互联网集市为您收集整理的java – UsbRequest.queue崩溃Android 3.1应用程序全部内容,希望文章能够帮你解决java – UsbRequest.queue崩溃Android 3.1应用程序所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。