不使用Glide等第三方库 通过URL加载图片时Activity崩溃如何解决
问题根因
- 核心错误是你把网络请求逻辑直接放在了
onCreate方法中执行,Android 3.0及以上版本强制禁止在主线程执行网络操作,直接运行会抛出NetworkOnMainThreadException导致应用闪退。 - 第二个错误是就算你绕过主线程网络限制,在子线程中直接调用
setImageBitmap修改UI控件,也会触发UI操作线程异常,Android要求所有UI控件的修改必须在主线程执行。 - 还有两处遗漏点:
- 没有在
AndroidManifest.xml中声明网络访问权限,会导致网络请求被拦截。 - 没有对加载的图片做采样压缩,该原图分辨率很高,直接解码会占用大量内存,极易触发OOM(内存溢出)异常。
- 没有在
修复方案
步骤1:添加网络权限
在AndroidManifest.xml的manifest标签下添加权限声明:
<uses-permission android:name="android.permission.INTERNET" />
步骤2:调整图片加载逻辑
将网络请求放在子线程执行,加载完成后切换到主线程更新UI,同时添加图片压缩逻辑:
String imgUrl = "https://st3.depositphotos.com/1008939/12603/i/950/depositphotos_126032722-stock-photo-roaring-singing-woman.jpg"; ImageView img = findViewById(R.id.imageArtist); // 启动子线程执行网络加载操作 new Thread(new Runnable() { @Override public void run() { Bitmap bitmap = null; InputStream input = null; try { // 第一次读取仅获取图片尺寸,不加载全图到内存 input = new URL(imgUrl).openStream(); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(input, null, options); input.close(); // 计算采样率,按ImageView的170dp(约510px)压缩图片 options.inSampleSize = calculateInSampleSize(options, 510, 510); options.inJustDecodeBounds = false; // 第二次读取加载压缩后的图片 input = new URL(imgUrl).openStream(); bitmap = BitmapFactory.decodeStream(input, null, options); } catch (IOException e) { e.printStackTrace(); } finally { if (input != null) { try { input.close(); } catch (IOException e) { e.printStackTrace(); } } } // 切换到主线程更新ImageView Bitmap finalBitmap = bitmap; runOnUiThread(new Runnable() { @Override public void run() { if (finalBitmap != null) { img.setImageBitmap(finalBitmap); } } }); } }).start();
步骤3:添加图片采样率计算工具方法
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { int height = options.outHeight; int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { int halfHeight = height / 2; int halfWidth = width / 2; // 采样率为2的指数,越高图片压缩比例越大 while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize; }
内容的提问来源于stack exchange,提问作者Nicolas Zanforlini
相关产品推荐
相关产品推荐

