智慧北京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 就不必了,基本上被淘汰了.

 

平板电脑和手机,平板须要另外开发,由于适配问题差异很大.

 

在公司里每每都用真机进行测试.

须要的分辨率手机能够向公司申请,可是不要买太多,适配太麻烦了

相关文章
相关标签/搜索