編輯:關於Android編程
LruCache
對於分配給LruCache的緩存大小,可以直接指定固定的數值,但是更好的做法應該是通過獲取最大內存(int)Runtime.getRuntime.maxMemory,然後通過返回的最大內存/int n的大小動態分配給LruCache。
LruCache是以為鍵值對形式存儲數據,所以它的讀寫方法都和HashMap
存儲
LruCache.put(Key,Values)
讀取
LruCache.get(Key)
public class CustomLruCache { private LruCachestringBitmapLruCache; int maxMemory = (int) Runtime.getRuntime().maxMemory();//獲取最大內存 int cacheSize = maxMemory / 16;//大小為最大內存的1/16 private static CustomLruCache customLruCache; /** * 私有化構造方法 */ private CustomLruCache() { stringBitmapLruCache = new LruCache (cacheSize) { @Override protected int sizeOf(String key, Bitmap value) { return value.getByteCount(); } }; } /** * 單例模式獲取實例,保證只有一個CustomLruCache對象,同時保證只有一個CustomLruCache.stringBitmapLruCache * * @return */ public static CustomLruCache getInstance() { if (customLruCache == null) { customLruCache = new CustomLruCache(); } return customLruCache; } public void addBitmapToMemoryCache(String key, Bitmap bitmap) { if (getBitmapFromMemoryCache(key) != bitmap)//如果緩存中不存在bitmap,就存入緩存 stringBitmapLruCache.put(key, bitmap); } public Bitmap getBitmapFromMemoryCache(String key) { return stringBitmapLruCache.get(key); } }
從網絡讀取圖片,存儲到緩存用昨天學過的AsyncTask異步任務類來處理邏輯
AsyncTaskstringVoidBitmapAsyncTask = new AsyncTask () { @Override protected Bitmap doInBackground(String... params) { Bitmap bitmap = null; try { CustomLruCache customLruCache = CustomLruCache.getInstance(); bitmap = customLruCache.getBitmapFromMemoryCache(params[0]); //先從緩存中讀取圖片,如果緩存中不存在,再請求網絡,從網絡讀取圖片添加至LruCache中 //啟動app後第一次bitmap為null,會先從網絡中讀取添加至LruCache,如果app沒銷毀,再執行讀取圖片操作時 //就會優先從緩存中讀取 if (bitmap == null) { //從網絡中讀取圖片數據 URL url = new URL(params[0]); bitmap = BitmapFactory.decodeStream(url.openStream()); //添加圖片數據至LruCache customLruCache.addBitmapToMemoryCache(params[0], bitmap); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return bitmap; } @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); imageView.setImageBitmap(bitmap); } }; stringVoidBitmapAsyncTask.execute(imageURL);
利用DiskLruCache從網絡上獲取到之後都會存入到本地緩存中,因此即使手機在沒有網絡的情況下依然能夠加載顯示圖片數據。DiskLruCache存儲的位置沒有限制,但是一般選擇存儲在context.ExternolStorageCacheDir(),即這個手機的外部存儲這個app的私有區域,即/sdcard/Android/data/應用包名/cache,因為是存儲在外部存儲私有區域,當app被卸載時,這部分的內容會被一起清除。
實例化DiskLruCache是通過 DiskLruCache.open(File directory, int appVersion, int valueCount, long maxSize),四個參數分別:為directory緩存的路徑;appVersion 應用版本;alueCount 指定同一個key可以對應多少個緩存文件,一般指定為1;maxSize 指定可以緩存多少字節的數據。
public File getDiskCacheDir(Context context, String uniqueName) { String cachePath; if (isExternalStorageWritable()) { cachePath = context.getExternalCacheDir().getPath();//如果掛載了sdcard,獲取外部存儲私有區域路徑 } else { cachePath = context.getCacheDir().getPath();//如果沒有掛載sdcard,則獲取內部存儲緩存區域 } return new File(cachePath + File.separator + uniqueName); }
其中isExternalStorageWritable()是檢測手機是否掛在sdcard。
private boolean isExternalStorageWritable() { String state = Environment.getExternalStorageState(); if (state.equals(Environment.MEDIA_MOUNTED)) { return true;//掛載了sdcard,返回真 } else { return false;//否則返回假 } }
因為要對外部存儲區域進行讀寫操作,所以要在androidManifest中添加相應的權限
public int getAppVersion(Context context) { try { PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); return info.versionCode; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return 1; }
當版本號變更時,緩存路徑下所有數據會被清除,因為DiskLruCache認為當版本更新時,所有數據應從網絡重新獲取
DiskLruCache從緩存讀取文件和寫入文件到緩存時是通過 key來識別的,所以一般是指定alueCountKey為1。
可以自己指定
通過上面的方法可以,這下可以完整的實例化一個DiskLruCache
private DiskLruCacheHelper(Context context) { try { File cacheDir = getDiskCacheDir(context, "bitmap"); //如果文件不存在,則創建 if (!cacheDir.exists()) { cacheDir.mkdirs(); } mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024); } catch (IOException e) { e.printStackTrace(); } }
為了方便操作,我們可以封裝一個類來進行操作
public class DiskLruCacheHelper { DiskLruCache mDiskLruCache = null; static DiskLruCacheHelper diskLruCacheHelper; private DiskLruCacheHelper(Context context) { try { File cacheDir = getDiskCacheDir(context, "bitmap"); //如果文件不存在,則創建 if (!cacheDir.exists()) { cacheDir.mkdirs(); } mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024); } catch (IOException e) { e.printStackTrace(); } } public static DiskLruCacheHelper getInstance(Context context) { if (diskLruCacheHelper == null) diskLruCacheHelper = new DiskLruCacheHelper(context); return diskLruCacheHelper; } public File getDiskCacheDir(Context context, String uniqueName) { String cachePath; if (isExternalStorageWritable()) { cachePath = context.getExternalCacheDir().getPath();//如果掛載了sdcard,獲取外部存儲私有區域路徑 } else { cachePath = context.getCacheDir().getPath();//如果沒有掛載sdcard,則獲取內部存儲緩存區域 } return new File(cachePath + File.separator + uniqueName); } /** * 檢查外部存儲是否可用 * * @return */ private boolean isExternalStorageWritable() { String state = Environment.getExternalStorageState(); if (state.equals(Environment.MEDIA_MOUNTED)) { return true;//掛載了sdcard,返回真 } else { return false;//否則返回假 } } /** * 獲取應用版本號 * 當版本號改變,緩存路徑下存儲的所有數據都會被清除掉,因為DiskLruCache認為 * 當應用程序有版本更新的時候,所有的數據都應該從網上重新獲取。 * * @param context * @return */ public int getAppVersion(Context context) { try { PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); return info.versionCode; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return 1; } /** * 寫入圖片數據到文件緩存 * * @param imageUrl * @param bitmap */ public void writeToCache(String imageUrl, Bitmap bitmap) { try { String key = hashKeyForDisk(imageUrl); DiskLruCache.Editor editor = mDiskLruCache.edit(key); if (editor != null) { OutputStream outputStream = editor.newOutputStream(0); if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)) { editor.commit(); } else { editor.abort(); } } mDiskLruCache.flush(); } catch (IOException e) { e.printStackTrace(); } } /** * 從緩存讀取數據 * * @param imageUrl * @return */ public Bitmap readFromCache(String imageUrl) { Bitmap bitmap = null; try { String key = hashKeyForDisk(imageUrl); DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key); if (snapShot != null) {//如果文件存在,讀取數據轉換為Bitmap對象 InputStream is = snapShot.getInputStream(0); bitmap = BitmapFactory.decodeStream(is); } } catch (IOException e) { e.printStackTrace(); } return bitmap; } /** * 將文件名轉換成"MD5"編碼 * * @param key * @return */ public String hashKeyForDisk(String key) { String cacheKey; try { final MessageDigest mDigest = MessageDigest.getInstance("MD5"); mDigest.update(key.getBytes()); cacheKey = bytesToHexString(mDigest.digest()); } catch (NoSuchAlgorithmException e) { cacheKey = String.valueOf(key.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(); } }
下面是從網絡獲取一張圖片顯示到ImageView上,第一次進入app時從網絡獲取,並存儲至外部存儲私有區域,以後(即使沒有網絡的情況)進入若相同的緩存文件存在,則直接從緩存讀取。
注:涉及到網絡操作,需要添加網絡操作的相關權限
public class MainActivity extends AppCompatActivity { ImageView mImageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mImageView = (ImageView) findViewById(R.id.get_image_from_network_img); new AsyncTask() { @Override protected Bitmap doInBackground(String... strings) { Bitmap bitmap = DiskLruCacheHelper.getInstance(MainActivity.this).readFromCache(strings[0]); if (bitmap == null) { try { URL url = new URL(strings[0]); bitmap = BitmapFactory.decodeStream(url.openStream()); DiskLruCacheHelper.getInstance(MainActivity.this).writeToCache(strings[0], bitmap); return bitmap; } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } return bitmap; } @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); mImageView.setImageBitmap(bitmap); } }.execute("http://file3.u148.net/2011/4/images/1302139153715.jpg"); } }
運行過後,我們可以到相應的緩存區域查看是否有文件生成
上面提到了內存緩存和文件緩存,可以將這兩者一起使用,形成二級緩存,第一層是內存緩存,如果內存緩存中沒有,則從文件緩存中讀取,之後如果本地也沒有相關緩存文件,再從網絡獲取。
前言: 首先很高興大家來閱讀王某人這篇文章,我干肯定大部分公司的發版流程是這樣的,android程序員小李打出各渠道包,發給運維小胡,小胡將個渠道包上傳各大應用
在蘋果的iOS下面,有個應用Air Video,可以在iOS下通過Wifi遠程直接播放電腦裡的視頻,而不需要把視頻復制到手機上再看。非常好用!最近用了Android的手
android退出應用程序會調用android.os.Process.killProcess(android.os.Process.myPid())或是System.ex
序言OkHttp 的強大算是毋庸置疑了;OkHttp 基本在網絡層能完成任何事情,適用任何情況;正因為如此 OkHttp 每次構建一個請求的時候不得不寫大量的代碼來完成相