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 就不必了,基本上被淘汰了.
平板电脑和手机,平板须要另外开发,由于适配问题差异很大.
在公司里每每都用真机进行测试.
须要的分辨率手机能够向公司申请,可是不要买太多,适配太麻烦了