手把手教你封裝自己的圖片快取工具-ImageLoader

朱少1992發表於2017-12-26

最近一直在學習男神+大神任玉剛老師的Android開發藝術探索,我個人非常推薦這本書,寫的真tm好,不好意思,有點激動。大家有空可以去看看。看完總覺得要寫點什麼吧,就將其中Bitmap載入和Cache的內容分享給還沒有來得及看過的朋友們。

快取策略在Android中有著廣泛的。。。算了,先輕鬆一下,來張美女鎮樓

老司機要發車了,來不及解釋了,大家快上車啊。

手把手教你封裝自己的圖片快取工具-ImageLoader
言歸正傳(畫風一轉)

快取策略在Android中有著廣泛的使用場景,尤其在圖片載入這個場景下,快取策略變得更為重要。今天就帶大家自己動手設計圖片快取工具。

提到快取,不得不提目前常用的一種快取演算法-LRU(Least Recently Used),LRU是近期最少使用演算法。採用這種演算法思想的快取有兩種:LruCache和DiskLruCache,LruCache用於實現記憶體快取,而DiskLruCache則充當了儲存裝置快取,通過兩者完美結合,就可以輕鬆實現我們的ImageLoader。

下面我們就正式開始了。。。

手把手教你封裝自己的圖片快取工具-ImageLoader

1.首先我們來看下兩個核心的東西:LruCache和DiskLruCache。

1.1LruCache

LruCache的實現比較簡單,大家可以參考它的原始碼。LruCache主要用來實現記憶體快取,我們拿圖片快取來舉例子,反手就是一段程式碼:

int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getRowBytes() * value.getHeight() / 1024;
            }
        };
複製程式碼

可以看出,我們只需要提供快取的總容量大小並重寫sizeOf方法即可。

除了LruCache的建立以外,還有快取的獲取和新增,這也很簡單,從LruCache中獲取一個快取物件,如下所示。

mMemoryCache.get(key)
複製程式碼

向LruCache中新增一個快取物件,如下所示。

mMemoryCache.put(key,bitmap)
複製程式碼

從Android3.1開始,LruCache就已經是Android原始碼的一部分了。

1.2DiskLruCache

DiskCahe用於實現儲存裝置快取,已經得到了Android官方的推薦,但不屬於SDK的一部分,我們要匯入引用一下:

compile 'com.jakewharton:disklrucache:2.0.2'

1.2.1先來看下DiskLruCache的建立

DiskLruCache提供open方法用於建立自身,如下所示。

pulic static DiskLruCache open(File directory,int appVersion,int valueCount,long maxSize)
複製程式碼

這裡有四個引數,稍微解釋下,有興趣的朋友,下去可以深入瞭解下:

directory:磁碟快取的儲存路徑。

appVersion:版本號,一般設為1即可。

valueCount:單個節點所對應的資料的個數,一般也設為1即可。

maxSize:快取的總大小。

整個建立過程,反手又是一段程式碼:

private static final int DISK_CACHE_SIZE = 50*1024*1024;
File diskCacheDir = getDiskCacheDir(mContext, "bitmap");
        if (!diskCacheDir.exists()) {
            diskCacheDir.mkdirs();
        }

if (getUsableSpace(diskCacheDir) > DISK_CACHE_SIZE) {
            try {
                mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, DISK_CACHE_SIZE);
                mIsDiskLruCacheCreated = true;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
複製程式碼

1.2.2DiskLruCache的快取新增

String key = hashKeyFormUrl(url);
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if(editor!=null){
    OutputStream output Stream = editor.newOutputStream(DISK_CACHE_INDEX);
}
複製程式碼

這裡的hashKeyFormUrl()方法主要是將url轉成key,之所以這麼做,是因為url中很可能有特殊字元,會影響url在Android中的直接使用。

有了檔案輸出流,接下來就是講網路下載的圖片時的檔案流寫入到檔案系統上了:

private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
        HttpURLConnection urlConnection=null;
        BufferedOutputStream out = null;
        BufferedInputStream in = null;
        try {
            URL url = new URL(urlString);
            urlConnection = (HttpURLConnection) url.openConnection();
            in = new BufferedInputStream(urlConnection.getInputStream(),IO_BUFFER_SIZE);
            out = new BufferedOutputStream(outputStream,IO_BUFFER_SIZE);
            int b;
            while ((b=in.read())!=-1){
                out.write(b);
            }
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(urlConnection!=null){
                urlConnection.disconnect();
            }
            MyUtils.close(out);
            MyUtils.close(in);
        }
        return false;
    }
