zoukankan      html  css  js  c++  java
  • 智慧北京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 就没必要了,基本上被淘汰了.

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

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

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

  • 相关阅读:
    Linux终端连接Linux服务器
    Linux常用命令大全
    linux中的两个命令setfacl和chmod有什么区别
    微信小程序 PHP后端form表单提交实例详解
    Facebook 开源 AI 所使用的硬件平台 'Big Sur'
    十大众筹PC:硅谷新生代如何打造下一代计算机
    <<开源硬件创客 15个酷应用玩转树莓派>>
    别小看树莓派 极客们玩出16个倍儿有趣的项目
    玩转12款Linux开源机器人
    2015业界良心迷你开发板大盘点
  • 原文地址:https://www.cnblogs.com/adventurer/p/5677793.html
Copyright © 2011-2022 走看看