当前位置: 首页 > 知识库问答 >
问题:

java.lang.安全异常:权限拒绝:写入android.support.v4.content.FileProvider uri

强保臣
2023-03-14

我正在尝试使用相机并保存图像。按照Commonware建议的步骤进行操作。我经常出错-

2018-11-12 02:10:54.588 3145-3173/inder.execE/Database aseUtils:写入异常到包裹inder.java:682安全异常:权限拒绝:写入android.support.v4.content.FileProvider uricontent://com.bisw.weac.provider/external_files/Android/data/com.bisw.weac/files/wallpaper/theme.jpg从pid=5566, uid=10071要求提供者被导出,或grantUriPersion()在android.content.ContentProvider.enforceWritePermission内(ContentProvider.java:713)在android.content.ContentProvider$Transport.enforceWritePermis(ContentProvider.java:519)在android.content.ContentProvider$Transport.enforceFilePermis(ContentProvider.java:491)在android.content.ContentProvider$Transport.openAssetFile(ContentProvider.java:389)在android.content.ContentProviderNative.onTransact(ContentProviderNative.java:251)在android.os.Bcom.bisw.weacTransact(Bjava.lang.)

我几乎尝试了所有类似-intent.add标志(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);但没有任何帮助。我正在从相机中裁剪图像。活动代码

public class LocalAlbumActivity extends BaseActivity implements View.OnClickListener {

    private static final int REQUEST_IMAGE_CAPTURE_THEME = 1;
    private static final int REQUEST_IMAGE_CAPTURE_QRCODE_LOGO = 4;
    private static final int REQUEST_IMAGE_CROP_THEME = 2;
    private static final int REQUEST_IMAGE_CROP_QRCODE_LOGO = 5;
    private static final int REQUEST_ALBUM_DETAIL = 3;

    public static final String ALBUM_PATH = "album_path";
    public static final String ALBUM_NAME = "album_name";
    private LocalAlbumAdapter mLocalAlbumAdapter;
    private List<ImageBucket> mLocalAlbumList;
    private AsyncTask<Void, Void, List<ImageBucket>> mBucketLoadTask;
    private ListView mLocalAlbumListView;


    /**
     * 访问本地相册类型:0,主题;1,扫码;2,造码
     */
    private int mRequestType;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        OttoAppConfig.getInstance().register(this);

        setContentView(R.layout.activity_local_album);
        ViewGroup backGround = (ViewGroup) findViewById(R.id.background);
        MyUtil.setBackgroundBlur(backGround, this);

        initAdapter();
        assignViews();

    }

    private void initAdapter() {

        mLocalAlbumList = new ArrayList<>();
        mLocalAlbumAdapter = new LocalAlbumAdapter(this, mLocalAlbumList);
        //trying permission



        mBucketLoadTask = new AsyncTask<Void, Void, List<ImageBucket>>() {

            @Override
            protected void onPreExecute() {
                super.onPreExecute();
//                showLoading();
            }

            @Override
            protected List<ImageBucket> doInBackground(Void... params) {

                return LocalAlbumImagePickerHelper.getInstance(LocalAlbumActivity.this)
                        .getImagesBucketList();
            }

            @Override
            protected void onPostExecute(List<ImageBucket> list) {
                dismissLoadingDialog();

                TextView emptyView = (TextView) findViewById(R.id.local_album_lv_empty);
                mLocalAlbumListView.setEmptyView(emptyView);

                mLocalAlbumList.addAll(list);
                mLocalAlbumAdapter.notifyDataSetChanged();
            }
        };

        mBucketLoadTask.execute();
    }

    private void dismissLoadingDialog() {
        ViewGroup progressBarLlyt = (ViewGroup) findViewById(R.id.progress_bar_llyt);
        progressBarLlyt.setVisibility(View.GONE);
    }

    private void assignViews() {
        TextView loadingMsg = (TextView) findViewById(R.id.loading_msg);
        loadingMsg.setText(R.string.scanning);

        ImageView backBtn = (ImageView) findViewById(R.id.action_back);
        TextView captureBtn = (TextView) findViewById(R.id.action_capture);

        backBtn.setOnClickListener(this);

        mRequestType = getIntent().getIntExtra(WeacConstants.REQUEST_LOCAL_ALBUM_TYPE, 0);
        switch (mRequestType) {
            // 主题
            case 0:
                // 造码
            case 2:
                captureBtn.setOnClickListener(this);
                break;
            // 扫码
            case 1:
                // 隐藏拍照按钮
                captureBtn.setVisibility(View.GONE);
                break;
        }

        mLocalAlbumListView = (ListView) findViewById(R.id.local_album_lv);
        mLocalAlbumListView.setAdapter(mLocalAlbumAdapter);
        mLocalAlbumListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                if (MyUtil.isFastDoubleClick()) {
                    return;
                }
                Intent intent = new Intent(LocalAlbumActivity.this, LocalAlbumDetailActivity.class);
                intent.putParcelableArrayListExtra(ALBUM_PATH,
                        mLocalAlbumAdapter.getItem(position).bucketList);
                intent.putExtra(ALBUM_NAME, mLocalAlbumAdapter.getItem(position).bucketName);
                intent.putExtra(WeacConstants.REQUEST_LOCAL_ALBUM_TYPE, mRequestType);
                startActivityForResult(intent, REQUEST_ALBUM_DETAIL);
            }
        });

