Android进阶——性能优化之APP启动过程相关源码解析(二)

引言

上一篇Android进阶——性能优化之APP启动时黑白屏的根源解析及对应的优化措施小结在总结APP黑白屏相关知识点时候提到了启动时间这一概念,也正是由于启动时间的不同造成了APP启动快慢的差异,所以有必要分析总结下APP启动时间的相关知识。性能优化系列文章链接:

一、启动原理概述

总所周知,Android系统是基于Linux内核的,从宏观上讲,当Android手机启动后,加载Linux内核,当Linux内核被加载完毕之后,自动触发Linux系统的init祖先进程fork出Zygote进程,所有的Android应用程序进程以及系统服务进程都是这个Zygote的子进程(均是由Zygote fork出来的),其中最重要的一个就是com.android.server下的SystemServer,在com.android.internal.os下ZygoteInit类的main方法中,会调用startSystemServer方法开启系统里面重要的服务,包括ActivityManagerService(Activity管理服务,负责Activity的生命周期管理的一个服务进程)、PackageManagerService(包管理服务,负责apk包的安装卸载的一个服务进程)、WindowManagerService(窗口管理服务)、BatteryManagerService(电池管理服务)、PowerManagerService(电量管理服务)等等系统服务)
这里写图片描述

1、SystemServer概述

SystemServer是Android系统的核心,他在Dalvik虚拟机启动后立即开始初始化和运行。其它的系统服务在SystemServer进程的环境运行(对应6.0源码路径/base/services/java/com/android/server/SystemServer.java)在SystemServer中,将可以看到它创建并初始化了Android中的基础核心服务,期间还完成了创建并开启ActivityThread初始化上下文设置默认主题。如果想要启动新的应用,ActivityManagerService会通过socket进程间通信(IPC)机制来通知Zygote进程fork出新的进程

public final class SystemServer {
    ..略掉部分无关代码...  
    private Context mSystemContext;
    private SystemServiceManager mSystemServiceManager;
    // TODO: remove all of these references by improving dependency resolution and boot phases
    private PowerManagerService mPowerManagerService;
    private ActivityManagerService mActivityManagerService;
    private DisplayManagerService mDisplayManagerService;
    private PackageManagerService mPackageManagerService;
    private PackageManager mPackageManager;
    private ContentResolver mContentResolver;

    //有main方法,说明不需要主动去调用的,是Zygote的主入口  
    public static void main(String[] args) {  
        new SystemServer().run();  
    }  

    public SystemServer() {  
        // Check for factory test mode.  
        mFactoryTestMode = FactoryTest.getMode();  
    }  

    private void run() {  

        ...略掉部分无关代码...   
        //加载本地系统服务库
        System.loadLibrary("android_servers");  
        nativeInit();  //调用本地方法初始化系统服务

        createSystemContext();   // 创建系统上下文 

        //初始化SystemServiceManager对象,下面的系统服务开启都需要调用SystemServiceManager.startService(Class<T>),这个方法通过反射来启动对应的服务  
        mSystemServiceManager = new SystemServiceManager(mSystemContext);  

        //开启其他服务  
        try {  
            startBootstrapServices();  //初始化AMS、包管理服务、电源管理服务等所必须的基础核心服务
            startCoreServices();  
            startOtherServices();  
        } catch (Throwable ex) {  
            Slog.e("System", "************ Failure starting system services", ex);  
            throw ex;  
        }  
    }  

    /*初始化系统上下文对象mSystemContext并设置默认的主题,mSystemContext实际上是一个ContextImpl对象。
    * 调用ActivityThread.systemMain()的时候,会调用ActivityThread.attach(true),
    *  而在attach()里面,则创建了Application对象,并调用了Application.onCreate()。  
    */
    private void createSystemContext() {  
        ActivityThread activityThread = ActivityThread.systemMain();  
        mSystemContext = activityThread.getSystemContext();  
        mSystemContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);  
    }  

    //初始化了几个基础的核心服务
    private void startBootstrapServices() {  

        ...略掉部分无关代码...  

        //初始化AMS,如果想要启动新的应用,ActivityManagerService会通过socket进程间通信(IPC)机制来通知Zygote进程fork出新的进程
        mActivityManagerService = mSystemServiceManager.startService(  
                ActivityManagerService.Lifecycle.class).getService();  
        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);  

        //初始化PowerManagerService,因为其他服务需要依赖这个Service,因此需要尽快的初始化  
        mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);  

        // 现在电源管理已经开启,ActivityManagerService负责电源管理功能  
        mActivityManagerService.initPowerManagement();  

        // 初始化DisplayManagerService  
        mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);  

        //初始化PackageManagerService  
        mPackageManagerService = PackageManagerService.main(mSystemContext, mInstaller,  
        mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);  
    }  
}  

