編輯:關於Android編程
LruCache
對於分配給LruCache的緩存大小,可以直接指定固定的數值,但是更好的做法應該是通過獲取最大內存(int)Runtime.getRuntime.maxMemory,然後通過返回的最大內存/int n的大小動態分配給LruCache。
LruCache是以為鍵值對形式存儲數據,所以它的讀寫方法都和HashMap
存儲
LruCache.put(Key,Values)
讀取
LruCache.get(Key)
public class CustomLruCache {
private LruCache stringBitmapLruCache;
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開發之Jenkins+Gradle實現持續集成、apk多渠道打包
前言: 首先很高興大家來閱讀王某人這篇文章,我干肯定大部分公司的發版流程是這樣的,android程序員小李打出各渠道包,發給運維小胡,小胡將個渠道包上傳各大應用
Android[安卓] 版Air Video 遠程播放電腦視頻
在蘋果的iOS下面,有個應用Air Video,可以在iOS下通過Wifi遠程直接播放電腦裡的視頻,而不需要把視頻復制到手機上再看。非常好用!最近用了Android的手
android 完全退出應用程序實現代碼
android退出應用程序會調用android.os.Process.killProcess(android.os.Process.myPid())或是System.ex
[Android]OkHttp的簡單封裝-輔助框架
序言OkHttp 的強大算是毋庸置疑了;OkHttp 基本在網絡層能完成任何事情,適用任何情況;正因為如此 OkHttp 每次構建一個請求的時候不得不寫大量的代碼來完成相