//        OverScrollDecoratorHelper.setUpOverScroll(mLocalAlbumListView);
    }

    private Uri mImageUri;

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            // 返回
            case R.id.action_back:
                myFinish();
                break;
            // 拍照
            case R.id.action_capture:
                PackageManager pm = getPackageManager();
                // FEATURE_CAMERA - 后置相机
                // FEATURE_CAMERA_FRONT - 前置相机
                if (pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)) {
                    // 访问相机类型
                    int requestType;
                    // 截取主题壁纸
                    if (mRequestType != 2) {
                        requestType = REQUEST_IMAGE_CAPTURE_THEME;
                    } else { // 截取二维码logo
                        requestType = REQUEST_IMAGE_CAPTURE_QRCODE_LOGO;
                    }

                    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                    //mImageUri = Uri.fromFile(MyUtil.getFileDirectory(this, "/Android/data/" +
                    //        getPackageName() + "/capture/temporary.jpg"));



                    mImageUri = FileProvider.getUriForFile(this,BuildConfig.APPLICATION_ID+".provider",MyUtil.getFileDirectory(this, "/Android/data/" +
                                    getPackageName() + "/capture/temporary.jpg"));

                    this.grantUriPermission(getPackageName(),mImageUri,Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

                    intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
                    startActivityForResult(intent, requestType);
                    overridePendingTransition(0, R.anim.zoomin);
                } else { // 没有可用相机
                    Intent intent = new Intent(this, MyDialogActivitySingle.class);
                    intent.putExtra(WeacConstants.TITLE, getString(R.string.prompt));
                    intent.putExtra(WeacConstants.DETAIL, getString(R.string.camera_error));
                    startActivity(intent);
                }
                break;
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode != RESULT_OK) {
            // 截图/相机返回
            overridePendingTransition(0, R.anim.zoomout);
            return;
        }

        // 扫描二维码相册详细取消
        if (data != null) {
            boolean isFinishMe = data.getBooleanExtra(LocalAlbumDetailActivity.FINISH_ACTIVITY, false);
            if (isFinishMe && !isFinishing()) {
                myFinish2();
                return;
            }
        }

        switch (requestCode) {
            // 拍照(截取主题壁纸)
            case REQUEST_IMAGE_CAPTURE_THEME:
                cropImage(0, REQUEST_IMAGE_CROP_THEME, WeacConstants.DIY_WALLPAPER_PATH);
                break;
            // 拍照(截取二维码logo)
            case REQUEST_IMAGE_CAPTURE_QRCODE_LOGO:
                cropImage(1, REQUEST_IMAGE_CROP_QRCODE_LOGO, WeacConstants.DIY_QRCODE_LOGO_PATH);
                break;
            // 截图(截取主题壁纸)
            case REQUEST_IMAGE_CROP_THEME:
                String filePath = MyUtil.getFilePath(this, WeacConstants.DIY_WALLPAPER_PATH);
                // 更新壁纸信息
                MyUtil.saveWallpaper(this, WeacConstants.WALLPAPER_PATH, filePath);
                // 发送壁纸更新事件
                OttoAppConfig.getInstance().post(new WallpaperEvent());
                myFinish();
                break;
            // 截图(截取二维码logo)
            case REQUEST_IMAGE_CROP_QRCODE_LOGO:
                String logoPath = MyUtil.getFilePath(this, WeacConstants.DIY_QRCODE_LOGO_PATH);
                // 保存自定义二维码logo地址
                MyUtil.saveQRcodeLogoPath(this, logoPath);
                // 发送自定义二维码logo截取地址事件
                OttoAppConfig.getInstance().post(new QRcodeLogoEvent(logoPath));
                myFinish();
                break;
            // 相册详细图片
            case REQUEST_ALBUM_DETAIL:
                assert data != null;
                String url = data.getStringExtra(WeacConstants.IMAGE_URL);
                OttoAppConfig.getInstance().post(new ScanCodeEvent(url));
                myFinish2();
                break;
        }
    }

    private void cropImage(int type, int requestType, String path) {

        ToastUtil.showLongToast(this, "path:"+path);
        Intent intent = MyUtil.getCropImageOptions(this, mImageUri, path, type);
        intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(intent, requestType);
            overridePendingTransition(0, 0);
        } else {
            // 不可以复制其他应用的内部文件
            // TODO: 全屏裁剪&自定义裁剪功能
            ToastUtil.showLongToast(this, getString(R.string.no_crop_action));
        }
    }

    @Subscribe
    public void finishMeEvent(FinishLocalAlbumActivityEvent event) {
        myFinish2();
    }

    @Override
    public void onBackPressed() {
        myFinish();
    }

    private void myFinish() {
        finish();
        if (mRequestType != 2) {
            overridePendingTransition(0, R.anim.zoomout);
        } else {
            overridePendingTransition(0, R.anim.move_out_bottom);
        }
    }

    private void myFinish2() {
        finish();
        overridePendingTransition(0, 0);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        OttoAppConfig.getInstance().unregister(this);
        if (null != mBucketLoadTask && mBucketLoadTask.getStatus() == AsyncTask.Status.RUNNING) {
            mBucketLoadTask.cancel(true);
        }
    }
}