複製程式碼

還有不要忘記了,editor要提交寫入操作。

editor.commit();
複製程式碼

1.2.3DiskLruCache快取的查詢

private Bitmap loadBitmapFromDiskCache(String url, int reqWidth, int reqHeight)
    throws IOException{
        if (Looper.myLooper() == Looper.getMainLooper()) {
            throw new RuntimeException("load bitmap from UI Thread,it's not recommended");
        }
        if(mDiskLruCache ==null){
        return null;
        }
        Bitmap bitmap = null;
        String key = hashKeyFormUrl(url);
        DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
        if(snapshot!=null){
            FileInputStream fileInputStream = (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX);
            FileDescriptor fileDescriptor = fileInputStream.getFD();
            bitmap = mImageResizer.decodeSampleFromFileDescriptor(fileDescriptor,reqWidth,reqHeight);
            if(bitmap !=null){
                addBitmapToMemoryCache(key,bitmap);
            }
        }
        return bitmap;
    }
複製程式碼

程式碼的意思應該好理解,將url轉成key,通過get方法得到一個Snapshot物件,通過這個物件即可得到快取的輸入流,有了輸入流,還怕拿不到bitmap物件嗎。

好了,介紹完兩個核心的知識點,喝口雪碧壓壓驚。

接下來就是重頭戲了,就是ImageLoader的實現。

手把手教你封裝自己的圖片快取工具-ImageLoader
2.ImageLoader的實現

要實現整個完整的ImageLoader功能,要怎麼做呢?先捋下思想,應該具備以下幾個功能:

-圖片的同步載入

-圖片的非同步載入

-圖片壓縮

-記憶體快取

-磁碟快取

-網路拉取

接下來,我們一步一步的實現。

手把手教你封裝自己的圖片快取工具-ImageLoader

2.1圖片壓縮功能的實現

將壓縮圖片功能抽出來單獨形成一個類,就叫ImageResizer吧,看下實現:

public class ImageResizer {
    private static final String TAG = "ImageResizer";
    public Bitmap decodeSampleFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res,resId,options);
    }
    public Bitmap decodeSampleFromFileDescriptor(FileDescriptor fd, int reqWidth, int reqHeight) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFileDescriptor(fd, null, options);
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFileDescriptor(fd, null, options);
    }
    private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        if (reqWidth == 0 || reqHeight == 0) {
            return 1;
        }
        int width = options.outWidth;
        int height = options.outHeight;
        int inSampleSize = 1;
        if (width < reqWidth || height < reqHeight) {
            return inSampleSize;
        }
        inSampleSize *= 2;
        while (width /inSampleSize>=reqWidth&&height/inSampleSize>=reqHeight){
            inSampleSize *=2;
        }
        Log.e(TAG, "inSampleSize: "+inSampleSize);
       return inSampleSize;
    }
}
複製程式碼

2.2記憶體快取和磁碟快取的實現

之前介紹過了,這裡放下具體實現:

初始化:

public ImageLoader(Context context) {
        mImageResizer = new ImageResizer();
        mContext = context;
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        int cacheSize = maxMemory / 8;
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getRowBytes() * value.getHeight() / 1024;
            }
        };
        File diskCacheDir = getDiskCacheDir(mContext, "bitmap");
        if (!diskCacheDir.exists()) {
            diskCacheDir.mkdirs();
        }
        if (getUsableSpace(diskCacheDir) > DISK_CACHE_SIZE) {
            try {
                mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, DISK_CACHE_SIZE);
                mIsDiskLruCacheCreated = true;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
複製程式碼

記憶體快取新增和獲取

private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        Log.e(TAG, "addBitmapToMemoryCache: ");
        if (getBitmapFromMemoryCache(key) == null) {
            mMemoryCache.put(key, bitmap);
        }
    }
private Bitmap getBitmapFromMemoryCache(String key) {
        return mMemoryCache.get(key);
    }
複製程式碼

磁碟快取新增和獲取

