Bitmap(一) http://www.cnblogs.com/fishbone-lsy/p/4486571.html
Bitmap(二) http://www.cnblogs.com/fishbone-lsy/p/4496166.html
在Bitmap(一)中介绍了,Bitmap如何通过BitmapFactory获得图片资源。在Bitmap(二)中,介绍了将Bitmap进行预加载,缩放,再载入ImageView。
显而易见,上面的过程已经是一个略复杂的耗时过程了。在移动开发中,所有的耗时过程,我们都不应该放在UI线程中,因为这样会造成卡顿甚至ANR等种种不良的用户体验。因此,我们应该为图片的加载开一个新的线程。Android中新开线程有很多种方法,在此只记录一种我认为最简单的方法,因为这不是重点。
首先,可以思考一下,我们希望这个方法是什么样的,它有怎样的输入,怎样的输出。既然是一个图片加载,我们一定至少需要4个输入,图片从哪里来,图片到哪里去,图片的宽和图片的高。
因此,图片从哪里来,可以参考Bitmap(一),在此,我们就选最简单的从Resourse里面来,到哪里去当然是一个ImageView,宽高就不用说了,跟Bitmap(二)里面一样。至于,输出,似乎不需要什么输出~
先上代码,代码来此Google Training
public LoadImageTask(ImageView imageView , int reqWidth , int reqHeight){ imageViewReference = new WeakReference<ImageView>(imageView); mReqWidth = reqWidth; mReqHeight = reqHeight; } @Override protected Bitmap doInBackground(Integer... params) { return decodeSampledBitmapFromResource(getResources() , params[0],mReqWidth , mReqHeight); } @Override protected void onPostExecute(Bitmap bitmap) { if(bitmap!=null && imageViewReference !=null){ final ImageView imageView = (ImageView) imageViewReference.get(); if(imageView != null){ imageView.setImageBitmap(bitmap); } } } }
这是一段看起来略繁琐的代码,但是,繁琐是有它的道理的。这里最有趣的地方无疑是WeakReference<ImageView>这个软引用。既然说到软引用,在这里做一个简单的记录。
在Java中,对象的引用有4种方法,分别是,强引用、软引用、弱引用、虚引用。
强引用是很难被回收的对象。通常应用宁愿OOM也不会回收强引用;软引用相较于强引用偏弱,是在当系统内存不足时,会选择回收软引用;弱引用更弱,只要GC启动,并发现了它,必定把它回收了;虚引用就“形同虚设”了,在任何时候都可能被GC回收,它的存在主要是为了跟踪GC的活动。
在这个图片加载的异步任务中,使用的是弱引用。之前我一直不明白,为什么不使用软引用,弱引用不是有被回收使这个异步任务失败的风险吗?后来查询资料得知,这个风险是必须承坦的。如果使用软引用,只有当系统快要OOM时,才会回收这个ImageView,而GC本身又是一个极耗内存的东西,因此,使用软引用才是真正在冒大风险。而弱引用,在一般的时候,GC是不会启动的。万一GC启动了,大多数情况也确实应该中止这个图片的载入,腾出内存,做更重要的事。大不了在内存充足后,重新加载就好了。
还有一个我过去常有的做法就是,直接在异步任务里调用全局的ImageView或者直接在异步任务里写ImageView tempImageView = imageView。这是很危险的。万一,我加载图片的任务刚开始,用户突然开始进行疯狂的操作(像我就是2333),或者网断了。此时,为了保证应用不闪退,我们很有可能不得不放弃这一次图片加载,如果这里用了ImageView tempImageView = imageView的方式,那么这个tempImageView是无法释放的,来来回回几次,app就炸(OOM)了。因此,在这里使用弱引用是非常安全的。在加载完成之后,我们需要谨慎地检查一遍所有的对象还是不是存在,有没有被回收,检查完毕之后,我们才ImageView tempImageView =(ImageView)imageViewReference.get();这样消耗内存的操作,赶紧加载完图片完事儿。
最后,我们再在UI线程里开启这个线程。
LoadImageTask task = new LoadImageTask(imageView1 , 100 , 100); task.execute(R.drawable.images);
以上就是一次“优雅”的图片加载。
但是,这个方法在高并发时是非常不适合的。(“谁允许你同时开这么多线程和Bitmap的”——GC)。所以,当我们要同时加载多个图片时,还有更深的套路,下次再记~。
Duang