值得注意的是在创建上下文的createSystemContext方法中完成了三件事:

  • 创建ActivityThread,这个ActivityThread就是我们熟悉的的“主线程”即UI线程,APP的主入口,ActivityThread随后会创建一个mainLooper来开启消息循环,这也就是为什么在”主线程”中我们使用Handler不需要手动创建Looper的原因。

    • 初始化上下文,其实应该来说是通过ActivityThread获得上下文,本质就是一个ContextImpl对象

    • 设置了默认的主题

2、SystemServerManager概述

最后通过ServerManager来管理所有系统服务,通过ServiceManager的< T extends SystemService> T startService(Class< T > serviceClass) 添加服务到mServices列表中,在ServiceManager的SystemService startService(String className)方法通过反射完成调用服务。

public class SystemServiceManager {
    ...略掉部分无关代码...   

    public SystemService startService(String className) {
        final Class<SystemService> serviceClass;
        try {
            serviceClass = (Class<SystemService>)Class.forName(className);
        } catch (ClassNotFoundException ex) {
            Slog.i(TAG, "Starting " + className);
            throw new RuntimeException("Failed to create service " + className
                    + ": service class not found, usually indicates that the caller should "
                    + "have called PackageManager.hasSystemFeature() to check whether the "
                    + "feature is available on this device before trying to start the "
                    + "services that implement it", ex);
        }
        return startService(serviceClass);
    }

    public <T extends SystemService> T startService(Class<T> serviceClass) {
        final String name = serviceClass.getName();
        Slog.i(TAG, "Starting " + name);

        // 创建系统服务
        if (!SystemService.class.isAssignableFrom(serviceClass)) {
            throw new RuntimeException("Failed to create " + name+ ": service must extend " + SystemService.class.getName());
        }
        final T service;
        try {
            Constructor<T> constructor = serviceClass.getConstructor(Context.class);
            service = constructor.newInstance(mContext);//通过本地代码创建
        } catch (InstantiationException ex) {
            throw new RuntimeException("Failed to create service " + name
                    + ": service could not be instantiated", ex);
        } catch (IllegalAccessException ex) {
            throw new RuntimeException("Failed to create service " + name
                    + ": service must have a public constructor with a Context argument", ex);
        } catch (NoSuchMethodException ex) {
            throw new RuntimeException("Failed to create service " + name
                    + ": service must have a public constructor with a Context argument", ex);
        } catch (InvocationTargetException ex) {
            throw new RuntimeException("Failed to create service " + name
                    + ": service constructor threw an exception", ex);
        }
        //注册并添加服务
        mServices.add(service);

        try {
            service.onStart();
        } catch (RuntimeException ex) {
            throw new RuntimeException("Failed to start service " + name+ ": onStart threw an exception", ex);
        }
        return service;
    }
}

二、从Launcher点击APP图标到启动背后的流程原理

Android系统其实就是一个运行APP的环境,所有功能都是由APP实现的,比如说拨号、设置、主界面Launcher等,手机开机之后会自动把Launcher启动起来,即我们手机的主界面,com.android.launcher2.Launcher继承自android.app.Activity,实现了点击事件、触摸、长按等接口,所以说我们点击APP图标本质上就是触发了Launcher里对应的onClick事件

1、首先触发了Launcher里对应的onClick事件

public void onClick(View v) {
        ...略掉部分无关代码...   
        Object tag = v.getTag();
        //判断是否是快捷方式图标
        if (tag instanceof ShortcutInfo) {
            // 不用看这个ShortcutInfo 肯定存储了对应的Intent或者Component信息 
            final Intent intent = ((ShortcutInfo) tag).intent;
            int[] pos = new int[2];
            v.getLocationOnScreen(pos);
            intent.setSourceBounds(new Rect(pos[0], pos[1],
                    pos[0] + v.getWidth(), pos[1] + v.getHeight()));

            boolean success = startActivitySafely(v, intent, tag);//打开android.app.Activity

            if (success && v instanceof BubbleTextView) {
                mWaitingForResume = (BubbleTextView) v;
                mWaitingForResume.setStayPressed(true);
            }
        } else if (tag instanceof FolderInfo) {//判读是否是文件夹
            if (v instanceof FolderIcon) {
                FolderIcon fi = (FolderIcon) v;
                handleFolderClick(fi);//打开文件夹
            }
        } else if (v == mAllAppsButton) {
            if (isAllAppsVisible()) {
                showWorkspace(true);
            } else {
                onClickAllAppsButton(v);
            }
        }
    }

2、在onClick方法里触发Launcher的startActivitySafely方法

public boolean startActivitySafely(View v, Intent intent, Object tag) {
        ...略掉部分无关代码...   
        try {
            success = startActivity(v, intent, tag);
        } catch (ActivityNotFoundException e) {
            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
            Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
        }
        return success;
    }

跟踪下去发现通过调用上下文的startActivity方法来最终会调用android.app.Instrumentation(Instrumentation这个类就是完成对Application和Activity初始化和生命周期的工具类)的execStartActivity()方法,在execStartActivity()方法内部通过ActivityManagerService的远程接口(其实就是AIDL的远端服务端的接口)向AMS发消息,让他启动一个Activity。换句话说在调用Activity的startActivity(Intent)之后, Instrumentation会通过Binder IPC机制(AIDL) 调用ActivityManagerService服务,AMS内部会通过socket通道传递参数给Zygote进程,Zygote孵化自身,并主动调用ZygoteInit.main()方法来实例化ActivityThread对象并最终返回新进程的pid,ActivityThread随后依次调用Looper.prepareLoop()和Looper.loop()来开启消息循环,在ActivityThread会创建并绑定Application,这个时候才会realStartActivity(),并且AMS会将生成的Activity加到ActivityTask的栈顶,并通知ActivityThread暂停当前Activity(暂停Launcher),进入intent对应的Activity
这里写图片描述

三、Application的初始化

前一篇文章Android进阶——性能优化之APP启动时黑白屏的根源解析及对应的优化措施小结,简单介绍了下应用的启动方式,其中采用冷启动方式时,系统会重新创建一个新的进程空间并分配给它,所以会先创建和初始化Application类,再创建和初始化MainActivity类(包括一系列的测量、布局、绘制等),最后显示在界面上,由此可见Application对于App启动影响也十分巨大。

1、在ActivityThread的main方法中,new 创建完ActivityThread对象之后,会调用ActivityThread .attach(false)方法

    public static void main(String[] args) {
        ...略掉部分无关代码...   
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        ...略掉部分无关代码...   
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

2、在ActivityThread的attach方法里通过Binder机制调用AMS里服务端的attachApplication方法


    private void attach(boolean system) {
        sCurrentActivityThread = this;

        if (!system) {

            final IActivityManager mgr = ActivityManagerNative.getDefault();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                // Ignore
            }
        } else {
            // Don't set application object here -- if the system crashes,
            // we can't display an alert, we just want to die die die.

            try {
                mInstrumentation = new Instrumentation();
                ContextImpl context = ContextImpl.createAppContext(
                        this, getSystemContext().mPackageInfo);
                mInitialApplication = context.mPackageInfo.makeApplication(true, null);
                mInitialApplication.onCreate();
            } catch (Exception e) {
                throw new RuntimeException(
                        "Unable to instantiate Application():" + e.toString(), e);
            }
        }


        ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {
            @Override
            public void onConfigurationChanged(Configuration newConfig) {
                synchronized (mResourcesManager) {

                    if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {
                        if (mPendingConfiguration == null ||
                                mPendingConfiguration.isOtherSeqNewer(newConfig)) {
                            mPendingConfiguration = newConfig;

                            sendMessage(H.CONFIGURATION_CHANGED, newConfig);
                        }
                    }
                }
            }
            @Override
            public void onLowMemory() {
            }
            @Override
            public void onTrimMemory(int level) {
            }
        });
    }

3、继续追踪在ActivityThread的handleBindApplication中触发创建Application的方法

 private void handleBindApplication(AppBindData data) {
        ...略掉部分无关代码...   
        data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);//这其实得到是一个LoaderApk对象
        final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
        IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
        if (b != null) {
            // In pre-boot mode (doing initial launch to collect password), not
            // all system is up.  This includes the connectivity service, so don't crash if we can't get it.
            IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
            try {
                final ProxyInfo proxyInfo = service.getProxyForNetwork(null);
                Proxy.setHttpProxySystemProperty(proxyInfo);
            } 
            ApplicationInfo instrApp = new ApplicationInfo();
            instrApp.packageName = ii.packageName;
            instrApp.sourceDir = ii.sourceDir;
            LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
                    appContext.getClassLoader(), false, true, false);
            ContextImpl instrContext = ContextImpl.createAppContext(this, pi);//初始化上下文对象

            try {
                java.lang.ClassLoader cl = instrContext.getClassLoader();
                mInstrumentation = (Instrumentation)
                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
            } 

        } else {
            mInstrumentation = new Instrumentation();
        }
        try {
            // If the app is being launched for full backup or restore, bring it up in a restricted environment with the base application class.
            Application app = data.info.makeApplication(data.restrictedBackupMode, null);//通过LoadedApk 创建Application

            try {
                mInstrumentation.onCreate(data.instrumentationArgs);
            }
            try {
                mInstrumentation.callApplicationOnCreate(app);//通过Instrumentation触发Application的onCreate
            }
        }
    }

4、在LoadedApk的makeApplication方法完成Application的创建和初始化,最后再返回ActivityThread的handleBindApplication方法内部通过调用instrumentation.callApplicationOnCreate()触发Application的onCreate生命周期方法

public Application makeApplication(boolean forceDefaultAppClass,  
            Instrumentation instrumentation) {  
            ...略掉部分无关代码...   
        if (mApplication != null) {  
            return mApplication;  
        }  
        Application app = null; 
        String appClass = mApplicationInfo.className;  
        if (forceDefaultAppClass || (appClass == null)) {  
            appClass = "android.app.Application";  
        }  
        try {  
            java.lang.ClassLoader cl = getClassLoader();  
            if (!mPackageName.equals("android")) {  
                initializeJavaContextClassLoader();  
            }  
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);  
            app = mActivityThread.mInstrumentation.newApplication(  
                    cl, appClass, appContext);  
            appContext.setOuterContext(app);  
        } catch (Exception e) {        }  
        mActivityThread.mAllApplications.add(app);  
        mApplication = app;  
       //传进来的是null,所以这里不会执行,onCreate在上一层被触发即在handleBindApplication方法中触发 
        if (instrumentation != null) {  
            try {  
                instrumentation.callApplicationOnCreate(app);  
            }
        }    
        ...略掉部分无关代码...           
       }  
        return app;  
    }  

总结下来可以用以下两幅图简单描述APP启动的流程
这里写图片描述
这里写图片描述

小结

简而言之,就是当在Launcher中触发Activity里的startActivity时候,和我们普通APP内调用的startActivity不同,在Launcher中调用的startActicity是开启一个新的APP进程,会首先加载main方法然后通过Zygote孵化一个独立的进程,分配方法区堆区Java栈,然后在Java栈中实例化Application,并调用Application的onCreate方法,然后才去调用MainActivity的onCreate方法。,若内存中已经存在相应的空间,则不会再去触发Application的onCreate方法,这其实也是冷启动和热启动的根本原因。下篇文章再继续APP启动优化实战环节

猜你喜欢

转载自blog.csdn.net/crazymo_/article/details/80029395