java.io.FileNotFoundException: No content provider: http

首先这个问题无法修复。
使用MediaPlayer的时候播放网络音频出现了以下错误,记录如下:
原代码:

    /**
     * 播放音频文件
     */
    private void playVoice(String voicePath){
        MediaPlayer player = MediaPlayer.create(context, Uri.parse(voicePath));
        player.start();
    }

错误日志:

W/MediaPlayer: Couldn't open https://dq-oss.oss-cn-beijing.aliyuncs.com/...
    java.io.FileNotFoundException: No content provider: https://dq-oss.oss-cn-beijing.aliyuncs.com/audio_recordrecord_20200414_14_32_58.mp3
        at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1688)
        at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1518)
        at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1435)
        at android.media.MediaPlayer.attemptDataSource(MediaPlayer.java:1154)
        at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1112)
        at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1019)
        at android.media.MediaPlayer.create(MediaPlayer.java:926)
        at android.media.MediaPlayer.create(MediaPlayer.java:903)
        at android.media.MediaPlayer.create(MediaPlayer.java:882)
        at com.example.qcsdk.adapter.ChatP2PAdapter.playVoice(ChatP2PAdapter.java:255)
        at com.example.qcsdk.adapter.ChatP2PAdapter.access$000(ChatP2PAdapter.java:40)
        at com.example.qcsdk.adapter.ChatP2PAdapter$2.onClick(ChatP2PAdapter.java:222)
        at android.view.View.performClick(View.java:7161)
        at android.view.View.performClickInternal(View.java:7138)
        at android.view.View.access$3500(View.java:811)
        at android.view.View$PerformClick.run(View.java:27419)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:224)
        at android.app.ActivityThread.main(ActivityThread.java:7520)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)

这个错误是在小米上,虽然报错了但是还是会有声音,不过为了防止出现其他问题,还是进行了修复,修改后的代码如下:

    /**
     * 播放音频文件
     */
    private void playVoice(String voicePath){
//        MediaPlayer player = MediaPlayer.create(context, Uri.parse(voicePath));
        MediaPlayer player = new MediaPlayer();
        try {
            player.setDataSource(voicePath);
        }catch (IOException e){
            e.printStackTrace();
        }
        player.prepareAsync();
        player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mediaPlayer) {
                mediaPlayer.start();
            }
        });
    }

这个问题的原因是因为 Uri.parse(voicePath)把网络路径转换成了本地路径,这时候本地没有这个文件才出现这个问题,比如上述的https://dq-oss.oss-cn-beijing.aliyuncs.com/audio_recordrecord_20200414_14_32_58.mp3使用 Uri.parse(voicePath)转换为路径后结果为/audio_recordrecord_20200414_14_32_58.mp3,这样当然不能播放,不过有的手机即使报错了依然可以播放,还不知道是为什么。不过按照Android源码来看的话,是绝对会报错的。下面解释源码的问题。

问题主要出在MediaPlayer上面,当使用Mediaplayer执行setDataSource(@NonNull Context context, @NonNull Uri uri)函数时候,最终会执行到

setDataSource(@NonNull Context context, @NonNull Uri uri,
            @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies)

里面,这里我们看下这个函数的代码:
MediaPlayer.java

/**
     * Sets the data source as a content Uri.
     *
     * To provide cookies for the subsequent HTTP requests, you can install your own default cookie
     * handler and use other variants of setDataSource APIs instead. Alternatively, you can use
     * this API to pass the cookies as a list of HttpCookie. If the app has not installed
     * a CookieHandler already, this API creates a CookieManager and populates its CookieStore with
     * the provided cookies. If the app has installed its own handler already, this API requires the
     * handler to be of CookieManager type such that the API can update the manager’s CookieStore.
     *
     * <p><strong>Note</strong> that the cross domain redirection is allowed by default,
     * but that can be changed with key/value pairs through the headers parameter with
     * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to
     * disallow or allow cross domain redirection.
     *
     * @param context the Context to use when resolving the Uri
     * @param uri the Content URI of the data you want to play
     * @param headers the headers to be sent together with the request for the data
     *                The headers must not include cookies. Instead, use the cookies param.
     * @param cookies the cookies to be sent together with the request
     * @throws IllegalArgumentException if cookies are provided and the installed handler is not
     *                                  a CookieManager
     * @throws IllegalStateException    if it is called in an invalid state
     * @throws NullPointerException     if context or uri is null
     * @throws IOException              if uri has a file scheme and an I/O error occurs
     */
    public void setDataSource(@NonNull Context context, @NonNull Uri uri,
            @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies)
            throws IOException {
        if (context == null) {
            throw new NullPointerException("context param can not be null.");
        }

        if (uri == null) {
            throw new NullPointerException("uri param can not be null.");
        }

        if (cookies != null) {
            CookieHandler cookieHandler = CookieHandler.getDefault();
            if (cookieHandler != null && !(cookieHandler instanceof CookieManager)) {
                throw new IllegalArgumentException("The cookie handler has to be of CookieManager "
                        + "type when cookies are provided.");
            }
        }

        // The context and URI usually belong to the calling user. Get a resolver for that user
        // and strip out the userId from the URI if present.
        final ContentResolver resolver = context.getContentResolver();
        final String scheme = uri.getScheme();
        final String authority = ContentProvider.getAuthorityWithoutUserId(uri.getAuthority());
        if (ContentResolver.SCHEME_FILE.equals(scheme)) {
            setDataSource(uri.getPath());
            return;
        } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
                && Settings.AUTHORITY.equals(authority)) {
            // Try cached ringtone first since the actual provider may not be
            // encryption aware, or it may be stored on CE media storage
            final int type = RingtoneManager.getDefaultType(uri);
            final Uri cacheUri = RingtoneManager.getCacheForType(type, context.getUserId());
            final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
            if (attemptDataSource(resolver, cacheUri)) {
                return;
            } else if (attemptDataSource(resolver, actualUri)) {
                return;
            } else {
                setDataSource(uri.toString(), headers, cookies);
            }
        } else {
            // Try requested Uri locally first, or fallback to media server
            if (attemptDataSource(resolver, uri)) {
                return;
            } else {
                setDataSource(uri.toString(), headers, cookies);
            }
        }
    }

