Android优化提升应用启动速度及Splash页面的设计
作者:冬日毛毛雨 发布时间:2022-02-18 06:02:27
目录
1.启动分为两种方式
2.如何测量一个应用的启动时间
3.应用启动的流程
4.减少应用的启动时间的耗时
5.如何设计延迟加载DelayLoad
1.启动分为两种方式
1) 冷启动:当直接从桌面上直接启动,同时后台没有该进程的缓存,这个时候系统就需要重新创建一个新的进程并且分配各种资源。
2) 热启动:该
app
后台有该进程的缓存,这时候启动的进程就属于热启动。热启动不需要重新分配进程,也不会
Application
了,直接走的就是app
的入口Activity
,这样速度就很快
2.如何测量一个应用的启动时间
使用命令行来启动app,同时进行时间测量。单位:毫秒
adb shell am start - W [PackageName] /[PackageName.MainActivity]
例: adb shell am start -W com.haocai.app/.activity.GuideActivity
热启动耗时:
打印的结果为:
ThisTime
该activity
启动耗时TotalTime
应用自身启动耗时=ThisTime+
应用application
等资源启动时间WaitTime
系统启动应用耗时=TotalTime
+系统资源启动时间
3.应用启动的流程
Application
从构造方法开始 ---> attachBaseContext()
---> onCreate()
构造方法 --->
ActivityonCreate()
---> 设置显示界面布局,设置主题、背景等等属性 ---> onStart()
---> onResume()
---> 显示里面的View(测量、布局、绘制,显示到界面上)
从构造方法我们知道,启动耗时的主要花费在各个启动流程中
4.减少应用的启动时间的耗时
根据应用的启动流程,我们从而得到以下减少应用启动耗时操作的建议:
不要在
Application
的构造方法、attachBaseContext()
、onCreate()
里面进行初始化耗时操作。MainActivity,由于用户只关心最后显示的这一帧,对我们的布局的层次要求减 自定义控件的测量、布局、绘制的时间。 同时 不要在
onCreate
、onStart
、onResume
当中的做耗时操作。对于
SharedPreference
的初始化
因为它初始化的时候是需要将数据全部读取出来放到内存当中。
优化1:可以尽可能减少sp文件数量(IO需要时间)
优化2:像这样的初始化最好放到线程里面
优化3:大量的数据缓存到数据库中
app启动的耗时主要在:Application
初始化 + MainActivity
的界面加载绘制时间。
由于MainActvity
的业务和布局复杂度非常高,甚至该界面必须要有一些初始化的数据才能显示。
那么这个时候MainActivity
就可能半天都出不来,这就给用户感觉App太卡了。
常规方法:
1.我们要做的就是给用户赶紧利落的体验。点击app就立马弹出我们的界面。
于是乎想到使用SplashActivity
--非常简单的一个欢迎页面上面都不干就只显示一个图片。
2.但是SplashActivity
启动之后,还是需要跳到MainActivity
。MainActivity
还是需要从头开始加载布局和数据。
想到SplashActivity里面可以去做一些MainActivity
的数据的预加载。然后需要通过意图传到MainActivity
。
更好的优化:
耗时的问题:Application
+Activity
的启动及资源加载时间;预加载的数据花的时间。
如果我们能让这两个时间重叠在一个时间段内并发地做这两个事情就省时间了。
比如:将SplashActivity
和MainActivity
合为一个。
一进来还是显示MainActivity
,SplashActivity
可以变成一个SplashFragment
,然后放一个FrameLayout
作为根布局直接显示SplashFragment
界面。SplashFragment
里面非常之简单,就是现实一个图片,启动非常快。
当SplashFragment
显示完毕后再将它remove
。同时在splash
的2S的友好时间内进行网络数据缓存。
这个时候我们才看到MainActivity
,就不必再去等待网络数据返回了。
新问题:SplashView
和ContentView
加载放到一起来做了 ,这可能会影响应用的启动时间?
解决:可以使用ViewStub延迟加载MainActivity
当中的View来达到减轻这个影响。viewStub
的设计就是为了防止MainActivity的启动加载资源太耗时了。延迟进行加载,不影响启动,用户友好。
但是viewStub加载也需要时间。等到主界面出来以后。viewStub.inflate(xxxx);
5.如何设计延迟加载DelayLoad
第一时间想到的就是在onCreate
里面调用Handler.postDelayed()
方法;
问题一:这个延时时间如何控制
不同的机器启动速度不一样,这个时间如何控制?
假设,先需要splash
做一个2s动画,然后在MainActivity
中主界面加载完成之后,关闭splash
页面
如果这样写:
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mProgressBar.setVisibility(View.GONE);
iv.setVisibility(View.VISIBLE);
}
}, 2500);
是无法在准确监听页面加载完的
问题:什么时候应用已经启动并加载完成,界面已经显示出来了。
采用onResume
执行完了之后才显示完毕?不行。
建议采用getDecorView()
获取上级view
然后添加视图
综合上诉方案,以下是关键代码:
public class MainActivity extends AppCompatActivity {
private Handler mHandler = new Handler();
private SplashFragment splashFragment;
private ViewStub viewStub;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
splashFragment = new SplashFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame, splashFragment);
transaction.commit();
/**
*这么写不好判断视图有没有加载完
*/
// mHandler.postDelayed(new Runnable() {
// @Override
// public void run() {
// mProgressBar.setVisibility(View.GONE);
// iv.setVisibility(View.VISIBLE);
// }
// }, 2500);
viewStub = (ViewStub) findViewById(R.id.content_viewstub);
//1.判断当窗体加载完毕的时候,立马再加载真正的布局进来
getWindow().getDecorView().post(new Runnable() {
@Override
public void run() {
// 开启延迟加载
mHandler.post(new Runnable() {
@Override
public void run() {
//将viewstub加载进来
viewStub.inflate();
}
});
}
});
//2.判断当窗体加载完毕的时候执行,延迟一段时间做动画。
getWindow().getDecorView().post(new Runnable() {
@Override
public void run() {
// 开启延迟加载,也可以不用延迟可以立马执行(我这里延迟是为了实现fragment里面的动画效果的耗时)
mHandler.postDelayed(new DelayRunnable(MainActivity.this, splashFragment), 2000);
}
});
//3.同时进行异步加载数据
//......
}
static class DelayRunnable implements Runnable {
private WeakReference<Context> contextWeakReference;
private WeakReference<SplashFragment> splashFragmentWeakReference;
public DelayRunnable(Context context, SplashFragment f) {
contextWeakReference = new WeakReference<Context>(context);
splashFragmentWeakReference = new WeakReference<SplashFragment>(f);
}
@Override
public void run() {
//移除Fragment
if (contextWeakReference != null) {
SplashFragment splashFragment = splashFragmentWeakReference.get();
if (splashFragment == null) {
return;
}
FragmentActivity activity = (FragmentActivity) contextWeakReference.get();
FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
transaction.remove(splashFragment);
transaction.commit();
}
}
}
}
activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ViewStub
android:id="@+id/content_viewstub"
android:layout="@layout/activity_main_viewstub"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<FrameLayout
android:id="@+id/frame"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
</RelativeLayout>
ps:测试数据是在老款三星手机下所得耗时数据。
不过相较于SplashActivity+MainActivity
启动速度优化还是挺明显的。大家可以在自己手机上试试。
来源:https://juejin.cn/post/7036981414893453319


