美文网首页
手写一个图片加载框架

手写一个图片加载框架

作者: 有腹肌的豌豆Z | 来源:发表于2020-08-26 08:28 被阅读0次

原材料

  • ImageLruCache:内存缓存
  • ImageDiskLruCache:磁盘缓存
  • BitmapUtils:图片处理
  • ImageThreadPoolExecutor:图片加载线程池
  • LoadBitmapTask:一个独立的加载任务
  • NetRequest:网络请求工具类
  • TaskHandler:任务调度
  • TaskResult:图片结果封装
  • BitmapCallback:图片加载回调
  • MD5Utils:MD5工具类
  • ImageLoader 图片加载封装

流程图

源代码

  • ImageLruCache:内存缓存
/**
 * ================================================
 * 作    者:SharkZ
 * 邮    箱:229153959@qq.com
 * 创建日期:2020/3/14  13:23
 * 描    述 图片内存缓存处理对象
 * 修订历史:
 * ================================================
 */
public class ImageLruCache extends LruCache<String, Bitmap> {


    private static ImageLruCache imageLruCache;
   
    /**
     *  获取分配给该应用的最大内存
     */
    public static int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
   
    /**
     * lruChache能获取的缓存大小为整个应用内存的八分之一
     */
    public static int cacheSize = maxMemory / 8;


    // =============================================================================================

    
    public static ImageLruCache getInstance() {
        if (imageLruCache == null) {
            synchronized (ImageLruCache.class) {
                if (imageLruCache == null) {
                    imageLruCache = new ImageLruCache();
                }
            }
        }
        return imageLruCache;
    }


    /**
     * @param maxSize for caches that do not override {@link #sizeOf}, this is
     *                the maximum number of entries in the cache. For all other caches,
     *                this is the maximum sum of the sizes of the entries in this cache.
     */
    private ImageLruCache(int maxSize) {
        super(maxSize);
    }

    private ImageLruCache() {
        super(cacheSize);
    }


    // =============================================================================================


    @Override
    protected int sizeOf(String key, Bitmap value) {
        //return value.getRowBytes()*value.getHeight()/
        return value.getByteCount() / 1024;
    }


    // =============================================================================================


    /**
     * 添加bitmap到内存缓存中
     *
     * @param key    key
     * @param bitmap bitmap
     */
    public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getBitmapFromMemCache(key) == null) {
            this.put(key, bitmap);
        }
    }

    /**
     * 从内存缓存中获取bitmap
     *
     * @param key key
     * @return bitmap
     */
    private Bitmap getBitmapFromMemCache(String key) {
        return this.get(key);
    }

    /**
     * 通过url获取内存缓存
     *
     * @param url 图片网络地址
     */
    public Bitmap loadBitmapFromMemCache(String url) {
        final String key = MD5Utils.hashKeyFromUrl(url);
        Bitmap bitmap = getBitmapFromMemCache(key);
        return bitmap;
    }

    /**
     * 删除一个具体的缓存
     */
    public void removeCacheWithKey(String key) {
        this.remove(key);
    }

    /**
     * 清空内存缓存
     */
    public void clearLruCache2() {
        this.evictAll();
    }

}

  • ImageDiskLruCache:磁盘缓存
public class ImageDiskLruCache {

    private DiskLruCache mDiskLruCache;
    private  boolean mIsDiskLruCacheCreated;                        // 是否初始化磁盘缓存对象
    private static final long DISK_CACHE_SIZE = 1024*1024*50;       // 磁盘缓存大小
    public static final int DISK_CACHE_INDEX = 0;                  // 缓存下标
    private static ImageDiskLruCache diskLruCache;

    // =============================================================================================

    public static ImageDiskLruCache getInstance(){
        if (diskLruCache==null){
            synchronized (ImageDiskLruCache.class){
                if (diskLruCache==null){
                    diskLruCache = new ImageDiskLruCache();
                }
            }
        }
        return diskLruCache;
    }

