zoukankan      html  css  js  c++  java
  • Diycode开源项目 磁盘图片缓存+自定义webViewClient+图片点击js方法

    1.磁盘图片缓存器DiskImageCache

    1.1.这个类很多情况都可能用的到,耦合性很低,所以分开讲。

      源代码:

    /*
     * Copyright 2017 GcsSloop
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *    http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     * Last modified 2017-03-12 00:56:52
     *
     * GitHub:  https://github.com/GcsSloop
     * Website: http://www.gcssloop.com
     * Weibo:   http://weibo.com/GcsSloop
     */
    
    package com.gcssloop.diycode.base.webview;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.os.Environment;
    import android.os.StatFs;
    import android.support.annotation.NonNull;
    import android.util.Log;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.Arrays;
    import java.util.Comparator;
    
    public class DiskImageCache {
        private static final String CACHE_SUFFIX = ".cache";
    
        private static final int MB = 1024 * 1024;
        private static final int CACHE_SIZE = 50;   // 缓存占用空间大小
        private static final int FREE_SD_SPACE_NEEDED_TO_CACHE = 10;    // 为 SD 卡保留多少空间
    
        private File cacheDir;
    
        public DiskImageCache(Context context) {
            cacheDir = getDiskCacheDir(context, "web-image");
            // 整理缓存
            organizeCache(cacheDir);
        }
    
        /**
         * 从缓存中获取图片
         **/
        public Bitmap getBitmap(final String key) {
            final String path = getCachePath(key);
            File file = new File(path);
            if (file.exists()) {
                Bitmap bmp = BitmapFactory.decodeFile(path);
                if (bmp == null) {
                    file.delete();
                } else {
                    updateFileTime(path);
                    return bmp;
                }
            }
            return null;
        }
    
        /**
         * 将图片存入文件缓存
         **/
        public void saveBitmap(String key, Bitmap bm) {
            if (bm == null) {
                return;
            }
            //判断sdcard上的空间
            if (FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
                return;     //SD空间不足
            }
            File file = new File(getCachePath(key));
            try {
                file.createNewFile();
                OutputStream outStream = new FileOutputStream(file);
                bm.compress(Bitmap.CompressFormat.PNG, 100, outStream);
                outStream.flush();
                outStream.close();
            } catch (FileNotFoundException e) {
                Log.w("ImageFileCache", "FileNotFoundException");
            } catch (IOException e) {
                Log.w("ImageFileCache", "IOException");
            }
        }
    
        /**
         * 保存 bytes 数据
         *
         * @param key   url
         * @param bytes bytes 数据
         */
        public void saveBytes(String key, byte[] bytes) {
            if (bytes == null) {
                return;
            }
            //判断sdcard上的空间
            if (FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
                return;     //SD空间不足
            }
            File file = new File(getCachePath(key));
            try {
                file.createNewFile();
                OutputStream outStream = new FileOutputStream(file);
                outStream.write(bytes);
                outStream.flush();
                outStream.close();
            } catch (FileNotFoundException e) {
                Log.w("ImageFileCache", "FileNotFoundException");
            } catch (IOException e) {
                Log.w("ImageFileCache", "IOException");
            }
        }
    
        /**
         * 获取一个本地缓存的输入流
         *
         * @param key url
         * @return FileInputStream
         */
        public FileInputStream getStream(String key) {
            File file = new File(getCachePath(key));
            if (!file.exists())
                return null;
            try {
                FileInputStream inputStream = new FileInputStream(file);
                return inputStream;
            } catch (FileNotFoundException e) {
                Log.e("getStream", "FileNotFoundException");
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * 获取本地缓存路径
         *
         * @param key url
         * @return 路径
         */
        public String getDiskPath(String key) {
            File file = new File(getCachePath(key));
            if (!file.exists())
                return null;
            return file.getAbsolutePath();
        }
    
        /**
         * 是否有缓存
         *
         * @param key url
         * @return 是否有缓存
         */
        public boolean hasCache(String key) {
            File file = new File(getCachePath(key));
            return file.exists();
        }
    
        /**
         * 计算存储目录下的文件大小,
         * 当文件总大小大于规定的CACHE_SIZE或者sdcard剩余空间小于FREE_SD_SPACE_NEEDED_TO_CACHE的规定
         * 那么删除40%最近没有被使用的文件
         */
        private boolean organizeCache(@NonNull File cacheDir) {
            File[] files = cacheDir.listFiles();
            if (files == null) {
                return true;
            }
            if (!Environment.getExternalStorageState().equals(
                    Environment.MEDIA_MOUNTED)) {
                return false;
            }
    
            int dirSize = 0;
            for (int i = 0; i < files.length; i++) {
                if (files[i].getName().contains(CACHE_SUFFIX)) {
                    dirSize += files[i].length();
                }
            }
    
            if (dirSize > CACHE_SIZE * MB || FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
                int removeFactor = (int) ((0.4 * files.length) + 1);
                Arrays.sort(files, new FileLastModifSort());
                for (int i = 0; i < removeFactor; i++) {
                    if (files[i].getName().contains(CACHE_SUFFIX)) {
                        files[i].delete();
                    }
                }
            }
    
            if (freeSpaceOnSd() <= CACHE_SIZE) {
                return false;
            }
    
            return true;
        }
    
        /**
         * 修改文件的最后修改时间
         **/
        public void updateFileTime(String path) {
            File file = new File(path);
            long newModifiedTime = System.currentTimeMillis();
            file.setLastModified(newModifiedTime);
        }
    
        /**
         * 计算sdcard上的剩余空间
         **/
        private int freeSpaceOnSd() {
            StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
            double sdFreeMB = ((double) stat.getAvailableBlocks() * (double) stat.getBlockSize()) / MB;
            return (int) sdFreeMB;
        }
    
        /**
         * 根据文件的最后修改时间进行排序
         */
        private class FileLastModifSort implements Comparator<File> {
            public int compare(File arg0, File arg1) {
                if (arg0.lastModified() > arg1.lastModified()) {
                    return 1;
                } else if (arg0.lastModified() == arg1.lastModified()) {
                    return 0;
                } else {
                    return -1;
                }
            }
        }
    
    
        /**
         * 获取缓存文件的绝对路径
         */
        private String getCachePath(String key) {
            return cacheDir.getAbsolutePath() + File.separator + convertKey(key);
        }
    
        /**
         * 获取磁盘缓存文件夹 优先获取外置磁盘
         *
         * @param context    上下文
         * @param uniqueName 自定义名字
         */
        public File getDiskCacheDir(Context context, String uniqueName) {
            String cachePath;
            if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
                    || !Environment.isExternalStorageRemovable()) {
                cachePath = context.getExternalCacheDir().getPath();
            } else {
                cachePath = context.getCacheDir().getPath();
            }
            File cacheDir = new File(cachePath + File.separator + uniqueName);
            if (!cacheDir.exists())
                cacheDir.mkdir();
            return cacheDir;
        }
    
        /**
         * 哈希编码
         */
        public String convertKey(String key) {
            String cacheKey;
            try {
                final MessageDigest mDigest = MessageDigest.getInstance("MD5");
                mDigest.update(key.getBytes());
                cacheKey = bytesToHexString(mDigest.digest());
            } catch (NoSuchAlgorithmException e) {
                cacheKey = String.valueOf(key.hashCode());
            }
            return cacheKey + CACHE_SUFFIX;
        }
    
        private 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();
        }
    
    }
    View Code

    1.2. 预览成员变量

      

      这里定义了后缀为.cache

      然后定义了数据单位

      然后定义了缓存占用空间最大为50M

      然后定义了为SD卡保留10M

      然后定义了文件缓存的路径

      

    1.3.磁盘图片缓存器的构造函数

      

      传入一个上下文后,先获取名字叫做“web-image”的缓存文件File。

      

      然后整理缓存。

      

      传入一个File参数,返回成功与否。

    1.4.如何从缓存中获取图片

      

      先获取缓存文件的绝对路径,返回一个String

      

      然后修改文件的最后修改时间

      

      file有一个属性lastModified可以记录这个时间

    1.5.如何将图片存入文件缓存文件

      

      参数为一个key,一个Bitmap数据。意思就是将这个图片存入一个可以利用这个key找的的一个文件中。

      这里利用了file的一个函数creaetNewFile,意思就是创建一个新的文件。

      图片的保存,是利用输入流输出流来保存的。

      比如这里bitmap.compress(Bitmap.CompressFormat.PNG,100,outStream)

      这个outStream就是输出流,然后outStream.flush(),outStream.close()来完成存储。

    1.6.如何保存bytes数据?

      

      这里用了一个方法来判断SD卡中还有多少空间。

      

      返回一个MB为单位的数据。记住就好。这里应该是调用了系统函数。

      保存bytes的方法和保存图片的方法基本一样。

    1.7.如何获取一个本地缓存的输入流?

      

      首先获取缓存文件的绝对路径。

      然后生成一个FileInputStream。

      最后返回即可。

    1.8.如何获取本地缓存路径?

      

      通过一个key,获取缓存文件的绝对路径。

      可以看到这个函数和getCachePath的区别了,getCachePath已经是其中的一个调用者,getDiskPath更加细节。

    1.9.根据文件的最后修改时间进行排序

      

      这是一个内部类,里面是一个比较函数,两个参数分别代表两个文件,即可得出谁最后修改的。

    1.10.获取缓存文件的绝对路径中调用了一个哈希编码的函数

      

      传入一个key,利用MessageDigest来MD5加密。

      将bytes数组转换为String

      

      然后将返回的字符串+后缀即得到了哈希编码

      然后加到返回函数getCachePath的结果的尾部。


    2.自定义web Client

    2.1.首先继承WebViewClient没的说

      

      放入一个上下文+一个磁盘图片缓存器

    2.2.实现关键的构造函数

      

      参数为上下文,外部的上下文传进来。

      新建一个图片磁盘缓存器。

    2.3.webView加载完成之际

      

      页面加载完成后回调函数中,给webView添加了一个监听器。

    2.4.加了什么监听器呢?

      

      因为是一个webView,给webView添加监听的方法就是写javascript函数。

      当然凭什么是javascript函数呢?加注解即可解决这个问题。

      这里使用webView.loadUrl来动态加载监听器

      ==>只要有img节点的地方,将链接提取出来,然后注入了图片点击事件。

    2.5.html链接打开方式

      webView中可能会有链接。

      下面自定义打开链接的方式,用手机自带的浏览器打开这个链接即可。

      

      如果你不想用手机自带的浏览器,而是想让它在本页面本WebView直接跳转,用下面的方法:

        view.loadUrl(url);

      

    2.6.加载资源的方式

      

      如果传入的字符串链接是图片就缓存,缓存方式

        Glide.with.load.asBitmap.into==>实现onResourceReady方法==>缓存器保存这个Bitmap。

      如果传入的字符串链接是gif就保存为bytes类型,缓存方式

        Glide.with.load.asGif.into==>实现onResourceReady方法==>缓冲器保存这个bytes。

    2.7.通知主程序webView处理的资源请求

       

      这里两个都写了。

      

    2.8.获取本地资源,看看有没有缓存

      

      上方拦截网络请求的原因:

        先判断本地缓存是否有缓存图片资源,再去决定是否加载。


    3.对应js方法而建立的一个监听器类

    3.1.因为在自定义webViewClient中有一个js方法

      

      所以对应应该有一个处理这个js方法的类。 

      这个类起一个引导作用,将webView和这个js函数发生关联,所以这个类的名字类似监听器。

      

    3.2.怎么关联的呢?

      这里看一下TopicContentActivity中的部分调用代码。

      

      这里调用了webView的一个方法:addJavascriptInterface(object,name)

      所以这个类就成了联系js方法的关键。

    3.3.具体看一下这个WebImageListener的成员变量

      

      这里有一个上下文。

      一个自定义BaseImageActivity。

      一个图片集合,用ArrayList<String>来保存。

    3.4.WebImageListener的构造函数

      

      这里将外部传进来的参数给到自己。

    3.5.收集图片,添加到集合中

      

       判断url如果是gif结尾的就不保存+如果图片不在之间的集合中也不保存

    3.6.图片被点击事件调用该方法

      

      将图片集合序列封装到intent中,然后跳转到图片详细页面。


    4.总结一下

    4.1.本篇博客讲述的是:如何自定义WebViewClient,如何建立一个磁盘图片缓存器,然后如何给webView中添加js

      方法,实现点击webView中的图片,获取到所有的图片,跳转到一个图片浏览页。

    4.2.对于建立一个磁盘图片缓存,这个可能和webView的点击事件没有半毛钱关系,但是真实项目中,肯定会有

      类似这样的功能需要去实现,这个是很科学的,因为既然浏览到了图片,那么下次用户很有可能再次点击图片

      所以最好的办法就是建立一个磁盘图片缓存器。

    4.3.这个磁盘图片缓存也就是通常需要实现的方法。将图片存入文件缓存,从缓存中获取图片,gif的话存放到bytes

      数组里面,获取一个本地缓存的输入流,获取本地缓存路径,整理缓存,修改文件的最后修改时间,计算sd卡

      上剩余空间,根据文件的最后修改时间进行排序,如何获取缓存文件的绝对路径,哈希编码的这些方法都是比

      较常用的。所以这个类可以当做通用类用。

    4.4.然后是自定义WebViewClient,注意添加一个磁盘图片缓存器到里面,然后实现一些复写方法。onPageFinished

      中来添加图片点击事件,就是添加js方法,采用webView.loadUrl(一些js代码)的方式。因为继承了

      WebViewClient,实现shouldOverrideUrlLoading==>html链接打开方式,实现onLoadResource==>

      加载资源的方式,注意将图片存入缓存。复写shouldInterceptRequest==>实现网络拦截,复写

      getWebResourceResponse==>判断是否缓存了。

    4.5.WebImageListener类来沟通webView的addJavascriptInterface,里面有两个参数,第一个参数是Object,就是

      这里定义的WebImageListener,第二个参数是一个字符串,“listener”,它将js函数名和这个Object对应起来,

      将它们联系起来了。因为js函数中要求实现两个函数,window.listener.collectImage和

      window.listener.onImageClicked,然后这两个函数在WebImageListener都有具体的实现,简直完美!

    既然选择了,便不顾风雨兼程。Just follow yourself.
  • 相关阅读:
    ORACLE 查看进程数,已执行任务数, 剩余任务数,删除指定任务
    ORACLE 收集统计整个用户数据
    解决Hystrix dashboard Turbine 一直 Loading…… 及其他坑
    利用 Maven 构造 Spring Cloud 微服务架构 模块使用 spring Boot构建
    AES加解密
    JAVA POI XSSFWorkbook导出扩展名为xlsx的Excel,附带weblogic 项目导出Excel文件错误的解决方案
    JAVA 文件的上传下载
    shell启停服务脚本模板
    JAVA 设计模式之 原型模式详解
    JAVA 设计模式之 工厂模式详解
  • 原文地址:https://www.cnblogs.com/Jason-Jan/p/7886400.html
Copyright © 2011-2022 走看看