Android 性能优化——APP启动优化详解

news2024/10/6 8:30:23

1.1 为什么要启动优化?

用户希望应用能够及时响应并快速加载,启动时间过长的应用不能满足这个期望,并且可能使用户失望。

启动太慢的结果

  • 体验效果差
  • 用户放弃使用你的应用
  • 时间越长用户流失越高
  • 产品死掉

1.2 启动优化流程及分类

1.2.1 开机启动流程

1.2.2 启动分类

  • 冷启动:应用从头开始启动(应用自设备启动后或系统终止应用后首次启动);
  • 热启动:将Activity带到前台(如果应用的所有Activity都还驻留在内存中,则应用无需重复对象初始化、布局扩充和呈现。需要注意的是,如果程序的某些内存被系统清除,比如调用了onTrimMemory方法,则需要重新创建这些对象以响应热启动事件);
  • 温启动:涵盖在冷启动期间发生的操作的一些子集,同时它的开销比热启动多(它与热启动最大的区别在于,必须通过调用onCreate方法开始重新创建活动,也可以从传递给onCreate方法中保存的实例状态中获得某些对象的恢复)。

冷启动流程:

  1. 加载并启动APP
  2. 启动后立即为该APP显示一个空白启动窗口;
  3. 创建APP进程(创建应用程序对象);
  4. 启动主线程,创建主Activity
  5. 加载布局,绘制。

启动总结

App从被系统调用,再到第一个页面渲染到手机屏幕,我们通常只需要关注Application中的onCreate方法,第一个ActivityonCreateonStartonResume方法。

注意:如果在App启动第一个Activity时,该Activity不但有自己的逻辑,还在onCreateonStart或者onResume方法中直接有跳转到了其它Activity页面,那么跳转后的Activity的这三个方法也需要进行优化。

1.2.3 黑白屏优化

在系统加载并启动App时,需要耗费相应的时间,即使时间不到1S,用户也会感觉到当点击App图标时会有“延迟”现象,为了解决这一个问题,Google的做法是在App创建的过程中,先展示一个空白的页面,让用户体会到点击图标之后立马就有响应,而这个空白页面的颜色则是根据我们在Manifest文件中配置的主题颜色 来决定的,现在一般默认为白色。

可以为应用的加载设置主题背景,从而使应用的启动屏幕在主题背景上与应用的后续效果保持一致,而不是采用系统主题。

方案一:设置LauncherTheme

LauncherTheme中,设置系统“取消预览(空白窗体)”为true,或者设置空白窗体为透明,这样用户从视觉上就无法看出黑白屏的存在:

<style name="AppTheme.LauncherTheme">
	<!--设置系统的取消预览(空白窗口)为true-->
  <item name="android:windowDisablePreview">true</item>
  <!--设置背景为透明-->
  <item name="android:windowIsTranslucent">true</item>
</style>

方案二:自定义Theme主题

  1. 自定义继承自AppTheme的主题;
  2. 将启动ActivityTheme设置为自定义主题;
  3. 在启动ActivityonCreate方法中,在super.onCreatesetContentView方法之前调用setTheme方法,将主题设置为最初的AppTheme

① 自定义主题

<style name="AppTheme.LaunchTheme1">
	<item name="android:windowBackground">@mipmap/ic_launcher</item>
</style>

② 设置启动Activity主题

<activity android:name=".MainActivity"
          android:theme="@style/AppTheme.LaunchTheme1">
	<intent-filter>
  	<action android:name="android.intent.action.MAIN"/>
    <category android:name="android.intent.category.LAUNCHER"/>
  </intent-filter>
</activity>

③ 在代码中将主题设置回来

protected void onCreate(Bundle savedInstanceState) {
  	setTheme(R.style.AppTheme)
    super.onCreate(savedInstanceState);
}

1.3 测量启动时间

1.3.1 测量方式

  • 系统日志输出:在Android4.4及更高的版本中,logcat包括一个输出行,其中包含命令为Displayed的值,此值代表从启动进程在屏幕上完成对应用Activity绘制所经过的时间(MI6测试,并没有);

    1. 启动进程;
    2. 初始化对象;
    3. 创建并初始化Activity:ActivityManager:displayed com.sty.ne.appperformance/.MainActivity: +550ms
    4. 扩充布局;
    5. 首次绘制应用。
  • adb命令:adb shell Activity Manager:

adb [ -d | -e | -s <serialNumber>] shell am start -S -W
com.sty.ne.appperformance/.MainActivity
-c android.intent.category.LAUNCHER
-a android.intent.action.MAIN