private Bitmap loadBitmapFromHttp(String url, int reqWidth, int reqHeight)
            throws IOException {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            throw new RuntimeException("can not visit netWork from UI Thread");
        }
        if (mDiskLruCache == null) {
            return null;
        }
        String key = hashKeyFormUrl(url);
        DiskLruCache.Editor editor = mDiskLruCache.edit(key);
        if(editor!=null){
            OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
            if(downloadUrlToStream(url,outputStream)){
                editor.commit();
            }else {
                editor.abort();
            }
            mDiskLruCache.flush();
        }
        return loadBitmapFromDiskCache(url,reqWidth,reqHeight);
    }
    private Bitmap loadBitmapFromDiskCache(String url, int reqWidth, int reqHeight)
    throws IOException{
        if (Looper.myLooper() == Looper.getMainLooper()) {
            throw new RuntimeException("load bitmap from UI Thread,it's not recommended");
        }
        if(mDiskLruCache ==null){
        return null;
        }
        Bitmap bitmap = null;
        String key = hashKeyFormUrl(url);
        DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
        if(snapshot!=null){
            FileInputStream fileInputStream = (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX);
            FileDescriptor fileDescriptor = fileInputStream.getFD();
            bitmap = mImageResizer.decodeSampleFromFileDescriptor(fileDescriptor,reqWidth,reqHeight);
            if(bitmap !=null){
                addBitmapToMemoryCache(key,bitmap);
            }
        }
        return bitmap;
    }
複製程式碼

2.3同步載入和非同步載入介面的設計

看下同步載入:

public Bitmap loadBitmap(String uri,int reqWidth,int reqHeight){
        Bitmap bitmap = loadBitmapFromMemCache(uri);
        if(bitmap!=null){
            Log.e(TAG, "loadBitmapFromMemCache,url "+uri);
            return bitmap;
        }
        try {
            bitmap = loadBitmapFromDiskCache(uri,reqWidth,reqHeight);
            if(bitmap!=null){
                Log.e(TAG, "loadBitmapFromDiskCache,url "+uri );
                return bitmap;
            }
            bitmap = loadBitmapFromHttp(uri,reqWidth,reqHeight);
            Log.e(TAG, "loadBitmapFromHttp,url "+uri);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if(bitmap==null&&!mIsDiskLruCacheCreated){
            Log.e(TAG, "encounter error,DiskLruCache is not created.");
            bitmap = downLoadBitmapFromUrl(uri);
        }
        return bitmap;
    }
複製程式碼

再看下非同步載入

public void bindBitmap(final String uri, final ImageView imageView, final int reqWidth, final int reqHeight){
      imageView.setTag(TAG_KEY_URI,uri);
      Bitmap bitmap = loadBitmapFromMemCache(uri);
      if(bitmap!=null){
          imageView.setImageBitmap(bitmap);
          return;
      }
      Runnable loadBitmapTask = new Runnable() {
          @Override
          public void run() {
              Bitmap bitmap = loadBitmap(uri,reqWidth,reqHeight);
              if(bitmap!=null){
                  LoaderResult result = new LoaderResult(imageView,uri,bitmap);
                  Message message = mMainHandler.obtainMessage(MESSAGE_POST_RESULT,result);
                  message.sendToTarget();
//                  sendToTarget();
              }
          }
      };
      THREAD_POOL_EXECUTOR.execute(loadBitmapTask);
    }
複製程式碼

仔細的同學發現了,這裡面好像用到了執行緒池和Handler,關於執行緒池的內容這裡就不扯了,大家自己去度娘。

這裡直接把擼好的程式碼放上來:

private Handler mMainHandler = new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(Message msg) {
           LoaderResult result = (LoaderResult) msg.obj;
            ImageView imageView = result.imageView;
            imageView.setImageBitmap(result.bitmap);
            String uri = (String) imageView.getTag(TAG_KEY_URI);
            if(uri.equals(result.uri)){
                imageView.setImageBitmap(result.bitmap);
            }else {
                Log.d(TAG, "set image bitmap,but url has changed,ignored!");
            }
        }
    };
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);
        @Override
        public Thread newThread(@NonNull Runnable r) {
            return new Thread(r,"ImageLoader#"+mCount.getAndIncrement());
        }
    };
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
            CORE_POOL_SIZE,MAXIMUM_POOL_SIZE,
            KEEP_ALIVE, TimeUnit.SECONDS
            ,new LinkedBlockingDeque<Runnable>(),sThreadFactory);
複製程式碼

哇哈哈哈哈哈哈哈哈。。。。。

手把手教你封裝自己的圖片快取工具-ImageLoader
終於要接近尾聲了,提取的不好,大家多見諒(有興趣的同學一定要去看看任玉剛老師的書)。

下面還是給下ImageLoader的完整程式碼。

手把手教你封裝自己的圖片快取工具-ImageLoader

