安卓性能优化总结

简介: 两个强引用对象之间有引用关系,且其中一个对象生命周期较长,在15年优化一个项目的时候,许多人会手动调用System.gc(),这是性能优化一大禁忌;简单说我们创造对象是可回收的环境,让GC需要的时候自己就来处理垃圾收,本身垃圾回收的操作也是消耗性能的

目录

最全的性能优化点总结

启动优化

1、项目背景

2检测启动时间

3、打印启动时间

4、优化理念

5、启动时透明页优化

6、MultiDex优化

7、多进程时,防止sdk多次初始化

8、最终结果:... 5

内存优化... 6

1项目背景... 6

2、性能优化的理念... 6

3、了解对象之间的引用关系和对象大小的占用... 6

4、了解Android中经常造成内存泄漏的点... 6

(1)、耗时任务:网络请求、属性动画、Timer6

(2)、handler6

(3)、匿名/非静态内部类... 7

(4)、单例、applicationContext 、ThreadLocal... 7

(5)、WebView内存泄露... 7

(6)、资源未及时关闭... 7

5、内存溢出问题... 7

6、内存抖动的问题... 7

7、体验优化... 7

8、内存视图查看;... 8

9、了解对象产生和分配过程;... 9

10、对象创建的过程中代码执行顺序... 9

11、SP文件的优化... 9

12、详细的优化过程... 9

13、优化结果... 10

 安装包瘦身... 10

 网络优化... 13

 第三方库的一些简单替代方案... 14

 图片加载库的问题以及性能比较... 16

1、问题背景:... 16

2、图片库测试结果:... 17

3、测试过程... 17

4、使用建议:... 17

七、锁的优化synchronized和lock. 17

锁的优化方向:... 17

延伸单例写法推荐... 18

MVP设计模式的弊端及解决方案(重点)19

Mvp存在的问题:... 19

(1)      首先是设计思路问题... 19

(2)      过多的present和model的问题... 19

(3)      内存泄露的问... 19

(4)      解决办法... 19

(5)      此mvp设计总结:... 25

、View绘制方面的优化... 25

(1)多嵌套问题:... 25

(2)各种控件性能比较:... 25

(3)使用占位符ViewStub... 25

(4)结语:... 25

十、一些知名的内存管理监测软件... 26

(1) LeakCanary. 26

(2) koom... 26

十一、数据加密优化... 26

项目背景:... 26

优化方案:... 26

对称加密... 26

非对称加密... 27

散列算法:... 27

常用的加密方案:... 27

总结:... 27

十二、修正一下编程思想!面向对象 or 面向过程... 27

(1)面向对象开发和面向过程开发... 27

十三、架构方面:对app代码业务逻辑的一些设计思考... 30

1、项目背景:... 30

2、优化思路:... 30

3、详细内容,举例说明... 30

十四、数据库和高并发优化... 33

1、问题背景... 33

2高并发优化... 33

3、数据库优化... 33

十五、android特有的库和一些代码基础写法... 33



最全的性能优化点总结:

一、启动优化

1、项目背景

19公司里的一个项目,是一个第三方库特别多的app,会启动10个广告sdk、若干游戏sdk。初期都是在application里面直接初始化的

2、检测启动时间

adb shell am start -W 包名/界面名        

1.jpg

3、打印启动时间

onCreateonResume等方法的耗时,尤其是初始化许多sdk 的方法(可使用aop方法、或者system时间相减,都可以;aop的包有apply plugin: 'android-aspectjx' implement 'org.aspectj:aspectjrt:1.8.+')

筛选启动时必须加载的sdk,其他的则放在子线程里面或者延时加载

4、优化理念

把十几个要实例化的sdk分成三部分:先加载、延迟加载、异步加载、懒加载

先加载:因为首页有开屏广告,所以开屏广告的sdk必须有先加载

2.jpg

把各种库分成四种情况都是为了尽可能提高启动速度;

5、启动时透明页优化:

设置透明主体或者提早设置背景页面;都比较常规的方法,我选择了设置背景页面,给用户一个第一时间我就打开了app的感觉,否则会有一种卡顿的感觉

6MultiDex优化

这个问题,在我们第三方sdk特别多的项目里尤为明显;体积小的apk一般就一个dex;开启多dex打包之后,就会产生很多dex文件;这个我用的主流的方法;异步加载其他dex;最恶心的过程就是手动分包,因为要保证启主dex体积比较小;能快速的加载出来,然后其他dex就放在子线程里边去加载

7、多进程时,防止sdk多次初始化

项目当中有一些音乐播放是夸进程,所以需要防止一些sdk多次初始化,这个比较简单,不贴代码里额

8、最终结果:

启动时间节省了一半以上

二、内存优化

1、 项目背景

这个是15年银行的电商app项目,由于当时的项目前后端整体设计不是特别完善;整个的图片的处理都是在客户端完成的;包括加水印、压缩等等,而且图片不管是上传还是下载的都是高清原图,所以对app的性能是极大的考验;也是当时的app前后端设计不是特别合理,所以草导致了这个问题,不过也因此我锻炼了内存优化的能力和知识

