android 6.0 权限崩溃分析:android.system.ErrnoException: statvfs failed: ENOENT (No such file or directory)

android 6.0 权限崩溃分析:android.system.ErrnoException: statvfs failed: ENOENT (No such file or directory)

问题复现

在某些 6.0 设备的设备上 程序因为使用了 app 的外置 SD 卡的私有目录,在没有 存储权限的情况下,会崩溃,出现:

05-03 09:53:02.337 W/System.err: java.lang.IllegalArgumentException: Invalid path: /storage/emulated/0/Android/data/gdky005.testexception/files
05-03 09:53:02.356 W/System.err:     at android.os.StatFs.doStat(StatFs.java:46)
05-03 09:53:02.370 W/System.err:     at android.os.StatFs.<init>(StatFs.java:39)
05-03 09:53:02.386 W/System.err:     at gdky005.testexception.SDCardUtil.getSDAvailableBlocks(SDCardUtil.java:166)
05-03 09:53:02.401 W/System.err:     at gdky005.testexception.MainActivity.testSDCard(MainActivity.java:69)
05-03 09:53:02.410 W/System.err:     at gdky005.testexception.MainActivity$1.onClick(MainActivity.java:26)
05-03 09:53:02.412 D/DhcpClient: Received packet: f4:5c:89:c2:6d:7b NAK, reason (none)
05-03 09:53:02.417 W/System.err:     at android.view.View.performClick(View.java:5637)
05-03 09:53:02.425 W/System.err:     at android.view.View$PerformClick.run(View.java:22429)
05-03 09:53:02.431 W/System.err:     at android.os.Handler.handleCallback(Handler.java:751)
05-03 09:53:02.435 W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:95)
05-03 09:53:02.439 W/System.err:     at android.os.Looper.loop(Looper.java:154)
05-03 09:53:02.442 W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:6119)
05-03 09:53:02.445 W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
05-03 09:53:02.449 W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
05-03 09:53:02.452 W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
05-03 09:53:02.461 W/System.err: Caused by: android.system.ErrnoException: statvfs failed: ENOENT (No such file or directory)
05-03 09:53:02.463 W/System.err:     at libcore.io.Posix.statvfs(Native Method)
05-03 09:53:02.466 W/System.err:     at libcore.io.BlockGuardOs.statvfs(BlockGuardOs.java:304)
05-03 09:53:02.469 W/System.err:     at android.system.Os.statvfs(Os.java:506)
05-03 09:53:02.471 W/System.err:     at android.os.StatFs.doStat(StatFs.java:44)
05-03 09:53:02.472 W/System.err:    ... 13 more

具体崩溃路径是:

Invalid path: /storage/emulated/0/Android/data/gdky005.testexception/files

代码中调用:

context.getExternalFilesDir(null);  //获取 外置 SD 卡 私有目录

具体原因

在主 app 中 因为使用了 targetSdkVersion 低于 android 6.0(23)的 19, 意味着 app 支持的最高目标版本是 android 4.4 (19)。 targetSdkVersion 是 Android 提供向前兼容的主要依据。 因此系统检查到 这个低于 android 6.0(23), 默认给 app 自动开启了所有的权限(这也是之前一直疑惑的点)。当在 手机的系统大约等于 android 6.0(23),默认拥有了所有的权限,因此 app 可以正常运行。

但是根据 听云 的崩溃手机信息来看,确实是崩溃了。

java.lang.IllegalArgumentException: Invalid path: /storage/emulated/0/Android/data/

经过大量尝试以及测试,终于发现问题是处在 targetSdkVersion 这里。 如果 tartgetSdkVersion 大约23,即使关闭 SD 卡的存储权限, 也可以通过 context.getExternalFilesDir(null); 拿到 SD 卡的位置 app 私有存储目录。

但是,当 tartgetSdkVersion < 23, 关闭 SD 卡的存储权限后, 通过 context.getExternalFilesDir(null); 不可以拿到 SD 卡的外置 app 私有目录,会提示 「Invalid path: /storage/emulated/0/Android/data/」。

因为在 6.0 以上的手机,检测到 app 的 tartgetSdkVersion < 23,默认给了所有的权限,但是当 关闭 存储权限后,意味着 SD 卡的读写权限都没有了。当调用到 context.getExternalFilesDir(null); 的时候,系统认为 当前 app 的最高 运行版本是 低于 23 的,所有不会给 app 在 SD 卡创建 app 的私有目录。所有尝试获取 SD 卡的 app 私有目录的时候,就会获取不到路径,因为没有 SD 卡的读写权限啊。