adb shell am start -W com.sty.ne.appperformance/.MainActivity
显示结果如下:
GGGdeMac-mini:NeAppPerformance tian$ adb shell am start -W com.sty.ne.appperformance/.activity.SplashActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.sty.ne.appperformance/.activity.SplashActivity }
Status: ok
Activity: com.sty.ne.appperformance/.MainActivity
ThisTime: 186  (最后一个Activity启动耗时)
TotalTime: 395  (所有Activity启动耗时)
WaitTime: 417  (AMS启动Activity的总耗时)
Complete
  • 手动获取:手动打印日志计算启动时间,只能记录应用内耗时。
private void findViews() {
  final View viewRoot = findViewById(R.id.root);
  viewRoot.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
    @Override
    public boolean onPreDraw() {
      viewRoot.getViewTreeObserver().removeOnPreDrawListener(this);
      LauncherTimer.logEnd("tag3");
      return false;
    }
  });
}

@Override
protected void onResume() {
  super.onResume();
  LauncherTimer.logEnd("tag1");
}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
  super.onWindowFocusChanged(hasFocus);
  LauncherTimer.logEnd("tag2");
}
// D/Time: 2/tag1 launcher time=101
// D/Time: 2/tag3 launcher time=139
// D/Time: 2/tag2 launcher time=146

1.3.2 方法耗时统计

  • traceview统计:可以用代码统计,也可以用Android Studio自带的cup profiler来统计;缺点是代码侵入性强,会拖慢程序运行。

    Debug Trace:

    @Override
    public void onCreate() {
      super.onCreate();
      Debug.startMethodTracing("Launcher");
    
      coreSize = Runtime.getRuntime().availableProcessors();
      executorService = Executors.newFixedThreadPool(Math.max(2, Math.min(coreSize - 1, 4)));
      application = this;
      context = this.getApplicationContext();
      AppProfile.context = context;
      ScreenUtil.init(context);
      initLog();
      AppForegroundWatcher.init(context);
      CrashReport.initCrashReport(getApplicationContext(), "e9bf59bd43", false);
    
      Debug.stopMethodTracing();
      //sdcard/Android/data/com.sty.ne.appperformance/files/Launcher.trace  --> save as 导出来,用Profiler打开
    }
    

    sdcard/Android/data/com.sty.ne.appperformance/files/Launcher.trace --> save as 导出来,用Profiler打开,如下图所示:

缺点:只能记录应用内程序执行时间。

CPU Profiler:

不需要侵入代码(无需写Debug.startMethodTracing("Launcher"),但是需要做如下配置:

  1. run -> edit configurations
  2. 勾选start recording a method trace on startup
  3. 从菜单中选择cpu记录配置(profiling菜单下勾选两个复选框);
  4. apply --> profile模式部署。
  • systrace统计

    在代码中添加命令:

    @Override
    public void onCreate() {
      super.onCreate();
      //systemtrace方式
      Trace.beginSection("Launcher");
    
      coreSize = Runtime.getRuntime().availableProcessors();
      executorService = Executors.newFixedThreadPool(Math.max(2, Math.min(coreSize - 1, 4)));
      application = this;
      context = this.getApplicationContext();
      AppProfile.context = context;
      ScreenUtil.init(context);
      initLog();
      AppForegroundWatcher.init(context);
      CrashReport.initCrashReport(getApplicationContext(), "e9bf59bd43", false);
    
      Trace.endSection();
    }
    

命令行终端进入如下目录:/Users/tian/Library/Android/sdk/platform-tools/systrace

输入如下命令进入监听状态:

    python systrace.py -o mynewtrace.html sched freq idle am wm gfx view binder_driver hal dalvik camera input res

此时运行代码,完成之后在命令行窗口按Enter键结束监听,然后会生成目标文件mynewtrace.html:

分析目标文件:

  • aop方式统计

1.4 优化方式

1.4.1 异步优化

异步优化主要是采用子线程来进行线程初始化,并行执行,减少执行时间。

@Override
public void onCreate() {
  super.onCreate();

  coreSize = Runtime.getRuntime().availableProcessors();
  executorService = Executors.newFixedThreadPool(Math.max(2, Math.min(coreSize - 1, 4)));
  application = this;
  context = this.getApplicationContext();
  AppProfile.context = context;
  ScreenUtil.init(context);
  async(new Runnable() {
    @Override
    public void run() {
      initLog();
    }
  });
  async(new Runnable() {
    @Override
    public void run() {
      AppForegroundWatcher.init(context);
    }
  });
  async(new Runnable() {
    @Override
    public void run() {
      CrashReport.initCrashReport(getApplicationContext(), "e9bf59bd43", false);
    }
  });
}

异步优化需要关注的点:

  1. 确定能不能异步优化;
  2. 执行的方法是否有先后顺序;
  3. 需要注意异步后程序能否正常执行;
  4. 异步线程中使用的api不能创建Handler
  5. 不能有UI操作。

1.4.2 延迟初始化

仅初始化立即需要的对象,不要创建全局静态对象,而是移动到单例模式,其中应用仅在第一次访问对象时初始化它们。

1.4.3 空闲时初始化

可以监听应用空闲时间,在空闲时间进行初始化。

public class DelayInit {
    private Queue<Runnable> delayQueue = new LinkedList<>();

    public void add(Runnable runnable) {
        delayQueue.add(runnable);
    }

    public void start() {
        Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
            @Override
            public boolean queueIdle() {
                Runnable poll = delayQueue.poll();
                if(poll != null) {
                    poll.run();
                }
                return !delayQueue.isEmpty();
            }
        });
    }
}

