Android三级缓存机制工具类的实现
一.三級緩存概述
(一)三級緩存的三級
第一級是內(nèi)存,最快,不需要網(wǎng)絡(luò)
第二級是本地,不需要網(wǎng)絡(luò)
第三級是網(wǎng)絡(luò),需要網(wǎng)絡(luò)請求
??????三級緩存機制的思想:
??????如果在內(nèi)存中獲取到數(shù)據(jù),就不去本地和網(wǎng)絡(luò)中獲取。
??????如果在本地中獲取到數(shù)據(jù)就不去網(wǎng)絡(luò)中獲取,
??????如果內(nèi)存和本地中不存在數(shù)據(jù),就要去網(wǎng)絡(luò)中請求數(shù)據(jù)
??????三級緩存技術(shù)能有效節(jié)省用戶的流量,但是也會增加一些內(nèi)存負擔。
二.使用示例展示三級緩存工具欄類的使用
??????程序運行后的頁面:
??????雖然只用一個按鈕和一個圖片顯示,但是通過測試(聯(lián)網(wǎng)狀態(tài)和斷網(wǎng)狀態(tài)對比)能知道圖片是從網(wǎng)絡(luò)中獲取還是從本地或者中內(nèi)存。
??????這里用到了幾個其他自己編程的小工具類。
(一)添加手機權(quán)限,網(wǎng)絡(luò)權(quán)限和SD卡寫的權(quán)限
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />(二)編寫布局文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><Button android:layout_width="match_parent"android:layout_height="wrap_content"android:onClick="start"android:text="加載圖片" /><ImageView android:id="@+id/main_iv"android:layout_width="match_parent"android:layout_height="wrap_content"/></LinearLayout>上面是一個非常簡單的布局文件。
(三)設(shè)計一個方便調(diào)試顯示個工具類
package com.lwz.threelevelt;import android.content.Context; import android.util.Log; import android.widget.Toast;/*** 本類用于簡易的顯示信息* 比如土司,或Log信息*/public class ShowUtils {//這里DEBUG的作用是,可以在程序完成后設(shè)置DEBUG的值為false,程序以后就不會在顯示以前的打印信息public static boolean DEBUG = true;//各種Log打印public static void e(Object o) {if (DEBUG)Log.e("TAG", "打印:------ " + o.toString());}public static void e(int i) {if (DEBUG)Log.e("TAG", "打印:------ " + i);}public static void e(float i) {if (DEBUG)Log.e("TAG", "打印:------ " + i);}public static void e(boolean b) {if (DEBUG)Log.e("TAG", "打印:------ " + b);}//各種土司public static void ts(Context context, Object object) {if (DEBUG)Toast.makeText(context, object + "", Toast.LENGTH_SHORT).show();}public static void tsl(Context context, Object object) {if (DEBUG)Toast.makeText(context, object + "", Toast.LENGTH_LONG).show();}}(四)文件操作的一個工具類
public class FileUtils {//判斷是否本地有sd卡,確定是否保存在SD卡內(nèi)String path;//文件存儲的地方/*** 通過構(gòu)造方法傳入存儲的路徑*/public FileUtils(Context context, String dirName) {//判斷是否本地有sd卡,這里代表的是SD卡在就緒的狀態(tài) //這里判斷相等狀態(tài)要使用.equal,使用==會匹配不到???if (Environment.getExternalStorageState() .equal( Environment.MEDIA_MOUNTED)) {ShowUtils.e("SD卡就緒狀態(tài)");path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + dirName;} else {ShowUtils.e("SD卡沒有就緒狀態(tài)");//保存在內(nèi)部存儲器中path = context.getCacheDir().getAbsolutePath() + "/" + dirName;}//創(chuàng)建文件new File(path).mkdirs();}/*** 文件的寫入* 傳入一個文件的名稱和一個Bitmap對象* 最后的結(jié)果是保存一個圖片*/public void saveToSDCard(String key, Bitmap bmp) {FileOutputStream fos = null;try {fos = new FileOutputStream(new File(path, key));} catch (FileNotFoundException e) {e.printStackTrace();}//保存圖片的設(shè)置bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);try {fos.close();//關(guān)閉流} catch (IOException e) {e.printStackTrace();}}/*** 文件的讀取,* 根據(jù)文件的名字,讀取出一個Bitmap的對象,* 如果之前保存過就有值,否則是null*/public Bitmap readFromSDCard(String key) {return BitmapFactory.decodeFile(new File(path, key).getAbsolutePath()); } }(五)最最重要的三級緩存功能的實現(xiàn)的工具類
package com.lwz.threelevelt;import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Handler; import android.os.Message; import android.support.annotation.NonNull; import android.support.v4.util.LruCache;import java.io.InputStream; import java.net.URL; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;import static com.lwz.threelevelt.ShowUtils.e;/*** 圖片三級緩存機制的實現(xiàn)類* <p>* 首先是內(nèi)存,最快* 其次是本地,不需要網(wǎng)絡(luò)* 最后是網(wǎng)絡(luò),需要網(wǎng)絡(luò)請求* 這如果在內(nèi)存中獲取到數(shù)據(jù),就不去本地和網(wǎng)絡(luò)中獲取,同樣如果在本地中獲取到數(shù)據(jù)就不去網(wǎng)絡(luò)中獲取,* 如果內(nèi)存和本地中不存在數(shù)據(jù),采取網(wǎng)絡(luò)中請求數(shù)據(jù)* <p>* ,這里結(jié)合的是另一個fileUtils的工具類來實現(xiàn)* <p>* 調(diào)用方法也是很簡單的:*/public class ImageLoader {//下載使用的線程池對象ExecutorService threadLooper;//緩存類,能過獲取和寫入數(shù)據(jù)到緩存中,短時間的存儲!!private static LruCache<String, Bitmap> cache;//文件操作類對象private FileUtils fileUtils;/*** 構(gòu)造方法,需要傳入一個保存文件的名字* 實例化:線程池對象,緩存類,文件操作類對象*/public ImageLoader(Context context, String dirName) {//獲取系統(tǒng)分配的最大內(nèi)存int maxSize = (int) (Runtime.getRuntime().maxMemory() / 8);//實例化緩存類的對象cache = new LruCache<String, Bitmap>(maxSize) {//每一個鍵所對應(yīng)的值的大小//自動釋放低頻率的文件@Overrideprotected int sizeOf(String key, Bitmap value) {return value.getByteCount();}};fileUtils = new FileUtils(context, dirName);//實例化文件操作類的對象threadLooper = Executors.newFixedThreadPool(5);//實例化線程池,并設(shè)置運行的最大線程數(shù)}/*** 下載圖片的方法,這也是提供給我們調(diào)用的方法* 需要傳入一個URL地址,和一個圖片下載成功后的回調(diào)方法*/public void loadImage(final String url, @NonNull final ImageLoadListener listener) {//去掉所有需要轉(zhuǎn)義的斜杠,把獲得的字符串作為Bitmap對象的一個標示符,通過它和它的Bitmap對象一一對應(yīng)final String key = url.replaceAll("[\\W]", "");//第一級,先判斷緩存類中是否有數(shù)據(jù)if (readFromCache(key) != null) {//直接拿出來e("從緩存中加載");listener.loadImage(readFromCache(key));} else {//第二級,再判斷本地中是否存在數(shù)據(jù)final Bitmap bitmap = fileUtils.readFromSDCard(key);//查看是否存在數(shù)據(jù)if (bitmap != null) {//本地中存在數(shù)據(jù)//存儲到緩存e("從SDCard中加載");saveToCache(key, bitmap);//把從SD卡中讀取到的數(shù)據(jù)保存到緩存中,//返回listener.loadImage(fileUtils.readFromSDCard(key));//返回一個數(shù)據(jù)給調(diào)用者} else {//第三級,從網(wǎng)絡(luò)中下載數(shù)據(jù)//要把數(shù)據(jù)分別存入本地中內(nèi)存中//下載,使用子線程//創(chuàng)建一個Handler對象,這里還是主線程,// 下載是在子線程,然后調(diào)用Handler對象發(fā)送數(shù)據(jù)給主線程final Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);e("從網(wǎng)絡(luò)下載");listener.loadImage((Bitmap) msg.obj);}};//線程池的使用,在里面下載數(shù)據(jù)threadLooper.execute(new Runnable() {@Overridepublic void run() {//開始下載try {URL u = new URL(url);InputStream inputStream = u.openStream();Bitmap bitmap1 = BitmapFactory.decodeStream(inputStream);//獲取bitmap對象fileUtils.saveToSDCard(key, bitmap1);//保存文件到SD卡saveToCache(key, bitmap1);//保存文件到內(nèi)存中//使用Handler對象給主線程發(fā)送消息Message msg = handler.obtainMessage();msg.obj = bitmap1;handler.sendMessage(msg);} catch (Exception e) {e.printStackTrace();}}});}}}/*** 取消子線程的任務(wù)*/public void cancelDownLoad() {threadLooper.shutdown();}/*** 定義一個接口,里面有一個方法,* 這里有一個Bitmap對象參數(shù),作用是讓調(diào)用這接收這個Bitmap對象,實際這bitmap對象就是緩存中的對象*/public interface ImageLoadListener {public void loadImage(Bitmap bmp);}/*** 使用緩存類存儲Bitmap對象*/private void saveToCache(String key, Bitmap bmp) {cache.put(key, bmp);}/*** 使用緩存類獲取Bitmap對象*/private Bitmap readFromCache(String key) {return cache.get(key);} }??????上面代碼中e(“XXX”) 和ShowUtils.e(“XXX”)效果是一樣的,因為導(dǎo)入方式是靜態(tài)的:import static com.lwz.threelevelt.ShowUtils.e;所以可以省略類名ShowUtils。
??????有些簡單的方法這樣使用是非常方便的。
(六)最后的調(diào)用類
package com.lwz.threelevelt;import android.graphics.Bitmap; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.ImageView;/*** 三級緩存工具類的調(diào)用測試* 1.創(chuàng)建三級緩存類的對象,傳入上下文和圖片名字* 2.調(diào)用三級緩存類的對象的loadImage方法,* 傳入兩個參數(shù),第一個參數(shù)是URL地址,第二個參數(shù)是回調(diào)接口,在回調(diào)接口內(nèi)可以接收到根據(jù)URL地址下載到的Bitmap對象*/ public class MainActivity extends AppCompatActivity {//定義布局的控件ImageView imageView;//定義三級緩存工具類ImageLoader loader;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ShowUtils.DEBUG = false;setContentView(R.layout.activity_main);imageView = (ImageView) findViewById(R.id.main_iv);loader = new ImageLoader(this, "test3");//創(chuàng)建文件夾}public void start(View v) {loader.loadImage("http://p3.so.qhmsg.com/bdr/326__/t018da60b972e086a1d.jpg", new ImageLoader.ImageLoadListener() {@Overridepublic void loadImage(Bitmap bmp) {imageView.setImageBitmap(bmp);}});} }??????可以看到實現(xiàn)了三級緩存的工具類后,調(diào)用還是非常簡單的。
??????程序運行后顯示的界面:
第一次點擊按鈕,頁面顯示:
顯示的Log數(shù)據(jù):
可以看到數(shù)據(jù)是從網(wǎng)絡(luò)中獲取到的。因為剛剛開始本地或緩存中都沒有數(shù)據(jù)。
第二次或多次點擊按鈕,顯示的Log數(shù)據(jù):
可以看到數(shù)據(jù)是從緩存中獲取到的。因為圖片的數(shù)據(jù)每次打開后,緩存中都會有它的數(shù)據(jù)。
退出程序后,再進入程序點擊按鈕,顯示的Log數(shù)據(jù):
可以看到數(shù)據(jù)是從本地中獲取到的。因為緩存中的數(shù)據(jù)很容易被回收,本地的數(shù)據(jù)不會被回收。
卸載程序后,再安裝程序點擊按鈕,顯示的Log數(shù)據(jù):
??????可以看到數(shù)據(jù)是從SD卡中獲取到的。因為程序存放的數(shù)據(jù)是在SD卡下的,程序卸載后數(shù)據(jù)依然存在,但是如果數(shù)據(jù)保存在內(nèi)部存儲器,卸載程序也會刪除數(shù)據(jù)。
??????上面就是三級緩存圖片工具類的實現(xiàn)和三級緩存圖片的一個簡單使用。
總結(jié)
以上是生活随笔為你收集整理的Android三级缓存机制工具类的实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 〖全域运营实战白宝书 - 运营角色认知篇
- 下一篇: Android中的三级缓存机制