zoukankan      html  css  js  c++  java
  • Android OkHttp与物理存储介质缓存:DiskLruCache(2)

    

    Android OkHttp与物理存储介质缓存:DiskLruCache(2)

    本文在附录文章8,9的基础之上,把Android OkHttp与DiskLruCache相结合,综合此两项技术,实现基于OkHttp的物理存储介质缓存DiskLruCache。
    用一个完整的例子加以说明。该例子的代码要实现这样的过程:代码启动后,要往一个ImageView里面加载一张网络图片,首先检查DiskLruCache是否已经存在该图片的缓存,如果存在,则直接复用缓存,如果不存在则使用OkHttp把图片异步从网络加载,当OkHttp异步加载网络图片成功后,要做两件事情:
    一, 毫无疑问,要把该图片设置到目标ImageView里面。代码启动后首先要检查本地的DiskLruCache物理存储介质上是否已经有特定图片的缓存,如果有,则直接复用,不再浪费网络资源重复加载。
    二,把该图片的数据写入DiskLruCache缓存中,为以后的缓存使用。此情况是当DiskLruCache不存在特定资源(本例是图片)缓存时候,要从网络加载。我使用OkHttp网络驱动加载,当OkHttp加载图片成功后,一方面要把图片设置到ImageView,另外一方面要把图片缓存到DiskLruCache以备后续使用。

    完整代码:

    package zhangphil.demo;
    
    import android.content.Context;
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.os.Environment;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.widget.ImageView;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    
    import okhttp3.Call;
    import okhttp3.OkHttpClient;
    import okhttp3.Request;
    import okhttp3.Response;
    import okhttp3.Callback;
    
    import com.jakewharton.disklrucache.DiskLruCache;
    
    public class MainActivity extends AppCompatActivity {
    
        private String TAG = "zhangphil_tag";
    
        private String UNIQUENAME = "zhangphil_cache";
    
        private DiskLruCache mDiskLruCache = null;
    
        //缓存大小
        private int DISK_CACHE_MAX_SIZE = 10 * 1024 * 1024;
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            //初始化DiskLruCache
            makeDiskLruCache();
    
            //在布局里面放一个ImageView,放网络请求后的图片
            final ImageView image = (ImageView) findViewById(R.id.imageView);
    
            //我的博客头像
            String image_url = "http://avatar.csdn.net/9/7/A/1_zhangphil.jpg";
    
            Bitmap bmp = readBitmapFromDiskLruCache(image_url);
    
            //首先检查DiskLruCache是否已经缓存了特定资源,如果有则直接复用。
            //如果没有则从网路加载。
            if (bmp != null) {
                image.setImageBitmap(bmp);
            } else {
                downloadBitmapFromNetwork(image, image_url);
            }
        }
    
        //从DiskLruCache中读取缓存
        private Bitmap readBitmapFromDiskLruCache(String url) {
            DiskLruCache.Snapshot snapShot = null;
            try {
                //把url转换成一个md5字符串,然后以这个md5字符串作为key
                String key = urlToKey(url);
    
                snapShot = mDiskLruCache.get(key);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            if (snapShot != null) {
                Log.d(TAG, "发现缓存:" + url);
                InputStream is = snapShot.getInputStream(0);
                Bitmap bitmap = BitmapFactory.decodeStream(is);
                Log.d(TAG, "从缓存中读取Bitmap.");
    
                return bitmap;
            } else
                return null;
        }
    
        //把byte字节写入缓存DiskLruCache
        private void writeToDiskLruCache(String url, byte[] buf) throws Exception {
            Log.d(TAG, url + " : 开始写入缓存...");
    
            //DiskLruCache缓存需要一个key,我先把url转换成md5字符串,
            //然后以md5字符串作为key键
            String key = urlToKey(url);
            DiskLruCache.Editor editor = mDiskLruCache.edit(key);
    
            OutputStream os = editor.newOutputStream(0);
            os.write(buf);
            os.flush();
            editor.commit();
    
            mDiskLruCache.flush();
    
            Log.d(TAG, url + " : 写入缓存完成.");
        }
    
        private void makeDiskLruCache() {
            try {
                File cacheDir = getDiskCacheDir(this, UNIQUENAME);
    
                if (!cacheDir.exists()) {
                    Log.d(TAG, "缓存目录不存在,创建之...");
                    cacheDir.mkdirs();
                } else
                    Log.d(TAG, "缓存目录已存在,不需创建.");
    
                //第二个参数我选取APP的版本code。DiskLruCache如果发现第二个参数version不同则销毁缓存
                //第三个参数为1,在写缓存的流时候,newOutputStream(0),0为索引,类似数组的下标
                mDiskLruCache = DiskLruCache.open(cacheDir, getVersionCode(this), 1, DISK_CACHE_MAX_SIZE);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private void downloadBitmapFromNetwork(final ImageView image, final String image_url) {
            Log.d(TAG, "从网络中加载图片资源 ...  @ " + image_url);
    
            //初始化OkHttpClient
            final OkHttpClient client = new OkHttpClient();
    
            //创建OkHttpClient针对某个url的数据请求
            Request request = new Request.Builder().url(image_url).build();
    
            Call call = client.newCall(request);
    
            //请求加入队列
            call.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    //此处处理请求失败的业务逻辑
                }
    
                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    //如果response响应成功则继续,否则返回
                    if (!response.isSuccessful())
                        return;
    
                    //我写的这个例子是请求一个图片
                    //response的body是图片的byte字节
                    byte[] bytes = response.body().bytes();
    
                    //已经获得图片数据,记到要写入硬盘缓存
                    //出于性能考虑,此处可以放到后台或者放到一个线程里面处理
                    //简单期间,我就在这儿直接写缓存了。
                    try {
                        writeToDiskLruCache(image_url, bytes);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
    
                    //把byte字节组装成图片
                    final Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
    
                    //回调是运行在非ui主线程,
                    //数据请求成功后,在主线程中更新
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            //网络图片请求成功,更新到主线程的ImageView
                            image.setImageBitmap(bmp);
                        }
                    });
                }
            });
        }
    
    
        /*
        *
        * 当SD卡存在或者SD卡不可被移除的时候,就调用getExternalCacheDir()方法来获取缓存路径,
        * 否则就调用getCacheDir()方法来获取缓存路径。
        * 前者获取到的就是 /sdcard/Android/data/<application package>/cache
        * 而后者获取到的是 /data/data/<application package>/cache 。
        *
        * */
        public File getDiskCacheDir(Context context, String uniqueName) {
            String cachePath = null;
            if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) {
                cachePath = context.getExternalCacheDir().getPath();
            } else {
                cachePath = context.getCacheDir().getPath();
            }
    
            File dir = new File(cachePath + File.separator + uniqueName);
            Log.d(TAG, "缓存目录:" + dir.getAbsolutePath());
    
            return dir;
        }
    
    
        //版本名
        public static String getVersionName(Context context) {
            return getPackageInfo(context).versionName;
        }
    
        //版本号
        public static int getVersionCode(Context context) {
            return getPackageInfo(context).versionCode;
        }
    
        private static PackageInfo getPackageInfo(Context context) {
            PackageInfo pi = null;
    
            try {
                PackageManager pm = context.getPackageManager();
                pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_CONFIGURATIONS);
    
                return pi;
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return pi;
        }
    
    
        public static String urlToKey(String url) {
            return getMD5(url);
        }
    
        /*
        * 传入一个字符串String msg,返回Java MD5加密后的16进制的字符串结果。
        * 结果形如:c0e84e870874dd37ed0d164c7986f03a
        */
        public static String getMD5(String msg) {
            MessageDigest md = null;
            try {
                md = MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
            md.reset();
            md.update(msg.getBytes());
            byte[] bytes = md.digest();
    
            String result = "";
            for (byte b : bytes) {
                // byte转换成16进制
                result += String.format("%02x", b);
            }
    
            return result;
        }
    }


    涉及到网络和读写存储,不要忘记加权限:

     <!-- SDCard中创建与删除文件权限 -->
        <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
        <!-- 向SDCard写入数据权限 -->
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    
        <uses-permission android:name="android.permission.INTERNET"></uses-permission>
    


    附录文章:
    1,《Android第三方异步网路加载库AsyncHttpClient内部实现缓存策略了吗?》链接:http://blog.csdn.net/zhangphil/article/details/48595817 
    2,《Android图片加载与缓存开源框架:Android Glide》链接:http://blog.csdn.net/zhangphil/article/details/45535693
    3,《Android获取App版本号和版本名》链接:http://blog.csdn.net/zhangphil/article/details/43795099
    4,《基于Java LinkedList,实现Android大数据缓存策略》链接:http://blog.csdn.net/zhangphil/article/details/44116885
    5,《使用新式LruCache取代SoftReference缓存图片,Android异步加载图片》链接:http://blog.csdn.net/zhangphil/article/details/43667415
    6,《使用Android新式LruCache缓存图片,基于线程池异步加载图片》链接:http://blog.csdn.net/zhangphil/article/details/44082287
    7,《Java MD5(字符串)》链接:http://blog.csdn.net/zhangphil/article/details/44152077
    8,《Android OkHttp(1)》链接:http://blog.csdn.net/zhangphil/article/details/51861503
    9,《Android二级缓存之物理存储介质上的缓存DiskLruCache》链接:http://blog.csdn.net/zhangphil/article/details/51888974

  • 相关阅读:
    技术汇总:第四章:使用Easyui做三级下拉列表
    全文检索工具elasticsearch:第四章:开发电商的搜索列表功能
    全文检索工具elasticsearch:第三章: Java程序中的应用
    技术汇总:第三章:拖拉验证
    分布式部署:第三章:Docker、MySQL、tomcat、Nginx、Redis部署
    分布式部署:第二章:MyCat和Nginx部署
    分布式部署:第一章:zookeeper集群和solrcloud及redisCluster集群搭建
    技术汇总:第二章:JUC
    大型电商网站:第四章:业务功能与数据结构分析
    大型电商网站:第二章:项目开发介绍
  • 原文地址:https://www.cnblogs.com/hehehaha/p/6147282.html
Copyright © 2011-2022 走看看