public class ImageLoader {
    private static final String TAG = "ImageLoader";
    private Context mContext;
    private LruCache<String, Bitmap> mMemoryCache;
    private DiskLruCache mDiskLruCache;
    private Bitmap bitmap;
    private static final int DISK_CACHE_SIZE = 50*1024*1024;
    private boolean mIsDiskLruCacheCreated;
    private static final int DISK_CACHE_INDEX  = 0;
    private ImageResizer mImageResizer;
    private String cachePath;
    private static final int IO_BUFFER_SIZE = 8*1024;
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int TAG_KEY_URI = R.id.imageloader_uri;
    private static final int CORE_POOL_SIZE = CPU_COUNT+1;
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT*2+1;
    private static final long KEEP_ALIVE = 10L;
    private Handler mMainHandler = new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(Message msg) {
           LoaderResult result = (LoaderResult) msg.obj;
            ImageView imageView = result.imageView;
            imageView.setImageBitmap(result.bitmap);
            String uri = (String) imageView.getTag(TAG_KEY_URI);
            if(uri.equals(result.uri)){
                imageView.setImageBitmap(result.bitmap);
            }else {
                Log.d(TAG, "set image bitmap,but url has changed,ignored!");
            }
        }
    };
    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);
        @Override
        public Thread newThread(@NonNull Runnable r) {
            return new Thread(r,"ImageLoader#"+mCount.getAndIncrement());
        }
    };
    public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
            CORE_POOL_SIZE,MAXIMUM_POOL_SIZE,
            KEEP_ALIVE, TimeUnit.SECONDS
            ,new LinkedBlockingDeque<Runnable>(),sThreadFactory);
    private static final int MESSAGE_POST_RESULT = 1;
    public ImageLoader(Context context) {
        mImageResizer = new ImageResizer();
        mContext = context;
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        int cacheSize = maxMemory / 8;
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getRowBytes() * value.getHeight() / 1024;
            }
        };
        File diskCacheDir = getDiskCacheDir(mContext, "bitmap");
        if (!diskCacheDir.exists()) {
            diskCacheDir.mkdirs();
        }
        if (getUsableSpace(diskCacheDir) > DISK_CACHE_SIZE) {
            try {
                mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, DISK_CACHE_SIZE);
                mIsDiskLruCacheCreated = true;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    @TargetApi(Build.VERSION_CODES.GINGERBREAD)
    private long getUsableSpace(File path) {
         if(Build.VERSION.SDK_INT> Build.VERSION_CODES.GINGERBREAD){
             return path.getUsableSpace();
         }
        StatFs statFs = new StatFs(path.getPath());
         return statFs.getBlockSize()*statFs.getAvailableBlocks();
    }
    private File getDiskCacheDir(Context mContext, String uniqueName) {
     boolean externalStorageAvailable = Environment
             .getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
       if(externalStorageAvailable){
            cachePath = mContext.getExternalCacheDir().getPath();
       }else {
           cachePath = mContext.getCacheDir().getPath();
       }
       return new File(cachePath+File.separator+uniqueName);
    }
    /**
     * load bitmap from memory cache or disk cache or network.
     * @param uri http url
     * @param reqWidth the width ImageView desired
     * @param reqHeight the height ImageView desired
     * @return bitmap,maybe null.
     */
    public Bitmap loadBitmap(String uri,int reqWidth,int reqHeight){
        Bitmap bitmap = loadBitmapFromMemCache(uri);
        if(bitmap!=null){
            Log.e(TAG, "loadBitmapFromMemCache,url "+uri);
            return bitmap;
        }
        try {
            bitmap = loadBitmapFromDiskCache(uri,reqWidth,reqHeight);
            if(bitmap!=null){
                Log.e(TAG, "loadBitmapFromDiskCache,url "+uri );
                return bitmap;
            }
            bitmap = loadBitmapFromHttp(uri,reqWidth,reqHeight);
            Log.e(TAG, "loadBitmapFromHttp,url "+uri);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if(bitmap==null&&!mIsDiskLruCacheCreated){
            Log.e(TAG, "encounter error,DiskLruCache is not created.");
            bitmap = downLoadBitmapFromUrl(uri);
        }
        return bitmap;
    }
    public void bindBitmap(final String uri, final ImageView imageView, final int reqWidth, final int reqHeight){
      imageView.setTag(TAG_KEY_URI,uri);
      Bitmap bitmap = loadBitmapFromMemCache(uri);
      if(bitmap!=null){
          imageView.setImageBitmap(bitmap);
          return;
      }
      Runnable loadBitmapTask = new Runnable() {
          @Override
          public void run() {
              Bitmap bitmap = loadBitmap(uri,reqWidth,reqHeight);
              if(bitmap!=null){
                  LoaderResult result = new LoaderResult(imageView,uri,bitmap);
                  Message message = mMainHandler.obtainMessage(MESSAGE_POST_RESULT,result);
                  message.sendToTarget();
//                  sendToTarget();
              }
          }
      };
      THREAD_POOL_EXECUTOR.execute(loadBitmapTask);
    }
    private Bitmap downLoadBitmapFromUrl(String uri) {
        Bitmap bitmap = null;
        HttpURLConnection urlConnection = null;
        BufferedInputStream in = null;
        try {
            URL url = new URL(uri);
            urlConnection = (HttpURLConnection) url.openConnection();
            in = new BufferedInputStream(urlConnection.getInputStream(),IO_BUFFER_SIZE);
            bitmap = BitmapFactory.decodeStream(in);
        } catch (IOException e) {
            Log.e(TAG, "Error in downloadBitmap:"+e);
        }finally {
            if(urlConnection!=null){
                urlConnection.disconnect();
            }
            MyUtils.close(in);
        }
        return bitmap;
    }
    private Bitmap loadBitmapFromMemCache(String url) {
        String key = hashKeyFormUrl(url);
        Bitmap bitmap = getBitmapFromMemoryCache(key);
        return bitmap;
    }
    private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        Log.e(TAG, "addBitmapToMemoryCache: ");
        if (getBitmapFromMemoryCache(key) == null) {
            mMemoryCache.put(key, bitmap);
        }
    }
    private Bitmap getBitmapFromMemoryCache(String key) {
        return mMemoryCache.get(key);
    }
    private Bitmap loadBitmapFromHttp(String url, int reqWidth, int reqHeight)
            throws IOException {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            throw new RuntimeException("can not visit netWork from UI Thread");
        }
        if (mDiskLruCache == null) {
            return null;
        }
        String key = hashKeyFormUrl(url);
        DiskLruCache.Editor editor = mDiskLruCache.edit(key);
        if(editor!=null){
            OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
            if(downloadUrlToStream(url,outputStream)){
                editor.commit();
            }else {
                editor.abort();
            }
            mDiskLruCache.flush();
        }
        return loadBitmapFromDiskCache(url,reqWidth,reqHeight);
    }
    private Bitmap loadBitmapFromDiskCache(String url, int reqWidth, int reqHeight)
    throws IOException{
        if (Looper.myLooper() == Looper.getMainLooper()) {
            throw new RuntimeException("load bitmap from UI Thread,it's not recommended");
        }
        if(mDiskLruCache ==null){
        return null;
        }
        Bitmap bitmap = null;
        String key = hashKeyFormUrl(url);
        DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
        if(snapshot!=null){
            FileInputStream fileInputStream = (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX);
            FileDescriptor fileDescriptor = fileInputStream.getFD();
            bitmap = mImageResizer.decodeSampleFromFileDescriptor(fileDescriptor,reqWidth,reqHeight);
            if(bitmap !=null){
                addBitmapToMemoryCache(key,bitmap);
            }
        }
        return bitmap;
    }
    private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
        HttpURLConnection urlConnection=null;
        BufferedOutputStream out = null;
        BufferedInputStream in = null;
        try {
            URL url = new URL(urlString);
            urlConnection = (HttpURLConnection) url.openConnection();
            in = new BufferedInputStream(urlConnection.getInputStream(),IO_BUFFER_SIZE);
            out = new BufferedOutputStream(outputStream,IO_BUFFER_SIZE);
            int b;
            while ((b=in.read())!=-1){
                out.write(b);
            }
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(urlConnection!=null){
                urlConnection.disconnect();
            }
            MyUtils.close(out);
            MyUtils.close(in);
        }
        return false;
    }
    private String hashKeyFormUrl(String url) {
        String cacheKey;
        MessageDigest mDigest;
        try {
            mDigest = MessageDigest.getInstance("MD5");
            mDigest.update(url.getBytes());
            cacheKey = bytesToHexString(mDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            cacheKey = String.valueOf(url.hashCode());
        }
        return cacheKey;
    }
    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();
    }
}
複製程式碼

自己模仿著寫了個專案分享到github上了。
Github地址

相關文章