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]能夠取出對應的參數
把ImageView和Url傳遞進來,而後進行開始下載
第二個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
//dp和px
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
注意:這裏的轉換其實是數字的轉換,並非說有種數據類型是dp和px
代碼裏通常直接寫的數字都是px,開發中要注意轉換成dp轉換的值
側邊欄的寬度,不該該寫死成200
獲取屏幕的寬度wm.getDefalutDisplay().getWidth();
經過width * 200/320 獲取比值,設置到側邊欄上
9,屏幕適配總結
養成良好的開發習慣,多用sp,dp;不用px,多用相對佈局和線性佈局,不用絕對佈局,代碼中若是必須設置像素的話,將dp轉爲px進行設置
項目開發後期,對適配問題進行驗證,若是發現有問題,針對性進行修改
480*800,1280*720,1920*1080 都要跑一下
320*480 就不必了,基本上被淘汰了.
平板電腦和手機,平板須要另外開發,由於適配問題差異很大.
在公司裏每每都用真機進行測試.
須要的分辨率手機能夠向公司申請,可是不要買太多,適配太麻煩了