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() {
            @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
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值