    /**
     * 私有构造方法
     */
    private ImageDiskLruCache(){
        File diskCacheDir = getDiskCacheDir(ImageLoader.getContext(), "bitmap");
        if(!diskCacheDir.exists()){
            diskCacheDir.mkdirs();
        }
        // sd卡的可用空间大于本地缓存的大小
        if(getUsableSpace(diskCacheDir)>DISK_CACHE_SIZE){
            try {
                //实例化diskLruCache
                mDiskLruCache =DiskLruCache.open(diskCacheDir,1,1,DISK_CACHE_SIZE);
                mIsDiskLruCacheCreated =true;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else{
            mIsDiskLruCacheCreated =false;
        }
    }


    // =============================================================================================


    /**
     * 加载 Bitmap
     * 先从缓存中加载
     * 如果没有那就网络下载
     *
     * @return bitmap
     */
    public synchronized Bitmap loadBitmap(String url, int reqWidth, int reqHeight){
        Bitmap bitmap = null;
        bitmap=loadBitmapFromDiskCache(url, reqWidth, reqHeight); // 从缓存中加载
        if (bitmap==null){
            // 网络请求
            try {
                downloadImageToDiskCache(url); // 通过URL将图片保存早本地
                bitmap=loadBitmapFromDiskCache(url, reqWidth, reqHeight); // 从缓存中加载
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return bitmap;
    }


    /**
     * 从本地缓存加载bitmap
     *
     * @param url           图片地址
     * @param reqWidth      图片宽度
     * @param reqHeight     图片高度
     * @return              经过处理的Bitmap
     */
    public  Bitmap loadBitmapFromDiskCache(String url, int reqWidth, int reqHeight)  {
        if(mDiskLruCache ==null){
            return null;
        }
        Bitmap bitmap=null;
        String key = MD5Utils.hashKeyFromUrl(url);
        DiskLruCache.Snapshot snapshot = null;
        try {
            snapshot = mDiskLruCache.get(key);
            if(snapshot!=null){
                FileInputStream fileInputStream =(FileInputStream)snapshot.getInputStream(DISK_CACHE_INDEX);
                if(reqWidth<=0||reqHeight<=0){
                    // 不压缩图片
                    bitmap = BitmapFactory.decodeFileDescriptor(fileInputStream.getFD());
                }else{
                    //按需求分辨率压缩图片
                    bitmap =BitmapUtils.getSmallBitmap(fileInputStream.getFD(),reqWidth,reqHeight);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }


    /**
     * 下载图片放入本地缓存
     *
     * @param urlString 下载图片的链接
     * @throws IOException
     */
    private void downloadImageToDiskCache(String urlString) throws IOException {
        if(mDiskLruCache == null){
            return ;
        }
        String key =MD5Utils.hashKeyFromUrl(urlString);
        DiskLruCache.Editor editor =mDiskLruCache.edit(key);
        if(editor != null){
            //打开本地缓存的输入流
            OutputStream outputStream =editor.newOutputStream(DISK_CACHE_INDEX);
            //将从网络下载并写入输出流中
            if(NetRequest.downloadUrlToStream(urlString,outputStream)){
                // 提交数据,并是释放连接
                editor.commit();
            }else{
                // 释放连接
                editor.abort();
            }
            mDiskLruCache.flush();
        }
    }


    /**
     * 获取本地缓存的File目录
     *
     * @param context       上下文
     * @param uniqueName    保存文件的文件夹名字
     */
    private File getDiskCacheDir(Context context, String uniqueName){
        //判断是否含有sd卡
        boolean externalStorageAvailable = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
        final String cachePath;
        if(externalStorageAvailable){
            cachePath =context.getExternalCacheDir().getPath();
        }else{
            //获取app自带的缓存目录
            cachePath =context.getCacheDir().getPath();
        }
        return new File(cachePath + File.separator+uniqueName);
    }

    /**
     * 获取该目录可用空间大小
     *
     * @param path 路径
     */
    private long getUsableSpace(File path){
       // if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.GINGERBREAD){
       //     return path.getUsableSpace();
       //  }
        final StatFs stats = new StatFs(path.getPath());
        return (long) stats.getBlockSize()*(long) stats.getAvailableBlocks();
    }

    /**
     * 返回磁盘缓存对象
     */
    public DiskLruCache getDiskLruCache() {
        return mDiskLruCache;
    }

    /**
     * 当前是否已经初始化了
     */
    public boolean ismIsDiskLruCacheCreated() {
        return mIsDiskLruCacheCreated;
    }



}

  • BitmapUtils:图片处理
/**
 * ================================================
 * 作    者:SharkZ
 * 邮    箱:229153959@qq.com
 * 创建日期:2020/3/14  13:48
 * 描    述
 * 修订历史:
 * ================================================
 */
public class BitmapUtils  {

    /**
     * 计算图片的缩放值
     * @param options options.inJustDecodeBounds = true时传入的option
     * @param reqWidth 需要按需压缩的宽度
     * @param reqHeight 需要按需压缩的高度
     * @return 采样率inSampleSize
     */
    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {
           /* final int heightRatio = Math.round((float) height/ (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;*/
            final int halfHeight =height/2;
            final int halfWidth =width/2;
            while((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize)>=reqWidth){
                inSampleSize*=2;
            }
        }
        return inSampleSize;
    }

    /**
     * 根据路径获得图片并压缩,返回bitmap用于显示
     * @param filePath file的地址
     * @param reqWidth 需求宽度
     * @param reqHeight 需求高度
     * @return bitmap
     */
    public static Bitmap getSmallBitmap(String filePath, int reqWidth, int reqHeight) {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filePath, options);
        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        //不需要缩放
        if(options.inSampleSize<=1){
            return BitmapFactory.decodeFile(filePath);
        }
        return BitmapFactory.decodeFile(filePath, options);
    }

    /**
     * 根据输入流获取图片并压缩,返回bitmap用于显示
     * @param reqWidth 需求宽度
     * @param reqHeight 需求高度
     * @return bitmap
     */
    public static Bitmap getSmallBitmap(FileDescriptor fileDescriptor, int reqWidth, int reqHeight){
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds =true;
        BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        options.inJustDecodeBounds = false;
        //不需要缩放
        if(options.inSampleSize<=1){
            return BitmapFactory.decodeFileDescriptor(fileDescriptor);
        }
        //inSampleSize!=1进行缩放
        return BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
    }


    /**
     * 把bitmap转换成String
     * @param filePath 图片的地址
     * @param reqWidth 需求宽度
     * @param reqHeight 需求高度
     * @return
     */
    public static String bitmapToString(String filePath, int reqWidth, int reqHeight) {

        Bitmap bm = getSmallBitmap(filePath, reqWidth, reqHeight);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bm.compress(Bitmap.CompressFormat.JPEG, 40, baos);
        byte[] b = baos.toByteArray();
        return Base64.encodeToString(b, Base64.DEFAULT);
    }
    //压缩图片质量,一般用于保存图片到本地或者上传图片到服务器时对图片质量进行压缩

    /**
     *
     * @param outputStream 压缩后bitmap写入的输入流
     * @param bitmap 需要压缩的bitmap
     * @param rate 质量率,传入30,则压缩率为70%
     */
    public static void saveBitmapToOutputStream(OutputStream outputStream, Bitmap bitmap, int rate){
        //使用此流读取
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        //第二个参数影响的是图片的质量,但是图片的宽度与高度是不会受影响滴
        bitmap.compress(Bitmap.CompressFormat.JPEG,rate,baos);
        //这个函数能够设定图片的宽度与高度
        //Bitmap map = Bitmap.createScaledBitmap(bitmap, 400, 400, true);
        //把数据转为为字节数组
        byte[] byteArray = baos.toByteArray();
        try {
            outputStream.write(byteArray);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(outputStream!=null){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

    }

}

  • ImageThreadPoolExecutor:图片加载线程池
/**
 * ================================================
 * 作    者:SharkZ
 * 邮    箱:229153959@qq.com
 * 创建日期:2020/3/14  13:56
 * 描    述 自定义的线程池,用于执行Runnable任务
 * 修订历史:
 * ================================================
 */
public class ImageThreadPoolExecutor extends ThreadPoolExecutor {

    private static ImageThreadPoolExecutor imageThreadPoolExecutor;
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();    // cpu 核心数
    private static final int CORE_POOL_SIZE = CPU_COUNT*4 +1;                           // 核心线程数量
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT*5 +1;                        // 最大线程数量
    private static final long KEEP_ALIVE =10L;                                          // 空闲存活时间


    // =============================================================================================


    /**
     * 获取线程池单列
     */
    public static synchronized ImageThreadPoolExecutor getInstance(){
        if(imageThreadPoolExecutor==null){
            imageThreadPoolExecutor = new ImageThreadPoolExecutor(CORE_POOL_SIZE,MAXIMUM_POOL_SIZE,KEEP_ALIVE, TimeUnit.SECONDS,new LinkedBlockingDeque<Runnable>(),sThreadFactory);
        }
        return imageThreadPoolExecutor;
    }


    // =============================================================================================


    /**
     * 用于给线程池创建线程的线程工厂类
     */
    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r,"EasyImageLoader#" + mCount.getAndIncrement());
        }
    };

    /**
     *
     * @param corePoolSize
     * @param maximumPoolSize
     * @param keepAliveTime
     * @param unit
     * @param workQueue
     * @param threadFactory
     */
    private  ImageThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
                                     TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
    }

}

  • LoadBitmapTask:一个独立的加载任务
/**
 * ================================================
 * 作    者:SharkZ
 * 邮    箱:229153959@qq.com
 * 创建日期:2020/3/14  14:25
 * 描    述
 * 修订历史:
 * ================================================
 */
public class LoadBitmapTask implements Runnable {

    private static final int MESSAGE_POST_RESULT = 1;
    private boolean mIsDiskLruCacheCreated = false;
    private String url;
    private int reqWidth;
    private int reqHeight;
    private Handler mMainHandler;
    private ImageView imageView;
    private BitmapCallback callback;


    // =============================================================================================


    /**
     * 用于Handler处理的构造函数
     *
     * @param handler     处理器
     * @param imageView   显示图片的控件
     * @param url         图片地址
     * @param reqWidth    图片宽度
     * @param reqHeight   图片高度
     */
    public LoadBitmapTask( Handler handler, ImageView imageView, String url, int reqWidth, int reqHeight) {
        this.mMainHandler =handler;
        this.url=url;
        this.reqHeight =reqHeight;
        this.reqWidth =reqWidth;
        this.imageView =imageView;
    }

    /**
     * 用于回调bitmap的重载构造函数
     *
     * @param callback   回调
     * @param url        图片地址
     * @param reqWidth   图片宽度
     * @param reqHeight  图片高度
     */
    public LoadBitmapTask(BitmapCallback callback, String url, int reqWidth, int reqHeight) {
        this.callback =callback;
        this.url=url;
        this.reqHeight =reqHeight;
        this.reqWidth =reqWidth;
    }


    // =============================================================================================


    @Override
    public void run() {
        // 从本地或者网络获取bitmap
        final Bitmap bitmap =loadBitmap(url, reqWidth, reqHeight);
        // 直接显示图片到控件
        if(mMainHandler!=null){
            TaskResult loaderResult = new TaskResult(imageView,url,bitmap);
            mMainHandler.obtainMessage(MESSAGE_POST_RESULT,loaderResult).sendToTarget();
        }
        // bitmap回调
        if(callback !=null){
            Handler handler = new Handler(Looper.getMainLooper());
            handler.post(new Runnable() {
                @Override
                public void run() {
                    callback.onResponse(bitmap);
                }
            });
        }
    }


    // =============================================================================================


    /**
     * 从本地或者网络去获取bitmap
     *
     * @param url url
     * @param reqWidth 需求宽度
     * @param reqHeight 需求高度
     * @return bitmap
     */
    private Bitmap loadBitmap(String url, int reqWidth, int reqHeight){
        Bitmap bitmap = null;
        // 从磁盘里面加载
        bitmap=ImageDiskLruCache.getInstance().loadBitmap(url, reqWidth, reqHeight);

        //添加到内存缓存中
        if (bitmap!=null){
            ImageLruCache.getInstance().addBitmapToMemoryCache(MD5Utils.hashKeyFromUrl(url), bitmap);
        }

        // 保险一下
        if(bitmap==null &&!mIsDiskLruCacheCreated){
            // Log.i(TAG, "sd卡满了,直接从网络获取");
            //如果sd卡已满,无法使用本地缓存,则通过网络下载bitmap,一般不会调用这一步
            bitmap =NetRequest.downloadBitmapFromUrl(url);
        }
        return bitmap;
    }
}

  • NetRequest:网络请求工具类
/**
 * ================================================
 * 作    者:SharkZ
 * 邮    箱:229153959@qq.com
 * 创建日期:2020/3/14  14:47
 * 描    述 图片的网络请求
 * 修订历史:
 * ================================================
 */
public class NetRequest {


    /**
     * 处理器
     */
    public static Handler handler = new Handler(Looper.getMainLooper());

    /**
     * 通过url下载图片,未缩放图片宽高
     *
     * @param urlString 图一片地址
     * @return bitmap
     */
    public static Bitmap downloadBitmapFromUrl(String urlString){
        Bitmap bitmap =null;
        HttpURLConnection urlConnection =null;
        BufferedInputStream in =null;
        try {
            final URL url = new URL(urlString);
            urlConnection = (HttpURLConnection) url.openConnection();
            in = new BufferedInputStream(urlConnection.getInputStream(),8*1024);
            bitmap = BitmapFactory.decodeStream(in);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(in!=null){
                    in.close();
                }
                if(urlConnection!=null){
                    urlConnection.disconnect();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return bitmap;
    }


    /**
     * 通过url下载图片获取字节流写入输出流中
     *
     * @param imageUrl         图片地址
     * @param outputStream     输出流
     * @return
     */
    public static boolean downloadUrlToStream(String imageUrl, OutputStream outputStream){
        HttpURLConnection urlConnection =null;
        BufferedInputStream in =null;
        BufferedOutputStream out =null;
        try {
            final URL url = new URL(imageUrl);
            urlConnection =(HttpURLConnection) url.openConnection();
            in =new BufferedInputStream(urlConnection.getInputStream(),8*1024);
            out =new BufferedOutputStream(outputStream,8*1024);
            int b;
            while ((b =in.read()) !=-1){
                out.write(b);
            }
            //写入成功返回true
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(urlConnection!=null){
                urlConnection.disconnect();
            }
            try {
                if(in!=null){
                    in.close();
                }
                if(out!=null){
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }


    public static <T> void getRequest(final String httpUrl, final Class<T> type, final BeanCallback<T> callBack){
        getRequest( httpUrl, new CallBack() {
            @Override
            public void onSuccess(String response) {
                Log.i("TAG",response);
                callBack.onSuccess(new Gson().fromJson(response,type));
            }

            @Override
            public void onError(Exception exception, String errorInfo) {
                callBack.onError(exception,errorInfo);
            }
        });
    }

    public static void getRequest(final String httpUrl, final CallBack callBack){
        new Thread(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection httpUrlConnection=null;
                try {
                    URL url = new URL(httpUrl);
                    httpUrlConnection =(HttpURLConnection)url.openConnection();
                    httpUrlConnection.setRequestMethod("GET");
                    //设置是否允许输入,默认都是true
                    // httpUrlConnection.setDoInput(true);
                    //设置是否允许输出,默认是false,post的时候需要改为true
                    //httpUrlConnection.setDoOutput(true);
                    InputStream inputStream =httpUrlConnection.getInputStream();
                    final String response =getStringFromInputStream(inputStream);
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            callBack.onSuccess(response);
                        }
                    });
                } catch (final MalformedURLException e) {
                    e.printStackTrace();
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            callBack.onError(e, "error");
                        }
                    });

                } catch (final IOException e) {
                    e.printStackTrace();
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            callBack.onError(e, "error");
                        }
                    });

                }
                finally {
                    if(httpUrlConnection!=null)
                        httpUrlConnection.disconnect();
                }
            }
        }).start();

    }


    public static <T> void postRequest(final String httpUrl, final HashMap<String, String> params, final Class<T> type, final BeanCallback<T> callBack){
        postRequest(httpUrl, params, new CallBack() {
            @Override
            public void onSuccess(String response) {
                callBack.onSuccess(new Gson().fromJson(response,type));
            }

            @Override
            public void onError(Exception exception, String errorInfo) {
                callBack.onError(exception,errorInfo);
            }
        });

    }

    public  static void postRequest(final String httpUrl, final HashMap<String, String> params, final CallBack callBack){
        new Thread(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection httpUrlConnection = null;
                PrintWriter printWriter;
                String stringParams=praseParams(params);

                try {
                    URL url = new URL(httpUrl);
                    httpUrlConnection = (HttpURLConnection)url.openConnection();
                    httpUrlConnection.setRequestMethod("POST");
                    httpUrlConnection.setDoOutput(true);
                    //获取UrlConnection对象对应的输入流
                    printWriter = new PrintWriter(httpUrlConnection.getOutputStream());
                    //发送请求参数到请求正文
                    printWriter.write(stringParams);
                    printWriter.flush();
                    //根据responseCode判断连接是否成功
                    if(httpUrlConnection.getResponseCode()!=200){
                        Log.i("response", "postRequest onError");
                    }else{
                        Log.i("response","postRequest success");
                    }
                    final String response=getStringFromInputStream(httpUrlConnection.getInputStream());
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            callBack.onSuccess(response);
                        }
                    });



                } catch (final MalformedURLException e) {
                    e.printStackTrace();
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            callBack.onError(e, "error");
                        }
                    });

                } catch (final IOException e) {
                    e.printStackTrace();
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            callBack.onError(e, "error");
                        }
                    });

                }
                finally {
                    httpUrlConnection.disconnect();
                }
            }
        }).start();

    }

    /**
     * 将输入字节流转化为String
     *
     * @param inputStream
     * @return
     */
    public static String getStringFromInputStream(InputStream inputStream){
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        StringBuffer buffer = new StringBuffer();
        String temp;
        try {
            while ((temp =reader.readLine())!=null){
                buffer.append(temp);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {

            try {
                if(reader!=null)
                    reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return  buffer.toString();
    }
    public static String praseParams(Map<String, String> params){
        StringBuffer postParams=new StringBuffer();
        //组织请求参数
        Iterator iterable = params.entrySet().iterator();
        while(iterable.hasNext()){
            Map.Entry entry =(Map.Entry)iterable.next();
            postParams.append(entry.getKey());
            postParams.append("=");
            postParams.append(entry.getValue());
            postParams.append("&");

        }
        if(postParams.length()>0){
            postParams.deleteCharAt(postParams.length()-1);
            Log.i("response", postParams.toString());
        }
        return  postParams.toString();
    }


    public interface CallBack{
        public void onSuccess(String response);
        public void onError(Exception exception, String errorInfo);
    }

    public interface BeanCallback<T>{
        public void onSuccess(T response);
        public void onError(Exception exception, String errorInfo);
    }
}

  • TaskHandler:任务调度
/**
 * ================================================
 * 作    者:SharkZ
 * 邮    箱:229153959@qq.com
 * 创建日期:2020/3/14  14:04
 * 描    述 消息处理 注意这里使用的是主线程的 Looper
 * 修订历史:
 * ================================================
 */
public class TaskHandler extends Handler {

    private static TaskHandler taskHandler;

    public static TaskHandler getInstance(){
        if (taskHandler==null){
            synchronized (TaskHandler.class){
                if (taskHandler==null){
                    taskHandler = new TaskHandler();
                }
            }
        }
        return taskHandler;
    }

    private TaskHandler() {
        super(Looper.getMainLooper());
    }


    // =============================================================================================


    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);

        //给imageView加载bitmap
        TaskResult result =(TaskResult) msg.obj;
        if (result==null){
            return;
        }

        // 显示图片的控件
        ImageView imageView =result.getImageView();
        if (imageView==null){
            return;
        }

        //判断是否数据错乱
        String url =(String)imageView.getTag();
        if (TextUtils.equals(url,result.getUrl())){
            if(result.getBitmap()!=null){
                imageView.setImageBitmap(result.getBitmap());
            }else {
                imageView.setImageResource(R.drawable.ic_error);
            }
        }else{
            // JUtils.Log("不是最新数据");
        }
    }

}

  • TaskResult:图片结果封装
/**
 * ================================================
 * 作    者:SharkZ
 * 邮    箱:229153959@qq.com
 * 创建日期:2020/3/14  14:09
 * 描    述 异步任务执行结果封装类
 * 修订历史:
 * ================================================
 */
public class TaskResult {

    private ImageView imageView;
    private String url;
    private Bitmap bitmap;

    public TaskResult(ImageView imageView, String url , Bitmap bitmap){
        this.imageView =imageView;
        this.url = url;
        this.bitmap =bitmap;
    }

    public ImageView getImageView() {
        return imageView;
    }

    public void setImageView(ImageView imageView) {
        this.imageView = imageView;
    }

    public String getUrl() {
        return url;
    }

    public void setUri(String url) {
        this.url = url;
    }

    public Bitmap getBitmap() {
        return bitmap;
    }

    public void setBitmap(Bitmap bitmap) {
        this.bitmap = bitmap;
    }
}

  • BitmapCallback:图片加载回调
/**
 * ================================================
 * 作    者:SharkZ
 * 邮    箱:229153959@qq.com
 * 创建日期:2020/3/14  14:26
 * 描    述 Bitmap 处理完成之后的回调
 * 修订历史:
 * ================================================
 */
public interface BitmapCallback {
    void onResponse(Bitmap bitmap);
}

  • MD5Utils:MD5工具类
/**
 * ================================================
 * 作    者:SharkZ
 * 邮    箱:229153959@qq.com
 * 创建日期:2020/3/14  13:28
 * 描    述 MD5工具类
 * 修订历史:
 * ================================================
 */
public class MD5Utils {

    /**
     * 将url通过MD5转化为唯一的字符串,用于标识
     */
    public static String hashKeyFromUrl(String url){
        String cacheKey;
        try {
            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
            mDigest.update(url.getBytes());
            cacheKey = bytesToHexString(mDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            cacheKey = String.valueOf(url.hashCode());
        }
        return cacheKey;
    }

    /**
     *
     * @param bytes
     * @return
     */
    private static String bytesToHexString(byte[] bytes){
        StringBuilder sb = new StringBuilder();
        for(int i =0;i<bytes.length;i++){
            String hex = Integer.toHexString(0xFF & bytes[i]);
            if(hex.length() == 1){
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString();
    }

}

  • ImageLoader 图片加载封装
/**
 * ================================================
 * 作    者:SharkZ
 * 邮    箱:229153959@qq.com
 * 创建日期:2020/3/14  13:17
 * 描    述 图片加载器   https://github.com/wenhuaijun/EasyImageLoader
 * 修订历史:
 * ================================================
 */
public class ImageLoader {

    private static ImageLoader instance = null;                           // 单列
    private static Context context;                                     // 全局的上下文

    private static ImageLruCache imageLrucache;                         // 图片内存缓存类
    private static ImageDiskLruCache imageDiskLrucache;                 // 图片磁盘缓存类
    private static ThreadPoolExecutor THREAD_POOL_EXECUTOR = null;      // 创建一个静态的线程池对象
    private static TaskHandler mMainHandler;                            // 创建一个更新ImageView的UI的Handler


    // =============================================================================================


    /**
     * 这个需要在 Application 中初始化
     *
     * @param context 上下文
     */
    public static void initContext(Context context) {
        ImageLoader.context = context;
    }

    /**
     * SDK 初始化 传入的上下文
     *
     * @return
     */
    public static Context getContext() {
        if (ImageLoader.context == null) {
            throw new NullPointerException("没有初始化你就调用 不空指针才怪 --> ImageLoader.initContext()");
        }
        return ImageLoader.context;
    }

    /**
     * 获取单列
     *
     * @return
     */
    public static ImageLoader getInstance() {
        if (instance == null) {
            synchronized (ImageLoader.class) {
                if (instance == null) {
                    instance = new ImageLoader();
                }
            }
        }
        return instance;
    }

    /**
     * 私有的构造方法,防止在外部实例化该ImageLoader 初始化的时候准备原料
     */
    private ImageLoader() {
        THREAD_POOL_EXECUTOR = ImageThreadPoolExecutor.getInstance();
        imageLrucache = ImageLruCache.getInstance();
        imageDiskLrucache = ImageDiskLruCache.getInstance();
        mMainHandler = TaskHandler.getInstance();
    }


    // =============================================================================================


    /**
     * 显示图片
     *
     * @param url       网络地址
     * @param imageView 显示图片的控件
     */
    public void displayBitmap(final String url, final ImageView imageView) {
        displayBitmap(url, imageView, 0, 0);
    }

    /**
     * 显示图片
     *
     * @param url       网络地址
     * @param imageView 显示图片的控件
     * @param reqWidth  宽度
     * @param reqHeight 高度
     */
    public void displayBitmap(final String url, final ImageView imageView, final int reqWidth, final int reqHeight) {
        // 设置加载loadding图片
        imageView.setImageResource(R.drawable.ic_loading);
        // 防止加载图片的时候数据错乱
        // imageView.setTag(TAG_KEY_URI, uri);
        imageView.setTag(url);

        // 从内存缓存中获取bitmap
        Bitmap bitmap = imageLrucache.loadBitmapFromMemCache(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }

        // 从磁盘中加载缓存
        bitmap = imageDiskLrucache.loadBitmapFromDiskCache(url, 0, 0);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }

        // 当前图片没有缓存
        LoadBitmapTask loadBitmapTask = new LoadBitmapTask(mMainHandler, imageView, url, reqWidth, reqHeight);
        //使用线程池去执行Runnable对象
        THREAD_POOL_EXECUTOR.execute(loadBitmapTask);
    }


    /**
     * @param url
     * @param callback
     */
    public void getBitmap(final String url, BitmapCallback callback) {
        getBitmap(url, callback, 0, 0);
    }

    /**
     * @param url       图片链接
     * @param callback  bitmap回调接口
     * @param reqWidth  需求宽度
     * @param reqHeight 需求高度
     */
    public void getBitmap(final String url, final BitmapCallback callback, int reqWidth, int reqHeight) {
        //从内存缓存中获取bitmap
        final Bitmap bitmap = imageLrucache.loadBitmapFromMemCache(url);
        if (bitmap != null) {
            Handler handler = new Handler(Looper.getMainLooper());
            handler.post(new Runnable() {
                @Override
                public void run() {
                    callback.onResponse(bitmap);
                }
            });
            return;
        }
        LoadBitmapTask loadBitmapTask = new LoadBitmapTask(callback, url, reqWidth, reqHeight);
        //使用线程池去执行Runnable对象
        THREAD_POOL_EXECUTOR.execute(loadBitmapTask);
    }
}

Demo

相关文章

网友评论

      本文标题:手写一个图片加载框架

      本文链接:https://www.haomeiwen.com/subject/osmzjktx.html