You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio中使用Intent访问文件遇异常及文件位置问题

Android使用Intent访问图片/音频:解决FileUriExposedException与"media not found"问题

问题背景

作为Android Studio新手,尝试用Intent访问设备图片和音频,原代码使用Uri.fromFile()触发FileUriExposedException,已配置FileProvider,但修改后点击按钮提示"media not found",文件放在storage/emulated/0/image.png,需修正代码并确认文件位置。

原错误代码(触发FileUriExposedException)

public class MainActivity extends AppCompatActivity
{
    private Button mBtnPlayMP3, mBtnViewImg;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBtnPlayMP3 = (Button)findViewById(R.id.btnPlayMP3);
        mBtnViewImg = (Button)findViewById(R.id.btnViewImg);

        mBtnPlayMP3.setOnClickListener(btnPlayMP3OnClick);
        mBtnViewImg.setOnClickListener(btnViewImgOnClick);
    }

    private View.OnClickListener btnPlayMP3OnClick = new View.OnClickListener()
    {
        public void onClick(View v)
        {
            Intent it = new Intent(Intent.ACTION_VIEW);
            String sMp3File = Environment.getExternalStorageDirectory().getPath() +
                    File.separator + "song.mp3";
            File file = new File(sMp3File);
            boolean b = file.exists();
            it.setDataAndType(Uri.fromFile(file), "audio/*");
            startActivity(it);
        }
    };

    private View.OnClickListener btnViewImgOnClick = new View.OnClickListener()
    {
        public void onClick(View v)
        {
            Intent it = new Intent(Intent.ACTION_VIEW);
            String sImgFile = Environment.getExternalStorageDirectory().getPath() +
                    File.separator + "image.png";
            File file = new File(sImgFile);
            it.setDataAndType(Uri.fromFile(file), "image/*");
            startActivity(it);
        }
    };
}

触发的错误

android.os.FileUriExposedException: file:///storage/emulated/0/image.png exposed beyond app through Intent.getData()

已完成的FileProvider配置

AndroidManifest.xml中的配置

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.provider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/provider_paths" />
</provider>

res/xml/provider_paths.xml配置(优化后)

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <!-- 单个external-path即可覆盖外部存储根目录,无需重复定义 -->
    <external-path name="external_files" path="."/>
</paths>

修正后的完整MainActivity代码

import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider;
import java.io.File;
import java.util.Objects;

public class MainActivity extends AppCompatActivity {
    private Button mBtnPlayMP3, mBtnViewImg;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBtnPlayMP3 = findViewById(R.id.btnPlayMP3);
        mBtnViewImg = findViewById(R.id.btnViewImg);

        mBtnPlayMP3.setOnClickListener(btnPlayMP3OnClick);
        mBtnViewImg.setOnClickListener(btnViewImgOnClick);
    }

    private final View.OnClickListener btnPlayMP3OnClick = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            openMediaFile("song.mp3", "audio/*");
        }
    };

    private final View.OnClickListener btnViewImgOnClick = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            openMediaFile("image.png", "image/*");
        }
    };

    // 封装通用方法,减少重复代码
    private void openMediaFile(String fileName, String mimeType) {
        File externalDir = Environment.getExternalStorageDirectory();
        File mediaFile = new File(externalDir, fileName);

        // 先检查文件是否存在
        if (!mediaFile.exists()) {
            Toast.makeText(this, "文件不存在:" + mediaFile.getAbsolutePath(), Toast.LENGTH_LONG).show();
            return;
        }

        Uri fileUri;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            // Android 7.0及以上使用FileProvider生成安全Uri
            fileUri = FileProvider.getUriForFile(
                    Objects.requireNonNull(getApplicationContext()),
                    BuildConfig.APPLICATION_ID + ".provider",
                    mediaFile);
        } else {
            // 低版本直接使用文件Uri
            fileUri = Uri.fromFile(mediaFile);
        }

        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(fileUri, mimeType);
        // 授予临时读取权限
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

        // 检查是否有应用能处理该Intent,避免崩溃
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivity(intent);
        } else {
            Toast.makeText(this, "没有找到可打开该文件的应用", Toast.LENGTH_LONG).show();
        }
    }
}

文件位置与权限说明

  1. 文件放置位置

    • 你当前放置的storage/emulated/0/image.png是正确的,对应手机文件管理器的「内部存储」根目录。
    • 务必确认文件名及后缀完全匹配(Android文件名区分大小写,比如不要写成Image.png或image.PNG)。
  2. 必要权限配置
    在AndroidManifest.xml中添加对应权限:

    <!-- Android 10及以下需要 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <!-- Android 13及以上,针对图片/音频需单独申请权限 -->
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
    
  3. 动态权限申请(必填,针对Android 6.0+)
    如果应用目标SDK为API 23及以上,需在运行时请求权限,否则会因权限不足导致无法访问文件,出现"media not found"。在MainActivity中添加以下代码:

    import android.Manifest;
    import android.content.pm.PackageManager;
    import androidx.core.app.ActivityCompat;
    import androidx.core.content.ContextCompat;
    
    private static final int REQUEST_PERMISSION_CODE = 100;
    
    @Override
    protected void onStart() {
        super.onStart();
        checkStoragePermissions();
    }
    
    private void checkStoragePermissions() {
        boolean hasPermission;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            hasPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED
                    && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_AUDIO) == PackageManager.PERMISSION_GRANTED;
        } else {
            hasPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
        }
    
        if (!hasPermission) {
            String[] permissions;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                permissions = new String[]{Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_AUDIO};
            } else {
                permissions = new String[]{Manifest.permission.READ_EXTERNAL_STORAGE};
            }
            ActivityCompat.requestPermissions(this, permissions, REQUEST_PERMISSION_CODE);
        }
    }
    
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_PERMISSION_CODE) {
            boolean allGranted = true;
            for (int result : grantResults) {
                if (result != PackageManager.PERMISSION_GRANTED) {
                    allGranted = false;
                    break;
                }
            }
            if (!allGranted) {
                Toast.makeText(this, "需要授予存储权限才能访问文件", Toast.LENGTH_LONG).show();
            }
        }
    }
    

内容的提问来源于stack exchange,提问作者Lo Dennis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 11:45:01