2、性能优化的理念

两个强引用对象之间有引用关系,且其中一个对象生命周期较长,在15年优化一个项目的时候,许多人会手动调用System.gc(),这是性能优化一大禁忌;简单说我们创造对象是可回收的环境,让GC需要的时候自己就来处理垃圾收,本身垃圾回收的操作也是消耗性能的;这就像在一个小区里边,有专门收垃圾的车进来,你只需要把垃圾放到垃圾桶中,其他的就不要管了,不要自己频繁的去呼叫垃圾车来收垃圾;一旦来收垃圾的时候你自己也要停止工作(stop the world),大家都耽误事儿

3、了解对象之间的引用关系和对象大小的占用

按默认开启指针压缩来算,除了基本数据类型之外,其他类型是占用四个字节,也就是多写一个成员变量对象就增大四个字节;和这个成员变量有没有具体引用到那个对象没关系,只要是生命了就会多占用内存

4、了解Android中经常造成内存泄漏的点

还有第三方控件,那个放在后面常见的内存泄漏情况有:

(1)耗时任务网络请求、属性动画、Timer

解决办法就是及时解除强引用关系、把指针置空、及时停止耗时任务;或者用弱引用设置指针关系

(2)handler

Handler一般是因为有延时任务,message里有个Handler类型的target变量,这个就是引用handler的成员变量,所以如果延时任务不结束,那么handler所在对象就无法被回收掉。解决办法就是重写handler或者及时清除延时任务

(3)、匿名/非静态内部类

这个就是内部类对象会持有外部类的引用,所以如果非静态内部类对象/匿名对象的成生命周期较长就会影响外部类对象的回收。

(4)、单例、applicationContextThreadLocal、内部类广播等

其实都是强引用关系和生命周期不同造成的的;解决办法就是用弱引用和及时解除他们的关系,因为一般单例、appContextThreadLocal不会被干掉,生命周期比较长,所以就只能及时解除关联关系,把变量的引用置空就可以了

(5)WebView内存泄露

这个问题存在很久了,也没注意现在有没有被修复,主要原因是webView内核webkitapplication有注册关系,所以一定要反注册解决办法就是activity关闭之前,获取webView的父布局,然后removeWebView,然后停止webView的一些方法、清楚历史清除views。最后再执行WebView.destroy()

(6)、资源未及时关闭

这个就是IO流、File文件流、Sqlite这些只用完毕时候要及时关闭

(7)、要反注册

比如:eventBus广播,以及ContentProvider等等,都需要在onDestory里面反注册;我写的mvp模式的evm也需要反注册;

5、内存溢出问题

这个在Android中,一般就是bitmap或者视频之类的容易造成内存溢出,内存溢出分两种,一种是直接溢出,一种是内存泄露累积起来的内存溢出,都是堆空间里满了造成的oombitmap就是压缩图片,bitmap的大小就是和像素的面积和每个像素点的色位决定的;这个就是按照bitmap要显示的实际大小进行压缩;一般就是计算压缩比例,然后抽取bitmap图片;

6、内存抖动的问题

这个就是要优化程序了,现在这个问题出的情况比较少,也是15年的时候优化过,里面有一些比较大的对象进行频发的创建和销毁就会造成内存抖动,其实就是会频繁的出发GC操作;延长这个对象的生命周期或者优化这个对象的大小

7、体验优化

这一般是整体架构的问题,加载一些图片的时候,就算手机内存足够大,但是网速也限制图片的下载速度,尤其是在一些列表图片功能里,体验就会很差,如果图片体积很小,那给人的感觉就不同;现在都比较正规了,这个问题也几乎没有了

8、内存视图查看;

学会使用Android profiler,先手动gc,然后根据类名寻找对象是否存在,就可以查找到Android对象是否内存泄漏了;除了内存外,cpu、网络、能耗都可以查得到

3.jpg

搜索对象是否存在

4.jpg

9、了解对象产生和分配过程;

可分配在堆内存、栈内存;堆内存中还包括新生代、老年代,还要根据对象的大小去判断;

10、对象创建的过程中代码执行顺序

静态变量、静态代码块,代码块;构造方法、父类和子类中这几种情况的执行顺序,了解这些会有助于了解代码的执行过程和内存的分配

11SP文件的优化

其实就是同步还是异步的问题,想要性能好肯定选择异步,然后合并多次commit();

commit(同步)apply(异步)

替代方案:mmkv 腾讯出的,从15年开始一直在微信上使用,可见其性能得到了考验

12、详细的优化过程

(1)、选择最优图片加载库:格比较了各种图片库的优缺点和性能问题,选择一个最稳定的,最终确定了使用frsco(选择过程后面,解析第三方图片库有讲解)

(2)、清除项目里手动GC操作

(3)、解除activity里的网络请求操作和主acitvity的匿名回调对象的关系;采用统一的回调方式,这样网络请求的耗时操作就不会影响正常的acitivty对象回收了

