Android-性能优化------启动优化

4.1 使用adb 命令方式(线下使用方便)

adb shell am start -W 包名/包名+类名

image

ThisTime:最后一个activity的启动耗时
TotalTime:所有activity的启动耗时
WaitTime:AMS启动activity的总耗时
这里由于我直接进入到主界面,中间并没有SplashActivity,所有ThisTime 和 TotalTime的时间是一样的

优势:在线下使用方便,适合于跑线下的产品,和获取竞品的时间,然后比对
缺点:不能带到线上,获取的时间,只能说是一个大概时间,不是很严谨。

4.2 手动打点方式

image

通过System.currentTimeMillis()来打时间戳
缺点:很明显,对代码侵入性非常的大,如果说我想要打出每一个任务花费的时间,那么代码看起来就很恶心了

5、优雅获取方法耗时

5.1 AOP Aspect Oriented Programming 面向切面编程

AOP:通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。它的核心思想就是将应用程序中的业务逻辑处理部分同对其提供通用服务部分即“横切关注点”进行分离。
OOP:引入封装,继承,多态等概念来建立一种对象层次结构,它允许开发者定义纵向的关系,但并不适合横向的关系。
可以说AOP是OOP的一种补充和完善。

5.2 aspectj的使用

AspectJ是一个面向切面编程的框架,是对java的扩展且兼容java,AspectJ定义了AOP语法,它有一个专门的编译器来生成遵守java字节编码规范的Class文件。

在项目的根目录的build.gradle添加依赖:

dependencies {
classpath ‘com.android.tools.build:gradle:3.5.2’
classpath ‘com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0’

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}

在app下的build.gradle添加依赖

apply plugin: ‘android-aspectjx’

在dependencies中添加

implementation ‘org.aspectj:aspectjrt:1.9.4’

然后创建一个类

package com.noahedu.myapplication.aspectj;

import android.util.Log;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

/**

  • @Description: //@Before 在切入点之前运行
  • // @After(“”)
  • //@Around 在切入点前后都运行
    
  • @Author: huangjialin
  • @CreateDate: 2020/7/10 14:07
    */
    @Aspect
    public class MyApplicationAspectj {

@Around(“call(* com.noahedu.myapplication.MyApplication.**(…))”)
public void getTime(ProceedingJoinPoint joinPoint){
Signature signature = joinPoint.getSignature();
String name = signature.getName();
long time = System.currentTimeMillis();
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
Log.e("MyApplicationAspectj " ,(name + " cost " + (System.currentTimeMillis() - time)));
}
}

这样我们运行的时候,就会直接在logcat中打印出application中的onCreate方法中所有调用方法的耗时情况了

2020-07-10 14:22:27.151 1619-1619/? E/MyApplicationAspectj: taskOne cost 150
2020-07-10 14:22:29.203 1619-1619/com.noahedu.myapplication E/MyApplicationAspectj: taskTwo cost 2052
2020-07-10 14:22:29.554 1619-1619/com.noahedu.myapplication E/MyApplicationAspectj: taskThrid cost 351
2020-07-10 14:22:30.556 1619-1619/com.noahedu.myapplication E/MyApplicationAspectj: taskFour cost 1001

这样我们几乎没有碰Application中的任何代码,也就够得出各个方法的耗时,几乎对代码无侵入。

6、启动优化的工具选择

6.1 traceview

TraceView是Android SDK中内置的一个工具,他可以加载trace文件,以图形化的形式展示相应代码的执行时间,次数及调用栈,便于我们分析。

Debug.startMethodTracing(“MyApplication”);
//TODO
Debug.stopMethodTracing();

运行项目就可以我们的SD卡中找到对应的trace文件了,如果是Android studio可以直接在右下角找到
DeviceFileExporer -->sdcard --> Android – > data -->files —>自己项目的包名
然后双击即可查看文件了
优点:使用简单,图形形式展示所执行的时间,调用栈等。
缺点:会影响到我们优化的方向,由于是图形化展示,也是比较占用CPU资源的,所以得到的时间往往是比实际的要大。

7、启动器

上面介绍了多了几个获取任务执行时间的方式和工具,那么当我们知道某个方法耗时了,我们该怎么处理呢?

package com.noahedu.myapplication;

import android.app.Application;
import android.os.Debug;
import android.util.Log;

