Android使用DocumentFile读写外置存储的问题

大家好,本篇文章主要讲的是Android使用DocumentFile读写外置存储的问题,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下

最近在维护项目,app遇到安装在高版本的Android时,以往直接授权和new File(path)的形式不再支持,日志也是说Permission denied。。。。。好吧,换为DocumentFile。

经过一番操作,也终于实再对存储目录的读和写了,下面记录一下:

首先建一个DocumentFile的Utils类:

 import android.annotation.TargetApi; import android.content.Context; import android.content.SharedPreferences; import android.net.Uri; import android.os.Build; import android.os.ParcelFileDescriptor; import android.preference.PreferenceManager; import android.provider.DocumentsContract; import android.util.Log; import androidx.documentfile.provider.DocumentFile; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import java.util.ArrayList; import java.util.List; public class DocumentsUtils { private static final String TAG = DocumentsUtils.class.getSimpleName(); public static final int OPEN_DOCUMENT_TREE_CODE = 8000; private static List sExtSdCardPaths = new ArrayList<>(); private DocumentsUtils() { } public static void cleanCache() { sExtSdCardPaths.clear(); } /** * Get a list of external SD card paths. (Kitkat or higher.) * * @return A list of external SD card paths. */ @TargetApi(Build.VERSION_CODES.KITKAT) private static String[] getExtSdCardPaths(Context context) { if (sExtSdCardPaths.size() > 0) { return sExtSdCardPaths.toArray(new String[0]); } for (File file : context.getExternalFilesDirs("external")) { if (file != null && !file.equals(context.getExternalFilesDir("external"))) { int index = file.getAbsolutePath().lastIndexOf("/Android/data"); if (index <0) { Log.w(TAG, "Unexpected external file dir: " + file.getAbsolutePath()); } else { String path = file.getAbsolutePath().substring(0, index); try { path = new File(path).getCanonicalPath(); } catch (IOException e) { // Keep non-canonical path. } sExtSdCardPaths.add(path); } } } if (sExtSdCardPaths.isEmpty()) sExtSdCardPaths.add("/storage/sdcard1"); return sExtSdCardPaths.toArray(new String[0]); } /** * Determine the main folder of the external SD card containing the given file. * * @param file the file. * @return The main folder of the external SD card containing this file, if the file is on an SD * card. Otherwise, * null is returned. */ @TargetApi(Build.VERSION_CODES.KITKAT) private static String getExtSdCardFolder(final File file, Context context) { String[] extSdPaths = getExtSdCardPaths(context); try { for (int i = 0; i = Build.VERSION_CODES.N) {//不同一目录 Uri renameSrcUri = DocumentsContract.renameDocument(context.getContentResolver(),//先重命名 srcDoc.getUri(), dest.getName()); res = DocumentsContract.moveDocument(context.getContentResolver(),//再移动 renameSrcUri, srcDoc.getParentFile().getUri(), destDoc.getUri()) != null; } } catch (Exception e) { e.printStackTrace(); } } } return res; } public static InputStream getInputStream(Context context, File destFile) { InputStream in = null; try { if (!canWrite(destFile) && isOnExtSdCard(destFile, context)) { DocumentFile file = DocumentsUtils.getDocumentFile(destFile, false, context); if (file != null && file.canWrite()) { in = context.getContentResolver().openInputStream(file.getUri()); } } else { in = new FileInputStream(destFile); } } catch (FileNotFoundException e) { e.printStackTrace(); } return in; } public static OutputStream getOutputStream(Context context, File destFile) { OutputStream out = null; try { if (!canWrite(destFile) && isOnExtSdCard(destFile, context)) { DocumentFile file = DocumentsUtils.getDocumentFile(destFile, false, context); if (file != null && file.canWrite()) { out = context.getContentResolver().openOutputStream(file.getUri()); } } else { out = new FileOutputStream(destFile); } } catch (FileNotFoundException e) { e.printStackTrace(); } return out; } /** * 获取文件流 * @param context * @param destFile 目标文件 * @param mode May be "w", "wa", "rw", or "rwt". * @return */ public static OutputStream getOutputStream(Context context, File destFile, String mode) { OutputStream out = null; try { if (!canWrite(destFile) && isOnExtSdCard(destFile, context)) { DocumentFile file = DocumentsUtils.getDocumentFile(destFile, false, context); if (file != null && file.canWrite()) { out = context.getContentResolver().openOutputStream(file.getUri(), mode); } } else { out = new FileOutputStream(destFile, mode.equals("rw") || mode.equals("wa")); } } catch (FileNotFoundException e) { e.printStackTrace(); } return out; } public static FileDescriptor getFileDescriptor(Context context, File destFile) { FileDescriptor fd = null; try { if (/*!canWrite(destFile) && */isOnExtSdCard(destFile, context)) { DocumentFile file = DocumentsUtils.getDocumentFile(destFile, false, context); if (file != null && file.canWrite()) { ParcelFileDescriptor out = context.getContentResolver().openFileDescriptor(file.getUri(), "rw"); fd = out.getFileDescriptor(); } } else { RandomAccessFile file = null; try { file = new RandomAccessFile(destFile, "rws"); file.setLength(0); fd = file.getFD(); } catch (Exception e){ e.printStackTrace(); } finally { if (file != null) { try { file.close(); } catch (IOException e) { e.printStackTrace(); } } } } } catch (FileNotFoundException e) { e.printStackTrace(); } return fd; } public static boolean saveTreeUri(Context context, String rootPath, Uri uri) { DocumentFile file = DocumentFile.fromTreeUri(context, uri); if (file != null && file.canWrite()) { SharedPreferences perf = PreferenceManager.getDefaultSharedPreferences(context); perf.edit().putString(rootPath, uri.toString()).apply(); Log.e(TAG, "save uri" + rootPath); return true; } else { Log.e(TAG, "no write permission: " + rootPath); } return false; } /** * 返回true表示没有权限 * @param context * @param rootPath * @return */ public static boolean checkWritableRootPath(Context context, String rootPath) { File root = new File(rootPath); if (!root.canWrite()) { Log.e(TAG,"sd card can not write:" + rootPath + ",is on extsdcard:" + DocumentsUtils.isOnExtSdCard(root, context)); if (DocumentsUtils.isOnExtSdCard(root, context)) { DocumentFile documentFile = DocumentsUtils.getDocumentFile(root, true, context); if (documentFile != null) { Log.i(TAG, "get document file:" + documentFile.canWrite()); } return documentFile == null || !documentFile.canWrite(); } else { SharedPreferences perf = PreferenceManager.getDefaultSharedPreferences(context); String documentUri = perf.getString(rootPath, ""); Log.i(TAG,"lum_2 get perf documentUri:" + documentUri); if (documentUri == null || documentUri.isEmpty()) { return true; } else { DocumentFile file = DocumentFile.fromTreeUri(context, Uri.parse(documentUri)); if (file != null) Log.i(TAG,"lum get perf documentUri:" + file.canWrite()); return !(file != null && file.canWrite()); } } }else{ Log.e(TAG,"sd card can write..."); } return false; } }

然后在app启动的地方检查下是否需要授权才能操作:

 if (DocumentsUtils.checkWritableRootPath(this, StringUtils.STORAGE_PATH)) {   //检查sd卡路径是否有 权限 没有显示dialog showOpenDocumentTree(); } private void showOpenDocumentTree() { Log.e("showOpenDocumentTree", "start check sd card..."); Intent intent = null; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { StorageManager sm = getSystemService(StorageManager.class); StorageVolume volume = sm.getStorageVolume(new File(StringUtils.STORAGE_PATH)); if (volume != null) { intent = volume.createAccessIntent(null); } } Log.e("showOpenDocumentTree", "intent=" + intent); if (intent == null) { intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); } startActivityForResult(intent, DocumentsUtils.OPEN_DOCUMENT_TREE_CODE); } //................................ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case DocumentsUtils.OPEN_DOCUMENT_TREE_CODE: if (data != null && data.getData() != null) { Uri uri = data.getData(); DocumentsUtils.saveTreeUri(this, StringUtils.STORAGE_PATH, uri); Log.i(TAG,"DocumentsUtils.OPEN_DOCUMENT_TREE_CODE : "  + uri); } break; } super.onActivityResult(requestCode, resultCode, data); } 

按以上代码,是可以实现对外置存储卡进行读和写操作了,只要机器没关机,多关打开关闭app,都不会弹下面这个授权框:

但是-----如果关机再重新开机,就要重新授权,这样很麻烦,用户体验也极其不好,所以又查询了很多资料,最后在stackoverflow上找到办法,关键是下面这句:

 grantUriPermission(getPackageName(), uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

是在ActivityResult回调里,添加上面这两行代码,就不用每次都弹授权,影响体验了

到此这篇关于Android使用DocumentFile读写外置存储的问题的文章就介绍到这了,更多相关Android DocumentFile读写外置存储内容请搜索0133技术站以前的文章或继续浏览下面的相关文章希望大家以后多多支持0133技术站!

以上就是Android使用DocumentFile读写外置存储的问题的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » 移动