本文介绍了在Android上写入外部SD卡的通用方式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! 在我的应用程序中,我需要在设备存储中存储大量图像。这些文件往往满足设备存储,我想让用户能够选择外部SD卡作为目标文件夹。 我到处都读到Android不允许用户写入外部SD卡,SD卡是指外部可安装的SD卡和不是外部的存储,但文件管理器应用程序设法在所有Android版本上写入外部SD。 在不同的API级别(Pre-KitKat,KitKat,Lollipop +)上授予对外部SD卡的读/写访问权限的更好方法是什么? 更新1 我从Doomknight的回答中尝试了方法1,但无济于事: As你可以看到我在尝试写SD之前在运行时检查权限: HashSet< String> extDirs = getStorageDirectories(); for(String dir:extDirs){ Log.e(SD,dir); 文件f =新文件(新文件(dir),TEST.TXT); try { if(ActivityCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)== PackageManager.PERMISSION_GRANTED){ f.createNewFile(); } } catch(IOException e){ e.printStackTrace(); } } 但我收到了一个访问错误,尝试了两种不同的设备:HTC10和Shield K1。 10-22 14:52:57.329 30280-30280 /? E / SD:/ mnt / media_rw / F38E-14F8 10-22 14:52:57.329 30280-30280 /? W / System.err:java.io.IOException:open failed:EACCES(Permission denied) 10-22 14:52:57.329 30280-30280 /? W / System.err:at java.io.File.createNewFile(File.java:939) 10-22 14:52:57.329 30280-30280 /? W / System.err:at com.myapp.activities.TestActivity.onResume(TestActivity.java:167) 10-22 14:52:57.329 30280-30280 /? W / System.err:在android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1326) 10-22 14:52:57.330 30280-30280 /? W / System.err:在android.app.Activity.performResume(Activity.java:6338) 10-22 14:52:57.330 30280-30280 /? W / System.err:在android.app.ActivityThread.performResumeActivity(ActivityThread.java:3336) 10-22 14:52:57.330 30280-30280 /? W / System.err:在android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3384) 10-22 14:52:57.330 30280-30280 /? W / System.err:在android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2574) 10-22 14:52:57.330 30280-30280 /? W / System.err:在android.app.ActivityThread.access $ 900(ActivityThread.java:150) 10-22 14:52:57.330 30280-30280 /? W / System.err:在android.app.ActivityThread $ H.handleMessage(ActivityThread.java:1399) 10-22 14:52:57.330 30280-30280 /? W / System.err:在android.os.Handler.dispatchMessage(Handler.java:102) 10-22 14:52:57.330 30280-30280 /? W / System.err:在android.os.Looper.loop(Looper.java:168) 10-22 14:52:57.330 30280-30280 /? W / System.err:在android.app.ActivityThread.main(ActivityThread.java:5885) 10-22 14:52:57.330 30280-30280 /? W / System.err:at java.lang.reflect.Method.invoke(Native Method) 10-22 14:52:57.330 30280-30280 /? W / System.err:at com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run(ZygoteInit.java:819) 10-22 14:52:57.330 30280-30280 /? W / System.err:at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:709) 10-22 14:52:57.330 30280-30280 /? W / System.err:引起:android.system.ErrnoException:open failed:EACCES(Permission denied) 10-22 14:52:57.330 30280-30280 /? W / System.err:at libcore.io.Posix.open(Native Method) 10-22 14:52:57.330 30280-30280 /? W / System.err:at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186) 10-22 14:52:57.330 30280-30280 /? W / System.err:at java.io.File.createNewFile(File.java:932) 10-22 14:52:57.330 30280-30280 /? W / System.err:... 14更多 解决方案 简短回答。 在KitKat尝试使用 Doomsknight方法1 或阅读Gnathonic的此回复或要点: public static HashSet< String> getExternalMounts(){ final HashSet< String> out = new HashSet< String>(); String reg =(?i)。* vold。*(vfat | ntfs | exfat | fat32 | ext3 | ext4)。* rw。*; String s =; try { final Process process = new ProcessBuilder()。command(mount) .redirectErrorStream(true).start(); process.waitFor(); final InputStream is = process.getInputStream(); final byte [] buffer = new byte [1024]; while(is.read(buffer)!= -1){ s = s + new String(buffer); } is.close(); } catch(最终例外e){ e.printStackTrace(); } //解析输出 final String [] lines = s.split(\ n); for(String line:lines){ if(!line.toLowerCase(Locale.US).contains(asec)){ if(line.matches(reg)){ String [] parts = line.split(); for(String part:parts){ if(part.startsWith(/)) if(!part.toLowerCase(Locale.US).contains(vold)) out.add(部分); } } } } 退出; } 同时阅读 Paolo Rovelli的解释并尝试使用KitKat后的 Jeff Sharkey的解决方案: 6。 Lollipop还引入了更改以及 DocumentFile 帮助程序类。 getStorageState 在API 19中添加,在API 21中弃用, 使用 getExternalStorageState(文件) 7。 Android 7.0提供了一个简化的API来访问外部存储目录。 8。 Android O更改。 9。相关问题和建议的答案。 如何获得Android 4.0+的外部SD卡路径? mkdir()在内部闪存存储区内工作,但不能在SD卡内工作吗? getExternalFilesDir和getExternalStorageDirectory()之间的差异 为什么getExternalFilesDirs()在某些设备上不起作用? 如何使用针对Android 5.0(Lollipop)提供的新SD卡访问API 在Android 5.0及更高版本中写入外部SD卡 使用SAF(存储访问框架)的Android SD卡写入权限 SAFFAQ:存储访问框架常见问题解答 10。 相关错误和问题。 错误:在Android 6上,当使用getExternalFilesDirs时,它不允许您在其结果中创建新文件 写入Lollipop上getExternalCacheDir()返回的目录失败,没有写入权限 In my application, I need to store lots of images in the device storage. Such files tend to fulfill the device storage, and I want to allow users to be able to choose external SD card as the destination folder.I read everywhere that Android doesn't allow users to write to external SD card, by SD card I mean the external and mountable SD card and not the external storage, but file manager applications manage to write to External SD on all Android versions.What is the better way to grant read/write access to external SD card on different API levels (Pre-KitKat, KitKat, Lollipop+)?Update 1I tried Method 1 from Doomknight's answer, with no avail:As you can see I'm checking for permissions at runtime before attempting to write on SD:HashSet<String> extDirs = getStorageDirectories();for(String dir: extDirs) { Log.e("SD",dir); File f = new File(new File(dir),"TEST.TXT"); try { if(ActivityCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)==PackageManager.PERMISSION_GRANTED) { f.createNewFile(); } } catch (IOException e) { e.printStackTrace(); }}But I get an access error, tried on two different devices: HTC10 and Shield K1.10-22 14:52:57.329 30280-30280/? E/SD: /mnt/media_rw/F38E-14F810-22 14:52:57.329 30280-30280/? W/System.err: java.io.IOException: open failed: EACCES (Permission denied)10-22 14:52:57.329 30280-30280/? W/System.err: at java.io.File.createNewFile(File.java:939)10-22 14:52:57.329 30280-30280/? W/System.err: at com.myapp.activities.TestActivity.onResume(TestActivity.java:167)10-22 14:52:57.329 30280-30280/? W/System.err: at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1326)10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.Activity.performResume(Activity.java:6338)10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3336)10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3384)10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2574)10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.access$900(ActivityThread.java:150)10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1399)10-22 14:52:57.330 30280-30280/? W/System.err: at android.os.Handler.dispatchMessage(Handler.java:102)10-22 14:52:57.330 30280-30280/? W/System.err: at android.os.Looper.loop(Looper.java:168)10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.main(ActivityThread.java:5885)10-22 14:52:57.330 30280-30280/? W/System.err: at java.lang.reflect.Method.invoke(Native Method)10-22 14:52:57.330 30280-30280/? W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:819)10-22 14:52:57.330 30280-30280/? W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:709)10-22 14:52:57.330 30280-30280/? W/System.err: Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied)10-22 14:52:57.330 30280-30280/? W/System.err: at libcore.io.Posix.open(Native Method)10-22 14:52:57.330 30280-30280/? W/System.err: at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186)10-22 14:52:57.330 30280-30280/? W/System.err: at java.io.File.createNewFile(File.java:932)10-22 14:52:57.330 30280-30280/? W/System.err: ... 14 more 解决方案 Short answer.ContextCompat.getExternalFilesDirs solves the access error when you don't need to share files.The secure way of sharing it is to use a content provider or the new Storage Access Framework.Summary.There is no universal way to write to external SD card on Android due to continuous changes:Pre-KitKat: official Android platform has not supported SD cards at all except for exceptions.KitKat: introduced APIs that let apps access files in app-specific directories on SD cards.Lollipop: added APIs to allow apps to request access to folders owned by other providers.Nougat: provided a simplified API to access common external storage directories.Based on Doomsknight's answer and mine, and Dave Smith and Mark Murphy blog posts: 1, 2, 3:Ideally, use the Storage AccessFrameworkandDocumentFileas Jared Rummler pointed. Or:Use your app specific path/storage/extSdCard/Android/data/com.myapp.example/files.Add read/write permission to manifest for pre-KitKat, no permission required later for this path.Try to use your App path and Doomsknight's methods considering KitKat and Samsung case.Filter and use getStorageDirectories, your App path and read/write permissions prior to KitKat.ContextCompat.getExternalFilesDirs since KitKat. Considering devices that return internal first.1. Permissions.You can grant read/write access to external SD card on the different api levels (API23+ at run time).Since KitKat, permissions are not necessary if you use app-specific directories, required otherwise:2. About the universal solution.The history says that there is not universal way to write to external SD card but continues...This fact is demostrated by these examples of external storage configurations for devices.3. About your update 1.I would use application-specific directories to avoid the issue of your updated question and ContextCompat.getExternalFilesDirs() using getExternalFilesDir documentation as reference.Improve the heuristics to determine what represents removable media based on the different api levels like android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKATRemember that Android 6.0 supports portable storage devices and third-party apps must go through the Storage Access Framework. Your devices HTC10 and Shield K1 are probably API 23.Your log shows a permission denied exception accessing /mnt/media_rw, like this fix for API 19+:<permission name="android.permission.WRITE_EXTERNAL_STORAGE" ><group gid="sdcard_r" /><group gid="sdcard_rw" /><group gid="media_rw" /> // this line is added via root in the link to fix it.</permission>I never tried it so I can not share code but I would avoid the for trying to write on all the returned directories and look for the best available storage directory to write into based on remaining space.Perhaps Gizm0's alternative to your getStorageDirectories() method it's a good starting point.ContextCompat.getExternalFilesDirs solves the issue if you don't need access to other folders.4. Request permissions in manifest (Api < 23) and at run time (Api >= 23).Add the next code to your AndroidManifest.xml and read Getting access to external storageIgnore the next note due to bugs, but try to use ContextCompat.getExternalFilesDirs(): Request permissions at runtime if API level 23+ and read Requesting Permissions at Run Time 5. Prior to KitKat try to use Doomsknight method 1, method 2 otherwise.Read Mark Murphy's explanation and recommended Dianne Hackborn and Dave Smith onesPrior to KitKat try to use Doomsknight method 1 or read this response by Gnathonic or gist:public static HashSet<String> getExternalMounts() { final HashSet<String> out = new HashSet<String>(); String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*"; String s = ""; try { final Process process = new ProcessBuilder().command("mount") .redirectErrorStream(true).start(); process.waitFor(); final InputStream is = process.getInputStream(); final byte[] buffer = new byte[1024]; while (is.read(buffer) != -1) { s = s + new String(buffer); } is.close(); } catch (final Exception e) { e.printStackTrace(); } // parse output final String[] lines = s.split("\n"); for (String line : lines) { if (!line.toLowerCase(Locale.US).contains("asec")) { if (line.matches(reg)) { String[] parts = line.split(" "); for (String part : parts) { if (part.startsWith("/")) if (!part.toLowerCase(Locale.US).contains("vold")) out.add(part); } } } } return out;}Also read Paolo Rovelli's explanation and try to use Jeff Sharkey's solution since KitKat:6. Lollipop also introduced changes and the DocumentFile helper class.getStorageState Added in API 19, deprecated in API 21,use getExternalStorageState(File)7. Android 7.0 provides a simplified API to access external storage dirs.8. Android O changes.9. Related questions and recommended answers.How can I get external SD card path for Android 4.0+?mkdir() works while inside internal flash storage, but not SD card?Diff between getExternalFilesDir and getExternalStorageDirectory()Why getExternalFilesDirs() doesn't work on some devices?How to use the new SD card access API presented for Android 5.0 (Lollipop)Writing to external SD card in Android 5.0 and aboveAndroid SD Card Write Permission using SAF (Storage Access Framework)SAFFAQ: The Storage Access Framework FAQ10. Related bugs and issues.Bug: On Android 6, when using getExternalFilesDirs, it won't let you create new files in its resultsWriting to directory returned by getExternalCacheDir() on Lollipop fails without write permission 这篇关于在Android上写入外部SD卡的通用方式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
08-05 17:46