Android:异步加载图片 妖狐艹你老母 2021-10-18 21:28 346阅读 0赞 我们知道Android为了不阻塞UI线程(main线程),不允许在非UI线程中进行UI操作以及网络请求等操作,为了不阻塞UI,我们往往就要进行异步加载. 我们以异步加载图片为例子,来学习一下异步加载 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NDUzMjY2_size_16_color_FFFFFF_t_70] ### 方法一:Thread+Handler+Message ### ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NDUzMjY2_size_16_color_FFFFFF_t_70 1] 1.我们新建线程,在线程中获取图片(`Bitmap对象`) new Thread() { @Override public void run() { super.run(); try { //Android中非主线程无法在线程中更新ui,可通过Handler把数据传递到主线程 Bitmap bitmap = getBitmapFromUrl(url); //新建Message对象作为载体 Message message = Message.obtain(); //将Bitmap对象与消息绑定 message.obj = bitmap; //通过Handler发送消息 mHandler.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); 2.新建`Handler`用来传递消息,重写`handleMessage`方法,从Message中取出图片,并设置给`ImageView`. private Handler mHandler = new Handler() { @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); if (mImageView.getTag().equals(mUrl)) //只有ImageView的tag为当前url时,才进行设置 mImageView.setImageBitmap((Bitmap) msg.obj); } }; ### 方法二:AsyncTask ### 1.新建一个`loadIMageAsynTask`类并继承`AsynTask`,重写`doInBackgroud和onPostExecute`方法 public class loadImageAsynTask extends AsyncTask<String, Void, Bitmap> { //Params,Progress,Result private ImageView mImageView; private String mUrl; public loadImageAsynTask(ImageView imageView, String url) { mImageView = imageView; mUrl = url; } //在doInBackground方法中获取Bitmap对象,并返回 @Override protected Bitmap doInBackground(String... urls) { Bitmap bitmap = null; try { //从网络上获取图片 bitmap = getBitmapFromUrl(urls[0]); if (bitmap != null) { //将下载好的图片保存到LruCache中s mLruCache.put(urls[0], bitmap); } } catch (IOException e) { e.printStackTrace(); } return bitmap; } //在onPostExecute方法中将Bitmap对象设置给ImageView @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); if (mImageView.getTag().equals(mUrl)) mImageView.setImageBitmap(bitmap); } } **与Thread+Handler对比,AsyncTask有两个好处:** 1. 方便实现异步通信,不需使用 “任务线程(如继承Thread类) + Handler”的复杂组合 2. 节省资源,采用线程池的缓存线程 + 复用线程,避免了频繁创建 & 销毁线程所带来的系统资源开销 ### 进阶:异步加载图片的优化 ### #### 问题1: #### 由于我们的模拟器网速很快,当我们加载上下滑动图片时不会发生什么问题,但是当我们网速很慢时(我们可以在获取图片时`Sleep`一秒来模拟网速慢的情况),我们就会发现当`item 1`,`item 2`,`item 3`都加载后我们加载`item 4`时,并不会直接在`item 4` 上设置`image 4`,而是先设置的`image 1`,`image 2`, `image 3`,再设置的`image 4`. #### 解决办法:为`ImageView`设置`Tag` #### 1.为View对象设置属性时,以图片的`url`为`key`,设置为图片`Tag`. viewHolder.ivIcon.setTag(mList.get(i).getNewsIconUrl()); 2.在获得图片(`Bitmap`)后,设置给`ImageView`时,先进行判断 if (mImageView.getTag().equals(mUrl)) mImageView.setImageBitmap(bitmap); #### 问题2: #### 我们每次滑动时,都是从网络重新获取图片,这对于用户来说,很耗流量. #### 解决办法:使用LruCache #### 1.新建`LruCache`,并重写`sizeof`方法,返回每次缓存的图片大小 mLruCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap value) { //在每次存入缓存中调用,告诉我们的系统存入的对象有多大 return value.getByteCount(); } }; 2.每次从网络上获取图片后,加入到缓存 //从网络上获取图片 bitmap = getBitmapFromUrl(urls[0]); if (bitmap != null) { //将下载好的图片保存到LruCache中s mLruCache.put(urls[0], bitmap); } 3.如果缓存中有就从缓存中取,缓存中没有再发送网络请求获取图片 Bitmap bitmap = null; //从缓存中取出对应的图片,如果缓存中没有,我们就从网络中去下载 bitmap = mLruCache.get(urlString); if (bitmap == null) { new loadImageAsynTask(imageView, urlString).execute(urlString); } else { imageView.setImageBitmap(bitmap); } **Android异步加载图片源码:[https://github.com/wantao666/AndroidDemo/tree/master/SynTask][https_github.com_wantao666_AndroidDemo_tree_master_SynTask]** [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NDUzMjY2_size_16_color_FFFFFF_t_70]: /images/20211018/afdad93bb00d42e790e6a772f084b4fd.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NDUzMjY2_size_16_color_FFFFFF_t_70 1]: /images/20211018/f636d39604254561a065a7a954a4e7dc.png [https_github.com_wantao666_AndroidDemo_tree_master_SynTask]: https://github.com/wantao666/AndroidDemo/tree/master/SynTask
还没有评论,来说两句吧...