通过 startService 在新进程中启动服务的流程(二)

在《通过 startService 在新进程中启动服务的流程(一)》中我们分析到了用 Process.start 启动了一个新的 ActivityThread,本文中接着继续分析 startService 剩下的流程。

5. ActivityThread 入口

前一篇说道,作为一个 java 入口类,ActivityThread 有自己的 main 函数,总的来看该函数做了两件事:1. ActivityThread.attach,2. Looper.loop(这也就是每个 android 应用程序的进程中主线程的 Looper 了)。除此之外,有几个有意思的点可以提一下:

public static void main(String[] args) {
1.  SamplingProfilerIntegration.start();

    ......

2.  Process.setArgV0("<pre-initialized>");

    ......
}
  1. SamplingProfilerIntegration.start() 在DDMS中打印出的内存快照hprof文件实际上与此相关,如果感兴趣的话,可以查阅 dalvik.system.profiler.SamplingProfiler 这个类的相关说明。
  2. 我们发现在主进程Looper开始循环、ActivityThread.attach 执行之前,进程名叫做"<pre-initialized>"。

在这里只对 ActivityThread.attach 的过程进行展开分析。

5.1 ActivityThread.attach

本文讨论的启动流程为非系统进程,对于非系统进程,先把进程所属的 ApplicationThread 设置到 RunntimeInit 中(用于 report 错误),然后再将其传递给 AMS,执行 attachApplication:

private void attach(boolean system) {
    sThreadLocal.set(this);
    mSystemThread = system;
    if (!system) {
        ViewRootImpl.addFirstDrawHandler(new Runnable() {
            public void run() {
                ensureJitEnabled();
            }
        });
        android.ddm.DdmHandleAppName.setAppName("<pre-initialized>");
        RuntimeInit.setApplicationObject(mAppThread.asBinder());
        IActivityManager mgr = ActivityManagerNative.getDefault();
        try {
            mgr.attachApplication(mAppThread);
        } catch (RemoteException ex) {
            // Ignore
        }
    } else {
        ......
    }
    ......
}

5.2 ActivityManagerService.attachApplicationLocked

AMS 中 attachApplication 又调到了 attachApplicationLocked, 第一步是从 mPidsSelfLocked 中取该进程对应的 ProcessRecord,这个 ProcessRecord 也就是《通过 startService 在新进程中启动服务的流程(一)》中讲的在 startProcessLocked 过程的最后中放入的(Process.start 之后,超时逻辑之前)。
如果是没有找到目标进程对应的 ProcessRecord,则判为非法进程,并kill掉:

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

    // Find the application record that is being attached...  either via
    // the pid if we are running in multiple processes, or just pull the
    // next app record if we are emulating process with anonymous threads.
    ProcessRecord app;
    if (pid != MY_PID && pid >= 0) {
        synchronized (mPidsSelfLocked) {
            app = mPidsSelfLocked.get(pid);
        }
    } else {
        app = null;
    }

    if (app == null) {
        Slog.w(TAG, "No pending application record for pid " + pid
                + " (IApplicationThread " + thread + "); dropping process");
        EventLog.writeEvent(EventLogTags.AM_DROP_PROCESS, pid);
        if (pid > 0 && pid != MY_PID) {
            Process.killProcessQuiet(pid);
        } else {
            try {
                thread.scheduleExit();
            } catch (Exception e) {
                // Ignore exceptions.
            }
        }
        return false;
    }

    ......

}

若成功查找到了指定 ProcessRecord,将该 ApplicationThread 的 IBinder 对象绑定给 ProcessRecord,并取消超时处理(在 startProcessLocked 最后会启动超时逻辑):

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

    ......

    EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.pid, app.processName);
    
    app.thread = thread;
    app.curAdj = app.setAdj = -100;
    app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
    app.setSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
    app.forcingToForeground = null;
    app.foregroundServices = false;
    app.hasShownUi = false;
    app.debugging = false;

    mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);

    ......

}

