Android 11 从外部存储读取文件到应用沙盒存储

Android10之前,访问外部存储目录即SDCard目录只需要

Environment.getExternalStorageDirectory().getAbsolutePath(),再通过new File()的形式访问。

Android 10 开始,Google建议开发者使用存储访问框架访问外部存储。

使用“存储访问框架”打开文件  |  Android 开发者  |  Android Developers

    new File()的形式只能访问自己应用的沙盒存储路径如:

        Context.getExternalFilesDir():SDCard/Android/data/应用包名/files/ 目录
        Context.getExternalCacheDir(): SDCard/Android/data/应用包名/cache/目录
        Context.getCacheDir():/data/data//cache目录
        Context.getFilesDir():     /data/data//files目录

     等......

Android 11以上,开始强制不能再通过new File()的形式访问外部存储区域了。

本文是关于Android 11以上 从外部存储读取文件到应用沙盒存储的Demo,效果:

1. FileHandlePresenter.java实现。
封装方法:发Intent拉起文件选择器
         文件选择后得到Uri拷贝文件到应用包名目录下
         后续根据自己业务需求操作该拷贝后的文件如重命名,解压等等......
package com.mikel.projectdemo.presenter;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.util.Log;
import android.widget.Toast;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

public class FileHandlePresenter {
    public static final String TAG = "FileHandlePresenter";
    public static final int PERMISSION_CODE_READ_EXTERNAL = 1;
    public static final int REQUEST_CODE_READ_FILE_FROM_EXTERNAL = 1000;
    private Fragment mFragment;
    public FileHandlePresenter(Fragment fragment) {
        mFragment = fragment;
    }

    /**
     * 打开文件选择器
     */
    public void requestReadExternalStorage() {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        //指定多种类型的文件
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("*/*");
        mFragment.startActivityForResult(Intent.createChooser(intent, "选择文件"), REQUEST_CODE_READ_FILE_FROM_EXTERNAL);
    }

    /**
     * 通过uri拷贝外部存储的文件到自己包名的目录下
     * @param uri
     * @param destFile
     */
    private void copyFieUriToInnerStorage(Uri uri, File destFile) {
        InputStream inputStream = null;
        FileOutputStream fileOutputStream = null;
        try {
            inputStream = mFragment.getActivity().getContentResolver().openInputStream(uri);
            if(destFile.exists()) {
                destFile.delete();
            }
            fileOutputStream = new FileOutputStream(destFile);
            byte[] buffer = new byte[4096];
            int redCount;
            while ((redCount = inputStream.read(buffer)) >= 0) {
                fileOutputStream.write(buffer, 0, redCount);
            }
        } catch (Exception e) {
            Log.e(TAG, " copy file uri to inner storage e = " + e.toString());
        } finally {
            try {
                if(fileOutputStream != null) {
                    fileOutputStream.flush();
                    fileOutputStream.getFD().sync();
                    fileOutputStream.close();
                }
                if(inputStream != null) {
                    inputStream.close();
                }
            } catch (Exception e) {
                Log.e(TAG, " close stream e = " + e.toString());
            }
        }
    }

    /**
     * 申请权限回调
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    public void onRequestPermissionsResult(int requestCode, @NonNull @NotNull String[] permissions, @NonNull @NotNull int[] grantResults) {
        if(requestCode == PERMISSION_CODE_READ_EXTERNAL) {
            if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                requestReadExternalStorage();
            }
        }
    }

    /**
     * 文件选择后回调
     * @param requestCode
     * @param resultCode
     * @param data
     */
    public void onActivityResult(int requestCode, int resultCode, @Nullable @org.jetbrains.annotations.Nullable Intent data) {
        if(resultCode == Activity.RESULT_OK) {
            switch (requestCode) {
                case REQUEST_CODE_READ_FILE_FROM_EXTERNAL:
                    try {
                        Uri fileUri = data.getData();
                        File destFile = File.createTempFile("temp", ".tmp", mFragment.getActivity().getCacheDir());
                        Log.d(TAG, " read external storage file = "+ fileUri.toString() + ", dest path = " + destFile.getAbsolutePath());
                        copyFieUriToInnerStorage(fileUri, destFile);
                        //todo 外部存储的文件uri 已变成了 应用包下的文件destFile,后续可以new File操作destFile
                        Toast.makeText(mFragment.getActivity(), "Read External Storage file Success! Save Path = " + destFile.getAbsolutePath(), Toast.LENGTH_LONG).show();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    break;
            }
        }
    }
}

2. Ui Fragment 负责Ui 展示、调用 FileHandlePresenter和动态权限申请......

package com.mikel.projectdemo.uiframework.subtab;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import com.mikel.projectdemo.R;
import com.mikel.projectdemo.presenter.FileHandlePresenter;
import org.jetbrains.annotations.NotNull;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;

public class SubTabFragment1 extends Fragment {
    FileHandlePresenter fileHandlePresenter;

    public static SubTabFragment1 build() {
        return new SubTabFragment1();
    }


    @Override
    public View onCreateView(@NonNull @NotNull LayoutInflater inflater, @Nullable @org.jetbrains.annotations.Nullable ViewGroup container, @Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
        View rootView = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_sub_tab_content1, null, true);
        fileHandlePresenter = new FileHandlePresenter(this);
        initUI(rootView);
        return rootView;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull @NotNull String[] permissions, @NonNull @NotNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        fileHandlePresenter.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, @Nullable @org.jetbrains.annotations.Nullable Intent data) {
        fileHandlePresenter.onActivityResult(requestCode, resultCode, data);
    }

    private void initUI(View rootView) {
        Button readFileBtn = rootView.findViewById(R.id.read_file_from_external_btn);
        readFileBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                /**
                 * android 6.0以上动态权限申请
                 */
                if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ContextCompat.checkSelfPermission(getActivity(),
                        Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                    requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, FileHandlePresenter.PERMISSION_CODE_READ_EXTERNAL);
                } else {
                    fileHandlePresenter.requestReadExternalStorage();
                }
            }
        });

        Button writeFileBtn = rootView.findViewById(R.id.write_file_to_external_btn);
        writeFileBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

    }
}

3. 另外不要忘了app工程的AndroidManifest.mxl下需要声明权限 

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Demo地址:

CODING | 一站式软件研发管理平台

​​​​​​MVVM: ViewModel+LiveData+DataBinding+Retrofit+Room+Paging+RxJava 总结与实践(Java实现)_xiaobaaidaba123的专栏-CSDN博客

android 嵌套ViewPager + Fragment实现仿头条UI框架Demo_xiaobaaidaba123的专栏-CSDN博客

Android 使用ViewPager2+ExoPlayer+VideoCache 实现仿抖音视频翻页播放_xiaobaaidaba123的专栏-CSDN博客 

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>