Android判断有无外置SD卡(TF卡),并读写文件
16lz
2021-01-23
转载来源:https://blog.csdn.net/qq_27061049/article/details/94178875
1.相关方法说明:
2.Android Manifest.xml 添加权限
3.DocumentsUtils文件
import android.annotation.TargetApi;import android.content.Context;import android.content.SharedPreferences;import android.net.Uri;import android.os.Build;import android.preference.PreferenceManager;import android.provider.DocumentsContract;import android.support.v4.provider.DocumentFile;import android.util.Log;import java.io.File;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.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 < extSdPaths.length; i++) { if (file.getCanonicalPath().startsWith(extSdPaths[i])) { return extSdPaths[i]; } } } catch (IOException e) { return null; } return null; } /** * Determine if a file is on external sd card. (Kitkat or higher.) * * @param file The file. * @return true if on external sd card. */ @TargetApi(Build.VERSION_CODES.KITKAT) public static boolean isOnExtSdCard(final File file, Context c) { return getExtSdCardFolder(file, c) != null; } /** * Get a DocumentFile corresponding to the given file (for writing on ExtSdCard on Android 5). * If the file is not * existing, it is created. * * @param file The file. * @param isDirectory flag indicating if the file should be a directory. * @return The DocumentFile */ public static DocumentFile getDocumentFile(final File file, final boolean isDirectory, Context context) { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { return DocumentFile.fromFile(file); } String baseFolder = getExtSdCardFolder(file, context); Log.i(TAG,"lum_ baseFolder " + baseFolder); boolean originalDirectory = false; if (baseFolder == null) { return null; } String relativePath = null; try { String fullPath = file.getCanonicalPath(); if (!baseFolder.equals(fullPath)) { relativePath = fullPath.substring(baseFolder.length() + 1); } else { originalDirectory = true; } } catch (IOException e) { return null; } catch (Exception f) { originalDirectory = true; //continue } String as = PreferenceManager.getDefaultSharedPreferences(context).getString(baseFolder, null); Uri treeUri = null; if (as != null) treeUri = Uri.parse(as); if (treeUri == null) { return null; } // start with root of SD card and then parse through document tree. DocumentFile document = DocumentFile.fromTreeUri(context, treeUri); if (originalDirectory) return document; String[] parts = relativePath.split("/"); for (int i = 0; i < parts.length; i++) { DocumentFile nextDocument = document.findFile(parts[i]); if (nextDocument == null) { if ((i < parts.length - 1) || isDirectory) { nextDocument = document.createDirectory(parts[i]); } else { nextDocument = document.createFile("image", parts[i]); } } document = nextDocument; } return document; } public static boolean mkdirs(Context context, File dir) { boolean res = dir.mkdirs(); if (!res) { if (DocumentsUtils.isOnExtSdCard(dir, context)) { DocumentFile documentFile = DocumentsUtils.getDocumentFile(dir, true, context); res = documentFile != null && documentFile.canWrite(); } } return res; } public static boolean delete(Context context, File file) { boolean ret = file.delete(); if (!ret && DocumentsUtils.isOnExtSdCard(file, context)) { DocumentFile f = DocumentsUtils.getDocumentFile(file, false, context); if (f != null) { ret = f.delete(); } } return ret; } public static boolean canWrite(File file) { boolean res = file.exists() && file.canWrite(); if (!res && !file.exists()) { try { if (!file.isDirectory()) { res = file.createNewFile() && file.delete(); } else { res = file.mkdirs() && file.delete(); } } catch (IOException e) { e.printStackTrace(); } } return res; } public static boolean canWrite(Context context, File file) { boolean res = canWrite(file); if (!res && DocumentsUtils.isOnExtSdCard(file, context)) { DocumentFile documentFile = DocumentsUtils.getDocumentFile(file, true, context); res = documentFile != null && documentFile.canWrite(); } return res; } public static boolean renameTo(Context context, File src, File dest) { boolean res = src.renameTo(dest); if (!res && isOnExtSdCard(dest, context)) { DocumentFile srcDoc; if (isOnExtSdCard(src, context)) { srcDoc = getDocumentFile(src, false, context); } else { srcDoc = DocumentFile.fromFile(src); } DocumentFile destDoc = getDocumentFile(dest.getParentFile(), true, context); if (srcDoc != null && destDoc != null) { try { if (src.getParent().equals(dest.getParent())) { res = srcDoc.renameTo(dest.getName()); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { res = DocumentsContract.moveDocument(context.getContentResolver(), srcDoc.getUri(), 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; } 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; } public static boolean checkWritableRootPath(Context context, String rootPath) { File root = new File(rootPath); if (!root.canWrite()) { if (DocumentsUtils.isOnExtSdCard(root, context)) { Log.i(TAG,"lum_ isOnExtSdCard"); DocumentFile documentFile = DocumentsUtils.getDocumentFile(root, true, context); return documentFile == null || !documentFile.canWrite(); } else { Log.i(TAG,"lum_2 get perf"); SharedPreferences perf = PreferenceManager.getDefaultSharedPreferences(context); String documentUri = perf.getString(rootPath, ""); if (documentUri == null || documentUri.isEmpty()) { return true; } else { DocumentFile file = DocumentFile.fromTreeUri(context, Uri.parse(documentUri)); return !(file != null && file.canWrite()); } } } return false; }}
4.MainActivity.java
import android.Manifest;import android.content.Context;import android.content.DialogInterface;import android.content.Intent;import android.content.pm.PackageManager;import android.net.Uri;import android.os.storage.StorageManager;import android.os.storage.StorageVolume;import android.provider.Settings;import android.support.v4.app.ActivityCompat;import android.support.v4.content.ContextCompat;import android.support.v7.app.AlertDialog;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.Toast;import java.io.File;import java.io.IOException;import java.io.OutputStream;import java.lang.reflect.Array;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private String TAG = "MainActivity: "; private String rootPath = ""; //外置 SD 卡路径 private List unPermissionList = new ArrayList(); //申请未得到授权的权限列表 private final int RequestCode = 100;//权限请求码 private AlertDialog mPermissionDialog; private String mPackName ; //获取 a'p'k 包名 private String[] permissionList = new String[]{ //申请的权限列表 Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, }; private Button mButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); checkPermission(); mPackName = getPackageName(); rootPath = getStoragePath(this, true); //获取 可以插拔的 sd 卡 路径 Log.i(TAG, "lum rootPath: " + rootPath); if (DocumentsUtils.checkWritableRootPath(this, rootPath)) { //检查sd卡路径是否有 权限 没有显示dialog showOpenDocumentTree(); } mButton = (Button) findViewById(R.id.but_id); mButton.setOnClickListener(this); } private void showOpenDocumentTree() { 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(rootPath)); if (volume != null) { intent = volume.createAccessIntent(null); } } if (intent == null) { intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); } startActivityForResult(intent, DocumentsUtils.OPEN_DOCUMENT_TREE_CODE); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case DocumentsUtils.OPEN_DOCUMENT_TREE_CODE: if (data != null && data.getData() != null) { Uri uri = data.getData(); DocumentsUtils.saveTreeUri(this, rootPath, uri); Log.i(TAG,"lum_uri : " + uri); } break; default: break; } } //权限判断和申请 public void checkPermission() { unPermissionList.clear();//清空申请的没有通过的权限 //逐个判断是否还有未通过的权限 for (int i = 0; i < permissionList.length; i++) { if (ContextCompat.checkSelfPermission(this, permissionList[i]) != PackageManager.PERMISSION_GRANTED) { unPermissionList.add(permissionList[i]);//添加还未授予的权限到unPermissionList中 } } //有权限没有通过,需要申请 if (unPermissionList.size() > 0) { ActivityCompat.requestPermissions( this,permissionList, 100); Log.i(TAG, "check 有权限未通过"); } else { //权限已经都通过了,可以将程序继续打开了 Log.i(TAG, "check 权限都已经申请通过"); } } /** * 5.请求权限后回调的方法 * * @param requestCode 是我们自己定义的权限请求码 * @param permissions 是我们请求的权限名称数组 * @param grantResults 是我们在弹出页面后是否允许权限的标识数组,数组的长度对应的是权限 * 名称数组的长度,数组的数据0表示允许权限,-1表示我们点击了禁止权限 */ @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); Log.i(TAG,"申请结果反馈"); boolean hasPermissionDismiss = false; if (100 == requestCode) { for (int i = 0; i < grantResults.length; i++) { if (grantResults[i] == -1) { hasPermissionDismiss = true; //有权限没有通过 Log.i(TAG,"有权限没有被通过"); break; } } } if (hasPermissionDismiss) {//如果有没有被允许的权限 showPermissionDialog(); } else { //权限已经都通过了,可以将程序继续打开了 Log.i(TAG, "onRequestPermissionsResult 权限都已经申请通过"); } } /** * 不再提示权限时的展示对话框 */ private void showPermissionDialog() { Log.i(TAG,"mPackName: " + mPackName); if (mPermissionDialog == null) { mPermissionDialog = new AlertDialog.Builder(this) .setMessage("已禁用权限,请手动授予") .setPositiveButton("设置", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { cancelPermissionDialog(); Uri packageURI = Uri.parse("package:" + mPackName); //去设置里面设置 Intent intent = new Intent(Settings. ACTION_APPLICATION_DETAILS_SETTINGS, packageURI); startActivity(intent); } }) .setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { //关闭页面或者做其他操作 cancelPermissionDialog(); } }) .create(); } mPermissionDialog.show(); } private void cancelPermissionDialog() { mPermissionDialog.cancel(); } @Override public void onClick(View view) { switch (view.getId()){ case R.id.but_id: Log.i(TAG,"点击"); try { writeDataToSD(); } catch (IOException e) { e.printStackTrace(); } } } /** * 通过反射调用获取内置存储和外置sd卡根路径(通用) * * @param mContext 上下文 * @param is_removale 是否可移除,false返回内部存储路径,true返回外置SD卡路径 * @return */ private static String getStoragePath(Context mContext, boolean is_removale) { String path = ""; //使用getSystemService(String)检索一个StorageManager用于访问系统存储功能。 StorageManager mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE); Class<?> storageVolumeClazz = null; try { storageVolumeClazz = Class.forName("android.os.storage.StorageVolume"); Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList"); Method getPath = storageVolumeClazz.getMethod("getPath"); Method isRemovable = storageVolumeClazz.getMethod("isRemovable"); Object result = getVolumeList.invoke(mStorageManager); for (int i = 0; i < Array.getLength(result); i++) { Object storageVolumeElement = Array.get(result, i); path = (String) getPath.invoke(storageVolumeElement); boolean removable = (Boolean) isRemovable.invoke(storageVolumeElement); if (is_removale == removable) { return path; } } } catch (Exception e) { e.printStackTrace(); } return path; } public void writeDataToSD() throws IOException { //写文件的例子 文件不存在,会创建 String str = "just a test\n"; String strRead = ""; String sdkOut = getStoragePath(this,true); //获取 可以插拔的 sd 卡 路径 String filePath = sdkOut +File.separator+"Android"+File.separator+"data"+File.separator+getPackageName()+ "/test"; Log.i(TAG,"lum_ sdkOut: " + filePath); File file = new File(filePath); if (!file.exists()){ file.mkdirs(); Log.i(TAG,"创建文件夹:" + filePath); } String fileWritePath = filePath + "/test.txt"; File fileWrite = new File(fileWritePath); Log.i(TAG,"lum 准备写入" ); try { OutputStream outputStream = DocumentsUtils.getOutputStream(this,fileWrite); //获取输出流 // OutputStream outputStream = new FileOutputStream(fileWrite); outputStream.write(str.getBytes()); outputStream.close(); Log.i(TAG,"lum 写入成功" ); Toast.makeText(this,"路径:" + fileWritePath + "成功",Toast.LENGTH_SHORT ).show(); } catch (IOException e) { e.printStackTrace(); Log.i(TAG,"lum 写入失败" ); Toast.makeText(this,"路径:" + fileWritePath + "失败",Toast.LENGTH_SHORT ).show(); } /* //读取文件 的例子 try { InputStream is = DocumentsUtils.getInputStream(this,fileWritePath); InputStreamReader input = new InputStreamReader(is, "UTF-8"); BufferedReader reader = new BufferedReader(input); while ((str = reader.readLine()) != null) { strRead += str; } Log.i(TAG,"lum:读取的文件是 " + strRead); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }*/ }}
参考链接:https://blog.csdn.net/xingnan4414/article/details/79388972
https://blog.csdn.net/u010784887/article/details/53560025
https://blog.csdn.net/omnispace/article/details/79302862
https://juejin.im/post/58b557de128fe10065e93cc8
https://blog.csdn.net/qq_36467463/article/details/88691726
更多相关文章
- java拷贝文件夹和android设置文件权限
- java|android加载src路径下面的图片文件
- 关于android录音权限被用户禁用的问题解决方案
- android动态申请拍照获取照片权限
- Android判断是否有拍照权限
- Mac通过Android aapt解析apk包名、版本号、权限等信息