智慧北京06_图片三级缓存_屏幕适配

1,图片缓存

1.1 基本原理:

 

①优先从内存中加载图片,速度最快,无流量消耗

②其次从本地(sdcard)加载图片,速度快,无流量消耗

③最后从网络下载图片,速度慢,消耗流量

1.2 网络缓存

①创建一个工具类myUtils,用来做缓存(bitMapUtils底层也是用的三级缓存)

创建一个方法display(imageView,url);

②先倒叙写流程(因为一开始没有图片,不先从网络加载,看不到图片)

创建一个网络缓存工具类: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 就没必要了,基本上被淘汰了.

平板电脑和手机,平板需要另外开发,因为适配问题差别很大.

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

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

原文地址:https://www.cnblogs.com/adventurer/p/5677793.html