(4)、针对项目里的内存抖动问题,尽量延长对象的声明周期、压缩对象的体积,目的旨在减少system.gc出来工作的次数

(5)bitmap优化,设置bitmap.ConfigARGB_4444或者565;再就是根据控件实际显示的大小,计算压缩比例,创建像素合适的bitmap;最后就是及时释放资源执行recycle()方法并且置空;因为项目里有bit加水印的操作,所以需要自己处理一下

(6)、写法上的优化,把加载fragment的写法从add改成replaceactivity里边减少成员变量,有一些只有个点击事件,并无其他操作,所以不必在activity创建成员变量,少写一个成员变量就节省了4个字节的空间量变引质变。直接再xml文件里写onClick方法,而且效率要远远比先findViewById然后再setOnclickListener高出很多很多;再就是尽量减少布局的嵌套、多用includemegreviewStub

(7)、测试一些父控件的执行效率,效率最差的RelativeLayoutLinearLayoutFragmentlayout性能相差无几;那时候ConstraintLayout还未流行起来,所以未做处理,一些简单布局优先使用Linear/FrameLayout

(8)、用android studio逐个activity检查内存泄漏的情况;检查过程就是先打开此activity页面,然后关闭,然后手动调用几次gc操作;最后查看此activity对象是否还存在,如果还存在就表示有内存泄漏,再逐个排查,多数都是匿名对象造成的(9)、检查有和单例、ApplicationContext扯上关系的对象,这些都是内存泄露潜在的点,所以要重点检查

(10)、检查handler的延时任务,那时候还不会弱引用,所以只是手动的在activity关闭的时候,自己手动清除handler的延时任务

(11)、把项目里的ListView替换成RecyclerViewRecyclerView性能比ListView好很多,也是经过测试的;

(12)app创建线程池,来统一管理一些定时任务,包括轮播、短信验证码等。

(13)RecyclerViewListView之类的控件,在滑动的时候要停止图片的加载

13、优化结果

项目从原来的崩溃率特别高,降到非常低,崩溃情况不会出现了

 

三、安装包瘦身

项目背景

也是16年银行的直销银行项目,android包体积过大,其中也主要是因为加入人脸识别、地图等各种so库造成的

优化方案和过程

1、so文件过多,按照cpu架构有个多个类型,这个看app的使用潜在人群,如果是手机一般只用v7就可以,如果是pad那就用x64,x86之类的,总之要具体选择

5.jpg

当时的项目用的是v7


2、 默认国际化:关掉默认国际化

6.jpg

因为android打包成apk之后,apk里会有一个叫resources.arsc的文件,里边都是res/values文件夹下文件生成的,而且默认会生成很多国家的语言像这样

7.jpg

我们只要设置只支持中文或者英文就可以了


3、 一些图片转换成webp格式,当然了压缩的时候肯定会有是真的情况,这个就看你怎么取舍了,如果失真不明显那么能压缩还是要压缩的;一些小的图标可以使用Vector矢量图,这个体积也是比png小很多,而且不需要进行适配,矢量图可以根据实际需求显示大小,不想使用png还要区分各种屏幕大小

8.jpg

矢量图vector,现在新建个项目androidlogo就是vector矢量图

9.jpg

4、 常规手段:混淆(代码混淆、资源文件混淆)、去除无用文件;下图是去处无用文件的,混淆我就不解释了

10.jpg

5、 优化结果:项目体积减小了很多,主要是so文件的功劳,so文件的选择对于android体积减小是最客观的,其他的作用都不是太大,但是积少成多效果很明显的;上面的优化方案:资源文件的混淆和Vector矢量图的优化方案在当时的项目是没用上的,那时候还不知道这些,但是借这次机会也总结出来分享给大家


四、网络优化

1、这个我觉得没什么太好的办法,首先根据当前网络情况来处理数据,如果是网络比较差的时候,可疑更换网络协议,直接使用tcpmqtt之类的;如果是http那么打开gzip 压缩;使用ip地址免解析等等

2、 自己创建数据解析和压缩的字典;

3、 数据缓存、连接池复用、合并请求


五、第三方库的一些简单替代方案

EventBus

这虽然是一个很好用的库,但是有很严重的性能问题,对于我这种代码洁癖的人来说不可忍受;他提供的库虽然很丰富,但是要遍历一个类里的所有方法,然后识别出需要的,尤其是在activity里边,本身activity的的方法和变量都特别多,无形之中就是消耗的许多性能;解决方案比如可以这样写:

11.jpg

想在哪里接受数据,就在哪里执行注册,比如在acitivty中:

12.jpg

面也有内存泄露的问题,匿名内部类对象;此处只做案例,如果想切换线程,也可在里面创建一个handler,就可以做到eventBus的全部工作,但是性能会比它好很多


注解绑定控件库

注解去实例化View控件也是存在这个问题;会遍历所有的成员变量;并且会额外产生类,并且包含你所有的控件成员变量,相当于acitivty站用内存相当于翻倍了,多声明一个成员变量就多占用四个字节的空间,而且还会多创建一个对象,16个字节;如果页面比较多累计起来不仅消耗内存还消耗性能,像Xuitlsbuffterknife等等都是相同的原理

