Android 6.0 访问图库时,报错 requires android.permission.READ_EXTERNAL_STORAGE异常

本文介绍了一个简单的Android代码示例用于打开图库选择图片,但在Android 7.0及更高版本上遇到权限拒绝错误。文章详细解释了如何在AndroidManifest.xml中添加必要的READ_EXTERNAL_STORAGE权限,并针对Android 6.0及以上版本如何实现动态权限申请。

代码如下:

   /**
     * 打开图库
     * @param context
     * @param requestCode
     */
    public static void openGallery(Activity context, int requestCode) {
        Intent intent = new Intent(Intent.ACTION_PICK, null);
        intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,"image/*");
        context.startActivityForResult(intent, requestCode);
    }

以上代码很简单,使用Intent开启图库,挑选图片,运行效果很不理想。

Android 7.0模拟器上,报错:

11-10 10:07:02.241 4449-4468/? E/DatabaseUtils: Writing exception to parcel
       java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider   uri content://media/external/file from pid=4337, uid=10049 requires android.permission.READ_EXTERNAL_STORAGE 
       , or grantUriPermission()

       at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:608)
        at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:483)
        at android.content.ContentProvider$Transport.query(ContentProvider.java:212)
        at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:112)
        at android.os.Binder.execTransact(Binder.java:565)

在AndroidManifest.xml中添加权限:

  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE">

其次,在android 6.0及其以上,需要动态赋予权限。

Android6.0动态权限

如何动态请求权限,请阅读 Android6.0权限处理篇Android EasyPermission权限库

注意点

若是程序中已经赋予写入权限,则不需要在写读取权限。写入权限已经包含了读取权限。

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">
package org.autojs.autojs.ui.shortcut; import android.annotation.SuppressLint; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.PersistableBundle; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.CheckBox; import android.widget.ImageView; import android.widget.TextView; import com.retesla.autoxjs.R; import org.autojs.autojs.external.ScriptIntents; import org.autojs.autojs.external.shortcut.Shortcut; import org.autojs.autojs.external.shortcut.ShortcutActivity; import org.autojs.autojs.external.shortcut.ShortcutManager; import org.autojs.autojs.model.script.ScriptFile; import org.autojs.autojs.tool.BitmapTool; import org.autojs.autojs.theme.dialog.ThemeColorMaterialDialogBuilder; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; /** * Created by Stardust on 2017/10/25. */ public class ShortcutCreateActivity extends AppCompatActivity { public static final String EXTRA_FILE = "file"; private static final String LOG_TAG = "ShortcutCreateActivity"; private ScriptFile mScriptFile; private boolean mIsDefaultIcon = true; @BindView(R.id.name) TextView mName; @BindView(R.id.icon) ImageView mIcon; @BindView(R.id.use_android_n_shortcut) CheckBox mUseAndroidNShortcut; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mScriptFile = (ScriptFile) getIntent().getSerializableExtra(EXTRA_FILE); showDialog(); } private void showDialog() { View view = View.inflate(this, R.layout.shortcut_create_dialog, null); ButterKnife.bind(this, view); mUseAndroidNShortcut.setVisibility(View.VISIBLE); mName.setText(mScriptFile.getSimplifiedName()); new ThemeColorMaterialDialogBuilder(this) .customView(view, false) .title(R.string.text_send_shortcut) .positiveText(R.string.ok) .onPositive((dialog, which) -> { createShortcut(); finish(); }) .cancelListener(dialog -> finish()) .show(); } @OnClick(R.id.icon) void selectIcon() { startActivityForResult(new Intent(this, ShortcutCreateActivity.class), 21209); } @SuppressLint("NewApi") //for fool android studio private void createShortcut() { createShortcutByShortcutManager(); } private void createShortcutByShortcutManager() { Icon icon; if (mIsDefaultIcon) { icon = Icon.createWithResource(this, R.drawable.ic_file_type_js); } else { Bitmap bitmap = BitmapTool.drawableToBitmap(mIcon.getDrawable()); icon = Icon.createWithBitmap(bitmap); } PersistableBundle extras = new PersistableBundle(1); extras.putString(ScriptIntents.EXTRA_KEY_PATH, mScriptFile.getPath()); Intent intent = new Intent(this, ShortcutActivity.class) .putExtra(ScriptIntents.EXTRA_KEY_PATH, mScriptFile.getPath()) .setAction(Intent.ACTION_MAIN); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { ShortcutManager.getInstance(this).addPinnedShortcut(mName.getText(), mScriptFile.getPath(), icon, intent); } else { ShortcutManager.getInstance(this).addDynamicShortcut(mName.getText(), mScriptFile.getPath(), icon, intent); } } @SuppressLint("CheckResult") @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode != RESULT_OK) { return; } String packageName = data.getStringExtra(ShortcutIconSelectActivity.EXTRA_PACKAGE_NAME); if (packageName != null) { try { mIcon.setImageDrawable(getPackageManager().getApplicationIcon(packageName)); mIsDefaultIcon = false; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return; } Uri uri = data.getData(); if (uri == null) { return; } Observable.fromCallable(() -> BitmapFactory.decodeStream(getContentResolver().openInputStream(uri))) .subscribeOn(Schedulers.computation()) .observeOn(AndroidSchedulers.mainThread()) .subscribe((bitmap -> { mIcon.setImageBitmap(bitmap); mIsDefaultIcon = false; }), error -> { Log.e(LOG_TAG, "decode stream", error); }); } } 这段代码中,我需要取消使用butterknife库,该怎么修改代码?
08-21
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值