前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android 开发艺术探索笔记三

Android 开发艺术探索笔记三

作者头像
Yif
发布2019-12-26 15:08:08
5360
发布2019-12-26 15:08:08
举报
undefined
undefined

Bitmap加载与Cache

android对单个应用所施加的内存限制,比如16M

常用的缓存策略:LruCacheDiskLruCache,其中LruCache用作内存缓存,而DiskLruCache用作磁盘缓存。

Bitmap

Bitmap支持从文件系统,资源,输入流与字节数组中加载。通过BitmapFactory.options来缩放图片,使用inSampleSize为1时,采样后图片与原始图片一样大,为2时,宽高均为原来电放费1/2,像素数为原图1/4.占用内存大小为原图1/4.inSampleSize取值应为2的指数,比如1,2,4,8,16,如果外界传来的是3,系统会选择2来代替,但并非在所有android版本都成立。如果采样率为3,那么缩放的图片大小就会小于所期望的大小,导致图片被拉伸而模糊。

获取采样率遵循的流程:

  1. BitmapFactory.options的inJustDecodeBounds参数设为true并加载图片
  2. BitmapFactory.options中取出图片原始宽高信息,它们对应于outWidtht与outHeight参数
  3. 根据采样率规则并结合目标view所需的大小计算出采样率inSampleSize
  4. BitmapFactory.options的inJustDecodeBounds参数设为false并重新加载图片 public static Bitmap decodeSampleBitmapFromResource(Resources res,int resId,int reqWidth,int reqHeight){ BitmapFactory.Options options=new BitmapFactory.Options(); options.inJustDecodeBounds=true; BitmapFactory.decodeResource(res,resId,options); options.inSampleSize = calculateInSampleSize(options,reqWidth,reqHeight); options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res,resId,options); } public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){ int height=options.outHeight; int width = options.outWidth; int inSampleSize=1; if(height>reqHeight||width>reqWidth){     int halfHeight = height/2;     int halfWidth = width/2;     while ((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize)>reqWidth){         inSampleSize *=2;     } } return inSampleSize;}

从内存中加载图片比从存储设备加载图片要快,既提高了程序的效率又节省了用户流量。

LruCache

LruCache:当缓存快慢时,会淘汰近期最少使用的缓存。 LruCache 的使用其实比较简单,可以归结为以下要点:

  1. LruCache 内部使用 LinkedHashMap 实现,所以 LruCache 保存的是键值对
  2. LruCache 本身对缓存项是强引用
  3. LruCache 的读写是线程安全的,内部加了 synchronized。也就是 put(K key, V value) 和 get(K key) 内部有 synchronized 4.key 和 value 不接受 null 。所以如果 get 到了 null ,那就说明是没有缓存

5. Override sizeOf(K key, V value) 方法

  1. 根据需要Override entryRemoved(boolean evicted, K key, V oldValue, V newValue)create(K key) 方法 使用LruCache建议采用support-v4兼容包

LRUCache,内部采用一个LinkedHashMap以强引用方式存储外界缓存对象,提供get和put方法来完成缓存的获取与添加。

final Bitmap bitmap=null;
int maxMemory= (int) (Runtime.*getRuntime*().maxMemory()/102);
int cacheSize=maxMemory/8;
LruCache<String,Bitmap> lruCache=new LruCache<String, Bitmap>(cacheSize){
    @Override
    protected int sizeOf(String key, Bitmap value) 
        return bitmap.getRowBytes()*bitmap.getHeight()/1024;
    }
};

DiskLruCache

DiskLruCache实现存储设备缓存,即磁盘缓存,通过将缓存对象写入文件系统实现缓存效果。

  1. DiskLruCache通过open方法进行创建。 public static DiskLruCache open(File directory,int appVersion,int valueCount,long maxSize)
    • 第一个参数表示磁盘缓存在文件系统中的存储路径,如果应用卸载就希望删除缓存文件,就选择SD卡上的缓存目录,希望保留缓存数据,就选择SD卡上的其它目录。**
  • 第二个参数表示版本号,一般设为1,即使应用版本发生改变,缓存文件却仍然有效
  • 第三个参数表示单个节点对应的数据个数,一般设为1
  • 第四个参数表示缓存总大小,当超过这个设定值后,就会清楚一些缓存
  1. DiskLruCache缓存添加通过Editor完成,Editor表示一个缓存对象的编辑对象。DiskLruCache不允许同时编辑一个缓存对象。之所以将url转换成key,因为图片url可能有特殊字符,一般采用url的md5值作为key.