帮助方法

/**
 * Returns specified directory(/mnt/sdcard/...).
 * directory will be created on SD card by defined path if card
 * is mounted. Else - Android defines files directory on device's
 * files(/data/data/<application package>/files) system.
 *
 * @param context context
 * @param path    file path (e.g.: "/AppDir/a.mp3", "/AppDir/files/images/a.jp")
 * @return File {@link File directory}
 */
public static File getFileDirectory(Context context, String path) {
    File file = null;
    if (isHasSDCard()) {
        file = new File(Environment.getExternalStorageDirectory(), path);
        if (!file.getParentFile().exists()) {
            if (!file.getParentFile().mkdirs()) {
                file = null;
            }
        }
    }
    if (file == null) {
        // 使用内部缓存[MediaStore.EXTRA_OUTPUT ("output")]是无法正确写入裁切后的图片的。
        // 系统是用全局的ContentResolver来做这个过程的文件io操作,app内部的存储被忽略。(猜测)
        file = new File(context.getFilesDir(), path);
    }
    return file;
}

/**
 * Returns specified directory(/mnt/sdcard/Android/data/<application package>/files/...).
 * directory will be created on SD card by defined path if card
 * is mounted. Else - Android defines files directory on device's
 * files(/data/data/<application package>/files) system.
 *
 * @param context context
 * @param path    file  path (e.g.: "/music/a.mp3", "/pictures/a.jpg")
 * @return File {@link File directory}
 */
public static File getExternalFileDirectory(Context context, String path) {
    File file = null;
    if (isHasSDCard()) {
        file = new File(context.getExternalFilesDir(null), path);
        if (!file.getParentFile().exists()) {
            if (!file.getParentFile().mkdirs()) {
                file = null;
            }
        }
    }
    if (file == null) {
        // 使用内部缓存[MediaStore.EXTRA_OUTPUT ("output")]是无法正确写入裁切后的图片的。
        // 系统是用全局的ContentResolver来做这个过程的文件io操作,app内部的存储被忽略。(猜测)
        file = new File(context.getFilesDir(), path);
    }
    return file;
}

public static boolean isHasSDCard() {
    return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
}

/**
 * Returns directory absolutePath.
 *
 * @param context context
 * @param path    file path (e.g.: "/AppDir/a.mp3", "/AppDir/files/images/a.jpg")
 * @return /mnt/sdcard/Android/data/<application package>/files/....
 */
