首页 > 解决方案 > 致命异常:通过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);
        }

    }

}

标签: android

解决方案


问题来自 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();
    }

推荐阅读