RxJava的替代方案:

可疑自己写一个简单,只要能符合当前app的业务需求,只是不满足,也可以继续改进满足,比如下面的例子:当然,如果你的业务中特别复杂,第三方库了的所有功能基本都能用到,那还是直接用他们的比较好;这个就是开发方便和性能之间的一个平衡。

13.jpg

使用方法:

14.jpg

1、 结语:大多数app的业务没那么复杂,第三方库提供能的功能可以说异常的丰富;对于大部分项目来说过于丰富,很多功能是用不上的,所以就造成了额外的性能消耗;当然自己写的时候要注意内存泄露的问题,如果对内存泄漏问题掌握的不够自信,那老老实实用第三方库也是可以的


六、图片加载库的问题以及性能比较

1、问题背景:

这个也是15年银行的电商app项目性能优化的时候,碰到的问题,因为内容比较多,所以单拿出来说一说;比如Imageloader性能最好,但是有很严重的内存泄露的问题;Fresco 会彻底解决内存的问题,但是使用起来却没有imageLoader流畅;picsso 在我看来和ImageLoader差不多;现在最长的是glide,因为glide有一些自动管理图片加载的机制和context的生命周期的管理;在15年的时候,列表类的图片,在滑动的过程中要自己手动停止图片加载功能;这个gilde都帮我们处理了;还有context的声明周期管理,避免的内存泄露的产生

2、图片库测试结果

Fresco稳定性最好;Glide体验最流畅也没有内存泄露的问题;有自动的生命周期管理,ImageLoaderXutilspiacsso基本被淘汰了;现如今反编译很多知名厂商app、使用的图片库都是Glide

3、测试过程:

当时是写了一个相册的功能,分别使用以上图片加载库去加载相册,然后反复打开关闭相册页面,记录打开次数、流畅度;测的最终上面的结论

4、使用建议

个人建议:xUitls3Imageloaderpicasso可以抛弃了;Imageloader有很严重的内存泄露问题,而且也不更新了xUtilspicasso估计也有,这是机制的问题;毕竟网络请求是一个耗时的 操作,都需要传入context上下文

建议使用Glide:理由

(1)、绝大多数主流app,反编译这些源码,使用的都是Glide;可见它的受欢迎程度

(2)、它自有的Context生命周期管理,可以在activity/frgment页面关闭的时候Glide可以在第一时间检测到,停止图片的下载,这样就防止了内存的泄露和cpu资源的消耗;

(3)glide支持gif图片

(4)、支持配合滑动列表滑动时候停止加载图片

(5)、最后说Fresco,这是个压箱底的东西,如果Glide都不能满足的时候,再把它拿出来;Frescoc写的库,把图片数据存储在了ashmem区域,这样就不占用jvm堆内存了,所以说它的终极大招;但是他使用起来却没有前面几个java的体验感更加流畅,所以把它放在最后的选择;

至于其他的一些缓存机制、缓存策略这些,比如缓存的是压缩后的图片还是原图,这个区别不大,而且都可以手动修改设置,所以这些机制就不做参考了

七、锁的优化synchronizedlock

这个就简单一说吧,要了解synchronized的锁升级过程、粗化、消除等等;lock里的抽象队列同步器,cas自旋、用户态、内核态、操作系统互斥量;内容较多,可以自行学习,我就简单说下结论:并发量小就用sync关键字,并发量大就使用Lock


锁的优化方向:

1、 尽量减小加锁部分代码的执行时间,因为可能有其他线程在等待,等待的线程越多,最后的那个线程能执行到加锁里的内容的时间越长

2、减小锁的力度或者是范围:比如Map中的ConcurrentHashMap,它是一个线程安全的集合,锁只是锁住了单独的桶,就算是两个线程同时写入数据,只要hash值算的下标不再同一个位置就不会有影响;

15.jpg

16.jpg

3、锁分离:把两种互不影响的操作,分别加锁,比如linkedBlockQueue

17.jpg

我们看到针对不同的操作,分别用不同的锁;

延伸单例写法推荐:

单例推荐静态内部类单例,即使线程安全的也是懒加载,而且不需要枷锁,所以性能上会节省一丢丢;

18.jpg

八、MVP设计模式的弊端及解决方案(重点)

Mvp存在的问题

从最开始接触mvp模式,不断的思考和改进mvp的写法,以达到最好的要求,代码量小,解耦;业务逻辑清晰,尝试过很多次,今年自己要写开源项目,所以干脆就根据多年积累重新整理了一个mvp的写法,已经放到github上了,欢迎交流

(1)首先是设计思路问题

是按照业务就划分View还按照接口去划分View;如何去划分Persentpresentmodel还有View之间是不是要一一对应的关系;其实这就牵扯了activity;如果按照接口去划分View那么View直接定义成ResponseData类型的返回值就行了;有几个接口就定义多少个View;另外一种方式就是按照业务去划分,比如登录的业务可能包括登录、验证码、短信登录、忘记密码,这些操作,把这些业务都写到一个LoginContract里边,再分别定义其他各种业务,我是比较推崇后者的