将在 AMS 中查到的 processName(进程名)、ApplicationInfo、Providers 等信息传至目标进程中执行 bindApplication,其中 Providers 即查找 APP 下的所有声明的可用的 Provider 组件:

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

    ......

    boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
    List providers = normalMode ? generateApplicationProvidersLocked(app) : null;
    ......

    try {

        ......

        thread.bindApplication(processName, appInfo, providers,
                app.instrumentationClass, profileFile, profileFd, profileAutoStop,
                app.instrumentationArguments, app.instrumentationWatcher, testMode, 
                isRestrictedBackupMode || !normalMode, app.persistent,
                mConfiguration, app.compat, getCommonServicesLocked(),
                mCoreSettingsObserver.getCoreSettingsLocked());

    }

    ......

}

5.3 ApplicationThread.bindApplication

ApplicationThread 的 bindApplication 方法发消息至主线程(H 是个神奇的名字,当然它是个主线程的 Handler)执行 handleBindApplication:

public final void bindApplication(...) {
        ......
        queueOrSendMessage(H.BIND_APPLICATION, data);
}

在 handleBindApplication 中我们去掉不关心的流程,大体分为6个步骤:

<center>

StatechartDiagram1.jpg

图 5.1 handleBindApplication 流程简介
</center>
代码如下所示:

private void handleBindApplication(AppBindData data) {
    
    ......

1.  Process.setArgV0(data.processName);
    android.ddm.DdmHandleAppName.setAppName(data.processName);

    ......

    /*
     * Before spawning a new process, reset the time zone to be the system time zone.
     * This needs to be done because the system time zone could have changed after the
     * the spawning of this process. Without doing this this process would have the incorrect
     * system time zone.
     */
2.  TimeZone.setDefault(null);

    /*
     * Initialize the default locale in this process for the reasons we set the time zone.
     */
    Locale.setDefault(data.config.locale);

    ......

3.  if (data.debugMode != IApplicationThread.DEBUG_OFF) {
        // XXX should have option to change the port.
        Debug.changeDebugPort(8100);
        if (data.debugMode == IApplicationThread.DEBUG_WAIT) {
            Slog.w(TAG, "Application " + data.info.getPackageName()
                  + " is waiting for the debugger on port 8100...");

            IActivityManager mgr = ActivityManagerNative.getDefault();
            try {
                mgr.showWaitingForDebugger(mAppThread, true);
            } catch (RemoteException ex) {
            }

            Debug.waitForDebugger();

            try {
                mgr.showWaitingForDebugger(mAppThread, false);
            } catch (RemoteException ex) {
            }

        } else {
            Slog.w(TAG, "Application " + data.info.getPackageName()
                  + " can be debugged on port 8100...");
        }
    }

    /**
     * Initialize the default http proxy in this process for the reasons we set the time zone.
     */
4.  IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
    IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
    try {
        ProxyProperties proxyProperties = service.getProxy();
        Proxy.setHttpProxySystemProperty(proxyProperties);
    } catch (RemoteException e) {}

    ......

    // 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;

    // don't bring up providers in restricted mode; they may depend on the
    // app's custom Application class
5.  if (!data.restrictedBackupMode){ 
        List<ProviderInfo> providers = data.providers;
        if (providers != null) {
            installContentProviders(app, providers);
            // For process that contains content providers, we want to
            // ensure that the JIT is enabled "at some point".
            mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
        }
    }

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

上面代码虽然已经减了不少,但是贴的还是有点啰嗦了。有几个有意思的点:

  • 真正的进程名是在此函数中正式赋值的;
  • 我们平时开发、调试应用时弹出的“Waiting For Debugger”是在此函数中弹的;
  • 在restricted mode中不会将Provider带起来;
  • Provider的onCreate执行(对应installContentProviders)在Application的onCreate(对应mInstrumentation.callApplicationOnCreate)之前;

5.4 启动目标服务

上面的是捋的 ApplicationThread.bindApplication 这条线,我们回到 ActivityManagerService.attachApplicationLocked 方法中,继续来看。比较关键的流程就剩下三块,正如代码中注释所示:

  1. See if the top visible activity is waiting to run in this process...
  2. Find any services that should be running in this process...
  3. Check if the next broadcast receiver is in this process...

我们只需关注第二点,将存在 mPendingServices 中的服务启动。从《通过 startService 在新进程中启动服务的流程(一)》中我们知道,在 bringUpServiceLocked 执行的最后将新建的 ServiceRecord 存在 mPendingServices 中,实际上就是要启动的但是进程还没准备好而没启动的服务列表了。

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

    ......

    // Find any services that should be running in this process...
    if (!badApp && mPendingServices.size() > 0) {
        ServiceRecord sr = null;
        try {
            for (int i=0; i<mPendingServices.size(); i++) {
                sr = mPendingServices.get(i);
                if (app.info.uid != sr.appInfo.uid
                        || !processName.equals(sr.processName)) {
                    continue;
                }

                mPendingServices.remove(i);
                i--;
                realStartServiceLocked(sr, app);
                didSomething = true;
            }
        } catch (Exception e) {
            Slog.w(TAG, "Exception in new application when starting service "
                  + sr.shortName, e);
            badApp = true;
        }
    }

    ......

}

