App的优化目的是为了让应用更快、更稳定、更小。网上也有很多相应的文章,但是很多因为花费时间跟优化程度不成正比,或者许多在开发过程中自己已经避免了所以仅仅记录一下自己最近用过的。
更快
启动白屏
<!--启动activity时白屏替换-->
<style name="SplashStyle" parent="AppTheme">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<!--如果需要效果更圆滑可以换成启动页的背景图-->
<item name="android:windowBackground">@color/theme_color</item>
<!--带有这个属性就会去除刘海屏-->
<item name="android:windowFullscreen">true</item>
</style>
复制代码
把启动页的主题换成
<activity
android:name=".SplashActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/SplashStyle"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
复制代码
注意
- 可能有些人直接换MainActivity的主题,这样会有些问题,背景在启动后处需要换过来。并且不能设置去除刘海屏的属性,有些手机不适配,刘海屏处是白的。可能是我自己没有找到控制刘海屏的方法。希望大佬指点一下
- 跳转到MainActivity不能直接把启动页finish掉,否则跳转时会有一瞬间白屏。需要延时几秒finish
//延时销毁,避免白屏
new Handler().postDelayed(() -> {
finish();
}, 3000);
复制代码
- 跳转时最好换一下系统默认的跳转动画,效果会比较圆滑
ARouter.getInstance().build(RouterPath.MAIN)
.withTransition(R.anim.common_popup_fade_in, R.anim.common_popup_fade_out)
.navigation(this);
复制代码
<alpha
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fromAlpha="1.0"
android:interpolator="@android:anim/decelerate_interpolator"
android:toAlpha="0.0"/>
复制代码
<alpha
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fromAlpha="0.0"
android:interpolator="@android:anim/decelerate_interpolator"
android:toAlpha="1.0"/>
复制代码
延时加载
/**
* 屏幕以外信息延时获取
*/
private void initDelay() {
// 拿到主线程的MessageQueue
Looper.myQueue().addIdleHandler(() -> {
// 最后返回false,后续不用再监听了。
return faslse;
});
}
复制代码
addIdleHandler 方法是当handle线程空闲再执行,基本上可以理解为界面显示后再执行。具体原理可以看mp.weixin.qq.com/s/KpeBqIEYe…
异步加载
对于一些没必要在主线程执行的代码,如sp、数据库等代码可以在子线程初始化。
布局优化
看看是否过度绘制以及嵌套层级是否过多。这种一般在写代码时注意一下后期可以避免很多工作。
- 同一层级时LinearLayout、FrameLayout比RelativeLayout性能要好。
- 写布局时background可避免的尽量不设置
- 灵活使用ViewStub,实例化才加载并且只加载一次。
一些其他
点击事件优化
很多app没有做点击误差处理,所以有时候点击两次会出现两个界面,虽然影响不大但是为了让用户体验更好。最好一开始就做好
- 使用RxBind具体看github
- 使用工具类
public class ViewOnClickUtils {
private static final int MIN_DELAY_TIME= 1000; // 两次点击间隔不能少于1000ms
private static long lastClickTime;
/**
* 判断是否快速点击
* @return
*/
public static boolean isFastClick() {
boolean flag = true;
long currentClickTime = System.currentTimeMillis();
if ((currentClickTime - lastClickTime) >= MIN_DELAY_TIME) {
flag = false;
}
lastClickTime = currentClickTime;
return flag;
}
/**
* 判断是否快速点击
* @param time 设置时间
* @return
*/
public static boolean isFastClick(int time) {
boolean flag = true;
long currentClickTime = System.currentTimeMillis();
if ((currentClickTime - lastClickTime) >= time) {
flag = false;
}
lastClickTime = currentClickTime;
return flag;
}
}
复制代码
tv.setOnClickListener(view -> {
if (ViewOnClickUtils.isFastClick()) {
return;
}
//逻辑操作
...
}
复制代码
图片压缩
更小的图片意味着加载更快,上传也更快。
- 以前在外包公司做的时候,ui给的图片很大,没有做到最大限度的压缩,一个背景图几百k,加载的时候回出现内存抖动,所以最好叫UI或者自己去网上找一下压缩。
- 图片上传时也应该使用压缩,一款非常棒的图片压缩开源框架github.com/Curzibn/Lub…
更稳定
避免内存泄漏、内存抖动。我个人觉得这跟平时写代码比较相关
注意
- 需要传入Context的地方,需要注意生命周期是否一致。比如单例应该用ApplicationContext
- 注册的监听需要销毁时反注册,或者循环播放的动画销毁时要记得停止写自定义view时尤其注意。
- 避免在短时间内大量创建对象,比如写列表时如果有这种需要可以使用单例代替或者做好判断。
出现卡顿是我们需要一些工具的辅助排查
- 经常用到LeakCanary,用于看是否有内存泄漏。一般一开始就会接入。接入方式可以看看博客www.jianshu.com/p/70b8c87ea…
- 其他一些工具的介绍例如as自带的monnitors和MAT(memoryAnalyzer:eclipse官方工具) www.jianshu.com/p/d71b51a0e…
崩溃问题
虽然在测试阶段已经会把这个过滤掉,但是实际使用时可能数据出错等等的原因都可能导致崩溃。这时我们最好接入Bugly,接入方法可以看官网,每次崩溃bugly都会记录。但是bugly每天只会推送一次,如果我们需要做一些预警,可以监听app崩溃做个通知发给钉钉机器人或者我们自己的后台方便我们做热修复。
钉钉群截图如下,具体实现可以看对应官方文档以及博客。相对来说比较简单没什么技术含量,只是提供个方案。
更小
可以去除没用的布局、资源以及库加上代码混淆。一般公司可能用不到插件化我也不是很熟悉。
代码混淆
篇幅较长不做过多讲解,学习混淆最主要的是知道混淆原则,就是哪些不应该被混淆。
混淆参考博客
- Android studio自带混淆
- 第三方混淆
建议阅读
关于性能优化的系列文章可以看看他写的文章有性能优化设计模式等等。