因为路径是网络链接,以http或者https开头,所以会执行到最后的else代码块中,然后会执行到 attemptDataSource(ContentResolver resolver, Uri uri)函数,下面看下这个函数:
MediaPlayer.java

    private boolean attemptDataSource(ContentResolver resolver, Uri uri) {
        try (AssetFileDescriptor afd = resolver.openAssetFileDescriptor(uri, "r")) {
            setDataSource(afd);
            return true;
        } catch (NullPointerException | SecurityException | IOException ex) {
            Log.w(TAG, "Couldn't open " + (uri == null ? "null uri" : uri.toSafeString()), ex);
            return false;
        }
    }

可以看到第一行代码是用来判断Asset的资源文件,这时候就会出现报错,因为我们的资源是网络资源…
暂时不知道怎么解决这个事情,等Android更新吧…

想验证这个问题的话把try里面的代码

AssetFileDescriptor afd = resolver.openAssetFileDescriptor(uri, "r")

复制出来测试下就行了

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: java.io.filenotfoundexception: openfailed: enoent (no such file or directory)是Java程序中常见的异常之一,表示在指定路径或目录下找不到文件或文件夹。 通常这种异常会在程序尝试打开一个文件时发生,可能是因为路径错误,文件不存在或者没有权限等原因导致。解决该异常的方法是检查程序中读取或写入文件的路径是否正确,确认文件是否存在,以及确保程序有足够的权限访问目标文件。 在处理该异常时,可以使用Java的异常处理机制来捕获该异常并给出相应的提示信息,如未找到文件或目录,请检查路径是否正确、文件是否存在、是否有权限等。同时,对于无法处理的情况,可以通过向用户显示错误消息或记录日志来提醒用户或开发者。 总之,遇到java.io.filenotfoundexception: openfailed: enoent (no such file or directory)异常时,一定不要惊慌,需要对代码进行仔细检查,找出问题所在,最终通过适当的处理方法来解决问题。 ### 回答2: 这个错误提示是Java编程中常见的IO异常之一。它产生的原因是程序尝试打开一个不存在的文件或文件夹。 当程序运行时,它会尝试打开一个指定的文件或文件夹。如果这个文件或文件夹不存在,那么Java就会抛出这个异常。通常这个错误提示会指示程序在执行open方法时出错,因为找不到指定的文件或文件夹。 如果你遇到了这个问题,首先要检查的是你的文件路径是否正确。你应该确保你指定的路径是一个存在的文件或文件夹。如果这个路径确实存在,那么你还需要检查该路径是否可读,是否被占用等等。 此外,这个错误提示也可能是由于文件权限的问题所致。如果你正在尝试读取或写入一个文件,但是你没有足够的权限来访问它,那么Java就会抛出这个异常。 总之,要解决这个问题,你需要仔细检查你的代码,确保文件路径正确并且你有足够的权限来访问它。如果问题仍未解决,你可能需要查看更多的异常信息来确定具体的原因。 ### 回答3: Java是一种运行于虚拟机上的编程语言。Java提供了一套完整的API,包括操作文件系统的API。在Java程序中,当我们想要打开一个文件时,会出现如下提示:java.io.filenotfoundexception: openfailed: enoent (no such file or directory)。 该异常表示在尝试打开该文件之前,Java未找到指定的文件或目录。通常情况下,这种错误会由于路径错误或文件名输入错误而引起。解决这个问题的方法如下: 1. 检查文件路径是否正确: 当我们向打开文件时,指定的文件路径必须是正确的。如果文件路径不正确,则会出现java.io.filenotfoundexception异常。因此,我们需要确保路径是指向正确的文件或目录。 2. 检查文件名是否正确: 文件名可能会输入错误,例如,大小写可能会错误地输入或者忘记输入扩展名。在这种情况下,我们需要检查文件名是否正确,并且输入的大小写是否符合实际情况。 3. 检查文件是否存在: 在尝试打开文件之前,我们需要确保该文件真正存在。因此,我们需要检查该文件是否存在,如果不存在,则需要创建新文件或引发NoSuchFileException异常。 总之,当我们在Java程序中打开文件时,如果出现java.io.filenotfoundexception: openfailed: enoent (no such file or directory)异常,我们需要首先检查路径和文件名是否正确,并且确保文件真实存在。如果以上三种方法无法解决问题,我们需要检查文件系统中的权限问题,或者文件是否正在使用等其他问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值