Android之打造自己加载高清大图及瀑布流框架.解决错位等问题.

首先看效果图如下:

https://github.com/q422013/ImageLoader

本框架支持本地图片和网络图片的获取.采用LruCache算法,最少使用的最先释放.有效的避免OOM,项目结构图:

核心加载类在于ImageLoader.采用了TreadPool去做并发请求.UI处理采用Handler去管理,实现的思路类似于AsnycTask类.该类采用单例模式:

public static ImageLoader getInstance(Context context) {if (null == loader) {synchronized (ImageLoader.class) {if (null == loader) {loader = new ImageLoader(context, defThreadCount, mType);}}}return loader;}public static ImageLoader getInstance(Context context, int threadCount, Type type) {if (null == loader) {synchronized (ImageLoader.class) {if (null == loader) {loader = new ImageLoader(context, threadCount, type);}}}return loader;}第一种类不需要配置线程池及加载方式.加载方式分为两种:1.先进先加载,2.后进先加载. /*** 队列调度模式*/public enum Type {FIFO, LIFO}工作线程中核心是用Loop去不断的取消息,取到消息后就加入到线程池当中去执行,这样减少了自己去维护轮训,减少内存开销.//工作线程mThread = new Thread() {@Overridepublic void run() {Looper.prepare();mPoolThreadHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {mThreadPool.execute(getTask());try {mPoolSemaphore.acquire();//信号量 + 1} catch (InterruptedException e) {e.printStackTrace();}}};mSemapHore.release();//初始化完成后信号量 -1Looper.loop();}};从上面代码可以看出PoolTreadHandler收到一个消息后会让mThreadPool去执行一个任务,该任务通过getTask()方法获得一个Runnable对象,并且让信号量增加表示,线程池中有一个任务了.

看看getTask()代码很简单,仅仅是将任务按不同的方式取出来:

/*** 获取任务** @return*/private synchronized Runnable getTask() {if (0 < mTask.size()) {if (mType == Type.LIFO)return mTask.removeFirst();elsereturn mTask.removeLast();}return null;}真正的工作在于mTask去add,mTask是一个LinkedList<Runnable>类型的集合.所以核心在于方法Load() /*** 加载图片** @param path* @param imageview*/public void load(final String path, final View view, final LoadListener<View> loadListener) {if (null == path)throw new RuntimeException("this path is null");if (null == loadListener)throw new RuntimeException("this loadListener is null");view.setTag(path);//1.从磁盘,2.从内存if (null == mDisPlayHandler)mDisPlayHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {int code = msg.what;ViewBeanHolder holder = (ViewBeanHolder) msg.obj;final View view = holder.view;Bitmap bm = holder.bitmap;String path = holder.path;switch (code) {case LOAD_SUCCESS://加载成功if (view.getTag().toString().equals(path)) {loadListener.LoadSuccess(view, bm, path);if (isNeedAnim)new LoadAnimCore(view);}break;case LOAD_ING://加载中if (view.getTag().toString().equals(path)) {loadListener.Loading(view, path);}break;case LOAD_FAILE://加载失败if (view.getTag().toString().equals(path)) {loadListener.LoadError(view, path, null);//暂时消息为空}break;}}};addTask(path, view);}其中view.setTag是为了防止错乱.上面代码可以看出来仅仅是用于callBack,核心的东西其实在addTask方法.我们看看addTask方法做了什么事情: /*** 添加任务** @param path* @param view*/private synchronized void addTask(final String path, final View view) {Runnable runnable = new Runnable() {@Overridepublic void run() {ViewBeanHolder holder = new ViewBeanHolder();holder.view = view;holder.path = path;sendMsg(LOAD_ING, holder);//TODO 从内存中获取Bitmap bitmap = LruCacheUtils.getInstance().get(path);if (null == bitmap) {//TODO 从磁盘中获取String tempPath = getImageFromDiskUrl(path);if (null != tempPath) {bitmap = decodeSampledBitmapFromResource(tempPath, (ImageView)view);} else {if (null == bitmap) {// TODO 从网络中获取bitmap = decodeSampledBitmapFromNetWork(path, (ImageView)view);} else {// TODO 失败sendMsg(LOAD_FAILE, holder);}}}//加载成功if (null != bitmap) {LruCacheUtils.getInstance().put(path, bitmap);holder.bitmap = bitmap;//唯一的sendMsg(LOAD_SUCCESS, holder);} else {//加载失败sendMsg(LOAD_FAILE, holder);}}};if (null == mPoolThreadHandler) {try {mSemapHore.acquire();} catch (InterruptedException e) {e.printStackTrace();}}mTask.add(runnable);mPoolThreadHandler.sendEmptyMessage(0x1000);mPoolSemaphore.release();//信号量 -1}

缓存策略:先从内存中获取,如果没有获取到,就从磁盘获取,磁盘也没有获取到,那就从网络获取.最后并将该bitmap设置到内存缓存,假象:如果设置非常多的bitmap到内存缓存中肯定会让内存占满导致OOM,所以便采用了google推荐使用的LruCache缓存算法.该算法可以实现固定内存加载,并且最近少使用的会被内存回收掉.

然后在MainActivity中可以使用如下:

ImageLoader.getInstance(MainActivity.this, 3, ImageLoader.Type.LIFO).load(IMAGES[position], holder.imageView);上面加载方式是直接交给内部处理.图片默认加载RGB_565. ImageLoader.getInstance(MainActivity.this, 3, ImageLoader.Type.LIFO).load(IMAGES[position], holder.imageView, new LoadListener<View>() {@Overridepublic <T> void Loading(View view, String path) {}@Overridepublic <T> void LoadSuccess(View view, Bitmap bitmap, String path) {((ImageView) view).setImageBitmap(bitmap);}@Overridepublic <T> void LoadError(View view, String path, String errorMsg) {Log.d("Tanck","加载失败:"+path);((ImageView)view).setImageResource(R.mipmap.ic_launcher);}});

采用几个加载配置方式内存对比:

RGB_565:

约11.31MB,效果如下:

ARGB_8888:

约12.86MB效果图如下:

可以看出差别不是很大.

但是ARGB_4444使用内存和RGB_565相近,但是效果很差,效果图如下:

版权声明:本文为博主原创文章,,未经博主允许不得转载。

相信优美的生命,就是一曲无字的挽歌,

Android之打造自己加载高清大图及瀑布流框架.解决错位等问题.

相关文章:

你感兴趣的文章:

标签云: