安卓启动原理及优化

版权声明:本文为博主原创文章,未经博主允许不得转载。 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);
            }
        });
    }
}

当前冷启动效果:

code_start_before_optimize

可以看到启动时白屏了很长时间.

冷启动时间优化

知道了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>

 总结:安卓的启动原理一些优化建议,启动白屏的三种处理方式.

猜你喜欢

转载自blog.csdn.net/liyan147/article/details/79280924