真正启动的函数就是 realStartServiceLocked 了,该函数我们需要关注的点有三个:

  1. 保存至已经运行的服务列表中(LRU)
  2. Service.onCreate
  3. Service.onStartCommand
private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app) throws RemoteException {
    
    ......    

1.  updateLruProcessLocked(app, true, true);

    boolean created = false;
    try {
        
        ......
        
2.      app.thread.scheduleCreateService(r, r.serviceInfo,
                compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo));
        r.postNotification();
        created = true;
    } 
    
    ......
    
3.  sendServiceArgsLocked(r, true);
}

1)updateLruProcessLocked 函数中就是将 ProcessRecord 插入到 mLruProcesses 中,key是传过来的 ApplicationThread 的 binder 对象,从《通过 startService 在新进程中启动服务的流程(一)》中我们知道,在 startServiceLocked 执行时首先通过调用者传进来的 IApplicationThread 查询调用者进程是否存在。

2)scheduleCreateService 实际binder调用到 ActivityThread 中的 scheduleCreateService,然后消息传递到主线程(H)执行 handleCreateService,我们可以看到 attach Context 的过程和 onCreate 的执行:

private void handleCreateService(CreateServiceData data) {
    ......

    try {

        ContextImpl context = new ContextImpl();
        context.init(packageInfo, null, this);

        Application app = packageInfo.makeApplication(false, mInstrumentation);
        service.attach(context, this, data.info.name, data.token, app, ActivityManagerNative.getDefault());
        service.onCreate();
        mServices.put(data.token, service);
        
        ......

    } catch (Exception e) {
        ......
    }
}

3)sendServiceArgsLocked 实际binder调用到 ActivityThread 中的 scheduleServiceArgs,然后消息传递到主线程执行 handleServiceArgs:

private void handleServiceArgs(ServiceArgsData data) {
    Service s = mServices.get(data.token);
    if (s != null) {
        try {
            if (data.args != null) {
                data.args.setExtrasClassLoader(s.getClassLoader());
            }
            int res;
            if (!data.taskRemoved) {
                res = s.onStartCommand(data.args, data.flags, data.startId);
            } 

            ......

        } catch (Exception e) {
            ......
        }
    }
}

5.5 小结

至此,startService 的流程基本完了,围绕 AMS、PMS、ActivityThread,大体流程可以总结为:1. 搜集目标服务/进程的信息,2. startProcessLocked,3. realStartServiceLocked。

标签: none

添加新评论