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