为什么当我将图像设置为 ImageView 并在 android 7.1.1 上 运行 时应用程序崩溃
Why app crashes when i set image to ImageView and run it on android 7.1.1
我是 Android 的新手。我想知道为什么我的应用程序在使用显式意图将图像设置为 imageView 时在 Android 7.1.1 上崩溃,因为它在其他版本的 Android 上运行良好。我试图理解并解决问题,但无法做到。
谁能帮我理解为什么会这样,背后的原因是什么?
谢谢。
这是它显示的内容:
eAnimators on 0x97c5ee80 (RippleDrawable) with handle 0x9affd880
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.himanshu.imageviewintent, PID: 5049
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=Intent { dat=content://media/external/images/media/41 }} to activity {com.example.himanshu.imageviewintent/com.example.himanshu.imageviewintent.MainActivity}: java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/images/media/41 from pid=5049, uid=10073 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
at android.app.ActivityThread.deliverResults(ActivityThread.java:4089)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4132)
at android.app.ActivityThread.-wrap20(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1533)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Caused by: java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/images/media/41 from pid=5049, uid=10073 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
at android.os.Parcel.readException(Parcel.java:1684)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:146)
at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:692)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1147)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:984)
at android.content.ContentResolver.openInputStream(ContentResolver.java:704)
at com.example.himanshu.imageviewintent.MainActivity.onActivityResult(MainActivity.java:44)
at android.app.Activity.dispatchActivityResult(Activity.java:6932)
at android.app.ActivityThread.deliverResults(ActivityThread.java:4085)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4132)
at android.app.ActivityThread.-wrap20(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1533)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Application terminated.
这是我的代码:
Java :
public class MainActivity extends Activity {
final static int ImageIntentRequest = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void setImage(View view) {
Intent intent = new Intent(Intent.ACTION_PICK);
File imageViewPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
String path = imageViewPath.getPath();
intent.setDataAndType(Uri.parse(path),"image/*");
startActivityForResult(intent,ImageIntentRequest);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(resultCode == RESULT_OK && requestCode == ImageIntentRequest) {
InputStream stream;
ImageView imageView = (ImageView)findViewById(R.id.imageView);
try {
stream = getContentResolver().openInputStream(data.getData());
Bitmap bitmap = BitmapFactory.decodeStream(stream);
imageView.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
Toast.makeText(this,"There is no File Present",Toast.LENGTH_SHORT).show();;
e.printStackTrace();
}
}
}
}
XML :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.himanshu.imageviewintent.MainActivity"
>
<Button
android:text="@string/set_image_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn_set_image"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:onClick="setImage"
/>
<ImageView
android:src="@mipmap/ic_launcher"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/imageView"
android:contentDescription="@null"
android:layout_above="@+id/btn_set_image"
android:layout_alignParentStart="true"
android:layout_marginBottom="10dp"
/>
</RelativeLayout>
我找到了一个简单的解决方法:
我刚换了,
Intent.ACTION_PICK by -
Intent intent = Intent(Intent.ACTION_GET_CONTENT);
现在该应用程序在 Android 的旧版本和最新版本上运行完全正常。
但仍有疑问:
为什么 ACTION_PICK 适用于旧版本的 Android 但不适用于新版本?
为什么 ACTION_GET_CONTENT 适用于所有版本?
如果有人澄清一下,我将不胜感激?
您需要获得用户的运行时权限才能读取外部存储。
您可以使用以下代码请求用户的许可。
希望对您有所帮助。
if(ActivityCompat.checkSelfPermission(this,Manifest.permission.READ_EXTERNAL_STORAGE)!=PackageManager.PERMISSION_GRANTED){
requestStoragePermission();
} else {
loadImageInImageview();
}
private void requestStoragePermission() {
final String[] permissions = new String[]{Manifest.permission.READ_EXTERNAL_STORAGE};
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
ActivityCompat.requestPermissions(this, permissions, 1000);
return;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1000) {
if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
loadImageInImageview();
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Allow permission.")
.setMessage("Please....")
.setCancelable(false)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivity(intent);
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
}).show();
}
}
}
您没有向清单添加存储权限,或者您在使用之前没有要求用户接受它们。
<manifest ...>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
...
</manifest>
您可以使用此功能请求用户许可
public boolean isStoragePermissionGranted() {
if (Build.VERSION.SDK_INT >= 23) {
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
Log.v(TAG,"Permission is granted");
return true;
} else {
Log.v(TAG,"Permission is revoked");
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
return false;
}
}
else { //permission is automatically granted on sdk<23 upon installation
Log.v(TAG,"Permission is granted");
return true;
}
}
如果您查看 documentation 关于 Android 牛轧糖中引入的更改,您会发现 -
For apps targeting Android 7.0, the Android framework enforces the
StrictMode API policy that prohibits exposing file:// URIs outside
your app. If an intent containing a file URI leaves your app, the app
fails with a FileUriExposedException exception.
To share files between applications, you should send a content:// URI
and grant a temporary access permission on the URI. The easiest way to
grant this permission is by using the FileProvider class.
在这里,你的意图
Intent intent = new Intent(Intent.ACTION_PICK);
File imageViewPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
String path = imageViewPath.getPath();
intent.setDataAndType(Uri.parse(path),"image/*");
让您的应用程序打开 Gallery/Camera 来选择一张图片,这会导致崩溃,因为它不遵循 Android 牛轧糖协议。
要解决您的问题,请将您的 gradle 文件中的 targetApi 从 24/25/26(无论您现在拥有哪个)向下更改为 23(棉花糖)。
或者,您可以在 File Sharing 上阅读更多内容并以不违反 StrictMode 政策的方式进行。
如果时间紧迫,可以使用EasyImage库来解决。
我是 Android 的新手。我想知道为什么我的应用程序在使用显式意图将图像设置为 imageView 时在 Android 7.1.1 上崩溃,因为它在其他版本的 Android 上运行良好。我试图理解并解决问题,但无法做到。
谁能帮我理解为什么会这样,背后的原因是什么?
谢谢。
这是它显示的内容:
eAnimators on 0x97c5ee80 (RippleDrawable) with handle 0x9affd880
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.himanshu.imageviewintent, PID: 5049
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=Intent { dat=content://media/external/images/media/41 }} to activity {com.example.himanshu.imageviewintent/com.example.himanshu.imageviewintent.MainActivity}: java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/images/media/41 from pid=5049, uid=10073 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
at android.app.ActivityThread.deliverResults(ActivityThread.java:4089)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4132)
at android.app.ActivityThread.-wrap20(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1533)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Caused by: java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/images/media/41 from pid=5049, uid=10073 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
at android.os.Parcel.readException(Parcel.java:1684)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:146)
at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:692)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1147)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:984)
at android.content.ContentResolver.openInputStream(ContentResolver.java:704)
at com.example.himanshu.imageviewintent.MainActivity.onActivityResult(MainActivity.java:44)
at android.app.Activity.dispatchActivityResult(Activity.java:6932)
at android.app.ActivityThread.deliverResults(ActivityThread.java:4085)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4132)
at android.app.ActivityThread.-wrap20(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1533)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Application terminated.
这是我的代码:
Java :
public class MainActivity extends Activity {
final static int ImageIntentRequest = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void setImage(View view) {
Intent intent = new Intent(Intent.ACTION_PICK);
File imageViewPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
String path = imageViewPath.getPath();
intent.setDataAndType(Uri.parse(path),"image/*");
startActivityForResult(intent,ImageIntentRequest);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(resultCode == RESULT_OK && requestCode == ImageIntentRequest) {
InputStream stream;
ImageView imageView = (ImageView)findViewById(R.id.imageView);
try {
stream = getContentResolver().openInputStream(data.getData());
Bitmap bitmap = BitmapFactory.decodeStream(stream);
imageView.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
Toast.makeText(this,"There is no File Present",Toast.LENGTH_SHORT).show();;
e.printStackTrace();
}
}
}
}
XML :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.himanshu.imageviewintent.MainActivity"
>
<Button
android:text="@string/set_image_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn_set_image"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:onClick="setImage"
/>
<ImageView
android:src="@mipmap/ic_launcher"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/imageView"
android:contentDescription="@null"
android:layout_above="@+id/btn_set_image"
android:layout_alignParentStart="true"
android:layout_marginBottom="10dp"
/>
</RelativeLayout>
我找到了一个简单的解决方法:
我刚换了,
Intent.ACTION_PICK by -
Intent intent = Intent(Intent.ACTION_GET_CONTENT);
现在该应用程序在 Android 的旧版本和最新版本上运行完全正常。
但仍有疑问:
为什么 ACTION_PICK 适用于旧版本的 Android 但不适用于新版本? 为什么 ACTION_GET_CONTENT 适用于所有版本?
如果有人澄清一下,我将不胜感激?
您需要获得用户的运行时权限才能读取外部存储。
您可以使用以下代码请求用户的许可。
希望对您有所帮助。
if(ActivityCompat.checkSelfPermission(this,Manifest.permission.READ_EXTERNAL_STORAGE)!=PackageManager.PERMISSION_GRANTED){
requestStoragePermission();
} else {
loadImageInImageview();
}
private void requestStoragePermission() {
final String[] permissions = new String[]{Manifest.permission.READ_EXTERNAL_STORAGE};
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
ActivityCompat.requestPermissions(this, permissions, 1000);
return;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1000) {
if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
loadImageInImageview();
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Allow permission.")
.setMessage("Please....")
.setCancelable(false)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivity(intent);
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
}).show();
}
}
}
您没有向清单添加存储权限,或者您在使用之前没有要求用户接受它们。
<manifest ...>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
...
</manifest>
您可以使用此功能请求用户许可
public boolean isStoragePermissionGranted() {
if (Build.VERSION.SDK_INT >= 23) {
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
Log.v(TAG,"Permission is granted");
return true;
} else {
Log.v(TAG,"Permission is revoked");
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
return false;
}
}
else { //permission is automatically granted on sdk<23 upon installation
Log.v(TAG,"Permission is granted");
return true;
}
}
如果您查看 documentation 关于 Android 牛轧糖中引入的更改,您会发现 -
For apps targeting Android 7.0, the Android framework enforces the StrictMode API policy that prohibits exposing file:// URIs outside your app. If an intent containing a file URI leaves your app, the app fails with a FileUriExposedException exception.
To share files between applications, you should send a content:// URI and grant a temporary access permission on the URI. The easiest way to grant this permission is by using the FileProvider class.
在这里,你的意图
Intent intent = new Intent(Intent.ACTION_PICK);
File imageViewPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
String path = imageViewPath.getPath();
intent.setDataAndType(Uri.parse(path),"image/*");
让您的应用程序打开 Gallery/Camera 来选择一张图片,这会导致崩溃,因为它不遵循 Android 牛轧糖协议。
要解决您的问题,请将您的 gradle 文件中的 targetApi 从 24/25/26(无论您现在拥有哪个)向下更改为 23(棉花糖)。
或者,您可以在 File Sharing 上阅读更多内容并以不违反 StrictMode 政策的方式进行。
如果时间紧迫,可以使用EasyImage库来解决。