其实出了启动优化外,Android 性能优化中还有 内存优化、网络优化、卡顿优化、存储优化……等,为了让大家一次都可以了解全,所以将其整合成名为《Android 性能优化核心知识点手册》,大家可以参考下:

《APP 性能调优进阶手册》:https://qr18.cn/FVlo89

启动优化

内存优化

UI优化

网络优化

Bitmap优化与图片压缩优化

多线程并发优化与数据传输效率优化

体积包优化

《Android 性能调优核心笔记汇总》:https://qr18.cn/FVlo89

《Android 性能监控框架》:https://qr18.cn/FVlo89

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/411035.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HDLBits-Modules 题解【Verilog模块例化】(中文翻译+英文原文,可顺带学习英文)

Moudule 概念介绍 到目前为止&#xff0c;你已经熟悉了一个模块&#xff0c;它是一个通过输入和输出端口与其外部交互的电路。更大、更复杂的电路是通过将较小的模块和其他连接在一起的部分&#xff08;例如赋值语句和always块&#xff09;组合而成的更大模块来构建的。因为模…

从零开始学OpenCV——图像灰度变换详解(线性与非线性变换)

文章目录图像灰度变化灰度变换介绍灰度线性变换灰度分段线性变换图像点运算灰度非线性变换线性点运算灰度的非线性变换&#xff1a;对数变换灰度的非线性变换&#xff1a;伽马变换灰度的非线性变换&#xff1a;对比拉伸灰度的非线性变换&#xff1a; S形灰度变换灰度的非线性变…

tomcat中出现RFC7230和RFC3986问题解析

问题截图 问题分析 出现上述问题&#xff0c;是因为各版本tomcat中对特殊字符和请求路径中携带中文参数而产生的错误提示。 解决办法 1、调整tomcat版本 tomcat 7.0.76之前的版本不会出现类似问题 2、tomcat9之前&#xff0c;修改tomcat目录底下的/conf/catalina.properti…

233:vue+openlayers绘制渐变填充色的圆形、多边形

第233个 点击查看专栏目录 本示例的目的是介绍如何在vue+openlayer中绘制带有渐变填充色的圆形、多边形。这里用canvas的方式去渲染,用到了DEVICE_PIXEL_RATIO,设备上的物理像素与设备无关像素 (dips) 之间的比率 (window.devicePixelRatio)。 直接复制下面的 vue+openlayer…

用ChatGPT创建一个REST API

ChatGPT是OpenAI公司开发的大型语言模型。在本文中&#xff0c;主要探讨如何使用ChatGPT在C#中创建REST API。 一、简介 ChatGPT是由人工智能研究中心OpenAI创建的尖端自然语言处理模型&#xff0c;OpenAI公司是由埃隆马斯克、萨姆奥特曼和格雷格布罗克曼共同创办的。该模型于…

360浏览器+Adobe Acrobat DC实现在线预览PDF大样校对

甲方&#xff1a;实现方正PDF文字大样校对&#xff0c;校对后在360浏览器中新开一个页面在线预览PDF文字大样校对结果。 我方实现过程&#xff1a; 1.方案选择 方案零&#xff1a;使用浏览器自带的PDF阅览器&#xff0c;经测试360极速模型,谷歌等软件能预览但是标记的PDF内容…

【笔记】大模型,大资料

大模型&#xff0c;大资料&#xff0c;loss会降低&#xff0c;准确率会增加 1大模型 1.1模型的顿悟时刻 举了一个一知半解的例子 1.2 模型 chain of thought 模型足够大时才会有比较好的作用 calibration 检测模型对于答案的confidence 会出现 “u-shape” 2.大资料 文法…

系统复杂度之【高可用】

接着&#xff0c;我们聊聊复杂度的第二个要求高可用。 参考维基百科&#xff0c;先来看看高可用的定义。 系统无中断地执行其功能的能力&#xff0c;代表系统的可用性程度&#xff0c;是进行系统设计时的准则之一。 这个定义的关键在于“ 无中断”&#xff0c;但恰好难点也在“…