通过Editorcommit方法来提交操作,通过Editor的abort方法来回退整个操作。

  1. 缓存查找,将url转换为key,通过DiskLruCache的get方法得到一个Snapshot对象,接着在通过Snapshot得到文件输入流,自然就得到Bitmap对象了。为了避免加载图片导致OOM,通过BitmapFactory.options加载缩放图片。

优秀的图片加载框架

一个优秀的图片加载框架ImageLoader具备:

  1. 图片同步加载
  2. 图片异步加载
  3. 图片压缩
  4. 内存缓存
  5. 磁盘缓存
  6. 网络拉取

综合技术

在Android中,有一个限制,就是整个应用的方法数不能超过65536,否则就会出现编译错误,并且程序无法安装到手机上。

Android中单个dex(它是Android系统可执行文件,包含应用程序全部指令与运行时数据)文件所能够包含的最大方法数为65536,这包含androidFrameWork,依赖的jar包,以及应用本身代码所有方法。

解决方法:

  • Google提供multidex方案专门解决这个问题,通过将一个dex文件拆分为多个dex避免单个dex文件方法数越界。
  • 另一种方案是动态加载,可以直接加载一个dex形式文件,将部分代码打包到一个单独的dex文件中,并在程序运行时动态加载dex中的类,既解决了方法数越界问题,也可以为程序提供按需加载的特性,同时还为应用按模块更新提供可能性。

CrashHandler

使用crashHandler来获取应用崩溃信息

  1. 新建一个类实现UncaughtExceptionHandler接口,重写uncaughtException方法,在这个方法中获取异常信息,可以选择将异常信息存储到sd卡中,然后找合适的机会上传到服务器上,这样开发人员就可以分析用户的crash场景并在以后版本中修复。**
  2. 当程序有未捕获的异常,系统会自动调用uncaughtException方法,其中thread为出现未捕获异常的线程,ex为未捕获的异常,有了这个ex,就可以获取到异常信息了。

Multidex

在代码中加入支持multidex的功能

  • 第一种方案:在manifest文件中指定Application为MultiDexApplication
  • 第二种方案:让Application继承自MultiDexApplication
  • 第三种方案:选择重写Application的attachBaseContext方法,这个方法比aplication的onCreate要先执行。

采用以上方法,如果越界,Gradle就会在apk中打包2个或多个dex文件。

产生的问题:

  1. 应用启动速度会降低。由于应用启动会加载额外的dex文件,导致启动速度降低。要避免生成较大的dex文件。是客观存在的。
  2. 由于Dalvik linerAlloc的bug,导致multidex无法在android4.0以前手机上运行。目前极少遇到。

Android动态加载技术

宿主指的是普通apk,而插件一般是指经过处理的dex或apk,在主流的插件化框架中多采用经过特殊处理的apk来作为插件,都需要用到代理activity概念。

使用插件化需要解决三个基础性的问题:

  1. 资源访问。当宿主调用未安装的apk,插件中凡是以R开头的资源无法访问,通过实现Context中的两个抽象方法来解决资源问题,getAssets()getResources()
  2. Activity的生命周期管理。反射方式与接口方式。过多使用反射方式会带来性能开销
  3. ClassLoader的管理。为了更好对多插件进行支持,合理管理各个插件的DexClassLoader,这样同一个插件可以采用同一个ClassLoader去加载类,避免多个ClassLoader加载同一个类所造成的类型转换错误

Android性能优化

布局优化

  1. 使用<include>标签
  2. 使用<merge>标签:一般和<include>一起使用,减少布局的层级

使用merge来组织子元素可以减少布局的层级。例如我们在复用一个含有多个子控件的布局时,肯定需要一个ViewGroup来管理,例如这样 :

&lt;FrameLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;       android:layout_width=&quot;fill_parent&quot;
android:layout_height=&quot;fill_parent&quot;&gt;
&lt;ImageView
android:layout_width=&quot;fill_parent&quot;            android:layout_height=&quot;fill_parent&quot;
android:scaleType=&quot;center&quot;
android:src=&quot;@drawable/golden_gate&quot; /&gt;
&lt;TextView
android:layout_width=&quot;wrap_content&quot;            android:layout_height=&quot;wrap_content&quot;            android:layout_marginBottom=&quot;20dip&quot;           android:layout_gravity=&quot;center_horizontal|bottom&quot;            android:padding=&quot;12dip&quot;
android:background=&quot;#AA000000&quot;
android:textColor=&quot;#ffffffff&quot;
android:text=&quot;Golden Gate&quot; /&gt;
&lt;/FrameLayout&gt;

将该布局通过include引入时就会多引入了一个FrameLayout层级,此时结构如下 :

img
img

使用merge标签就会消除上图中蓝色的FrameLayout层级。示例如下 :

