版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liyan147/article/details/79280924
今天打开我的博客一看,居然有一年没有更新了,不知道什么原因自己似乎把去年决定写博客的事情给忘了.最近报了一个培训班,突然产生一个想法,想把学习的东西用博客的形式都记录下来.想起来以前的坚持被自己轻易就放下了心里很难过,不过现在捡起来也不晚吧,只能这样安慰自己了.
ok,进入正题.
APP启动原理及优化
第一步:我们的手机系统也是一个APP,系统界面上放置了我们下载的各种APP,当我们点击我们的一个APP的图标的时候,Lancer就会把对应的应用程序启动起来.下面是APP启动过程的草图.
整个应用程序的启动过程要执行很多步骤,但是整体来看,主要分为以下五个阶段:
一. Step1 - Step 11:Launcher通过Binder进程间通信机制通知ActivityManagerService,它要启动一个Activity;
二. Step 12 - Step 16:ActivityManagerService通过Binder进程间通信机制通知Launcher进入Paused状态;三. Step 17 - Step 24:Launcher通过Binder进程间通信机制通知ActivityManagerService,它已经准备就绪进入Paused状态,于是ActivityManagerService就创建一个新的进程,用来启动一个ActivityThread实例,即将要启动的Activity就是在这个ActivityThread实例中运行;
四. Step 25 - Step 27:ActivityThread通过Binder进程间通信机制将一个ApplicationThread类型的Binder对象传递给ActivityManagerService,以便以后ActivityManagerService能够通过这个Binder对象和它进行通信;五. Step 28 - Step 35:ActivityManagerService通过Binder进程间通信机制通知ActivityThread,现在一切准备就绪,它可以真正执行Activity的启动操作了。
在Android系统中,应用程序是由Launcher启动起来的,其实,Launcher本身也是一个应用程序,其它的应用程序安装后,就会Launcher的界面上出现一个相应的图标,点击这个图标时,Launcher就会对应的应用程序启动起来
Launcher 源码
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.commands.uiautomator;
import android.os.Process;
import java.util.Arrays;
/**
* Entry point into the uiautomator command line
*
* This class maintains the list of sub commands, and redirect the control into it based on the
* command line arguments. It also prints out help arguments for each sub commands.
*
* To add a new sub command, implement {@link Command} and add an instance into COMMANDS array
*/
public class Launcher {
/**
* A simple abstraction class for supporting generic sub commands
*/
public static abstract class Command {
private String mName;
public Command(String name) {
mName = name;
}
/**
* Returns the name of the sub command
* @return
*/
public String name() {
return mName;
}
/**
* Returns a one-liner of the function of this command
* @return
*/
public abstract String shortHelp();
/**
* Returns a detailed explanation of the command usage
*
* Usage may have multiple lines, indentation of 4 spaces recommended.
* @return
*/
public abstract String detailedOptions();
/**
* Starts the command with the provided arguments
* @param args
*/
public abstract void run(String args[]);
}
public static void main(String[] args) {
// show a meaningful process name in `ps`
Process.setArgV0("uiautomator");
if (args.length >= 1) {
Command command = findCommand(args[0]);
if (command != null) {
String[] args2 = {};
if (args.length > 1) {
// consume the first arg
args2 = Arrays.copyOfRange(args, 1, args.length);
}
command.run(args2);
return;
}
}
HELP_COMMAND.run(args);
}
private static Command findCommand(String name) {
for (Command command : COMMANDS) {
if (command.name().equals(name)) {
return command;
}
}
return null;
}
private static Command HELP_COMMAND = new Command("help") {
@Override
public void run(String[] args) {
System.err.println("Usage: uiautomator <subcommand> [options]\n");
System.err.println("Available subcommands:\n");
for (Command command : COMMANDS) {
String shortHelp = command.shortHelp();
String detailedOptions = command.detailedOptions();
if (shortHelp == null) {
shortHelp = "";
}
if (detailedOptions == null) {
detailedOptions = "";
}
System.err.println(String.format("%s: %s", command.name(), shortHelp));
System.err.println(detailedOptions);
}
}
@Override
public String detailedOptions() {
return null;
}
@Override
public String shortHelp() {
return "displays help message";
}
};
private static Command[] COMMANDS = new Command[] {
HELP_COMMAND,
new RunTestCommand(),
new DumpCommand(),
new EventsCommand(),
};
}
如何计算启动花费的时间
打开cmd输入
adb shell am start -W [packageName]/[packageName.launchActivity]
APP启动方式
1
冷启动:当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就是冷启动。
冷启动因为系统会重新创建一个新的进程分配给它,所以会先创建和初始化 Application 类,再创建和初始化 MainActivity
类,最后显示在界面上。
2 热启动:当启动应用时,后台已有该应用的进程(例:按back键、home键,应用虽然会退出,但是该应用的进程是依然会保留
在后台,可进入任务列表查看),所以在已有进程的情况下,这种启动会从已有的进程中来启动应用,这个方式叫热启动。热启动
因为会从已有的进程中来启动,所以热启动就不会走 Application 这步了,而是直接走 MainActivity,所以热启动的过程不必
创建和初始化 Application,因为一个应用从新进程的创建到进程的销毁,Application 只会初始化一次。
3 首次启动:首次启动严格来说也是冷启动,之所以把首次启动单独列出来,一般来说,首次启动时间会比非首次启动要久,首次
启动会做一些系统初始化工作,如缓存目录的生产,数据库的建立,SharedPreference的初始化,如果存在多 dex 和插件的情况
下,首次启动会有一些特殊需要处理的逻辑,而且对启动速度有很大的影响,所以首次启动的速度非常重要,毕竟影响用户对 App 的
第一映像。
冷启动白屏优化
1, 代码分析
以网上的一段代码为例.
因为这个App集成了Bugly, Push, Feedback等服务, 所以Application的onCreate有很多第三方平台的初始化工作...
public class GithubApplication extends MultiDexApplication {
@Override
public void onCreate() {
super.onCreate();
// init logger.
AppLog.init();
// init crash helper
CrashHelper.init(this);
// init Push
PushPlatform.init(this);
// init Feedback
FeedbackPlatform.init(this);
// init Share
SharePlatform.init(this);
// init Drawer image loader
DrawerImageLoader.init(new AbstractDrawerImageLoader() {
@Override
public void set(ImageView imageView, Uri uri, Drawable placeholder) {
ImageLoader.loadWithCircle(GithubApplication.this, uri, imageView);
}
});
}
}
当前冷启动效果:
可以看到启动时白屏了很长时间.
冷启动时间优化
知道了Android冷启动时间的原理之后,就可以通过一些小技巧来对冷启动时间进行优化,从而让你app加载变得”快“一些(视觉体验上的快)。
我们可制作一个启动Activity的背景样式的.9图片
在安卓社区中,65k方法数的限制是一个被多次提及的问题。目前解决这个问题的办法就是用multidex。虽然multidex是谷歌给出的一个非常棒的办法,
但是我发现了它对app启动性能存在严重的影响,这点还没有在社区引起重视。大家可以看看这篇文章了解一下
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/1223/3796.html
Application是程序的主入口,特别是很多第三方SDK都会需要在Application的onCreate里面做很多初始化操作,不得不说,各种第三方SDK,都特别喜欢这个『兵家必争之地』,
再加上自己的一些库的初始化,会让整个Application不堪重负。
优化的方法,无非是通过以下几个方面:
● 延迟初始化
● 后台任务
● 界面预加载
减少应用启动时的耗时
1、在Application的构造器方法、attachBaseContext()、onCreate()方法中不要进行耗时操作的初始化,一些数据预取放在异步线程中,可以采取Callable实现。
2、对于sp的初始化,因为sp的特性在初始化时候会对数据全部读出来存在内存中,所以这个初始化放在主线程中不合适,反而会延迟应用的启动速度,对于这个还是需要放在异步线程中处理。
3、对于MainActivity,由于在获取到第一帧前,需要对contentView进行测量布局绘制操作,尽量减少布局的层次,考虑StubView的延迟加载策略,当然在onCreate、onStart、onResume方法中避免做耗时操作。
白屏优化
上面介绍的方法都是减少白屏时间的方法,虽然时间减少了但是用户还是能看到白屏.ok,下面介绍几种去掉白屏的方法.
● 为application的them设置背景图片
.
给人程序启动快的感觉,界面先显示背景图,然后再刷新其他界面控件,刷新不同步。
<!-- 为 Theme 设置背景图 --> <style name="AppTheme" parent="android:style/Theme.Black.NoTitleBar.Fullscreen"> <item name="android:windowBackground">@drawable/splash_bg</item> </style>
● 设置透明属性
给人程序启动慢的感觉,界面会一次性刷出来,刷新同步。
<!-- 为 Theme 设置透明属性 --> <style name="AppTheme" parent="android:style/Theme.Black.NoTitleBar.Fullscreen"> <item name="android:windowIsTranslucent">true</item> </style>
●
设置android:windowDisablePreview属性
和透明属性一样会在点击按钮的时候有一秒左右的延迟
<!-- 为 Theme 设置禁用窗口预览动画属性 --> <style name="AppTheme" parent="android:style/Theme.Black.NoTitleBar.Fullscreen"> <item name="android:windowDisablePreview">true</item> </style>
总结:安卓的启动原理一些优化建议,启动白屏的三种处理方式.