ImageLoader源码阅读

本文介绍了一个基于单例模式实现的图片加载器,详细阐述了其实现过程,包括线程同步管理、图片缓存策略、图片压缩及适配等关键技术点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、以单例方式获得该实例对象
public static ImageLoader getInstance()
{

    if (mInstance == null)
    {
        synchronized (ImageLoader.class)
        {
            if (mInstance == null)
            {
                mInstance = new ImageLoader(1, Type.LIFO);
            }
        }
    }
    return mInstance;
}

这里利用synchronized 关键字,通过同步锁的方式,进行了线程同步的管理。
关于synchronized 我们进行简单的说明,synchronized 关键字可以用于修饰方法或者代码块上,而不论放在哪里,它取得的锁都是对象,而不是把一段代码或函数当作锁,每个对象只有一个锁(lock)与之相关联。在上述方法中,通过synchronized (ImageLoader.class) 我们可以明确,它锁住的是ImageLoader 类的所有对象。
当然作为单例模式,构造函数的私有化也是必须的,如下:
private ImageLoader(int threadCount, Type type)
{
init(threadCount, type);
}

private void init(int threadCount, Type type)
{
    // loop thread
    mPoolThread = new Thread()
    {
        @Override
        public void run()
        {
            Looper.prepare();

            mPoolThreadHander = new Handler()
            {
                @Override
                public void handleMessage(Message msg)
                {
                    mThreadPool.execute(getTask());
                    try
                    {
                        mPoolSemaphore.acquire();
                    } catch (InterruptedException e)
                    {
                    }
                }
            };
            // 释放一个信号量
            mSemaphore.release();
            Looper.loop();
        }
    };
    mPoolThread.start();

    // 获取应用程序最大可用内存
    int maxMemory = (int) Runtime.getRuntime().maxMemory();
    int cacheSize = maxMemory / 8;
    mLruCache = new LruCache<String, Bitmap>(cacheSize)
    {
        @Override
        protected int sizeOf(String key, Bitmap value)
        {
            return value.getRowBytes() * value.getHeight();
        };
    };

    mThreadPool = Executors.newFixedThreadPool(threadCount);
    mPoolSemaphore = new Semaphore(threadCount);
    mTasks = new LinkedList<Runnable>();
    mType = type == null ? Type.LIFO : type;

}

通过上面我们可以知道,在初始化的方法里,我们做了下面几个事情:
1、启动一个线程,利用Handler机制进行任务的轮询。在收到Message后,通过getTask()方法获取当前所要运行的Runnable,添加到线程池。
private synchronized Runnable getTask()
{
if (mType == Type.FIFO)
{
return mTasks.removeFirst();
} else if (mType == Type.LIFO)
{
return mTasks.removeLast();
}
return null;
}
2、获取应用程序的最大内存,定义ImageLoader 可用maxSize(最大内存)为其值得1/8,重写sizeOf 方法,定义缓存中每项的大小,当我们缓存进去一个数据后,当前已缓存的Size就会根据这个方法将当前加进来的数据也加上,便于统计当前使用了多少内存,如果已使用的大小超过maxSize就会进行清除动作。
3、定义一个可控最大并发数为threadCount的定长线程池,超出的线程会在队列中等待。
4、定义一个值为threadCount信号量mPoolSemaphore,用于控制线程池的线程同步。
5、定义任务队列。
6、明确队列的调度方式,枚举Type有LIFO (后进先出)和FIFO (先进先出),默认情况采用LIFO 。

二、查看公共方法loadImage()

public void loadImage(final String path, final ImageView imageView)
{
// set tag
imageView.setTag(path);
// UI线程
if (mHandler == null)
{
mHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
ImgBeanHolder holder = (ImgBeanHolder) msg.obj;
ImageView imageView = holder.imageView;
Bitmap bm = holder.bitmap;
String path = holder.path;
if (imageView.getTag().toString().equals(path))
{
imageView.setImageBitmap(bm);
}
}
};
}

    Bitmap bm = getBitmapFromLruCache(path);
    if (bm != null)
    {
        ImgBeanHolder holder = new ImgBeanHolder();
        holder.bitmap = bm;
        holder.imageView = imageView;
        holder.path = path;
        Message message = Message.obtain();
        message.obj = holder;
        mHandler.sendMessage(message);
    } else
    {
        addTask(new Runnable()
        {
            @Override
            public void run()
            {

                ImageSize imageSize = getImageViewWidth(imageView);

                int reqWidth = imageSize.width;
                int reqHeight = imageSize.height;

                Bitmap bm = decodeSampledBitmapFromResource(path, reqWidth,
                        reqHeight);
                int degree=reaPictureDegree(path);
                Bitmap rotaingBitmap=rotaingImageView(degree,bm);
                addBitmapToLruCache(path, rotaingBitmap);
                ImgBeanHolder holder = new ImgBeanHolder();
                holder.bitmap = getBitmapFromLruCache(path);
                holder.imageView = imageView;
                holder.path = path;
                Message message = Message.obtain();
                message.obj = holder;
                mHandler.sendMessage(message);
                mPoolSemaphore.release();
            }
        });
    }

}

1、定义Handler用于异步更新ImageView的Bitmap。
2、从LruCache中获取一张图片,如果不存在就返回null。当不为null时,mHandler发送Message;否则,新建一个Runnable加入任务队列,根据ImageView获得适当的压缩的宽和高,对图片进行压缩,把图片存储进内存,mHandler发送Message。

三、具体查看下根据ImageView获得适当的压缩的宽和高的方法,这里不做过多解释,如下:
private ImageSize getImageViewWidth(ImageView imageView)
{
ImageSize imageSize = new ImageSize();
final DisplayMetrics displayMetrics = imageView.getContext()
.getResources().getDisplayMetrics();
final LayoutParams params = imageView.getLayoutParams();

    int width = params.width == LayoutParams.WRAP_CONTENT ? 0 : imageView
            .getWidth(); // Get actual image width
    if (width <= 0)
        width = params.width; // Get layout width parameter
    if (width <= 0)
        width = getImageViewFieldValue(imageView, "mMaxWidth"); 
    if (width <= 0)
        width = displayMetrics.widthPixels;
    int height = params.height == LayoutParams.WRAP_CONTENT ? 0 : imageView
            .getHeight(); // Get actual image height
    if (height <= 0)
        height = params.height; // Get layout height parameter
    if (height <= 0)
        height = getImageViewFieldValue(imageView, "mMaxHeight"); 
    if (height <= 0)
        height = displayMetrics.heightPixels;
    imageSize.width = width;
    imageSize.height = height;
    return imageSize;

}

四、具体查看下图片压缩的方法,这里不做过多解释,直接看注释即可,如下:
private Bitmap decodeSampledBitmapFromResource(String pathName,
int reqWidth, int reqHeight)
{
// 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, options);
// 调用上面定义的方法计算inSampleSize值
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// 使用获取到的inSampleSize值再次解析图片
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(pathName, options);

    return bitmap;
}

private int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight)
{
// 源图片的宽度
int width = options.outWidth;
int height = options.outHeight;
int inSampleSize = 1;

    if (width > reqWidth && height > reqHeight)
    {
        // 计算出实际宽度和目标宽度的比率
        int widthRatio = Math.round((float) width / (float) reqWidth);
        int heightRatio = Math.round((float) width / (float) reqWidth);
        inSampleSize = Math.max(widthRatio, heightRatio);
    }
    return inSampleSize;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值