当前位置: 首页 > news >正文

Android 文件选择器

选择本地文件,返回文件地址;原本有一个工具类,就随手粘贴上传了,结果,太坑了,同事使用各种崩溃,自己疯狂被打脸;

真的随着Android版本的变化,真的需要把之前的工具类梳理优化下了。

目录

工具类

版本适配,主要考虑是Android 10 以上版本

机型适配


工具类

public class FilePickerUtil {

    public static final int PICK_FILE_REQUEST_CODE = 1;
    private static FilePickerListener filePickerListener;

    public interface FilePickerListener {
        void onFilePicked(String filePath);
    }

    public static void pickFile(Activity activity, FilePickerListener listener) {
        filePickerListener = listener;
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType("*/*");
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
        }
        activity.startActivityForResult(Intent.createChooser(intent, "Select a file"), PICK_FILE_REQUEST_CODE);
    }

    public static void pickFile(Fragment fragment, FilePickerListener listener) {
        filePickerListener = listener;
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType("*/*");
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
        }
        fragment.startActivityForResult(Intent.createChooser(intent, "Select a file"), PICK_FILE_REQUEST_CODE);
    }

    public static void onActivityResult(Context context, int requestCode, int resultCode, @Nullable Intent data) {
        if (requestCode == PICK_FILE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
            if (data != null) {
                Uri uri = data.getData();
                if (uri != null) {
                    String filePath = getFilePath(context, uri);
                    if (filePickerListener != null) {
                        filePickerListener.onFilePicked(filePath);
                    }
                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && data.getClipData() != null) {
                    // Handle multiple files selected
                    for (int i = 0; i < data.getClipData().getItemCount(); i++) {
                        Uri fileUri = data.getClipData().getItemAt(i).getUri();
                        String filePath = getFilePath(context, fileUri);
                        if (filePickerListener != null) {
                            filePickerListener.onFilePicked(filePath);
                        }
                    }
                }
            }
        }
    }

    private static String getFilePath(Context context, Uri uri) {
        Log.d("FilePickerUtil", "URI: " + uri.toString());
        String filePath = null;
        String authority = uri.getAuthority();

        // 定义一个映射表,存储不同品牌文件管理器的 URI 处理逻辑
        Map<String, String> authorityToRootPathMap = new HashMap<>();
        authorityToRootPathMap.put("com.hihonor.filemanager.share.fileprovider", "/root");
        authorityToRootPathMap.put("com.huawei.filemanager.share.fileprovider", "/root");
        authorityToRootPathMap.put("com.mi.android.globalFileexplorer.myprovider", "/root");
        authorityToRootPathMap.put("com.coloros.filemanager.fileprovider", "/root");
        authorityToRootPathMap.put("com.vivo.filemanager.share.fileprovider", "/root");
        // 可以根据需要添加更多品牌的文件管理器

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            if (DocumentsContract.isDocumentUri(context, uri)) {
                String docId = DocumentsContract.getDocumentId(uri);
                String[] split = docId.split(":");
                String type = split[0];
                Uri contentUri = null;
                if ("primary".equalsIgnoreCase(type)) {
                    filePath = context.getExternalFilesDir(null) + "/" + split[1];
                } else if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }
                if (contentUri != null) {
                    filePath = getDataColumn(context, contentUri, "_id=?", new String[]{split[1]});
                }
                Log.d("FilePickerUtil", "DocumentsContract URI: " + filePath);
            }
        }
        if (filePath == null) {
            // 使用映射表处理不同品牌文件管理器提供的 URI
            if (authorityToRootPathMap.containsKey(authority)) {
                String rootPath = authorityToRootPathMap.get(authority);
                filePath = uri.getPath().replace(rootPath, "");
            } else {
                filePath = getDataColumn(context, uri, null, null);
            }
        }
        Log.d("FilePickerUtil", "File Path: " + filePath);
        return filePath;
    }

    private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
        Cursor cursor = null;
        final String[] projection = {"_data", "_display_name", "_size"}; // 移除不存在的列 "data"
        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
            if (cursor != null && cursor.moveToFirst()) {
                for (String column : projection) {
                    int columnIndex = cursor.getColumnIndex(column);
                    if (columnIndex != -1) {
                        return cursor.getString(columnIndex);
                    }
                }
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }
}

版本适配,主要考虑是Android 10 以上版本

  • Android 10 及以上版本 强调隐私和安全,要求使用 SAF 来访问文件,并引入了范围存储。
  • Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
  • Android 10 以下版本 访问文件的方式相对简单,应用程序可以直接访问外部存储空间中的文件。
  • Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
  • 访问外部存储空间的权限要求发生了变化,应用程序需要请求 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE 权限来访问公共存储空间。
  • 在 Android 11 及以上版本中,应用程序还需要请求 MANAGE_EXTERNAL_STORAGE 权限来获得对外部存储的完全访问权限。

机型适配

特定的文件管理器应用程序,共享的文件路径。FileProvider 是 Android 中的一种机制,允许应用程序之间安全地共享文件,而不必直接暴露文件的实际路径。 所以在解析URL的时候需要添加机型版本的适配,

是的,每个机型返回的共享文件路径都是不一样。

// 定义一个映射表,存储不同品牌文件管理器的 URI 处理逻辑
        Map<String, String> authorityToRootPathMap = new HashMap<>();
        authorityToRootPathMap.put("com.hihonor.filemanager.share.fileprovider", "/root");
        authorityToRootPathMap.put("com.huawei.filemanager.share.fileprovider", "/root");
        authorityToRootPathMap.put("com.mi.android.globalFileexplorer.myprovider", "/root");
        authorityToRootPathMap.put("com.coloros.filemanager.fileprovider", "/root");
        authorityToRootPathMap.put("com.vivo.filemanager.share.fileprovider", "/root");
        // 可以根据需要添加更多品牌的文件管理器


       // 使用映射表处理不同品牌文件管理器提供的 URI
            if (authorityToRootPathMap.containsKey(authority)) {
                String rootPath = authorityToRootPathMap.get(authority);
                filePath = uri.getPath().replace(rootPath, "");
            } else {
                filePath = getDataColumn(context, uri, null, null);
            }

我是做了一个映射表,目前只有 荣耀、小米、华为、vivo、oppo;

相关文章:

  • 做网站备案照片的要求策划网络营销方案
  • 做网站5年工资多少seo站内优化站外优化
  • 凡科做的网站怎么改壁纸百度权重3的网站值多少
  • 上饶专业做网站建设自助建站系统源码
  • 属于网络营销站点推广的是网站搭建一般要多少钱
  • 中国商标注册申请官网天津百度整站优化服务
  • 如何在 Apifox 中与其他成员共用数据库连接配置
  • 探秘Transformer系列之(24)--- KV Cache优化
  • CSS 符号
  • 一文详解OpenGL环境搭建:Windows使用CLion配置OpenGL开发环境
  • 超详细解读:数据库MVCC机制
  • 【扩展KMP】P10634 BZOJ2372 music |省选-
  • 第 27 场 蓝桥月赛
  • 巧用sort
  • JSON工具-JSONUtil
  • 【OS】Process Management(3)
  • 蓝桥杯比赛对于时间和空间的限制
  • 进程同步和进程互斥的区别
  • 用栈实现队列
  • android audiorecord
  • PowerBI累计分析
  • Agent 开发 笔记
  • 本地项目HTTPS访问问题解决方案
  • 【后端开发】Maven
  • LeetCode热题100记录-【二分查找】
  • 单片机软件设计文档模板