猜你喜欢
- 一、概要介绍 本文要介绍的是Java中的transient关键字,transient是短暂的意思。对于transie
- 概述:开发过程中,看到有些界面用到一道光线在屏幕中掠过的效果,觉得挺炫的。所以查找相关资料自己实现了一遍。先上个预览图:实现思路:简单来说就
- 在java开发的实际场景中,我们经常要对时间进行格式化处理,但是每次获取开发中自己需要的格式都要重新写一个方法,这样的代码看起来是非常的笨重
- 本文实例讲述了java求最大公约数与最小公倍数的方法。分享给大家供大家参考,具体如下:Gongyueshu.java文件:package m
- 文章来源:互联网 作者:ggg82/CSDN现在许多用户界面都使用工具栏制作菜单条,小弟最近对此感兴趣,便从网上求助,可是得到的帮助大多是B
- 本文实例讲述了java GUI编程之paint绘制操作。分享给大家供大家参考,具体如下:import java.awt.*;public c
- 用Java编写一个简单的酒店管理系统,供大家参考,具体内容如下为某个酒店编写程序:酒店管理系统,模拟订房、退房、打印所有房间状态等功能。1、
- 单选题:(每道题目2分)1. 下列哪个声明是错误的?(B) A. int i=10;B. float f=1.1;&
- 前言Spring的一个核心功能是IOC,就是将Bean初始化加载到容器中,Bean是如何加载到容器的,可以使用Spring注解方式或者Spr
- 前言BeanPostProcessor是一个工厂钩子,允许Spring框架在新创建Bean实例时对其进行定制化修改。例如:通过检查其标注的接
- 客户端代码:/// <summary>/// 批量上传图片/// </summary>/// <param n
- 一、Java 运行时数据区域友情提示:这部分内容可能大部分同学都有一定的了解了,可以跳过直接进入下一小节哈。Java 虚拟机在执行 Java
- for循环语句重复执行语句,直到条件变为 false。语法for ( init-expression ; cond-expression ;
- 业务场景通常微服务对于用户认证信息解析有两种方案在 gateway 就解析用户的 token 然后路由的时候把 userId 等相关信息添加
- 目录题目及要求:提示:原创代码:代码思路:题目及要求:给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。提示:0 <
- 一、简介  Apache ShardingSphere 是一套开源的分布式数据库解决方案组成的生态圈,它
- 本文实例讲述了C#检测远程计算机端口是否打开的方法。分享给大家供大家参考。具体分析如下:这段C#代码用于检测远程计算机的3389端口是否处理
- 本文实例为大家分享了C++实现哈夫曼树的编码解码,供大家参考,具体内容如下代码:#pragma once#include<iostre
- 通过上一篇的分析,我们知道了独占模式获取锁有三种方式,分别是不响应线程中断获取,响应线程中断获取,设置超时时间获取。在共享模式下获取锁的方式
- springBoot框架的特点就是舍去了繁琐的配置。使开发者可以把更多的精力放在业务逻辑的开发上。springBoot搭建分三步。1.创建一