这里需要说明一下:当 tartgetSdkVersion > 23,调用 context.getExternalFilesDir(null); ,系统会给 app 在 SD 卡的 Android/data/ + 包名 + files , 以这样的形式 创建一个 app 的私有文件夹,这也是 6.0 以后的新特性。例如:

/storage/emulated/0/Android/data/gdky005.testexception/files

这个目录其实就等同于 Linux 里面的一个 app 的私有账户目录,只有当前 app 能访问,其他 app 直接访问不了,所以也被称之为 app 的外置私有目录。 app 的 tartgetSdkVersion < 23, 因为系统没有给 它创建 私有目录,而且他没有 SD 卡的读写权限,所以 也不可以自行创建 app 的私有目录。

总结

由于 app 的 targetSdkVersion < 23,默认 app 拥有所有的权限,但是当 用户手动禁用 SD 卡的存储权限,或者 Rom, 第三方服务 禁用,那么就会造成很严重的问题。无法使用外置app的私有目录,就会报错。

  1. 不过在 原生的 android 系统里面会提示用户:
  2. 如果第三方服务或者 rom 自动禁用的话,也许看不到警告。
  3. 例如三星等系统,在 app 安装好以后,会自动弹出权限框,让用户选择权限。

处理措施:
1. try catch
2. targetSdkVersion >= 23 参考:http://chinagdg.org/2016/01/picking-your-compilesdkversion-minsdkversion-targetsdkversion/
3. 尝试 自动创建文件 检测是否成功,并验证,如果失败,立即使用 app 的内置私有目录

发布了46 篇原创文章 · 获赞 23 · 访问量 21万+
展开阅读全文

java.lang.IllegalArgumentException求大神帮帮忙!!!

08-24

异常提示:Servlet.service() for servlet [dispatcher] in context with path [/comsed1] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: Bean object must not be null; nested exception is java.lang.IllegalArgumentException: Bean object must not be null] with root cause java.lang.IllegalArgumentException: Bean object must not be null at org.springframework.util.Assert.notNull(Assert.java:112) at org.springframework.beans.BeanWrapperImpl.setWrappedInstance(BeanWrapperImpl.java:213) at org.springframework.beans.BeanWrapperImpl.setWrappedInstance(BeanWrapperImpl.java:202) at org.springframework.beans.BeanWrapperImpl.<init>(BeanWrapperImpl.java:151) at org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper.<init>(DirectFieldAccessFallbackBeanWrapper.java:35) at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.getId(JpaMetamodelEntityInformation.java:117) at org.springframework.data.repository.core.support.AbstractEntityInformation.isNew(AbstractEntityInformation.java:51) at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.isNew(JpaMetamodelEntityInformation.java:196) at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:376) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:405) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:390) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:344) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.java:111) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) at com.sun.proxy.$Proxy136.save(Unknown Source) at iot.service.ProductMasterService.modifyProductMaster(ProductMasterService.java:32) at iot.controller.productMasterController.modifyProductMaster(productMasterController.java:95) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 代码部分: @RequestMapping(value = "modifyProductMasterInit") public String modifyProductMasterInit(@RequestParam("productId") String productId,Model model){ // Iterable<ProductMaster> productMasterList = productMasterDAO.findByProductId(productId); // model.addAttribute("productMasterList", productMasterList); ProductMaster productMaster = this.productMasterService.findByProductId(productId); model.addAttribute("productMaster", productMaster); return "productMaster/Pro_Master_modify"; } @RequestMapping(value = "modifyProductMaster") public String modifyProductMaster(ProductMaster productMaster,@RequestParam("productId") String productId){ ProductMaster productMasterOld = this.productMasterService.findByProductId(productId);//经断点调试productMasterOld为空 try { BeanUtils.copyProperties(productMaster, productMasterOld); } catch (Exception e) { e.printStackTrace(); } this.productMasterService.modifyProductMaster(productMasterOld); return "redirect:/productMaster/loadProductMaster"; } Service方法: @Service public class ProductMasterService { @Autowired(required = false) private ProductMasterDAO productMasterDAO; public void addproductMaster(ProductMaster productMaster) { this.productMasterDAO.save(productMaster); } public ProductMaster findByProductId(String productId) { return this.productMasterDAO.findByProductId(productId); } public void modifyProductMaster(ProductMaster productMaster) { this.productMasterDAO.save(productMaster); } } ProductMasterDAO部分: public interface ProductMasterDAO extends PagingAndSortingRepository<ProductMaster, String>,JpaSpecificationExecutor<ProductMaster>{ @Query("SELECT p FROM ProductMaster p WHERE p.productId = ?1") public ProductMaster findByProductId(String productId); } 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览