当前位置: 首页 > 面试题库 >

UsbRequest.queue导致Android 3.1应用程序崩溃

贺俊材
2023-03-14
问题内容

我正在开发一个Android 3.1应用程序,该应用程序使用USB主机模式通过USB上的MIDI与我的键盘(Korg
M3)进行通信。这是在装有Android
4.0.3的Xoom上运行的。我可以通过USB接收MIDI消息而没有任何问题,但是将音符数据发送回键盘的效果是好坏参半,延迟半秒钟后便会频繁崩溃。

这是我在点击操作栏上的按钮发送注释时不断遇到的错误:

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

我已经检查/尝试以找出原因的内容:

  • 由于代码是多线程的,因此我在synchronized访问输出请求池,输入请求池(根据Android文档中的ADB示例)以及用于当前输出请求和相关ByteBuffer对象引用的自定义锁定对象方面具有Java 块。我已经构造了执行这些锁定的代码,以最大程度地减少发生死锁的可能性。
  • 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()语句已经在synchronized使用该_outputLock对象的块内发生。在Run()阅读器线程的方法中,我还必须使用以下命令将调用包装到requestWait()一个synchronized块中outputLock

UsbRequest request = null;
synchronized(_outputLock)
{
    // requestWait() and request.queue() appear not to be thread-safe.
    request = _connection.requestWait();
}

鉴于这种情况发生在while循环中并requestWait阻塞了线程,我发现的一个主要问题是,当试图将发送请求排队时,锁会导致饥饿。结果是,当应用程序及时接收传入的MIDI数据并对其进行操作时,输出的MIDI事件会大大延迟。

作为此问题的部分解决方案,我yieldwhile循环结束前插入了一条语句以使UI线程保持畅通。(UI线程被暂时用作注释事件,因为它是通过按下按钮触发的;它最终将使用单独的播放线程。)因此,它更好,但并不完美,因为在第一次输出注释之前还有相当长的延迟已发送。

更好的解决方案:

为了解决对异步读取和写入的互锁的需求,异步queue()requestWait()方法仅用于读取操作,这些操作保留在单独的“读取器”线程上。因此,该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
                    }
                }

            }           
        }
    }

起初,我担心异步代码与上述代码冲突(因为它们共享相同的UsbDeviceConnection),但这似乎不是问题,因为它们使用的是完全不同的UsbEndpoint实例。

好消息是,在我弹奏键盘的同时发送笔记时,应用程序运行更流畅且不会崩溃。

因此,一般而言(对于在单独端点上的双向USB通信),异步方法似乎最适合于读/输入操作,在这种情况下,我们无需担心定义轮询行为(无论这是否在内部发生)
),而同步bulkTransfer()方法可以更好地服务于输出/发送操作。



 类似资料:
  • 这里是Android开发者新手。我在MainActivity中使用recyclerview,应用程序不断崩溃。 任何帮助都将受到赞赏! 编辑:对不起,我是新来的。我已经附加了Logcat。和其他xml文件。谢谢 这是我的代码: 列出你的布局。xml: activity_main.xml: } ProductAdapter。java类: } Logcat: 致命异常:主进程:e.wolverine2

  • 我正在使用内置于Web View的Android开发浏览器。其中我面临的一个问题是,当我访问http://crashmybrowser.com测试浏览器上的选项卡崩溃时,我的整个浏览器应用程序都会崩溃。但是,当在chrome或Opera上进行相同的测试时,这些浏览器会在崩溃中幸存下来,并且只有特定的选项卡崩溃是由于访问上述网站而预期的结果。有人能帮助理解我如何在使用Webview的浏览器上处理此崩

  • 问题内容: 我已经在我的应用中实现了GCM通知。我现在尝试在用户注销时注销该应用程序。我正在使用以下代码。执行此代码时,它将导致应用程序崩溃,并显示以下logcat: 这是代码: 问题答案: 将支持库更新为25.0.0后,我遇到了同样的问题。对我来说,更新下面的库之后,在应用程序gradle文件中,问题消失了。

  • 当我初始化的元素应用程序崩溃,如果我不初始化任何元素的应用程序显示的布局只是罚款和做一些基本的功能,我已经添加了使用在XML的onClick属性的方法 我需要从EditText元素中提取数据来进行计算。 java代码: 坠机记录: 致命例外:主进程:com.ajaydubey.fifamobilebulktradeprofitcounter,PID:17627java.lang.运行时异常:无法启

  • 我试图从我的框架中获得轮廓,这就是我所做的: .................................................... 程序在处崩溃,我收到以下错误消息: OpenCV错误:不支持的格式或格式组合([开始]FindContour只支持8uC1和32sC1图像)在未知的功能,文件......\src\openc v\模块\imgproc\src\contours.cp

  • 随着xamarin的最新更新(安装了xamarin android N sdk、xamarin studio 6.1等),当设备与调试器断开连接时,我的android应用程序会崩溃。当调试器连接到设备时,它可以很好地工作。 我已经尝试过Nexus 7、三星Galaxy Note 2和Redmi Note 3。但是当我在断开设备与调试器的连接后尝试运行应用程序时,它不起作用。 共享相同的堆栈跟踪和设