Android性能优化

一、性能优化的难点

1. 性能表现差

  • APP启动慢、卡顿、丢帧
  • 内存占用高、抖动频繁
  • 耗电、网络请求慢
  • 崩溃率、异常率高

2. 线上问题无从追查

  • 如何保证异常感知灵敏度
  • 如何复原“案发现场”
  • 如何快速“止血”成功

3. 性能优化的长期开销大

  • 如何扼杀问题于萌芽

二、启动速度优化

  1. 冷启动
  • 耗时最多,是启动速度的衡量标准
    Click Event - IPC - Process.start - ActivityThread - bindApplication - LifeCycle - ViewRootImpl
  1. 热启动
  • 最快
    后台 - 前台
  1. 温启动
  • 较快
    LifeCycle

冷启动之前:

  • 启动APP
  • 加载空白Window
  • 创建进程

随后:

  • 创建Application
  • 启动主线程
  • 创建MainActivity
  • 加载布局
  • 布置屏幕
  • 首帧绘制

优化方向:
Application和Activity生命周期


1. 启动时间的测量方式

(1)adb

adb shell am start -W packageName/packageName.SplashActivity
输出说明:
totalTime: 所有Activity启动耗时;
ThisTime: 最后一个Activity启动耗时;
WaitTime: AMS启动Activity的总耗时。

特点:

  • 线下使用方便,不能带到线上
  • 非严谨精确的时间

(2)手动打点

启动时候打点,结束时候计算
在Application的attachBaseContext(Context cntext)中记录初始计时

public class LaunchTimer {
    private static long  sStartTime;

    /**
     * 开始计算
     * 在Application的attachBaseContext方法调用
     */
    public static void startRecord(){
        sStartTime = System.currentTimeMillis();
    }

    /**
     * 结束计时
     * 在界面显示成功的时候计算
     * ViewTreeObserver viewTreeObserver = mView.getViewTreeObserver();
     * viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
     *     @Override
     *     public boolean onPreDraw() {
     *         viewTreeObserver.removeOnPreDrawListener(this);
     *         finishRecord();
     *         return true;
     *     }
     * });
     */
    public static void finishRecord() {
        long time = System.currentTimeMillis() - sStartTime;
        Log.d("启动耗时", time + "");
    }
}

2. 启动优化工具的选择

(1)traceview

a. 使用方式

Debug.startMethodTracing("");
Debug.stopMethodTracing();
生成本地文件路径:Androdi/data/packagename/files

b. 特点
  • 运行时开销严重,整体都会变慢
  • 由于会导致整体变慢,所以可能带偏优化方向
  • traceview与cpu profiler

(2)systrace

  • 结合Android内核的数据,生成Html报告
  • API18以上才可使用,推荐使用TraceCompat
a.使用方式

python systrace.py -t 10 [other-options][categories]

b.特点
  • 轻量级,开销小
  • 直观反映CPU利用率
c.cputime与walltime的区别
  • walltime是代码执行时间
  • cputme是代码小号cpu的时间(重点指标)
  • 举例:锁冲突

3. 获取方法耗时

(1)常规方法

代码插入System.currentTimeMills()

特点
  • 侵入性强
  • 工作量大

(2)AOP

面向切面编程

a. 特点
  • 针对同一类问题的统一处理
  • 无侵入添加代码
    AspectJ使用
    https://www.jianshu.com/p/f577aec99e17
    classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0'
    apply plugin: 'android-aspectjx'
    implementation 'org.aspectj:aspectjrt:1.8.13'
b. 名词解释
  1. Join Points
    程序运行时的执行点,可以作为切面的地方
  • 方法调用、执行
  • 获取、设置变量
  • 类初始化
  1. PointCut
    带条件的JoinPoints
  2. Advice
    一种Hook,要插入代码的位置
  • Before:PointCut之前执行
  • After:PointCut之后执行
  • Around:PointCut之前、之后分别执行
  1. 语法
@Before("execution(*android.app.Activity.on**(..))")
public void onActivityCalled(JoinPoint joinPoint) throw Throwable {
}
  • Before:Advice,具体插入位置
  • execution:处理Join Point的类型,call、execution
  • (android。app.Activity.on*(…)):匹配规则
  • onActivityCalled:要插入的代码
c. 使用
  1. 类上添加注解@Aspect
  2. 方法添加注解@Around(call(* com.example.app.TestActivity.**(..)))
public void getDuration(ProceedingJoinPoint point) {
	Signature signature = point.getSignature();
	String methodName = signature.toShorString();
	long time = System.currentTmeMillis();
	point.proceed();
	Log.d("耗时", methodName + " " + (System.currentTmeMillis() - time));
}

三、异步优化

1. 技巧

Theme切换(感觉上会快)

  • drawable中创建文件
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:opacity="opaque">
    <item android:drawable="@android:color/white" />
    <item>
        <bitmap
            android:gravity="bottom"
            android:src="@drawable/first_ic_logo" />
    </item>
</layer-list>
<style name="AppTheme.Launcher">
	<item name="android:background">@drawable/first_layer</item>
</style>
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
	setTheme(R.style.MyTheme);
    super.onCreate(savedInstanceState);
}

2. 核心思想

子线程分担主线程任务,并行减少时间

int processors = Runtime.getRuntime().availableProcessors();
int count = Math.max(2, Math.min(processors - 1, 4));
ExecutorService executorService = Executors.newFixedThreadPool(count);
executorService.submit(new Runnable() {
    @Override
    public void run() {
        //todo:需要在子线程执行的方法
        
        countDownLatch.countDown();
    }
});
//该int值表示要执行多少次countDown方法,只有执行完对应次数之后,才不会await,表示要等子线程的方法执行完
CountDownLatch countDownLatch = new CountDownLatch(int次数);`
countDownLatch.countDown();
countDownLatch.await();

表示,执行了int次的countDown方法之后,就不再await方法
注意:

  • 不符合异步的要求
  • 需要在某些阶段完成(CountDownLatch )
  • 区分CPU密集型和IO密集型任务

缺点:

  • 代码不优雅
  • 场景不好处理(如这个方法需要上个方法的结果)
  • 维护成本高

3. 启动器

异步初始化的最优解
充分利用CPU多核,自动梳理任务顺序

流程:

  • 代码Task化,启动逻辑抽象成Task
  • 根据所有任务依赖关系生成一个有向无环图
  • 多线程按照排序后的优先级依次执行

代码见【3-10 异步初始化最优解-启动器-2】

4. 延迟初始化

对延迟任务进行分批初始化
可以提升用户体验
利用IdleHandler特性,在空闲的时候执行
【3-11 更优秀的延迟初始化方案】

5. 其他方案

  • 启动阶段抑制GC
  • CPU锁频(会增加耗电量)

6. 其他

  • 提前异步处理SharedPreferences
  • 启动阶段不启动子进程
  • 提前异步类加载

三、内存优化

1. 内存优化介绍及工具选择

(1)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值