盗版毕加索

突然感触blog对于开发人员是多麽的重要,无论你是否写过blog,只要你身在技术的阵营中,就一定从blog中受益过。至于写与不写,只是个人习惯问题。

我是android菜鸟,此篇blog写给同样刚刚进入android怀抱的菜鸟们,让我们尽情的fighting with android!

最近在开发一个程序,用到了GridView这个View,遇到的问题就是当加载的图片达到50k以上时,GridView的滑动会出现明显卡顿,其实不光是GridView控件,其他控件listview、viewpager等都会遇到这个问题。甚至出现oom,也就是由于内存不足而crash。
出现卡顿问题原因:显而易见,图片大,导致imageView.setImageResource需要更多时间,图片没加载完,滑动就卡主
oom出现原因:大。

知道了原因就好解决了,三级缓存技术+异步加载,于是我就想自己写个库以后再遇到图片的加载就都是这个套就ok了,谁知github上有个叫毕加索的,都是套路,这个Picasso那是相当的nb,不但三级缓存+异步加载,而且还可以设置bitmap属性、设置图片大小、设置加载前显示的图片、加载错误显示的图片、从resourceId加载图片、从sd卡加载图片、从网络加载图片;服务可真周全,只有你想不到的。使用起来相当简单。
Picasso.with(context).load(url).placeholder(R.drawable.user_placeholder).error(R.drawable.user_placeholder_error).into(imageView);

技术是自己的,只有学到了知识,自己才会强大。把别人的库用的很6什么也证明不了。

三级缓存:内存、sd卡、网络。当需要加载一个image时,首先检测是否存在于内存,然后检测sd卡,都没有那就乖乖的从网络加载。
异步加载:当gridView滑动时,图片太大,滑动就阻塞在哪里。解决办法就是异步加载,开个线程,线程在后台慢慢折腾,流畅对我们更重要。别tm问我流畅是谁

一口吃不了胖子,Picasso很厉害,我们这里只实现精简版的Picaso,我们要实现的功能就是二级缓存+异步加载。二级就是内存和sd卡,异步就是后台线程。我们的Picaso初步设计成单利模式,这样所有的Image加载都可以轻松使用,当有image需要加载时,我们首先查看内存,内存没有就将这个任务添加到堆栈中,Picaso会有个线程一直循环检测这个stack,有任务就执行加载,没有任务就wait等待。理想效果就是滑动的时候,即使图片很大,也不会阻塞,顶多就是滑动到大图片,显示空白但是滑动流畅,慢慢的空白被异步线程加载。

下面就构思下,我们要实现的库,伪代码先给出框架

public class Picaso{//not Picasso
    private static Picaso;//单利模式
    private MemoCache memCache;//内存缓存
    private Stack taskStack;//任务队列
    private LoadThread loadThread;//后台加载线程

    public Picaso instancePicaso(){}
    public void loadImage(){}

    private class MemoCache{}
    private class LoadThread extends Thread{}
}

下面对上面的代码说下思路:

  • instancePicaso函数是单例模式,就是返回本地的Picaso变量,而不是每次都实例化一个Picaso类。
  • MemoCache 内存缓存类,使用软引用,将加载过的bitmap存储到map中,下次再使用相同的item时直接返回。
    (这里涉及到的知识点为对象的强引用、软引用、弱引用、虚引用;还有lruCache类的使用。)
  • taskStack是个堆栈,作用就是当加载的image不在内存缓存中,那么new个新任务,并添加到这个栈中,这样另外的一个线程负责循环读取堆栈并执行加载任务。
  • loadThread线程,这个就是后台负责循环加载任务的线程,taskStack中有数据就读取并执行,没有数据就睡觉。
    (这里涉及的知识点有线程的中止方式isInterrupted,队列数据的原子操作synchronized,线程的wait和notifyAll)

到这里你可以自己动手实现你的毕加索了。后面会给我的源码。

这里在普及下加载图片的基础知识
无论是从drawable资源中还是从cs卡中,方法都差不多。
我们最终是将pic添加到imageview中,imageview的方法是ImageView.setImageBitmap(Bitmap bitmap),而bitmap的来源是使用BitmapFactory.decodeStream(InputStream)或者BitmapFactory.decodeStream(InputStream, Rect, Options)
InputStream来源是context.getResources().openRawResource(resourseId);
Rect是限制大小的类
Options是一个关键的类,里面可以压缩数据,设置图片编码方式。
图片的编码方式有:
编码方式有:(ARGB分别代表透明度、红、绿、蓝)
Bitmap.Config ALPHA_8 占8位(具体情况不清楚)
Bitmap.Config ARGB_4444 占16位(ARGB各4位)//废弃了
Bitmap.Config ARGB_8888 占32位(ARGB各8位)//包含三基色的同时还包含透明度,所以很大
Bitmap.Config RGB_565 占16位(R5位G6位B5位)//显示效果还可以,大小适中
下面给出使用方法的栗子,对,栗子:

    /********************************************
     * 根据资源id和context获取特定options的bitmap 
     * @param resourseId    资源id
     * @param context       环境上下文
     * @return      获取到的bitmap
     ***********************************************/
    private Bitmap getImageBitmap(int resourseId, Context context){
        BitmapFactory.Options opts = new BitmapFactory.Options();
        opts.inJustDecodeBounds = true;
        Bitmap oldBitmap = BitmapFactory.decodeResource(context.getResources(), resourseId);
        int oldWidth = oldBitmap.getWidth();
        int oldHeight = oldBitmap.getHeight();
        opts.inSampleSize = calcCompressSize(oldWidth, oldHeight, 128, 128);
        opts.inJustDecodeBounds = false;
        opts.inPreferredConfig = Bitmap.Config.RGB_565;
        //InputStream is = context.getResources().openRawResource(resourseId);
        //return BitmapFactory.decodeStream(is, null, opts);
        return BitmapFactory.decodeResource(context.getResources(), resourseId, opts);
    }
    /********************************************
     *   * 获取压缩比率,根据最小的高度比或宽度比,来获取倍率
     * @param oldWidth  pic的原始宽度
     * @param oldHeight pic的原始高度
     * @param reqWidth  pic指定宽度
     * @param reqHeight pic指定高度
     * @return  返回压缩比率,应该为2的倍数,如果不是,会自定找离2最近的那个整数倍的数
     ****************************************************************/
    private int calcCompressSize(int oldWidth, int oldHeight, int reqWidth, int reqHeight){
        if(oldWidth >= reqWidth || oldHeight >= reqHeight){
            return 1;
        }
        int widthRatio = Math.round(oldWidth/reqWidth);
        int heightRatio = Math.round(oldHeight/reqHeight);
        return widthRatio >= heightRatio ? widthRatio:heightRatio;
    }

最后给出Picaso的完整代码,在需要加载image的地方使用Picaso.instansePicaso,最后用完了记得stop下,为了关闭它的那个线程。

- -!, 我是链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值