Android应用下载图片代码模拟器正常但打包APK后失效如何解决?
问题诱发原因
- 权限适配缺失:模拟器通常运行低版本Android系统(API<23)无需动态申请存储权限,而用户真机普遍运行Android 6.0及以上版本,存储权限属于危险权限,仅在AndroidManifest静态声明不生效;同时Android 10及以上版本引入分区存储机制,原有旧API调用会被限制。
- 资源混淆/收缩:打包release版APK时默认会开启资源混淆、未使用资源收缩逻辑,你通过字符串拼接
"bild"+counter动态查找drawable资源的方式,会被打包工具判定为资源未被直接引用,要么被删除要么资源名被混淆,导致getIdentifier返回0,最终拿不到Bitmap。 - 异常逻辑缺失:
MediaStore.Images.Media.insertImage执行失败时会返回null,代码未做判空直接解析Uri会触发崩溃,且无法感知执行失败原因。
修复方案
1. 权限适配
首先在AndroidManifest.xml中声明对应权限:
<!-- 安卓9及以下存储写入权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <!-- 安卓13及以上读取媒体图片权限(如果后续需要读取自己保存的图片可加) --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" android:minSdkVersion="33" />
在触发下载逻辑前,先对Android 6.0-Android 9的设备动态申请WRITE_EXTERNAL_STORAGE权限,拿到权限后再执行保存逻辑。
2. 防资源收缩/混淆配置
在proguard-rules.pro中添加规则,防止R类的drawable资源被混淆:
-keep class 你的应用包名.R$drawable { public static final int bild*; }
在模块级build.gradle的android闭包中添加规则,保留bild开头的drawable资源不被收缩:
android { buildTypes { release { shrinkResources true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' // 保留目标资源 resValue "string", "keep_bild_res", "@drawable/bild*" } } }
3. 兼容分区存储的保存逻辑替换
废弃已过时的insertImage方法,适配全版本系统的保存逻辑示例如下:
public void download (View view) { // 先校验权限再执行后续逻辑,此处省略权限判断代码 int resId = getResources().getIdentifier("bild" + counter, "drawable", getPackageName()); if (resId == 0) { Toast.makeText(MainActivity.this, "资源不存在", Toast.LENGTH_LONG).show(); return; } Drawable drawable = getResources().getDrawable(resId); Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); ContentValues values = new ContentValues(); values.put(MediaStore.Images.Media.DISPLAY_NAME, "bild" + counter); values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); // 安卓10及以上指定存储路径 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES); values.put(MediaStore.Images.Media.IS_PENDING, 1); } ContentResolver resolver = getContentResolver(); Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); if (uri == null) { Toast.makeText(MainActivity.this, "保存失败", Toast.LENGTH_LONG).show(); return; } try { OutputStream outputStream = resolver.openOutputStream(uri); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream); outputStream.close(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { values.clear(); values.put(MediaStore.Images.Media.IS_PENDING, 0); resolver.update(uri, values, null, null); } Toast.makeText(MainActivity.this, "Image Saved Successfully", Toast.LENGTH_LONG).show(); } catch (IOException e) { e.printStackTrace(); Toast.makeText(MainActivity.this, "保存失败:" + e.getMessage(), Toast.LENGTH_LONG).show(); } }
内容的提问来源于stack exchange,提问作者stefan
相关产品推荐
相关产品推荐

