智慧北京06_圖片三級緩存_屏幕適配

1,圖片緩存android

1.1 基本原理:算法

 

 

①優先從內存中加載圖片,速度最快,無流量消耗api

②其次從本地(sdcard)加載圖片,速度快,無流量消耗數組

③最後從網絡下載圖片,速度慢,消耗流量緩存

 

1.2 網絡緩存服務器

①建立一個工具類myUtils,用來作緩存(bitMapUtils底層也是用的三級緩存)網絡

建立一個方法display(imageView,url);app

②先倒敘寫流程(由於一開始沒有圖片,不先從網絡加載,看不到圖片)異步

建立一個網絡緩存工具類:NetCacheUtils netUtils;工具

建立一個方法:經過url路徑,設置圖片給ImageView

 

1.3 AsyncTask的使用(android裏提供的異步封裝工具,能夠實現異步請求及主界面更新,其實是對線程池 + handler的封裝)

①在NewCacheUtils,建立一個類繼承AsyncTask<Void,Void,Void>//不肯定泛型傳Void

重寫方法

onPreExecute()//預加載,運行在主線程中

 

doInBackground()//正在加載,運行在子線程(能夠異步請求)

 

onPostExecute()//加載結束,運行在主線程(核心方法)

 

onProgressUpdate()//更新進度的方法,運行在主線程中

 

②在建立的方法中

BitMapTask().execute();//啓動AsyncTask

 

③泛型上的三個參數

第一個Void,影響doInBackground()方法上的參數

doinBackground()的參數是經過execute(參數....)傳遞過來的

doInBackground()的參數爲可變參數,其實是一個數組

params[index]能夠取出對應的參數

ImageViewUrl傳遞進來,而後進行開始下載

 

第二個Void,影響onProgressUpdate()裏的參數類型(參考使用Integer)

這個參數也是一個數組,總大小,之類的均可以傳入這裏

 

第三個Void,影響doInBackground()的返回值類型,onPostExecute()的參數類型

doInBackground()返回結果給onPostExecute()方法

而後在onPostExecute()方法中對結果進行處理(這裏參考使用BitMap)

 

1.4 ①在doInBackground()方法中建立一個下載的方法

建立 HttpURLConnection conn;

setConnectionTimeout()//設置連接超時時間

setReadTimeout(5000);//讀取超時,連接上了服務器,可是沒有數據

拿到響應碼,判斷是否成功(200)

Is = conn.getInputStream();拿到輸入流

BitMapFactory.decodeStream(is)//經過輸入流拿到 BItMap 對象

 

額外1:onProgressUpdate()方法的使用,

PublishProgress(參數2)//調用此方法實現進度更新,回調onProgressUpdate()方法,當使用while循環下文件的時候,能夠經過調用該方法設置進度

額外2:釋放資源

  if(conn!=null){

                        conn.disconnect();

                        conn = null;

                    }

②在結果中判斷返回的BitMap對象是否爲null;

就給BitMap對象設置數據

 

③設置默認圖片,在工具類建立的方法裏,直接就對ImageView設置數據(默認圖)

 

額外1:網絡緩存和ListView條目複用的BUG

  有可能出現圖片錯位(網速慢的狀況),新的圖片未加載出來,複用舊的圖片會顯示異常(圖片與文字對不上號).

解決1:onInBackgound()方法中,打一個標記ImageView.setTag(url)//綁定圖片和imageView,而後在加載結束onPostExecute()方法中,拿到標記getTag();

判斷兩個url是否一致

一致就設置圖片,不一致就不設置圖片

 

2.本地緩存

建立一個類LocalCacheUtils

主要是兩個方法setLocalCache();//寫本地緩存

  getLocalCache();//讀本地緩存

建立一個變量,記錄須要保存的路徑

(緩存文件夾,通常在sd卡中)(注意,文件夾不要帶中文)

 

setLocalCache(url)方法中

判斷該文件夾是否存在,還須要判斷是不是文件夾

緩存文件的命名:經過md5運算url(由於裏面會有特殊字符)

bitMap.compress(格式,100(壓縮比),輸出流)//壓縮圖片,格式,壓縮比,輸出流(文件)

