編輯:關於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;
}
}
涉及到網絡和讀寫存儲,不要忘記加權限:
Android開發應用中Broadcast Receiver組件詳解
BroadcastReceiver(廣播接收器)是Android中的四大組件之一。下面就具體介紹一下Broadcast Receiver組件的用法。下面是Android
Android開發ViewPager和Fragment結合使用實現新聞類app( 三 )(基本成型的app)
提示:因為該新聞app已經基本完成,所以下方代碼量較大,請謹慎!或者從 ViewPager和Fragment結合使用實現新聞類app(一)一步步向下看!經過幾天的努力,不
Android仿支付寶支付密碼輸入框
本文實例為大家分享了Android實現一個仿支付寶支付密碼的輸入框,主要實現如下:PasswordView.javapackage com.jackie.alipay.p
Android源碼的結構
bionic 目錄– libc (C庫)| |– arch-arm (ARM架構,包含系統調用匯編實現)| |– arch-x86 (