加载网络图片和加载字符串类似:走 Http 拿到 InputStream,再用 BitmapFactory.decodeStream。耗时必须放异步任务。列表多图时还要处理滚动错位:把 AsyncTask 绑到 ImageView 上,按 URL 决定取消还是复用。
一、从 URL 解码 Bitmap
URL url = new URL(httpUrl);
InputStream in = url.openStream();
Bitmap bitmap = BitmapFactory.decodeStream(in);
url.openStream() 等价于:
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
InputStream is = conn.getInputStream();
源码就是封装这两行:
/**
* Equivalent to {@code openConnection().getInputStream(types)}.
*/
public final InputStream openStream() throws IOException {
return openConnection().getInputStream();
}
二、单张图:AsyncTask + ProgressBar
public Bitmap downLoadBitmap(String httpUrl) {
InputStream in = null;
try {
URL url = new URL(httpUrl);
in = url.openStream();
Bitmap bitmap = BitmapFactory.decodeStream(in);
return bitmap;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
String httpUrl = "http://d.hiphotos.baidu.com/image/h%3D360/sign=ba3bf5f839c79f3d90e1e2368aa1cdbc/f636afc379310a55c01f6ef3b54543a9822610f9.jpg";
new AsyncTask<String, Void, Bitmap>() {
protected void onPreExecute() {
mProgressBar.setVisibility(View.VISIBLE);
}
@Override
protected Bitmap doInBackground(String... params) {
String httpUrl = params[0];
return downLoadBitmap(httpUrl);
}
@Override
protected void onPostExecute(Bitmap result) {
mProgressBar.setVisibility(View.GONE);
if (result != null)
mShowImg.setImageBitmap(result);
}
}.execute(httpUrl);
界面用 ImageView,ProgressBar 表示加载中:

三、多图:GridView 里直接开任务会错位
取图方式和单张一样,显示换成 GridView。一屏时看不出问题;图多、上下滚时 getView 反复启动 AsyncTask,就会图片错位。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final ImageView loaderImageView;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_my_gridview_layout, null);
loaderImageView = (ImageView) convertView.findViewById(R.id.my_gridview_img);
convertView.setTag(loaderImageView);
} else {
loaderImageView = (ImageView) convertView.getTag();
}
String itemUrl = (String) getItem(position);
new AsyncTask<String, Void, Bitmap>() {
@Override
protected Bitmap doInBackground(String... params) {
return downLoadBitmap(params[0]);
}
@Override
protected void onPostExecute(Bitmap result) {
if (result != null)
loaderImageView.setImageBitmap(result);
}
}.execute(itemUrl);
return convertView;
}
效果如下:

四、按 URL 绑定 / 取消 AsyncTask
把任务引用存进专用 AsyncDrawable。新 URL 与正在跑的不同就 cancel;相同则不再开新任务。完成后用 WeakReference 确认 ImageView 仍在,且当前任务仍是它身上那个,才 setImageBitmap。
public void loadBitmap(Resources res, String imageUrl, ImageView imageView, int resId) {
if (cancelPotentialWork(imageUrl, imageView)) {
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
AsyncDrawable asyncDrawable = new AsyncDrawable(res,
BitmapFactory.decodeResource(res, resId), task);
imageView.setImageDrawable(asyncDrawable);
task.execute(imageUrl);
}
}
public static boolean cancelPotentialWork(String imageUrl, ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (bitmapWorkerTask != null) {
final String bitmapData = bitmapWorkerTask.imageUrl;
if (!bitmapData.equals(imageUrl)) {
bitmapWorkerTask.cancel(true);
} else {
return false;
}
}
return true;
}
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
if (imageView != null) {
final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncDrawable) {
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
return asyncDrawable.getBitmapWorkerTask();
}
}
return null;
}
static class AsyncDrawable extends BitmapDrawable {
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
return (BitmapWorkerTask) bitmapWorkerTaskReference.get();
}
}
class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private String imageUrl = "";
public BitmapWorkerTask(ImageView imageView) {
imageViewReference = new WeakReference<ImageView>(imageView);
}
@Override
protected Bitmap doInBackground(String... params) {
imageUrl = params[0];
return downLoadBitmap(imageUrl);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = (ImageView) imageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (this == bitmapWorkerTask && imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
| 场景 |
做法 |
| 取流 |
URL.openStream() = openConnection().getInputStream() |
| 解码 |
BitmapFactory.decodeStream |
| 单张 |
AsyncTask + ProgressBar |
| 列表复用 |
AsyncDrawable 存任务引用,URL 不同则 cancel |
一句话总结:网络图 = 开流 decodeStream,必须异步;GridView 复用时要把 AsyncTask 绑在 ImageView 上,按 URL 取消旧任务,避免滚动错位。