public static String getFilePath(Context context, String path) {
    return getExternalFileDirectory(context, path).getAbsolutePath();
}

/**
 * set intent options
 *
 * @param context  context
 * @param uri      image path uri
 * @param filePath save path (e.g.: "/AppDir/a.mp3", "/AppDir/files/images/a.jpg")
 * @param type     0,截取壁纸/拍照;1,截取Logo
 * @return Intent
 */
public static Intent getCropImageOptions(Context context, Uri uri, String filePath, int type) {
    int width;
    int height;
    // 截取壁纸/拍照
    if (type == 0) {
        width = context.getResources().getDisplayMetrics().widthPixels;
        height = context.getResources().getDisplayMetrics().heightPixels;
    } else { // 截取logo
        width = height = dip2px(context, 30);
    }
    //filePath="/Internal storage/Download/a.jpg";
    LogUtil.e(LOG_TAG, " filePath:" + filePath );
    Intent intent = new Intent();
    intent.setAction("com.android.camera.action.CROP");
    intent.setDataAndType(uri, "image/*");
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);


    intent.putExtra("crop", "true");
    // 裁剪框比例
    intent.putExtra("aspectX", width);
    intent.putExtra("aspectY", height);
    // 保存路径


    //#ToDO: Uri.fromFile change to FileProvider.getUriForFile
    Uri mImageUri;
    //mImageUri=Uri.fromFile(getExternalFileDirectory(context, filePath));

    intent.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(context,BuildConfig.APPLICATION_ID+".provider",getExternalFileDirectory(context, filePath)));

    // 是否去除面部检测
    intent.putExtra("noFaceDetection", true);

    intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    // 是否保留比例
    intent.putExtra("scale", true);
    intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
    // 裁剪区的宽高
    intent.putExtra("outputX", width);
    intent.putExtra("outputY", height);

    // 是否将数据保留在Bitmap中返回
    intent.putExtra("return-data", false);
    return intent;
}

共有1个答案

颛孙飞
2023-03-14

您需要在清单文件中添加以下in-provider声明

 <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="<application-id>.fileprovider" // com.abc.def 
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths" /> 
    </provider>
 类似资料:
  • 我越来越- 在 我已经检查了这个帖子,但无法解决它。我该如何解决这个问题?

  • 我试图用docker运行一个简单的命令,读取我的主目录中的文件,并在本地机器上的同一目录中写入输出文件(

  • 我已经尝试了通过研究所能找到的一切。什么都没用。我有一个带有片段对话的活动。在这个对话框中,我有一个带有按钮的图像视图。按下此按钮时,会弹出一个警报,提示拍摄照片、从gallery中选择照片或取消。“取消”和“多媒体资料”按钮都很有效,但当我尝试拍照时,我会在标题中看到错误消息: 致命的例外:java。lang.SecurityException:权限拒绝:从ProcessRecord{c6d8b

  • 我的应用程序中有一个活动,允许用户逐个从设备中选择多个文件,我正在使用这样的意图: 这工作得很好,我正在获取所选文件的Uri,它们看起来像这样: 然后,如果文件是图像,我将使用以下方式对其进行解码: 当用户单击按钮时,我通过 intent 将 Uris 列表传递给另一个活动,在此活动中,在 AsyncTask 中,我正在以 base64 编码文件以通过网络发送: 问题是,当我打开inputStre

  • 这个错误真的很奇怪,我不知道如何重现它,也不知道如何修复它,因为我做了很多搜索,但没有什么有用的。 这是stacktrace: 这是我的AndroidManifest。xml 请不要费心问我是否在我的清单中有正确的INTERNET权限,因为这个应用程序已经上市2年了:P 我还注意到(来自Crittercism)所有错误都来自Android 4.1. x版本(JB)。我不知道设备是否已root或什么

  • 我的Firestore数据库有一些用户和一些s。如果没有其他人认领,用户可以认领。他们通过在上创建一个文档来声明,该文档有一个名为的字段,该字段是对他们自己的用户文档的引用。 我想我已经在这些安全规则中捕捉到了: 但是,当为所有权删除文档时,我获得了: 我是这样做的: 如果我打开read和write设置为true的文档,就不会出现此异常。 我的规则怎么了?他们用模拟器测试正常。