(2)过多的presentmodel的问题

如果一个页面中,只有一个接口,一个简单的功能,也需要额外写一个presentmodelview吗?这样就很冗余;也不利于开发速度;如果这样的activity还特别多,那就是写一个actvity就要写一个viewpresent,显然这样代码量太大太蠢了

(3)内存泄露的问题

我们知道网络请求是耗时操作,一旦网络不好的情况用户又关闭了activity,此时这个activity是无法被回收掉的这就造成了内存泄露问题。Activity一般是内存占用大户,虽然可以用弱引用去处理,但是弱引用也会额外的创建对象,会增大内存的占用

(4)解决办法

创建Evm中间类,让presentViewmodel中间,不产生强引用关系,所以也就不会产生内存泄露的问题;再就是presentmodle都可以自由定义,想定义几个就定义几个,都可以通过EVM中间类进行关联,而且采用的是反射的方法,只获取接口,所以对性能无影响,不像EventBusButterKnife那样要遍历所有的方法或者变量,而且如果View已经回收,则会生成一个临时该接口类型的对象,不需要是否为空判断

19.jpg

代码是:

/**

* description:

* author: tianhonglong

* new date: 2021/7/9

* version: v 1.0

*/

public class EVM {

 

  private EVM() {

   }

 

   private Map<String, EasyView> views = new HashMap<>();

   private Map<String, EasyPresent> presents = new HashMap<>();

 

   private <T extends EasyView> T getView(Class<T> clazz) {

       EasyView easyView = views.get(clazz.getSimpleName());

       if (easyView == null) {

           try {

               return clazz.newInstance();

           } catch (IllegalAccessException e) {

               e.printStackTrace();

           } catch (InstantiationException e) {

               e.printStackTrace();

           }

       }

       return (T) easyView;

   }

 

   private <T extends EasyPresent> T getPre(Class<T> clazz) {

       EasyPresent present = presents.get(clazz.getSimpleName());

       if (present == null) {

           try {

               T t = clazz.newInstance();

              presents.put(clazz.getSimpleName(), t);

               return t;

           } catch (IllegalAccessException e) {

               e.printStackTrace();

           } catch (InstantiationException e) {

               e.printStackTrace();

           }

       }

       return (T) present;

   }

 

   private void managerView(EasyView easyView, boolean registerOrNot) {

       Class[] classes = easyView.getClass().getInterfaces();

       for (Class clazz : classes) {

           if (EasyView.class.isAssignableFrom(clazz)) {

               if (registerOrNot) {

                  views.put(clazz.getSimpleName(), easyView);

               } else {

                   views.remove(clazz.getSimpleName());

               }

           }

       }

   }

 

   public static void register(EasyView easyView) {

       EVM.ins().managerView(easyView, true);

   }

 

   public static void unregister(EasyView easyView) {

       EVM.ins().managerView(easyView, false);

   }

 

   public static <T extends EasyView> T getV(Class<T> clazz) {

       return EVM.ins().getView(clazz);

   }

 

   public static <T extends EasyPresent> T getP(Class<T> clazz) {

       return EVM.ins().getPre(clazz);

   }

 

   private static class InnerClass {

       private static EVM easyPresent = new EVM();

   }

 

   private static EVM ins() {

       return InnerClass.easyPresent;

   }

}

basectivity中使用:


20.jpg

而且只搜索存在接的接口,一个类的接口不会太多,所以不会影响性能

21.jpg

定义一个基类接口

写一个LoginContract,把和登录有关的Viewpresent都定义在里边

22.jpg

LoginActivity中,

23.jpg

在看看present代码:优雅的很

24.jpg

(5)mvp设计总结:

1、 彻底解除ViewPresentmodel之间的关联,其中modelViewpresent各自想写几个就写几个,通过evm中间类关联,都可以互相调用;

2、 解决了View的内存泄露问题,不产生直接强引用就能互相调用,所以不会影响内存回收;

3、 只需要关注业务的开发,针对接口去设计model

25.jpg

4、 只需要在present里面处理业务流程,actvity处理业务的发起和结果接受;modle按照接口文档傻瓜式写上就可以了,也不需要加各种try catch;底层会帮业务处理好,直接回调给当前的View


View绘制方面的优化

1)多嵌套问题:

优化别人代码的时候,布局文件中最常见的就是过多嵌套问题,很多子空间就可以实现的,很多人非要加个父控件,比如LinearLayoutRelativeLayout等等,再就是用merge可以减少嵌套,由于空间渲染计算是递归形式,在以前的老旧机型里,只要嵌套7Layout,就会有明显的卡顿产生,但是如果同级别里,就算是14层也不会有任何问题,所以某些界面使用LinearLayout并不会比RelativeLayout带来更多控件层级时,优先考虑LinearLayout;

2)各种控件性能比较:

