国内最全IT社区平台 联系我们 | 收藏本站
华晨云阿里云优惠2
您当前位置:首页 > php开源 > 综合技术 > Android之打造自己加载高清大图及瀑布流框架.解决错位等问题.

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

来源:程序员人生   发布时间:2016-03-21 08:58:22 阅读次数:4364次

首先看效果图以下:

 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种类不需要配置线程池及加载方式.加载方式分为两种: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();//初始化完成后信号量 ⑴ Looper.loop(); } };

从上面代码可以看出PoolTreadHandler收到1个消息后会让mThreadPool去履行1个任务,该任务通过getTask()方法取得1个Runnable对象,并且让信号量增加表示,线程池中有1个任务了.

看看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是1个LinkedList类型的集合.所以核心在于方法Load() /** * 加载图片 * * @param path * @param imageview */ public void load(final String path, final View view, final LoadListenerloadListener) { 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;//唯1的 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();//信号量 ⑴ }

缓存策略:先从内存中获得,如果没有获得到,就从磁盘获得,磁盘也没有获得到,那就从网络获得.最后并将该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() { @Override publicvoid Loading(View view, String path) { } @Override publicvoid LoadSuccess(View view, Bitmap bitmap, String path) { ((ImageView) view).setImageBitmap(bitmap); } @Override publicvoid 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相近,但是效果很差,效果图以下:


生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠
程序员人生
------分隔线----------------------------
分享到:
------分隔线----------------------------
关闭
程序员人生