最近在工作中遇到一个异常,在Android N版本上进行开发,在短信应用中设置消息通知,这个操作需要访问本地存储,但是结果短信闪退,出现如下日志:
----- timezone:GMT
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: FATAL EXCEPTION: main
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: Process: com.android.mms, PID: 2646
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: java.lang.RuntimeException: Unable to resume activity {com.android.mms/com.mediatek.setting.NotificationPreferenceActivity}: java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/audio/media/171 from pid=2646, uid=10073 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3506)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3546)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1577)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:110)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at android.os.Looper.loop(Looper.java:203)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:6251)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1063)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:924)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: Caused by: java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/audio/media/171 from pid=2646, uid=10073 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at android.os.Parcel.readException(Parcel.java:1683)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:188)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:151)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:692)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1179)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:998)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:921)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at android.media.RingtoneManager.isRingtoneExist(RingtoneManager.java:998)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at com.mediatek.setting.NotificationPreferenceActivity.setRingtoneSummary(NotificationPreferenceActivity.java:189)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at com.mediatek.setting.NotificationPreferenceActivity.onResume(NotificationPreferenceActivity.java:124)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1269)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at android.app.Activity.performResume(Activity.java:6770)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3477)
06-08 16:05:55.464339 2646 2646 E AndroidRuntime: ... 8 more
日志是由于没有权限引起,但是我们看AndroidManifest.xml里面已经有READ_EXTERNAL_STORAGE这个权限了,为什么还报这个异常呢?后来查看资料,发现Android sdk版本>=23后,需要对权限进行动态授权,否则应用会出现crash。经过对代码进行动态授权后,就解决了这个问题。加了如下代码:
@Override
protected void onCreate(Bundle icicle) {
if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED)
{
requestPermissions(
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
}
super.onCreate(icicle); ....
再进入下个Activity的时候,先判断是否有那个READ_EXTERNAL_STORAGE权限,如果没有这个权限,系统就会弹出请求权限的Dialog,选中同意后,会回调onRequestPermissionsResult(),重写该方法。
private static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 1;
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
{
if (requestCode == MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE)
{
if (grantResults[0] != PackageManager.PERMISSION_GRANTED)
{
finish(); //关闭该Activity
}
return;
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
当弹出Dialog的时候,第一次弹出和第二次弹出的现象不一致,如下:
第一次弹出Dialog如左图,没有不再询问这个选项框。第二次弹出如右图,有这个框。所以大家在开发的过程中,看到这种现象不要以为是故障,其实这是正常现象,Android机制就是如此。