android - 致命异常:通过ClipData暴露在app之外,点击时Camera崩溃
问题描述
每当我点击相机时,我的应用程序就会崩溃。如果我点击我在较低的 SDK 目标设备上测试,它运行良好。现在,我仍在学习如何使用适用于 23 岁以上 SDK 的文件提供程序。我遵循了一个教程。请用下面的代码帮助我修复文件提供程序,我将从那里学习。非常感谢你!我创建了 xml 文件夹并创建了provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_files" path="."/>
</paths>
我还在 MANIFEST.XML 中创建了
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
...
<application
...
android:grantUriPermissions="true"
android:requestLegacyExternalStorage="true"
...
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
</application>
</manifest>
这是定义相机和画廊的类。
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
ButterKnife.bind(this);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
changeImageButton.setOnClickListener(this);
addQuoteButton.setOnClickListener(this);
fontButton.setOnClickListener(this);
WishTV.setOnClickListener(this);
SharedPreferenceUtils.setImageId(this, R.drawable.man1);
SharedPreferenceUtils.setFont(this, 0);
SharedPreferenceUtils.setFontSize(this, 30);
SharedPreferenceUtils.setTextAlignment(this, SharedPreferenceUtils.TEXT_ALIGNMENT_CENTER);
WishTV.setText(getResources().getString(R.string.title_text));
requestPermissions();
}
private void requestPermissions() {
if (Build.VERSION.SDK_INT >= 23) {
RuntimePermissionHelper runtimePermissionHelper = RuntimePermissionHelper.getInstance(this);
if (!runtimePermissionHelper.isAllPermissionAvailable()) {
runtimePermissionHelper.setActivity(this);
runtimePermissionHelper.requestPermissionsIfDenied();
}
}
}
@Override
protected void onStart() {
super.onStart();
if(SharedPreferenceUtils.isImage(this)) {
int imageId = SharedPreferenceUtils.getSelectedImageId(this);
imageView.setImageResource(imageId);
}
else {
String imagePath=SharedPreferenceUtils.getSelectedImagePath(this);
Glide.with(this).load(imagePath).into(imageView);
}
int fontId = SharedPreferenceUtils.getFont(this);
WishTV.setTypeface(CustomFontsLoader.getTypeface(this, fontId));
int fontSize = SharedPreferenceUtils.getFontSize(this);
WishTV.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize);
if(SharedPreferenceUtils.getTextColor(MainActivity.this)==0){
WishTV.setTextColor(Color.WHITE);
}else{
int textColor=SharedPreferenceUtils.getTextColor(MainActivity.this);
WishTV.setTextColor(textColor);
}
int alignment = SharedPreferenceUtils.getTextAlignment(this);
setAlignment(alignment);
}
@Override
protected void onResume() {
super.onResume();
int alignment = SharedPreferenceUtils.getTextAlignment(this);
setAlignment(alignment);
}
private void setAlignment(int alignment) {
switch (alignment){
case SharedPreferenceUtils.TEXT_ALIGNMENT_START:
WishTV.setGravity(Gravity.START);
if(Build.VERSION.SDK_INT >=17) {
WishTV.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_START);
}
WishTV.setText(WishTV.getText().toString().trim());
break;
case SharedPreferenceUtils.TEXT_ALIGNMENT_END:
WishTV.setGravity(Gravity.END);
if(Build.VERSION.SDK_INT >=17) {
WishTV.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_END);
}
WishTV.setText(WishTV.getText().toString().trim());
break;
case SharedPreferenceUtils.TEXT_ALIGNMENT_CENTER:
WishTV.setGravity(Gravity.CENTER);
if(Build.VERSION.SDK_INT >=17) {
WishTV.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
}
WishTV.setText(WishTV.getText().toString().trim());
break;
}
}
public void showChangeLangDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
LayoutInflater inflater = MainActivity.this.getLayoutInflater();
View view=inflater.inflate(R.layout.add_quote_dialog, null);
View empty=new View(MainActivity.this);
builder.setView(empty);
empty.requestFocus();
builder.setView(view);
quoteText =(EditText) view.findViewById(R.id.addQouteEditText);
if(changed) {
quoteText.setText(WishTV.getText().toString());
}
builder.setCancelable(false);
builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
String quote= quoteText.getText().toString().trim();
if(quote.length()<1){
Toast.makeText(MainActivity.this,"No quote entered",Toast.LENGTH_SHORT).show();
}
else{
WishTV.setText(quote);
changed=true;
}
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
builder.create().show();
}
@Override
public void onClick(View v) {
if(v.getId()==R.id.changeImageButton){
Intent intent = new Intent(this, ImageActivity.class);
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(this);
String text = WishTV.getText().toString();
intent.putExtra("text",text);
ActivityCompat.startActivity(this,intent,options.toBundle());
}else if(v.getId() == R.id.addQuoteButton){
showChangeLangDialog();
}else if(v.getId() == R.id.fontButton){
Intent intent = new Intent(this, FontActivity.class);
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(this);
text = WishTV.getText().toString();
intent.putExtra("text",text);
ActivityCompat.startActivity(this, intent, options.toBundle());
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_save:
frameLayout.setDrawingCacheEnabled(true);
frameLayout.buildDrawingCache();
Bitmap bitmap = frameLayout.getDrawingCache();
saveImageTask = new SaveImageTask();
saveImageTask.execute(bitmap);
return true;
default:
// If we got here, the user's action was not recognized.
// Invoke the superclass to handle it.
return super.onOptionsItemSelected(item);
}
}
private void createDirectory() {
if (Build.VERSION.SDK_INT >= 23) {
RuntimePermissionHelper runtimePermissionHelper = RuntimePermissionHelper.getInstance(this);
if (runtimePermissionHelper.isAllPermissionAvailable()) {
File directory = new File(Environment.getExternalStorageDirectory()+"/"+Environment.DIRECTORY_PICTURES+"/QuoteCards/");
if(!directory.exists()) {
directory.mkdirs();
}
} else {
runtimePermissionHelper.setActivity(this);
runtimePermissionHelper.requestPermissionsIfDenied();
}
}else{
File directory = new File(Environment.getExternalStorageDirectory()+"/"+Environment.DIRECTORY_PICTURES+"/QuoteCards/");
if(!directory.exists()) {
directory.mkdirs();
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
Map<String, Integer> perms = new HashMap<>();
// Fill with results*/
for (int i = 0; i < permissions.length; i++)
perms.put(permissions[i], grantResults[i]);
boolean isAllPermissionsAllowed = false;
for(int i : grantResults){
if(i== PackageManager.PERMISSION_GRANTED){
isAllPermissionsAllowed = true;
}else{
isAllPermissionsAllowed = false;
break;
}
}
if(isAllPermissionsAllowed){
File directory = new File(Environment.getExternalStorageDirectory()+"/"+Environment.DIRECTORY_PICTURES+"/QuoteCards/");
if(!directory.exists()) {
directory.mkdirs();
}
} else {
Toast.makeText(this,"Please allow all permissions",Toast.LENGTH_SHORT).show();
}
}
private class SaveImageTask extends AsyncTask<Bitmap,Void,Void>{
ProgressDialog progressDialog;
@Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog = new ProgressDialog(MainActivity.this);
progressDialog.setMessage("Saving image....");
progressDialog.show();
}
@Override
protected Void doInBackground(Bitmap... params) {
Bitmap bitmap = params[0];
FileOutputStream out = null;
String timeStamp = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss").format(new Date());
path = Environment.getExternalStorageDirectory()+"/"+Environment.DIRECTORY_PICTURES+"/QuoteCards/IMG_"+timeStamp+".png";
createDirectory();
try {
out = new FileOutputStream(new File(path));
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
} catch (Exception e) {
Log.e(TAG, "doInBackground: ",e );
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
Log.e(TAG, "doInBackground: ", e);
}
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
progressDialog.dismiss();
frameLayout.destroyDrawingCache();
scan();
postSaveDialogBox();
}
}
public void postSaveDialogBox()
{
LayoutInflater layoutInflater = LayoutInflater.from(this);
View view = layoutInflater.inflate(R.layout.on_save_dialog_box, null);
final AlertDialog alertD = new AlertDialog.Builder(this).create();
alertD.setTitle("Image Saved");
ImageView share = (ImageView) view.findViewById(R.id.shareMain);
ImageView viewGallery = (ImageView) view.findViewById(R.id.viewMain);
share.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
alertD.dismiss();
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("image/*");
i.putExtra(Intent.EXTRA_STREAM, Uri.parse(path));
try {
startActivity(Intent.createChooser(i, getResources().getString(R.string.app_name)));
} catch (android.content.ActivityNotFoundException ex) {
Log.e(TAG, "onClick: ",ex );
}
}
});
viewGallery.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
alertD.dismiss();
Intent i=new Intent();
i.setAction(Intent.ACTION_VIEW);
i.setDataAndType(Uri.fromFile(new File(path)), "image/*");
startActivity(i);
}
});
alertD.setView(view);
alertD.show();
}
private void scan() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
{
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(path);
Uri contentUri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider",(f));
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
mediaScanIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
else
{
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse(path)));
}
}
@Override
public void onBackPressed() {
super.onBackPressed();
SharedPreferenceUtils.clear(this);
}
}
我为文件提供程序路径设置了 xml 文件夹。我还将文件提供程序添加到 Maniest。我唯一的问题是在下面的代码中定义它。它给了我错误file:///storage/emulated/0/Pictures/QuoteCards/IMG_TEMP_20201006_041943.jpg
这是 LogCat
2020-10-06 04:19:43.225 24419-24419/com.quotes.app.cards E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.quotes.app.cards, PID: 24419
android.os.FileUriExposedException: file:///storage/emulated/0/Pictures/QuoteCards/IMG_TEMP_20201006_041943.jpg exposed beyond app through ClipData.Item.getUri()
at android.os.StrictMode.onFileUriExposed(StrictMode.java:1960)
at android.net.Uri.checkFileUriExposed(Uri.java:2357)
at android.content.ClipData.prepareToLeaveProcess(ClipData.java:942)
at android.content.Intent.prepareToLeaveProcess(Intent.java:9850)
at android.content.Intent.prepareToLeaveProcess(Intent.java:9835)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1610)
at android.app.Activity.startActivityForResult(Activity.java:4502)
at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:676)
at android.app.Activity.startActivityForResult(Activity.java:4460)
at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:663)
at com.quotes.app.cards.activity.ImageActivity$2.onClick(ImageActivity.java:253)
at androidx.appcompat.app.AlertController$ButtonHandler.handleMessage(AlertController.java:167)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6549)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:888)
这是 ImageActivity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ButterKnife.bind(this);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
generateBackgroundImageList();
String text = getIntent().getStringExtra("text");
wishTV.setText(text);
RecyclerView recyclerView = (RecyclerView)findViewById(R.id.imageList);
ImageListAdapter adapter = new ImageListAdapter(backgroundImages,this);
adapter.setOnItemClickListener(this);
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setHasFixedSize(true);
}
@Override
protected void onStart() {
super.onStart();
if (SharedPreferenceUtils.isImage(this)) {
int imageId = SharedPreferenceUtils.getSelectedImageId(this);
imageView.setImageResource(imageId);
} else {
String imagePath = SharedPreferenceUtils.getSelectedImagePath(this);
Glide.with(this).load(imagePath).into(imageView);
}
int fontId = SharedPreferenceUtils.getFont(this);
wishTV.setTypeface(CustomFontsLoader.getTypeface(this, fontId));
if(SharedPreferenceUtils.getTextColor(ImageActivity.this)==0){
wishTV.setTextColor(Color.RED);
}else{
int textColor=SharedPreferenceUtils.getTextColor(ImageActivity.this);
wishTV.setTextColor(textColor);
}
int fontSize = SharedPreferenceUtils.getFontSize(this);
wishTV.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize);
int alignment = SharedPreferenceUtils.getTextAlignment(this);
setAlignment(alignment);
}
private void setAlignment(int alignment) {
switch (alignment){
case SharedPreferenceUtils.TEXT_ALIGNMENT_START:
wishTV.setGravity(Gravity.START);
if(Build.VERSION.SDK_INT >=17) {
wishTV.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_START);
}
break;
case SharedPreferenceUtils.TEXT_ALIGNMENT_END:
wishTV.setGravity(Gravity.END);
if(Build.VERSION.SDK_INT >=17) {
wishTV.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_END);
}
break;
case SharedPreferenceUtils.TEXT_ALIGNMENT_CENTER:
wishTV.setGravity(Gravity.CENTER);
if(Build.VERSION.SDK_INT >=17) {
wishTV.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
}
break;
}
}
private void generateBackgroundImageList() {
backgroundImages = new ArrayList<>();
BackgroundImage Card1 = new BackgroundImage(R.drawable.wishcard1);
BackgroundImage Card2 = new BackgroundImage(R.drawable.wishcard2);
BackgroundImage Card3 = new BackgroundImage(R.drawable.wishcard3);
BackgroundImage Card4 = new BackgroundImage(R.drawable.wishcard4);
BackgroundImage Card5 = new BackgroundImage(R.drawable.wishcard5);
BackgroundImage Card6 = new BackgroundImage(R.drawable.wishcard6);
backgroundImages.add(Card1);
backgroundImages.add(Card2);
backgroundImages.add(Card3);
backgroundImages.add(Card4);
backgroundImages.add(Card5);
backgroundImages.add(Card6);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Respond to the action bar's Up/Home button
case android.R.id.home:
supportFinishAfterTransition();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onItemClick(View itemView, int position) {
if(position == 0){
startDialog();
}else {
imageView.setImageResource(backgroundImages.get(position-1).getImageId());
SharedPreferenceUtils.setImageId(this, backgroundImages.get(position-1).getImageId());
}
}
private void startDialog() {
AlertDialog.Builder myAlertDialog = new AlertDialog.Builder(this);
myAlertDialog.setTitle("Upload Pictures Option");
myAlertDialog.setMessage("How do you want to set your picture?");
myAlertDialog.setPositiveButton("Gallery",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
Intent pictureActionIntent = null;
pictureActionIntent = new Intent(
Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(
pictureActionIntent,
GALLERY_PICTURE);
}
});
myAlertDialog.setNegativeButton("Camera",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(getPackageManager()) != null) {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File f = new File(fileDir);
fileDir = Environment.getExternalStorageDirectory() + "/" + Environment.DIRECTORY_PICTURES + "/WishCards/IMG_TEMP_" + timeStamp + ".jpg";
outputFileUri= Uri.fromFile(f);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
startActivityForResult(intent, CAMERA_REQUEST);
}
}
});
myAlertDialog.show();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && requestCode == CAMERA_REQUEST) {
if(outputFileUri!=null)
{
Intent intent = new Intent(this,CropActivity.class);
intent.putExtra("URI",outputFileUri);
startActivityForResult(intent,CROP_REQUEST_CODE, null);
}
} else if (resultCode == RESULT_OK && requestCode == GALLERY_PICTURE) {
if (data != null) {
Uri selectedImage = data.getData();
Intent intent = new Intent(this,CropActivity.class);
intent.putExtra("URI",selectedImage);
Bundle bundle = new Bundle();
bundle.putParcelable("URI",selectedImage);
startActivityForResult(intent,CROP_REQUEST_CODE, bundle);
} else {
Toast.makeText(getApplicationContext(), "Cancelled",
Toast.LENGTH_SHORT).show();
}
} else if (resultCode == RESULT_OK && requestCode == CROP_REQUEST_CODE) {
String imagePath = data.getStringExtra("CROP_IMAGE_PATH");
SharedPreferenceUtils.setImagePath(this,imagePath);
}
}
}
解决方案
问题来自 ImageActivity.java。由于文件 Uri 暴露在剪辑数据之外,上面的代码给出了错误。上述方法仅适用于 <23 的 SDK,但为了更新此解决方案,下面的解决方案可以解决。在阅读了这么多文档后,我得到了解决方案。祝大家好运。
myAlertDialog.setNegativeButton("Camera",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
// Error occurred while creating the File
Log.i(TAG, "IOException");
}
// Continue only if the File was successfully created
if (photoFile != null) {
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
startActivityForResult(intent, CAMERA_REQUEST);
}
}
}
private File createImageFile() throws IOException{
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, // prefix
".jpg", // suffix
storageDir // directory
);
// Save a file: path for use with ACTION_VIEW intents
fileDir = "file:" + image.getAbsolutePath();
return image;
}
});
myAlertDialog.show();
}
推荐阅读
- python - 使用 python-dataframes 将 200 万行插入到 timscaldb
- c# - Unity 停止响应基于网格的顶点数
- azure-devops - 创建矩阵小部件
- jquery - 如果文件未上传,Jquery Validate 会出错
- image - 我该如何定位
- excel - 我试图找到“1”的位置并在一个范围内找到下一个“1”
- perl - Win32::OLE::Const->Load('Microsoft Excel'); 取决于 Excel 版本
- python - 使用 NumPy 进行快速插值
- javascript - 如何从 svg 访问 html 对象标记中的 javascript 函数
- javascript - 在 React 中创建私有路由时遇到三元运算符的问题