性能最差的是RelativeLayout;如果LinearLayoutFrameLaout都满足的情况下,优先排除RelativeLayout; 如果再复杂就是使用google新出的ConstraintLayout; 还有RecyclerView的性能都要比ListView好很多,比如加载fragment除了用懒加载外,replaceadd要节省内存

3)使用占位符ViewStub

在一些界面里的控件,需要最开始状态是View.GONE;但即使是View.GONE,这个控件依然会执行各种实例化方法创建对象占用内存,只是最后没有通过wms渲染在手机屏幕上而已,ViewStub是一个轻量级的View;使用ViewStub后,那么原来的控件就相当于懒加载了,只有用户用手操作让它显示的时候才会去加载,如果一直不要求显示那就永远不会加载;只是会多一个轻量级的ViewStub

4结语

写代码的过程中,有许多不经意的点都是可以节省内存的,只要节省每一处的内存和cpu的使用;累计起来就是一个好的项目;如果对内存不省吃俭用,累积起来的内存泄露造成的oom是最难解决和优化的;


九、一些知名的内存管理监测软件

(1) LeakCanary

就是用WeakReferenceReferenceQueue这两个类的机制,来检查内存是否泄露的;可疑帮助你快速定位内存泄露的点;但是不能帮你解决内存泄露的问题,只是帮你发现问题

(2) koom

快手自研OOM解决方案。效率据说比leakCanary好的多,具体还没使用过,推荐给大家;

 

十、数据加密优化

项目背景

这个是20年优化的一个项目,因为在银行和金融app行业经验丰富一些,所以在一些非银行类app中的数据安全这一块,做的有问题;直接用非对称去加密、解密数据;这个性能是有很大问题的;

优化方案

要把对称加密和非对称加密结合起来使用,通信数据要用对称加密去加密和解密,然后把对称加密的钥匙,用非对称加密进行加解密,然后把加密后的钥匙拼接在隐藏在数据中,这样在安全性不变的情况下,性能会大大的提升

对称加密

名称

密钥长度

运算速度

安全性

资源消耗

DES

56

较快

3DES

112位或168

AES

128192256

非对称加密

名称

成熟度

安全性(取决于密钥长度)

运算速度

资源消耗

RSA

DSA

只能用于数字签名

ECC

(计算量小,存储空间占用小,带宽要求低)


散列算法:

名称

安全性

速度

SHA-1

MD5


常用的加密方案:

MD5:比如用户输入的密码,用MD5进行加密,直接存储在后台的就是MD5数据,再就是校验数据的完整性

Sha-1:一般签名密钥里东西

对称加密:加密基本数据,因为性能比非对称加密好的多

非对称加密:加密对称加密的钥匙,组合使用,这样安全性即达到了非对称级别,性能也上去了;


总结:

MD5校验完整性+对称加密加密全部数据+非对称加密对称加密的密钥

最常用的就是MD5+RSA+AES


十一、修正一下编程思想!面向对象 or 面向过程

1)面向对象开发和面向过程开发

这个是优化现在公司里的一个项目;虽然java是面向对象语言,但是在很多项目中的同事,仍然使用面向过程的思维模式开发;举个简单的例子,比如曾经做的人脸识别app中,有这样一个场景,在人脸识别拿到数据之后,产生了一个用户对象,里面包含了用户的起止时间用户身份;先看面向过程写法,极其简单的一个案例:

26.jpg

再看面向对象写法:

27.jpg

可读性和简洁性对比明显;就是在写代码的时候要分清业务流程和业务细节;此处的流程就是此人是否可通行,业务细节就是判断此人是否可通行的过程;这部分代码不要出现在流程里边

 

上面只是写了个简单的例子,我们项目中实际的是否允许通过的判断要复杂的多,包括多分组、多时段、所以判断过程很复杂代码量也不少

 

十二、架构方面:对app代码业务逻辑的一些设计思考

1、项目背景:

这个是优化了代码的业务逻辑,其中最典型的人脸识别页面activity,有7500+行代码,里面包含了各种业务包括:数据的同步、UI的显示(识别结果展示)、人脸认证的过程(包括人脸、温度、口罩、距离识别、硬件接口回调等等),还包括一些业务细节的处理比如最后两次人脸是否同一个人,wifi状态监听的等等吧;

2、优化思路:

按照业务分类,可分为UI部分、验证流程部分、验证细节处理部分、面向对象部分;拆分成三个activity,原来是一个FaceVerifyActivity,如今拆分成FaceBusinessActivityFaceUIActivity,其中继承关系:

FaceVerifyActivity 继承FaceBusinessActivity继承 FaceUIActivity

FaceUIActivity的功能应该只包含UI部分,和人脸认证没有任何逻辑关系,只是提供了人脸结果出来的时候,可能需要显示的各种dialog、或者其他UI

FaceBusinessActivity里面包含最后两次是否同一个人的判断方法,数据同步的方法等等

FaceVerifyActivity:纯粹是业务流程的判断,然后根据每次判断的结果,来调用FaceUIActivityFaceBusinessActivity的方法,这样整个业务逻辑就清晰很多

