Android需要打开很多文件或文件描述符时底层抛出“Too many open files”

Posted Engineer-Jsp

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android需要打开很多文件或文件描述符时底层抛出“Too many open files”相关的知识,希望对你有一定的参考价值。

android需要打开很多文件或文件描述符时底层抛出“Too many open files”

需求背景描述

A设备有个脚踏开关,当脚踏开关被踩下时下位机会给上位机上报B指令,上位机需要在收到B指令后播放对应的输出音效,而当用户抬脚时脚踏开关弹起,这时上位机就不需要再播放输出音效,也就是说如果用户踩下且长时间未抬脚时,从脚踏开关被踩下到弹起的这个过程需要输出音效一直播放且不能被中途打断更要有规律的连续播放

错误案例示范

    /**
     * start play
     * first take @link AssetFileDescriptor object into audio play queue, if audio play thread not started, then start audio play thread, otherwise don't doing anything
     *
     * @param assetFileDescriptor @link AssetFileDescriptor instance object
     */
    private void startPlay(AssetFileDescriptor assetFileDescriptor) 
        boolean isEmpty = audioPlayQueue.isEmpty();
        audioPlayQueue.add(assetFileDescriptor);
        if (isEmpty) 
            synchronized (queueDetectLock) 
                queueDetectLock.notify();
            
        
        if (audioPlayThread != null) return;
        release = false;
        audioPlayThread = new Thread(this);
        audioPlayThread.setName(TAG);
        audioPlayThread.start();
    

    /**
     * stop play
     */
    private void stopPlay() 
        if (audioPlayThread == null) return;
        release = true;
        synchronized (playLock) 
            playLock.notify();
        
    

    @Override
    public void onCompletion(MediaPlayer mediaPlayer) 
        synchronized (playLock) 
            playLock.notify();
        
    

    @Override
    public void run() 
        AssetFileDescriptor assetFileDescriptor;
        while (!release) 
            try 
                while (!audioPlayQueue.isEmpty()) 
                    assetFileDescriptor = audioPlayQueue.peek();//poll
                    if (assetFileDescriptor == null) 
                        audioPlayQueue.poll();
                        continue;
                    
                    play(assetFileDescriptor);
                    synchronized (playLock) 
                        // blocking until current audio assetFileDescriptor play completed
                        // Waiting for onCompletion method called, unlock this blocking lock
                        playLock.wait(3000);//2500
                    
                    audioPlayQueue.poll();
                    assetFileDescriptor.close();
                    if (release) break;
                
                // Waiting for next audio assetFileDescriptor
                synchronized (queueDetectLock) 
                    // isEmpty() may take some time, so we set timeout to detect next audio assetFileDescriptor
                    queueDetectLock.wait(3000);//500ms
                
             catch (Exception e) 
                e.printStackTrace();
            
        
        audioPlayThread = null;
    

首先分析出错代码的流程,当有需要播放的输出音效文件描述符时,会先判断队列是否未空然后把该音频文件给加入到播放队列当中去,再执行判断如果为空表示此时线程已经处于空闲等待状态,对其进行唤醒然后转而处理队列中的元素,当然这一切的前提是线程已被启动,所以当线程对象不为空时,说明此时播放线程已经处于工作状态

当线程进入工作状态时,最先执行的代码块是判断音频文件队列是否是空的,如果非空那就循环处理队列中的元素直至全部处理完毕为止。通过peek获取队首的第一个元素但不移除,如果音频文件为空则将其从队首的位置弹出且移除继续取自动收缩后的队列列首,如果非空则进行播放并且阻塞3000毫秒,阻塞时间根据音频文件的播放时长决定,如果音效播放时长小于1秒,那就可以阻塞1500毫秒,当音频播放完毕且onCompletion方法被回调时此时播放所playLock会被唤醒继续执行后面的逻辑,将该音频从列首弹出移除最后执行close,如果队列的元素被处理完后会进入队列检测状态,每3000毫秒对队列是否为空进行一次判断,当如果有新的音频文件需要播放时,此时3000毫秒的阻塞会被唤醒打断再次进入队列的元素处理逻辑

错误分析处理

上述错误示范代码看似没有问题实则已经存在了埋雷点,因为startPlay每次被调用都会执行一个文件FD的open操作,代码笔者没有贴上来,如果长时间没抬起脚踏就会产生问题导致程序崩溃。肯定很多小伙伴会说代码最后再每次音效播放完毕后都进行了闭环啊执行了close呀,这就是一个致命的地雷!Linux再底层对文件open是有数量限制的,也就是文件打开句柄。通常Linux默认一般都是1024个,也就是说不管你最后有没有close,只要open文件个数超出了1024程序会立马崩溃

根据项目实际情况进行分析,因为音效种类只有8种,所以只需要把音效文件的raw id或者本地路径跟文件的FD进行绑定就行了,也就是说只需要执行一次open然后将他们保存到map中去,需要的时候get一下就行了,再闭环的时候迭代map进行close就行,当然具体问题具体分析,每个项目的场景或许都不一样,但处理思路肯定大同小异

以上是关于Android需要打开很多文件或文件描述符时底层抛出“Too many open files”的主要内容,如果未能解决你的问题,请参考以下文章

Android需要打开很多文件或文件描述符时底层抛出“Too many open files”

Android需要打开很多文件或文件描述符时底层抛出“Too many open files”

在 Python 中使用管道文件描述符时如何检查 EOF?

android studio 中jni底层日志的打印

通过android意图打开在线pdf文件?

NPOI读取excel文件导出数据, 而此时文件正在打开中抛异常怎么办