Android-笔记 解析Bitmap如何高效加载

Bitmap是如何加载的

介绍bitmap高效加载之前,先说一下bitmap是如何加载的。首先呢,bitmap在安卓里面你可以理解为就是指的是一张图片,如何加载bitmap呢,BitmapFactory提供了四个方法:decodeFile、decodeResource、decodeStream和decodeByteArray,对应的就是从文件系统、资源、输入流和字节数组中加载出一个bitmap对象,然后再调用各类view的set**Bitmap方法,我看了一下这四个方法的实现,前俩个方法最后还是会调用decodeStream方法,也就是说实际上最后都是通过流或者字节数组去生成一个bitmap对象,具体如何生成的,这里就不去讨论了,底层是几个native方法,感兴趣的 自己去瞅瞅吧。

Bitmap实现高效加载要素

如何高效的加载Bitmap呢?四个字,按需加载,按照控件的大小来加载所需的尺寸的图片。说详细点儿就是,通过BitmapFactory.options的inSampleSize参数,也就是采样率来缩放图片大小,从而减少内存占用和绘制时间来做到高效加载。举个例子,比如一张图片是1024x1024那么大,而你的imageview实际上是100x100,如果bitmap不处理,按照ARG8888格式,那么加载这一张图片占有的内存为1024x1024x4=4MB。那如果我们通过BitmapFactory.options来缩放图片,当inSampleSize(采样率)等于1的时候,大小不变,比如为2,那么采样后的图片宽高都为原先图片宽高的1/2,像素大小就是原来的1/4,imageview大小为100*100时,那么宽高就大约缩小了10倍,这时候所占内存大小就是102x102x4=0.04MB,结果对比一下就知道了吧,下面看看代码如何实现这个流程:

public static Bitmap decodeSampledFromResuorce(Resources res, 
                        int resId, int reqWidth, int reqHeight){
    BitmapFactory.Options options = new BitmapFactory.Options();
                        /**
                     * 最关键在此,把options.inJustDecodeBounds = true;
                     * 表示只读图片,不加载到内存中,设置这个参数为ture,就不会给图片分配内存空间,但是可以获取到图片的大小等属性; 设置为false, 就是要加载这个图片
                     */
    options.inJustDecodeBounds = true;
    
    BitmapFactory.decodeResource(res, resId, options);
    //计算缩放比
    options.inSampleSize = calculateInSampleSize(options,reqHeight, reqWidth)
    ;
    option.inJustDecodeBounds = false;
    
    return BitmapFactory.decodeResource(res, resId, options);
}

private static int calculateInSampleSize(BitmapFactory.Options options, 
                                            int reqHeight, int reqWidth){
    int height= options.outHeight;
    int width = options.outWidth;
    int inSampleSize = 1;
    if (height>reqHeight||width>reqWidth){
        int halfHeight = height / 2;
        int halfWidth = width / 2;

        //计算缩放比,是2的指数
        while (halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth{
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}
mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(),R.mipmap.ic_launcher,100,100);

主要流程如下:
1.将BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片
2.从BitmapFactory.Options中取出图片的原始宽高信息,它们对应于outWidth和outHeight参数。
3.根据采样率的规则并结合目标View的所需大小计算出采样率inSampleSize。
4.将BitmapFactory.Options的inJustDecodeBounds参数设为false,然后重新加载图片。

这里我再贴段代码,主要是用来传入网络图的url生成bitmap,因为之前碰到了一个坑,这里记录一下。
当时是用HttpURLConnection去做请求,亲娘诶,他有个建立连接的步骤,图片要是比较大,那是真的耗时,返回半天无响应(debug是那里耗时比较久,可能是别的原因),所以我用Rxjava配合Retrofit写了个方法,但是没有做缓存,项目实际没这个需求。

    private void getUrlBitMap(final String imageUrl) {
        commObserver_url = new CommObserver<ResponseBody>(getApplicationContext(), false) {
            @Override
            public void success(ResponseBody data) {
                try {

                    InputStream inputStream = data.byteStream();
                    byte[] inputStream2ByteArr = inputStream2ByteArr(inputStream);
                    BitmapFactory.Options options = new BitmapFactory.Options();
                    /**
                     * 最关键在此,把options.inJustDecodeBounds = true;
                     * 表示只读图片,不加载到内存中,设置这个参数为ture,就不会给图片分配内存空间,但是可以获取到图片的大小等属性; 设置为false, 就是要加载这个图片
                     * InputStream 被调用两次,第一次调用流被关闭清空了!!!
                     */
                    options.inJustDecodeBounds = true;
                    BitmapFactory.decodeByteArray(inputStream2ByteArr, 0, inputStream2ByteArr.length, options);
                    // BitmapFactory.decodeStream(inputStream, null, options);
                    originalWidth = options.outWidth;
                    originalHeight = options.outHeight;
                    options.inJustDecodeBounds = false;
                    Message message = new Message();
                    message.obj = BitmapFactory.decodeByteArray(inputStream2ByteArr, 0, inputStream2ByteArr.length, options);
                    weakHandler.sendMessage(message);
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void error(Throwable e) {
                ALog.e(e.toString());
            }
        };

        RetrofitFactory.getInstance().create(AppApi.class)
                .getImage(imageUrl).subscribeOn(Schedulers.io())
                .observeOn(Schedulers.newThread())
                .subscribe(commObserver_url);

    }

Bitmap的缓存策略

其实缓存策略,很多都是通用的原理,这个原理就是LRU(least recently used)算法 ,最近最少使用。
我们不可能无限的去缓存,一般来说我们会去设置一个值,当缓存的大小快要越过这个值的时候,我们就会删除一些缓存,要删除那些缓存呢?不怎么用的呗,那就是最近最少使用的咯。

Lru算法安卓的实现主要是俩个类,一个是LruCache,另外一个是DiskLruCache,瞅名字就知道了,一个是内存缓存,一个是磁盘缓存。说到这是不是想起了Glide的三级缓存,其实都差不多,都这么个原理,下面说一下这俩个类的用法:

LruCache使用demo:

int MaxMemory = (int)(Runtime.getRuntime().maxMemory() / 1024);// kB 获取当前进程的可用内存
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String,Bitmap>(cacheSize){
    @Override
    protected int sizeOf(String key,Bitmap bitmap){
        //bitmap.getByteCount() = bitmap.getRowBytes() * bitmap.getHeight(); 
        return bitmap.getRowBytes() * bitmap.getHeight() / 1024;// KB
    } 
}//初始化
mMemoryCache.get(key)
mMemoryCache.put(key,bitmap)//存取值
mMemoryCache.remove()//删除某一个值

DiskLruCache使用demo:

private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50;// 50MB

File diskCacheDir = new File(mContext,"bitmap");
if(!diskCacheDir.exists()){
   diskCacheDir.mkdirs();
}
mDiskLruCache = DiskLruCache.open(diskCacheDir,1,1,DISK_CACHE_SIZE);

DiskLruCache不能通过new来实例化,只能通过open方法,其中四个参数分别对应的是:diskCacheDir 缓存文件夹, 1 应用版本号,一般写为1,1 单个节点所对应的数据的个数,一般写1, 缓存大小。

算了,没时间了 ,详情参考这个:Android缓存的俩种实现方式

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值