&lt;merge xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;        &lt;ImageView
android:layout_width=&quot;fill_parent&quot;            android:layout_height=&quot;fill_parent&quot;
android:scaleType=&quot;center&quot;
android:src=&quot;@drawable/golden_gate&quot; /&gt;
&lt;TextView
android:layout_width=&quot;wrap_content&quot;            android:layout_height=&quot;wrap_content&quot;            android:layout_marginBottom=&quot;20dip&quot;           android:layout_gravity=&quot;center_horizontal|bottom&quot;            android:padding=&quot;12dip&quot;
android:background=&quot;#AA000000&quot;
android:textColor=&quot;#ffffffff&quot;
android:text=&quot;Golden Gate&quot; /&gt;
&lt;/merge&gt;

效果图如下 :

img
img
  1. 使用ViewStub,它是非常轻量级且宽高都是0,按需加载布局文件,在布局中不可见,提高了程序的初始化性能。 ((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE); 或者 View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();

绘制优化

指的是View的onDraw方法要避免大量操作

  1. onDraw中不要创建新的局部对象,这是因为onDraw可能会被频繁调用,就会一瞬间产生大量临时对象,不仅占用过多内存,还会导致系统频繁gc,降低程序执行效率
  2. onDraw方法中不要做耗时任务,也不能执行成千上万的循坏操作,大量循坏十分抢占CPU时间片,造成View绘制过程不流畅。View的绘制帧率保持在60fps最佳,要求每帧绘制时间不超过16ms(16ms=1000/60)

内存优化

  1. 避免静态变量导致的内存泄漏
  2. 避免单例模式导致的内存泄漏,由于单例模式特点是它的生命周期与Application保持一致,导致activity对象无法及时释放
  3. 避免属性动画导致的内存泄漏。在Activity的onDestory方法中调用animator.cancel()来停止动画

响应速度优化

避免在主线程中做太多事情

  1. Activity如果5秒之内无法响应屏幕触摸事件或者键盘输入事件就会出现ANR
  2. Broadcast如果10秒未执行完操作就会ANR
  3. Service是20秒

当一个进程中出现ANR,系统会在/data/anr目录下创建一个文件traces.txt。通过这个文件就可以定位出ANR原因。

ANR对话框,是在AMS收到SHOW_NOT_RESPONDING_UI_MSG消息之后弹出的。这个消息分别来自于:

  • 后台服务超时;
  • 前台服务超时;
  • ContentProvider超时;
  • input事件分派超时;

在input事件分派超时的时候,有两种情况不会弹框,分别是:

  • 处于debug时;
  • 来自子进程;(这个情况下会直接kill掉子进程)

ListView与Bitmap优化

ListView优化
  1. 采用viewHolder并避免在getView中执行耗时操作
  2. 根据列表滑动状态来控制任务执行频率,比如当列表快速滑动显然不适合开启大量异步任务
  3. 开启硬件加速(Android 4.0之后默认开启硬件加速)
Bitmapy优化

通过BitmapFactory.Options根据需要对图片进行采样

线程优化

使用线程池

其他一些性能优化建议

  1. 避免过多创建对象
  2. 不要过多使用枚举,枚举占用内存空间比整型大
  3. 常量请使用static final修饰
  4. 使用一些Android特有的数据结构,比如SparseArrayPair等,它们具有更好的性能
  5. 适当使用软引用与弱引用
  6. 采用内存缓存和磁盘缓存
  7. 尽量采用静态内部类,避免潜在的由于内部类而导致的内存泄漏
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年7月19日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Bitmap加载与Cache
    • Bitmap
      • LruCache
        • DiskLruCache
          • 优秀的图片加载框架
          • 综合技术
            • CrashHandler
              • Multidex
                • Android动态加载技术
                • Android性能优化
                  • 布局优化
                    • 绘制优化
                      • 内存优化
                        • 响应速度优化
                          • ListView与Bitmap优化
                            • ListView优化
                            • Bitmapy优化
                          • 线程优化
                            • 其他一些性能优化建议
                            相关产品与服务
                            图片处理
                            图片处理(Image Processing,IP)是由腾讯云数据万象提供的丰富的图片处理服务,广泛应用于腾讯内部各产品。支持对腾讯云对象存储 COS 或第三方源的图片进行处理,提供基础处理能力(图片裁剪、转格式、缩放、打水印等)、图片瘦身能力(Guetzli 压缩、AVIF 转码压缩)、盲水印版权保护能力,同时支持先进的图像 AI 功能(图像增强、图像标签、图像评分、图像修复、商品抠图等),满足多种业务场景下的图片处理需求。
                            领券
                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档