Android 简单的ImageLoader实现

ImageLoader的简单实现

这是一个ImageLoader的简单实现,通过这个项目,可以帮助我们了解Android图片加载框架的工作原理,
会在这个基础上持续维护.

效果

ImageLoader

原理分析

计算Bitmap的采样率

防止图片消耗过大的内存,图片根据ImageView的宽高适配,不会造成图片失真.

设置inJustDecodeBounds为true后,decodeFile并不分配空间,但可计算出原始图片的长度和宽度
,即opts.width和opts.height。有了这两个参数,再通过一定的算法,即可得到一个恰当的inSampleSize。

/**
 * 计算Bitmap的采样率
 *
 * @return inSampleSize of each bitmap should be the power of 2.
 */
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
    if (reqHeight == 0 || reqWidth == 0) {
        return 1;
    }
    // raw width and height of this bitmap.
    int width = options.outWidth;
    int height = options.outHeight;
    int sampleSize = 1;

    if (width > reqHeight || height > reqHeight) {
        int halfWidth = width / 2;
        int halfHeight = height / 2;

        while ((halfHeight / sampleSize) >= reqHeight && (halfWidth / sampleSize) >= reqWidth) {
            sampleSize *= 2;
        }
    }
    // 选用最小的压缩比,可以保证图片会大于ImageView的尺寸,不会失真
    Log.d(TAG, "calculateInSampleSize: inSampleSize=" + sampleSize);
    return sampleSize;
}

缓存策略

使用Memory-Cache >> Disk-Cache >> Network的顺序加载图片,
其中Memory-Cache 和 Disk-Cache 均由Lru实现

// Memory-Cache >> Disk-Cache >> Network
private Bitmap loadBitmapAsync(String uri, int reqWidth, int reqHeight) {
    Bitmap bitmap = loadBitmapFromMemCache(uri);
    if (bitmap != null)
        return bitmap;
    try {
        bitmap = loadBmpFromDisk(uri, reqWidth, reqHeight);
        if (bitmap != null)
            return bitmap;
        bitmap = loadBmpFromHttp(uri, reqWidth, reqHeight);
    } catch (IOException e) {
        e.printStackTrace();
    }
    // 从网络中获取图片
    if (bitmap == null && !mIsDiskLruCacheCreated)
        bitmap = downloadBmpFromNet(uri);
    return bitmap;
}

从内存中进行加载

直接从LruCache中进行读取即可

   /**
     * Load bitmap from LruCache.
     */
    private Bitmap loadBitmapFromMemCache(String uri) {
        final String key = hashKeyFromUri(uri);
        return mMemCache.get(key);
    }

从磁盘中进行加载

/**
 * 从硬盘缓存中加载图片
 */
private Bitmap loadBmpFromDisk(String uri, int reqWidth, int reqHeight) throws IOException {
    // 判断当前线程
    if (Looper.myLooper() == Looper.getMainLooper()) {
        Log.w(TAG, "loadBmpFromDisk: in UI-Thread isn't recommend!");
    }
    if (mDiskLruCache == null) {
        return null;
    }

    Bitmap bitmap = null;
    String key = hashKeyFromUri(uri);
    DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
    if (snapshot != null) {
        FileInputStream fis = (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX);
        FileDescriptor fd = fis.getFD();
        bitmap = mResizer.decodeSampledBmpFromFD(fd, reqWidth, reqHeight);
        if (bitmap != null) {
            addBmpToMemCache(key, bitmap);
        }
    }
    return bitmap;
}

从网络中进行加载


    /**
     * 从网络中取图片
     */
private Bitmap loadBmpFromHttp(String uri, int reqWidth, int reqHeight) throws IOException {
    if (Looper.myLooper() == Looper.getMainLooper()) {
        throw new RuntimeException("Can't visit Network in UI-Thread");
    }
    if (mDiskLruCache == null) {
        return null;
    }

    String key = hashKeyFromUri(uri);
    DiskLruCache.Editor editor = mDiskLruCache.edit(key);
    if (editor != null) {
        OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
        if (downloadUrlToStream(uri, outputStream)) {
            editor.commit();
        } else {
            editor.abort();
        }
        mDiskLruCache.flush();
    }
    return loadBmpFromDisk(uri, reqWidth, reqHeight);
}

简单使用

//~ ImageAdapter中
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    LayoutInflater inflater = LayoutInflater.from(MainActivity.this);
    ViewHolder viewHolder;
    if (convertView == null) {
        convertView = inflater.inflate(R.layout.item_image, parent, false);
        viewHolder = new ViewHolder();
        viewHolder.mIvImg = (ImageView) convertView.findViewById(R.id.id_iv_img);
        convertView.setTag(viewHolder);
    } else {
        viewHolder = (ViewHolder) convertView.getTag();
    }
    // 判断ImageView是否加载正确的图片
    if (!getItem(position).equals(viewHolder.mIvImg.getTag())) {
        Log.d(TAG, "getView: item " + getItem(position) + " Img " + viewHolder.mIvImg.getTag());
        viewHolder.mIvImg.setImageResource(R.drawable.image_default);
    }
    // 给ImageView 设置Tag, 表示ImageView所要显示的图片
    viewHolder.mIvImg.setTag(mUris[position]);
    ImageLoader.getInstance(MainActivity.this).bindBmp(mUris[position], viewHolder.mIvImg, mImageWidth, mImageWidth);

    return convertView;
}

Github: https://github.com/yangtianrui95/Android-ImageLoader

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值