/**

  • @Description: java类作用描述
  • @Author: huangjialin
  • @CreateDate: 2020/7/10 9:59
    */
    public class MyApplication extends Application {

@Override
public void onCreate() {
super.onCreate();
Debug.startMethodTracing(“MyApplication”);
taskOne();
taskTwo();
taskThrid();
taskFour();
Debug.stopMethodTracing();
}

public void taskOne(){
try {
Thread.sleep(150);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public void taskTwo(){
try {
Thread.sleep(2050);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public void taskThrid(){
try {
Thread.sleep(350);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public void taskFour(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

现在application的onCreate方法中有几个任务,各个耗时是不一样的。可能很多同学就会说了,异步处理啊,开线程,放到线程池中,或者创建一个IntentService来执行。那么我们就要考虑几个问题了
第一:异步处理,如果在某个页面需要用到某个SDK,但是又没有初始化完成呢?
第二:假如说taskTwo需要taskOne的某个返回值呢?怎么保证taskOne在taskTwo之前执行完毕呢?
第三:开线程,开多少个线程呢?多了会造成资源浪费,少了资源又没有合理的利用。

我个人觉得针对于启动优化,在Application中的onCreate()进行初始化任务操作,我们首先需要对这些任务进行一个优先级划分,针对于那些优先级高的任务,我们可以优先进行处理,对于那些优先级较低的,我们可以适当的延迟进行加载。

其次有很多同学喜欢把那些优先级较低的任务进行延迟加载,比如new Handler().postDelayed(),这种我觉得是非常不可取的,假如说放在postDelayed中的任务耗时2s,延迟1s进行处理,那么在执行2s任务的过程中,有用户进行操作,那岂不是很卡吗,很明显,这是指标不治本的。

7.1 启动器的思想

针对上面说的几个痛点,怎么在处理上面的几个痛点,又能保证代码的可维护性呢?换句话说就是一个新人不需要理解整个过程,直接就可以开干呢?那么,启动器来了。
启动器核心思想:充分利用CPU多核,自动梳理任务顺序

7.2 启动器的原理

1、任务全部封装成Task对象,传入到集合中。
2、根据所有的任务依赖关系,形成一个有向无环图,然后通过拓扑排序排列出任务的执行流程
3、通过CountDownLatch来控制某一个任务是否执行完毕才进行下一步。
4、线程池创建核心线程的数量,由手机的核数量决定的。

7.3启动器使用方式

image

7.4启动器核心代码

image

进行任务排序

package com.noahedu.launchertool.launchstarter.sort;

import com.noahedu.launchertool.launchstarter.task.Task;
import com.noahedu.launchertool.launchstarter.utils.DispatcherLog;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import androidx.annotation.NonNull;
import androidx.collection.ArraySet;

public class TaskSortUtil {

private static List sNewTasksHigh = new ArrayList<>();// 高优先级的Task

/**

  • 任务的有向无环图的拓扑排序
  • @return
    */
    public static synchronized List getSortResult(List originTasks,
    List<Class<? extends Task>> clsLaunchTasks) {
    long makeTime = System.currentTimeMillis();

Set dependSet = new ArraySet<>();
Graph graph = new Graph(originTasks.size());
for (int i = 0; i < originTasks.size(); i++) {
Task task = originTasks.get(i);
if (task.isSend() || task.dependsOn() == null || task.dependsOn().size() == 0) {
continue;
}
for (Class cls : task.dependsOn()) {
int indexOfDepend = getIndexOfTask(originTasks, clsLaunchTasks, cls);
if (indexOfDepend < 0) {
throw new IllegalStateException(task.getClass().getSimpleName() +
" depends on " + cls.getSimpleName() + " can not be found in task list ");
}
dependSet.add(indexOfDepend);
graph.addEdge(indexOfDepend, i);
}
}
List indexList = graph.topologicalSort();
List newTasksAll = getResultTasks(originTasks, dependSet, indexList);

DispatcherLog.i("task analyse cost makeTime " + (System.currentTimeMillis() - makeTime));
printAllTaskName(newTasksAll);
return newTasksAll;
}

@NonNull
private static List getResultTasks(List originTasks,
Set dependSet, List indexList) {
List newTasksAll = new ArrayList<>(originTasks.size());
List newTasksDepended = new ArrayList<>();// 被别人依赖的
List newTasksWithOutDepend = new ArrayList<>();// 没有依赖的
List newTasksRunAsSoon = new ArrayList<>();// 需要提升自己优先级的,先执行(这个先是相对于没有依赖的先)
for (int index : indexList) {
if (dependSet.contains(index)) {
newTasksDepended.add(originTasks.get(index));
} else {
Task task = originTasks.get(index);
if (task.needRunAsSoon()) {
newTasksRunAsSoon.add(task);
} else {
newTasksWithOutDepend.add(task);
}
}
}
// 顺序:被别人依赖的————》需要提升自己优先级的————》需要被等待的————》没有依赖的
sNewTasksHigh.addAll(newTasksDepended);
sNewTasksHigh.addAll(newTasksRunAsSoon);
newTasksAll.addAll(sNewTasksHigh);
newTasksAll.addAll(newTasksWithOutDepend);
return newTasksAll;
}

private static void printAllTaskName(List newTasksAll) {
if (true) {
return;
}
for (Task task : newTasksAll) {
DispatcherLog.i(task.getClass().getSimpleName());
}
}

public static List getTasksHigh() {
return sNewTasksHigh;
}

/**

  • 获取任务在任务列表中的index
  • @param originTasks
  • @return
    */
    private static int getIndexOfTask(List originTasks,
    List<Class<? extends Task>> clsLaunchTasks, Class cls) {
    int index = clsLaunchTasks.indexOf(cls);
    自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

学习分享

在当下这个信息共享的时代,很多资源都可以在网络上找到,只取决于你愿不愿意找或是找的方法对不对了

很多朋友不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘

如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。

2021最新上万页的大厂面试真题

七大模块学习资料:如NDK模块开发、Android框架体系架构…

只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。

这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
**第三,**到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。

由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

系架构…**

[外链图片转存中…(img-NQ92YAaG-1713778373762)]

只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。

这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
**第三,**到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。

由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值