Java 并发编程面试题——Future

目录1.什么是 Future 模式&#xff1f;Java 中是如何实现的&#xff1f;2.Callable、Future 与 FutureTask 分别是什么&#xff1f;2.1.Callable 接口2.2.Future 接口2.3.FutureTask 类3.CompletableFuture 类有什么用&#xff1f;1.什么是 Future 模式&#xff1f;Java 中是如…

windows系统管理_Windows server 2016 组管理与授权

组账户的概述 在 windows 服务器中&#xff0c;当我们需要为多个用户设置相同的权限时&#xff0c;一个一个的逐一设置会比较 麻烦&#xff0c;这个时候我们就需要用到另一种模式&#xff0c;组账户&#xff0c;使用此账户来进行简化操作。 在以后的职场中&#xff0c;每家公司…

Flink 优化 (五) --------- Job 优化

目录一、使用 DataGen 造数据1. DataStream 的 DataGenerator2. SQL 的 DataGenerator二、算子指定 UUID三、链路延迟测量四、开启对象重用五、细粒度滑动窗口优化一、使用 DataGen 造数据 开发完 Flink 作业&#xff0c;压测的方式很简单&#xff0c;先在 kafka 中积压数据&a…

全景图像畸变校正

1.简介 理想的相机基本上是小孔成像的&#xff0c;在小孔成像模型中&#xff0c;如果焦距一定&#xff0c;那么图像传感器像素平面的面积直接决定了相机视场角的大小&#xff0c;超过这个视场角范围的物体不会被镜头获取到。因此基于透镜成像原理的相机&#xff0c;视场角无法…

JAVA解析XML时的内存消耗问题

问题出现 最近一个项目中&#xff0c;有个需求功能是从外部传入的XML读取数据&#xff0c;然后写入到数据库中。 写完之后&#xff0c;有在本地电脑上的Tomcat跑了一下&#xff0c;正常读取到XML中的数据&#xff0c;并整理好后&#xff0c;插入了数据库保存了。但是线上运行的…

C语言文件操作复习回顾(2)

TIPS 文件的顺序读写&#xff1a;fgetc, fputc, fputs&#xff08;一行字符串的输出\n注意一下&#xff09;, fgets&#xff08;一行字符串的输入\n三特性&#xff09;&#xff0c;fprintf&#xff08;格式化字符串的输出联想printf很简单&#xff09;&#xff0c;fscanf&…

pyspark 实验二,rdd编程

1.环境准备 start-all.sh启动Hadoop ./bin start-all.sh 启动spark 上传数据集 1.求该系总共多少学生 linessc.textFile("file:///home/data.txt") res lines.map(lambda x:x.split(",")).map(lambda x:x[0]) sumres.distinct() sum.cont() 2.求该系设置…

【MybatisPlus快速入门】—— 进阶入门

进阶篇 1.1 映射专题 Mybatis 框架之所以能够简化数据库操作&#xff0c;是因为他内部的映射机制&#xff0c;通过自动映射&#xff0c;进行数据的封装&#xff0c;我们只要符合映射规则&#xff0c;就可以快速高效的完成SQL操作的实现既然 MybatisPlus 是基于Mybatis 的增强…

程序员如何能提高自己的编程水平?

这些实用的小建议&#xff0c;能帮你迅速地提高编程水平&#xff1a; 不要做无意义的奋斗 拒绝喊口号和无意义的奋斗&#xff0c;包括但不限于&#xff1a; ①做了计划表却从未有执行的一天&#xff1b; ②每天都是最早来、最晚走&#xff0c;但是工作进度趋近于0&#xff1b…

ASP.NET Core MVC 从入门到精通之接化发(一)

随着技术的发展&#xff0c;ASP.NET Core MVC也推出了好长时间&#xff0c;经过不断的版本更新迭代&#xff0c;已经越来越完善&#xff0c;本系列文章主要讲解ASP.NET Core MVC开发B/S系统过程中所涉及到的相关内容&#xff0c;适用于初学者&#xff0c;在校毕业生&#xff0c…

PathCore:IAD文献解读

论文链接&#xff1a;[Towards Total Recall in Industrial Anomaly Detection]Towards Total Recall in Industrial Anomaly Detection &#xff1a;数据集&#xff0c; &#xff1a;标签 : 在ImageNet上预训练后的网络 第 张图 网络中第 层 1. Locall…

Sentinel学习笔记

Sentinel 官方文档&#xff1a; https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5 SpringCloud Alibaba&#xff1a; https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel 是什么…