getLocalCache(url)方法中

Md5運行url,查找對應的文件是否存在

若是存在,經過文件輸入流解析成BitMap對象

 

③使用位置:在緩存工具類中,建立一個本地緩存工具的引用,傳遞給網絡緩存

在結果方法中,設置緩存

在加載網路以前.判斷是否有緩存(是否爲空)

有緩存就再也不訪問網絡了,設置圖片,return掉邏輯

 

3,內存緩存,建立一個類:MemoryCacheUtils

兩個方法 setMemoryCache();//寫內存緩存

 getMemoryCache();//讀內存緩存

內存緩存,是運行起來以後纔有的

 

建立一個集合HashMap<String(url),BitMap>,表明圖片緩存的鍵值對

 

寫緩存就put一個值進去,讀緩存就經過url讀取一個BitMap對象

 

在緩存工具類中,建立一個引用,優先從內存中加載圖片(把引用傳遞給另兩個工具)

寫緩存的位置:讀取到了本地緩存以後寫一個內存緩存

讀取到了網絡緩存以後也寫一個內存緩存

 

讀緩存的時候,若是從內存讀取到了,一樣設置圖片,而後return

 

4.使用軟引用改造內存

4.1由於上面的寫法,當圖片數量比較多的時候,有內存溢出oom的可能.

android有默認分配的應用內存,通常爲:16MB,一旦超出就會內存溢出了

 

4.2 垃圾回收器不起做用

由於垃圾回收器只回收沒有引用的對象

 

 

引用的,因此不會進行回收

即便它會回收,也不會即時回收

 

4.3 ①只要讓有引用的對象會被回收,就可讓垃圾回收器起做用

通常狀況下的引用稱爲強引用,不會被回收

軟引用:垃圾回收器會考慮回收

弱引用:垃圾回收器更會考慮回收,優先級高一些

虛引用:垃圾回收器會最優先回收,優先級相對最高

 

Java中有對應的幾個類表示這幾類引用

SoftReference 軟引用

WeakReference 弱引用

PhantomReference 虛引用(用得比較少,太容易被回收了)

 

③使用改造對象,SoftReference<BitMap>//須要包裝的對象當作泛型傳遞進去

使用軟引用包裝

SoftReference<BitMap> soft = new SoftReference(bitMap);

獲取內存中的對象時,要判斷是否爲null,由於它有可能被回收了.

 

使用軟引用改造以後,讀取緩存就不必定會讀取到內存的,由於它被回收了,相對硬引用而言,提高的效率可能少一些,可是通常不會出現OOM的狀況

 

5.LruCache的使用(google推薦使用它來管理內存溢出的方法)

api9,android2.3之後,垃圾回收器更傾向於回收軟引用和弱引用,無論內存是否充足.

因此谷歌在v4包中提供了一個LruCache來替代上面的軟引用和弱引用

Lru: least recently used 最近最少使用算法,能夠將最近最少使用的對象回收掉,從而保證內存不會超出範圍

Runtime.getRuntime().maxMemory();//獲取分配給 app的內存大小

通常用內存最大值/8做爲內存緩存大小

LruCache = new LruCache<String,bitMap>(int){//參數爲內存空間大小

//重寫方法,返回每一個對象的大小

sizeOf(arg1,arg2)

經過參數上的bitMap.getByteCount()//返回圖片的總大小,api12

查看源碼能夠看到底層其實是經過

getRawBytes()//拿到每一行的像素*getHeight()//獲得全部像素點個數==佔用byte

bitMap.getRowBytes() * bitMap.getHeight()//兼容低版本的寫法

}

 

5.2 lruCache源碼簡析:

①底層維護了一個LinkedHashMap.

put一個對象的時候,給集合中添加了一個對象

 全局維護了一個size,會把sizeOf()返回的對象大小記錄下來

trimToSize()方法

 這個方法裏有一個while循環

循環裏對size與構造裏傳進來的總大小作比較

若是大於總大小,拿到最先(迭代器next()得到)放進來的對象,remove()刪除掉

 

這個算法其實是當內存超出預期的時候,刪掉早期添加進來的對象

 

6,用本身工具類替代BitMapUtils();