3、详细内容,举例说明

由于原项目代码量特别大,所以也不可能全部贴出来;所以此处举例说明:

首先是UIActivity,定义了一些需要显示的UI对话框之类的

28.jpg

其次是业务Activity,定义了开门、数据同步,是否同一个人等等

29.jpg

最后是主流程activity:纯粹的流程判断

30.jpg

1、 在案例中每个页面30~40行代码,如果不拆分,不按照面向对象写那么在一个页面中就会有110行的代码,这只是案例,真实项目中把案例代码量扩充70倍,才是我们项目中的实际代码,一个activity7000多行代码,谁不头疼?拆分后分成三部分,每部分就两千多行,而且除了FaceVerifyActivity外,其他几个类中方法之间没有调用,都是单独的业务方法,以后修改起来会简单容易的多;这其实也是属于模块化思想,按类型分类,就是目的都是为了让代码单一、简洁、易修改、便于扩展;

十三、数据库和高并发优化

1、问题背景

首先说惭愧;这个问题不是我实际工作的经验;这个是去某家公司面试的时候,面试官问的问题,问的高并发的数据存储和数据优化,回答的不是太好,也确实没这方面经验,本着不会就要学习的态度,还是研究了很多文章和代码,在此也分享出来,在此直说客户端如何解决

2、万人群高并发优化

问题1:消息从未读到已读,这个消息如何发送? 如果是实时的,那一条已读消息要发送给一万个人,如果一万个人同事读了这条消息,就会发生一万个人同事给一万个人发消息,并发量可想而知;这个解决办法就是降低频率,是否已读,10秒钟才去刷新一次,这样就大大降低了消息的并发数

问题2:一个人接受到不同人的多条信息时,建立缓冲区。,合并多条消息给一人,甲乙丙丁同时给A发送消息,那么就建立缓冲机制,把甲乙丙丁合成一条消息,这样最后技术层面A只收到一套消息,总之目的就是降低并发数量;

3、数据库优化

数据库的并发优化和IM通信类似,数据里边有事务,其实就相当于建立缓冲区合并多条数据,然后开启专门的现成去执行数据库的操作

(1)、开启事务

Android中的sqlite是默认开启事务的,就算只是只有一条数据的插入更新也会帮你开启事务,所以当并发量大的时候,把多条执行语句放在一个事务里,这样就提高了性能,不用每次都打开关闭事务;

(2)、建立索引

索引就是把数据库里的数据,建立一个目录,在查找的时候不用一页一页去查了,索引一般是平衡树结构;简单说就是利用算法和数据结构提高效率,但是貌似在数据量特别大的时候,维护索引也会产生不小的开销;

(3)、耗时的话进行异步操作

比如有些数据的存储和同步不需要知道返回结果,这样建立一个线程池去执行这些任务,这样就不会影响主线程操作


十四、常见的ANR操作

原理也很简单,系统服务amswms会检测app响应时间,也就是本地的applicationThread是否会及时的给amswsm发送binder信息,如果超过一定的时间未发送,系统就会认为你卡住了;就会提示无响应,因为通过applicationThread给系统服务发送信息都是通过主线程来执行的,所以一旦在主线程中执行耗时操作就会引起ANR

十五、android特有的库和一些代码基础写法

1、对象的序列化android 特有的ParcelableSerializable的性能好

2、 Android特有的集合:在数据量小的情况下使用SparseArrayArrayMap代替java集合

3、 Map的多种遍历方式,那种效率最快

4、 在写基础库的时候,尤其是处理数据,线程安全的情况下使用StringBuilder、线程不全的情况下使用StringBuffer

5、 根据数据存储和使用情况来判断使用链表集合还是数组集合

6、 尽量使用基本数据类型,比如int类型的成员变量一共就占用四个字节的堆空间。如果用Integer除了所在类中成员变量的四个字节外,还会有Integer对象的16个字节;

7、 循环中减少对变量的重新计算

比如:for(int I = 0; i < list.size(); i++) 改为for(int I = 0, len = list.size(); i = len; i++)

8、 避免使用二位数组,数据比较特殊,不管你是否存入对象,数组创建的那一刻,内存已经消耗掉了,数组里每个指针占用四个字节;不算对象头和数组长度,一个长度为10的二维空数组创建的那一刻就是占用10*10*4 = 400个字节;还不算对象头类指针数组长度;像ArrayList也一样,因为都是数组

Json序列化性能对比:数据量小就用gson,数据量大就用阿里巴巴的fastjson



作者:田洪龙

