Android之打造自己加载高清大图及瀑布流框架.解决错位等问题.
来源:互联网 发布:如何经营一家淘宝网店 编辑:程序博客网 时间:2024/04/30 13:52
首先看效果图如下:
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() { @Override public void run() { Looper.prepare(); mPoolThreadHandler = new Handler() { @Override public void handleMessage(Message msg) { mThreadPool.execute(getTask()); try { mPoolSemaphore.acquire();//信号量 + 1 } catch (InterruptedException e) { e.printStackTrace(); } } }; mSemapHore.release();//初始化完成后信号量 -1 Looper.loop(); } };
从上面代码可以看出PoolTreadHandler收到一个消息后会让mThreadPool去执行一个任务,该任务通过getTask()方法获得一个Runnable对象,并且让信号量增加表示,线程池中有一个任务了.
看看getTask()代码很简单,仅仅是将任务按不同的方式取出来:
/** * 获取任务 * * @return */ private synchronized Runnable getTask() { if (0 < mTask.size()) { if (mType == Type.LIFO) return mTask.removeFirst(); else return 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() { @Override public 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() { @Override public 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>() { @Override public <T> void Loading(View view, String path) { } @Override public <T> void LoadSuccess(View view, Bitmap bitmap, String path) { ((ImageView) view).setImageBitmap(bitmap); } @Override public <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相近,但是效果很差,效果图如下:
2 0
- Android之打造自己加载高清大图及瀑布流框架.解决错位等问题.
- Android 加载高清大图
- 一招解决Android 加载高清大图
- 通过webView全屏加载高清大图片,解决不能全屏高清加载图片问题
- Android中加载高清大图及图片压缩方式
- 浅谈android中加载高清大图及图片压缩方式(二)
- Android 高清加载长图或大图方案
- ListView性能优化及加载图片出现图片错位,闪朔等问题的解决
- AsyncLoadLocalImage ios 加载本地高清大图
- 解决Android大图片加载问题
- android 中解决gridview 等加载大图片出现oom的问题
- CFRunloop 优化TableView加载高清大图UI卡顿问题。单独分批加载
- [Android实例] Android有效解决加载大图片内存溢出问题及优化虚拟机内存
- android大图、高清图片处理
- Android之ListView(一)异步加载图片错位、重复、闪烁问题分析及解决方案
- android开发中解决ListView异步加载图片错位问题
- 解决Android ListView中图片异步加载错位问题
- -------------解决Android ListView中图片异步加载错位问题
- TatukGIS Editor使用教程:GPS和等高线
- 一张表可以有多少个主键
- java.util.concurrent代码总结
- java观察者(Observer)模式
- 上海房屋租赁合同登记备案证明办理流程
- Android之打造自己加载高清大图及瀑布流框架.解决错位等问题.
- …… are only available on JDK 1.5 and higher 错误(spring 的jdk版本检测在jdk 8下的修订)
- selenium2+webdriver+java环境搭建关于引用包的问题
- Android开发入门教程--2.Android应用程序结构分析
- 字符串
- jdbc 连接池
- HDU - 3974 Assign the task(线段树 区间修改)
- 接口应用——策略模式
- PHP Yii AR数据库操作实例