發如今快速滑動切換頁籤的時候仍是會報錯,開發中仍是用BitMapUtils.

BitMapUtils底層使用的也是三級緩存,使用了lruCache來解決加載圖片內存溢出問題.

 

7.屏幕適配

常見屏幕適配

 圖片適配,佈局適配,尺寸適配,權重適配,代碼適配

 

7.1 圖片適配

把圖片放到不一樣的文件夾下

設備密度(不絕對)

Hdpi 高分辨率 >> 480*800 0.75

Ldpi 低分辨率 >> 240*320 1

Mdpi 中等分辨率 >> 320*240 1.5

Xhdpi 超高分辨率 >> 1280*720 2

Xxhdpi 超超高分辨率 >> 1920*1080 3

 

開發中:若是發現圖片不符合預期,就針對具體的機型放置圖片便可

常規作法:作一套圖:好比1280*720 切圖,放在哪一個文件夾均可以,通常Hdpi,若是摸個屏幕出了問題,再針對該屏幕,對相關出問題的圖片進行替換

 

7.2 佈局適配

有時候即便用了dp去設置,也會出現佈局混亂的狀況

 

出現這種狀況,能夠針對對應的屏幕寫一個文件夾layout-800x480(針對480*800),在這下面寫對應的佈局文件(名稱一致,id一致,就是佈局樣式是根據不一樣屏幕來的,不建議對控件類型和個數進行調整,否則代碼編寫會很麻煩的)

 

7.3 尺寸適配

①設備密度

拿到設備密度:float density = getResource().getDisplayMetrics().density

//dppx

Dp:根據設備轉換大小

px = dp*設備密度

 

values>>dimens.xml 定義尺寸的文件

<dimens name=」width」>160dp</diments>

在屬性中引入@dimens:width

能夠指定多個values文件夾 values-1280x720,而後寫一個dimens文件,指定該屏幕的dp;

 

③能夠經過尺寸適配來替代佈局適配

 

7.4,權重適配

LinearLayout中指定屬性:weightSum = 3;能夠指定總權重

子控件設置的權重比就是根據這個3爲比值作顯示

經過權重適配,能夠適配全部屏幕.

7.5 代碼適配

拿到屏幕的寬高,WindowManager wm = getWindowManager();

wm.getDefaultDisplay().getWidth();//獲取寬高

 

須要設置的控件,拿到對應的佈局參數(佈局參數根據父控件來拿,好比LinearLayout)

LinearLayout.LayoutParams params;

而後設置想要設置的寬高

一樣的,也是不用考慮屏幕分辨率

 

8,解決智慧北京的遺留的適配問題

例如:新手引導小圓點的間距,是在代碼中寫的,表明的是px

 

寫一個工具類DensityUtils

dp2px>>>dp轉換成px(咱們通常都是按dp來設置的)

//拿到屏幕密度

context.getResources().getDisplayMetrics().density

Dp * density >>>獲得轉換過來的px

 

可是float 4.9強轉成整數>>>4

 因此Dp*density + 0.5 就能實現四捨五入

 

一樣也能夠px2dx,px轉換成dp

 

注意:這裏的轉換其實是數字的轉換,並非說有種數據類型是dppx

代碼裏通常直接寫的數字都是px,開發中要注意轉換成dp轉換的值

 

側邊欄的寬度,不該該寫死成200

獲取屏幕的寬度wm.getDefalutDisplay().getWidth();

經過width * 200/320 獲取比值,設置到側邊欄上

 

9,屏幕適配總結

養成良好的開發習慣,多用sp,dp;不用px,多用相對佈局和線性佈局,不用絕對佈局,代碼中若是必須設置像素的話,dp轉爲px進行設置

項目開發後期,對適配問題進行驗證,若是發現有問題,針對性進行修改

480*800,1280*720,1920*1080 都要跑一下

320*480 就不必了,基本上被淘汰了.

 

平板電腦和手機,平板須要另外開發,由於適配問題差異很大.

 

在公司裏每每都用真機進行測試.

須要的分辨率手機能夠向公司申請,可是不要買太多,適配太麻煩了

相關文章
相關標籤/搜索