相关文章
|
1月前
|
缓存 监控 Android开发
安卓应用性能优化的实用策略
【4月更文挑战第2天】 在竞争激烈的应用市场中,一款应用的性能直接影响用户体验和市场表现。本文针对安卓平台,深入探讨了性能优化的关键要素,包括内存管理、代码效率、UI渲染和电池使用效率。通过分析常见的性能瓶颈,并提供针对性的解决策略,旨在帮助开发者构建更加流畅、高效的安卓应用。
|
3天前
|
缓存 Java Android开发
安卓应用性能优化实践
【5月更文挑战第9天】在移动开发领域,应用的性能是决定用户体验的关键因素之一。特别是对于安卓平台,由于设备的多样性,性能优化变得尤为重要。本文将深入探讨针对安卓应用的性能优化策略,从内存管理、多线程处理到布局优化等方面提供实用的技术建议和最佳实践,帮助开发者提升应用的流畅度与响应速度。
|
4月前
|
Java 调度 数据库
Android 性能优化: 如何进行多线程编程以提高应用性能?
Android 性能优化: 如何进行多线程编程以提高应用性能?
57 0
|
12天前
|
机器学习/深度学习 人工智能 缓存
安卓应用性能优化实践探索深度学习在图像识别中的应用进展
【4月更文挑战第30天】随着智能手机的普及,移动应用已成为用户日常生活的重要组成部分。对于安卓开发者而言,确保应用流畅、高效地运行在多样化的硬件上是一大挑战。本文将探讨针对安卓平台进行应用性能优化的策略和技巧,包括内存管理、多线程处理、UI渲染效率提升以及电池使用优化,旨在帮助开发者构建更加健壮、响应迅速的安卓应用。 【4月更文挑战第30天】 随着人工智能技术的迅猛发展,深度学习已成为推动计算机视觉领域革新的核心动力。本篇文章将深入分析深度学习技术在图像识别任务中的最新应用进展,并探讨其面临的挑战与未来发展趋势。通过梳理卷积神经网络(CNN)的优化策略、转移学习的实践应用以及增强学习与生成对
|
13天前
|
缓存 移动开发 Android开发
安卓应用性能优化实践指南
【4月更文挑战第30天】在移动开发领域,一个流畅的用户体验是至关重要的。对于安卓开发者来说,理解并实施性能优化策略能够显著提升应用的响应速度和稳定性。本文将深入探讨针对安卓平台的性能瓶颈诊断、内存管理、UI渲染优化以及电池使用效率提升等方面的实用技巧,旨在帮助开发者构建更加高效、响应迅速的安卓应用。
|
13天前
|
缓存 Android开发 iOS开发
打造高效移动应用:Android与iOS性能优化策略
【4月更文挑战第29天】 在移动设备日益成为用户日常互动的主要平台的今天,应用程序的性能已成为决定其成功的关键因素之一。本文将探讨针对Android和iOS平台的性能优化技巧,涵盖内存管理、多线程处理、网络请求优化以及用户界面的流畅性提升等方面。通过分析不同操作系统的架构特点,我们旨在提供一套综合性的策略,帮助开发者构建快速、响应迅捷且用户体验良好的应用。
|
14天前
|
移动开发 API Android开发
Android应用性能优化实战
【4月更文挑战第28天】在移动开发领域,一个流畅的用户体验是至关重要的。对于Android开发者而言,应用的性能优化是一项既挑战性也极其重要的工作。本文将深入探讨Android应用性能优化的多个方面,包括内存管理、UI渲染、多线程处理以及电池效率等,旨在为开发者提供实用的性能提升策略和具体的实施步骤。通过分析常见的性能瓶颈,并结合最新的Android系统特性和工具,我们的目标是帮助读者打造更加高效、响应迅速的Android应用。
|
16天前
|
缓存 监控 Android开发
Android 应用性能优化实战
【4月更文挑战第27天】 在竞争激烈的移动应用市场中,性能优越的应用更能吸引和保留用户。针对Android平台,本文将深入探讨影响应用性能的关键因素,并提供一系列实用的优化策略。我们将从内存管理、UI渲染、多线程处理以及电池使用效率等方面入手,通过具体案例分析如何诊断常见问题,并给出相应的解决方案。文中所提技巧旨在帮助开发者构建更加流畅、高效的Android应用。
24 2
|
20天前
|
移动开发 API 数据处理
构建高效安卓应用:探究Android 12中的新特性与性能优化策略
【4月更文挑战第23天】 随着移动设备的普及,用户对应用程序的性能和效率要求越来越高。安卓系统作为市场占有率最高的移动操作系统之一,其版本更新带来了众多性能提升和新特性。本文将深入探讨Android 12版本中引入的关键性能优化技术,并分析这些技术如何帮助开发者构建更加高效的安卓应用。我们将从最新的运行时权限、后台任务优化、以及电池使用效率等方面入手,提供具体的实践建议,旨在帮助开发者更好地利用这些新工具,以提升应用的响应速度、降低能耗,并最终提高用户的满意度。
|
1月前
|
缓存 Java Android开发
安卓应用性能优化实践
【4月更文挑战第12天】 在竞争激烈的移动应用市场中,性能优越的应用更容易获得用户青睐。本文针对安卓平台,深入分析了影响应用性能的关键因素,并提出了一系列实用的性能优化策略。通过对内存管理、多线程处理、UI渲染等方面的细致调优,开发者可以显著提升应用的响应速度和流畅度,进而提高用户满意度。