Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android OkHttp與物理存儲介質緩存:DiskLruCache(2)

Android OkHttp與物理存儲介質緩存:DiskLruCache(2)

編輯:關於Android編程

本文在附錄文章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//cache
    * 而後者獲取到的是 /data/data//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;
    }
}

 

 

涉及到網絡和讀寫存儲,不要忘記加權限:

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved