Application启动过程初探

一直以来,Application都是每个项目刚建立的时候会关注下这个类,因为平时并没有多少需要去用他的地方,只有一些简单的方法,比如onCreate()、attachBaseContext(Context base)、onTrimMemory(int level)等需要继承。

今天同事遇到一个问题,是个只会在三星手机上4.3版本上遇到的问题。问题原因本身倒是跟Application并无多大关系,只是由于Multidex导致的一个处理问题。不过突然让我想到了这么多年都没怎么好好关注过这个类,所以初探一番。

先是几个使用过程中需要注意的问题:
1. 如果用了multidex这个东西,由于是针对65k的方法数问题而提出的,所以会导致应用启动时速度变慢,故应该做过度处理,如

@Override
    protected void attachBaseContext(Context base) {
        try {
            super.attachBaseContext(base);

            if (!quickStart() && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {

                if (needWait(base)) {
                    waitForDexopt(base);
                }
                MultiDex.install(this);
            } else {
                return;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

具体的实现方法网上有,不详述了。
2. 如果application中有多个进程,要注意Application类会被多次初始化,如果不在某些初始化的地方过滤,会导致一些耗时操作重复被执行,甚至引发一些逻辑上的问题。此时由于运行在不同进程中,互相之间是隔绝的。

——————————————————-华丽的分割线———————————————————–

接下来我们来看下application是如何被拉起的。这里主要分为两部分,第一部分是系统启动后,SystemServer进程启动过程中做了一些事情。第二部分是我们新起一个应用时,怎么起这个应用。

第一部分:

在zygoteinit起了system_server进程后,


context = ActivityManagerService.main(factoryTest);
ActivityManagerService.setSystemProcess();//这里会addService("Activity", ****),,下文会用到
ActivityManagerService.installSystemProviders();

ActivityManagerService.main()会被调用,先来看下这个ActivityManagerService,这个类很复杂,继承自Binder并实现了IActivityManager接口。

这里写图片描述

AMS提供了一个ArrayList mHistory来管理所有的activity,activity在AMS中的形式是ActivityRecord,task在AMS中的形式为TaskRecord,进程在AMS中的管理形式为ProcessRecord。

1. 所有的ActivityRecord会被存储在mHistory管理;
2. 每个ActivityRecord会对应到一个TaskRecord,并且有着相同TaskRecord的ActivityRecord在mHistory中会处在连续的位置;
3. 同一个TaskRecord的Activity可能分别处于不同的进程中,每个Activity所处的进程跟task没有关系;

ActivityManagerService.java

public static final Context main(int factoryTest) {
        *******************
        ActivityThread at = ActivityThread.systemMain();
        mSystemThread = at;
         *******************
        /// @}
        return context;
    }

此处可以看到ActivityThread.systemMain()调用,其实就是new了一个ActivityThread实例,ActivityThread类,阅读下面这段google说明:


 * This manages the execution of the main thread in an
 * application process, scheduling and executing activities,
 * broadcasts, and other operations on it as the activity
 * manager requests.
 *

很清楚,它负责管理一个application的主线程,管理activitys,broadcast等等。其主要成员如下:
这里写图片描述
其中
1. ApplicationThread mAppThread :ApplicationThread 是ActivityThread的内部类,该实例会通过传给AMS,并发起bindApplication等信息。并最终调用内部的mHhhandler处理。
2. ArrayMap

 public static ActivityThread systemMain() {
        HardwareRenderer.disable(true);
        ActivityThread thread = new ActivityThread();
        thread.attach(true);//注意参数,因为是从systemserver的初始化函数进入的,所以为true。
        return thread;
    }

来看下attach()中做了什么:

private void attach(boolean system) {
        sCurrentActivityThread = this;
        ***************************
        if (!system) {
            //如果不是从systemserver进入
            android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                                                    UserHandle.myUserId());
            RuntimeInit.setApplicationObject(mAppThread.asBinder());
            IActivityManager mgr = ActivityManagerNative.getDefault();//获取实例
            try {
                logAppLaunchTime(TAG, "attachApplication -> AMS"); /// M: It's for debugging App Launch time
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                // Ignore
            }
        } else {
             //这里如果是systemserver中进来,进入这个分支
            android.ddm.DdmHandleAppName.setAppName("system_process",
                                                    UserHandle.myUserId());
            try {
                mInstrumentation = new Instrumentation();//生成Instrumentation实例
                ContextImpl context = new ContextImpl();
                context.init(getSystemContext().mPackageInfo, null, this);//初始化上下文
                Application app = Instrumentation.newApplication(Application.class, context);
                mAllApplications.add(app);
                mInitialApplication = app;
                app.onCreate();
            } catch (Exception e) {
                throw new RuntimeException(
                        "Unable to instantiate Application():" + e.toString(), e);
            }
        }
******************************************
    }

这里根据参数不同分为2条路径,一路是从SystemServer进程调用的,直接传入了Application.class进行了创建(这里不讨论)。

第二部分:
当我们启动一个应用时,根据之前的文章,我们可以知道zygoteinit这个东西已经给我们开启了一条监听,会调用Zygote.forkAndSpecialize()将该应用的一些信息参数传入,fork出一个新的进程,dalvik会最终调用到ActivityThread的main()入口。至于怎么调用这里不展开。

ActivityThread.java

public static void main(String[] args) {
        ****************************

        Looper.prepareMainLooper();

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

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

        AsyncTask.init();
        **********************
    }

这里又出现了attach函数。只是参数是false。说明它并不是从systemserver进入的。这里就重新进入到了上文所说的2个分支路线。这一路在获取实例IActivityManager mgr = ActivityManagerNative.getDefault();后调用了mgr.attachApplication(mAppThread);这个mgr是什么?IActivityManager实例,这里出现了ActivityManagerNative类,也就是图1中AMS的父类。
这里可以看到我们前文中注册进去的“activity”服务的代理接扣IActivityManager被获取到了。
同时注意下传入的mAppThread是个IApplicationThread的实例,这个在下文中会被使用通过Binder+Message的方式来调回来app进程。

private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };

因为这已经是处于不同进程了,所以要通过这种方式去调用AMS类中的,至于为什么要调用它,因为开头说了,它有权力知道所有当前的活动进程信息。

@Override
    public final void attachApplication(IApplicationThread thread) {
        logAppLaunchTime(TAG,"attachApplication +"); /// M: It's for debugging App Launch time
        /// M: AMS log enhancement @{
        if (!IS_USER_BUILD)
           Slog.d(TAG, "ACT-attachApplication pid " + Binder.getCallingPid() + " to thread " + thread);
        /// @}
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid);//注意这里调用,看下文
            Binder.restoreCallingIdentity(origId);
        }
        logAppLaunchTime(TAG,"attachApplication -"); /// M: It's for debugging App Launch time
    }

这里bindApplication,

private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {

         ****************************

        try {
            ****************************
            thread.bindApplication(processName, appInfo, providers,
                    app.instrumentationClass, profileFile, profileFd, profileAutoStop,
                    app.instrumentationArguments, app.instrumentationWatcher,
                    app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
                    isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(mConfiguration), app.compat, getCommonServicesLocked(),
                    mCoreSettingsObserver.getCoreSettingsLocked());
            updateLruProcessLocked(app, false, null);
            app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
        }  
         ****************************
    }

bindApplication函数中,最终会调用app进程中的handler,最终调用handleBindApplication()

private void handleBindApplication(AppBindData data) {
        mBoundApplication = data;

        *************************************
        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);//注意这里!!!!
            mInitialApplication = app;

            // Do this after providers, since instrumentation tests generally start their
            // test thread at this point, and we don't want that racing.
            try {
                mInstrumentation.onCreate(data.instrumentationArgs);
            }
            catch (Exception e) {
                throw new RuntimeException(
                    "Exception thrown in onCreate() of "
                    + data.instrumentationName + ": " + e.toString(), e);
            }

            try {
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!mInstrumentation.onException(app, e)) {
                    throw new RuntimeException(
                        "Unable to create application " + app.getClass().getName()
                        + ": " + e.toString(), e);
                }
            }
        } finally {
            StrictMode.setThreadPolicy(savedPolicy);
        }
    } 

上面的标注处data.info是什么?就是LoadedApk info; LoadedApk—–顾名思义,一个已经load好的apk,这个类包含了这个apk的各种信息。

LoadedApk类中makeApplication()中,mActivityThread.mInstrumentation.newApplication()函数。初始化完毕后,instrumentation.callApplicationOnCreate()函数调用了我们自定义的Application类的onCreate()函数。

LoadedApk.java


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();
            ContextImpl appContext = new ContextImpl();
            appContext.init(this, null, mActivityThread);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);//此处会去初始化application
            appContext.setOuterContext(app);
        } catch (Exception e) {
            if (!mActivityThread.mInstrumentation.onException(app, e)) {
                throw new RuntimeException(
                    "Unable to instantiate application " + appClass
                    + ": " + e.toString(), e);
            }
        }
        mActivityThread.mAllApplications.add(app);
        mApplication = app;

        if (instrumentation != null) {
            try {
                instrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!instrumentation.onException(app, e)) {
                    throw new RuntimeException(
                        "Unable to create application " + app.getClass().getName()
                        + ": " + e.toString(), e);
                }
            }
        }

        return app;
    }

其中Instrumentation类调用了我们的Application类构造函数,这里就调用到了Application的attach()函数。

Instrumentation.java

static public Application newApplication(Class<?> clazz, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = (Application)clazz.newInstance();
        app.attach(context);
        return app;
    }

在这里我们可以看到我们自定义的attachBaseContext()函数就会被调用到。

Application.java

/* package */ final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }

如上就是启动Application的过程。主要记住“启动过程中主要是ActivityThread在负责,AMS参与其中。”这样就大致可以了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值