Android Framework- Activity启动2

写在最前面

juejin.cn/post/740445...

这篇文章写了一年有余,我前几天哥们问启动流程的时候 。 我信誓旦旦这玩意简单,张口就来。然后嗯啊了半天,什么也没说出来,丢了个大的。 进入了张无忌学太极拳的境界,我已经全部忘记了。

所以要理解去记。 带几个疑问去看代码,才能真的记下来,不要死记方法,知道核心点就行。

梳理问题

  • 系统是怎么判断进程是否存在的?
  • 那系统是怎么启动一个新的进程的?
  • 系统启动进程后,是怎么把我们要的Activity拉起来的?

问题梳理

系统是怎么判断进程是否存在的?

让我们梦回ActivityTaskSupervisorstartSpecificActivity()

js 复制代码
void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {
    // Is this activity's application already running?
    final WindowProcessController wpc =
            mService.getProcessController(r.processName, r.info.applicationInfo.uid);

    boolean knownToBeDead = false;
    if (wpc != null && wpc.hasThread()) {
        try {
            realStartActivityLocked(r, wpc, andResume, checkConfig);
            return;
        } catch (RemoteException e) {
            Slog.w(TAG, "Exception when starting activity "
                    + r.intent.getComponent().flattenToShortString(), e);
        }

        // If a dead object exception was thrown -- fall through to
        // restart the application.
        knownToBeDead = true;
        // Remove the process record so it won't be considered as alive.
        mService.mProcessNames.remove(wpc.mName, wpc.mUid);
        mService.mProcessMap.remove(wpc.getPid());
        
        
        //省略部分代码
        
        
mService.startProcessAsync(r, knownToBeDead, isTop,
        isTop ? HostingRecord.HOSTING_TYPE_TOP_ACTIVITY
                : HostingRecord.HOSTING_TYPE_ACTIVITY);

    }

我们可以看到 有一句注释 Is this activity's application already running? 其实很简单 就是去 ATMS 根据processNameuid 去查一下,你的wpc 是否存在 并且你的 ApplicationThread 也存在,就证明你的进程是存在的。 就可以直接启动对应页面。然后 return

总结一下

看看进程对应的WindowProcessControllerApplicationThread 是否存在。

那系统是怎么启动一个新的进程的?

我们看到上面代码部分有 调用到 mService.startProcessAsync 这里一路调用下去 会进入ZygoteProcessopenZygoteSocketIfNeeded 然后调用到 attemptConnectionToPrimaryZygote();

java 复制代码
@GuardedBy("mLock")
private void attemptConnectionToPrimaryZygote() throws IOException {
    if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
        primaryZygoteState =
                ZygoteState.connect(mZygoteSocketAddress, mUsapPoolSocketAddress);

        maybeSetApiDenylistExemptions(primaryZygoteState, false);
        maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
    }
}

这里不要记代码,就知道最后搞了个socket进行了通信。 我们这里要知道和哪里进行了通信。 其实这个是在ZygoteInitmain 方法里

java 复制代码
zygoteServer = new ZygoteServer(isPrimaryZygote);


if (startSystemServer) {
    Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
    // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
    // child (system_server) process.
    if (r != null) {
        r.run();
        return;
    }
}


// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
caller = zygoteServer.runSelectLoop(abiList);

这里 的zygoteServer.runSelectLoop(abiList) 上面注释 意思就是等待一个字进程来fork 在这里面。不贴代码了,这里主要是根据连接过来的的句柄,然后进行返回一个 Runnable,咱们简单理解就是你socket 一连,这边发现多一个,嘿, 帮你构建一个Runnable 返回回去。

java 复制代码
final Runnable command =
        connection.processCommand(this, multipleForksOK);
      
if (mIsForkChild) {
    // We're in the child. We should always have a command to run at
    // this stage if processCommand hasn't called "exec".
    if (command == null) {
        throw new IllegalStateException("command == null");
    }

    return command;
}
java 复制代码
//ZygoteConnection
Runnable result = Zygote.forkSimpleApps(argBuffer,
        zygoteServer.getZygoteSocketFileDescriptor(),
        peer.getUid(), Zygote.minChildUid(peer), parsedArgs.mNiceName);

最后调用到 RunTimeInit.applicationInit(),其实就构建了 反射调用 ActivityThreadmain 方法的一个Runnable 并传入了 参数 "seq=42",这里的42并不是固定值,我只是演示用。 最后返回 Runnable 被执行,反射启动了ActivityThread

总结一下。

在启动 SystemSerVer的时候,启动了一个死循环,等待Socket的连接, 当我们启动Activity 发现进程没启动的时候,会构建Socket 和这里链接,反射构建了ActivityThreadmain 方法并传入了参数 seq=x。 其实到这里还没有解决第三个问题,怎么启动的Activity,我们放到第三个问题里回答。

系统启动进程后 是怎么把我们要的Activity拉起来的?

ActivityThreadmain 方法部分解析

java 复制代码
//ActivityThread

final ApplicationThread mAppThread = new ApplicationThread();

public static final String PROC_START_SEQ_IDENT = "seq=";
public static vodi  main(String[] args){
//省略部分代码。。。
long startSeq = 0;
if (args != null) {
    for (int i = args.length - 1; i >= 0; --i) {
        if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
            startSeq = Long.parseLong(
                    args[i].substring(PROC_START_SEQ_IDENT.length()));
        }
    }
}

ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
//省略部分代码。。。

//调用到attachApplication 
attachApplication(mAppThread, startSeq);

}

其实上面代码就是把seq 进行了获取,比如我们拿到了42,然后调用 ActivityThreadattch 方法。 调用到了AMSattachApplication 方法 ,继续调用到了 attachApplicationLocked, 注意 在ActivityThread 里面的 ApplicationThread 是直接构建出来的 ,后面直接作为参数传到了AMS

java 复制代码
//AMS
 private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
            int pid, int callingUid, long startSeq) {

     
        ProcessRecord app;
      //省略。。
      
   
   //seq的使用
if (app == null && startSeq > 0) {
    final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq);
    if (pending != null && pending.getStartUid() == callingUid
            && pending.getStartSeq() == startSeq
            && mProcessList.handleProcessStartedLocked(pending, pid,
                pending.isUsingWrapper(), startSeq, true)) {
        app = pending;
    }
}


 //省略。。

thread.bindApplication(processName, appInfo,
        app.sdkSandboxClientAppVolumeUuid, app.sdkSandboxClientAppPackage,
        providerList, null, profilerInfo, null, null, null, testMode,
        mBinderTransactionTrackingEnabled, enableTrackAllocation,
        isRestrictedBackupMode || !normalMode, app.isPersistent(),
        new Configuration(app.getWindowProcessController().getConfiguration()),
        app.getCompat(), getCommonServicesLocked(app.isolated),
        mCoreSettingsObserver.getCoreSettingsLocked(),
        buildSerial, autofillOptions, contentCaptureOptions,
        app.getDisabledCompatChanges(), serializedSystemFontMap,
        app.getStartElapsedTime(), app.getStartUptime());




//省略 

//
if (normalMode) {
    try {
        didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
    } catch (Exception e) {
        Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
        badApp = true;
    }
}

}

这里可以根据传入的thread 也就是 我们从ActivityThread ,调用了bindApplication ,回到了ActivitThread ,最终回调了 handleBindApplication(data); 开始了Application的构建。这里不展开拓展。但是这里要看到 AMSApp之间的交互 ,依赖于ApplicationThread

到这里是Application的构建,那我们要启动的Activity 去哪里了?

其实是在 mAtmInternal.attachApplication 方法里。注意他的参数是app.getWindowProcessController()。 也就是我们之前说如何判断app进程是否存在的抓手。 然后调用到 mRootWindowContainer.attachApplication(wpc); 继续调用RootWindowContainer 内部的, AttachApplicationHelperprocess 方法。 process 方法会调用到他自己的 test方法 。该方法里调用到了mTaskSupervisor.realStartActivityLocked() 方法。 这里我们就很熟悉了,然后就是熟悉的构建 ActivityLifecycleItem mService.getLifecycleManager().scheduleTransaction(clientTransaction); 推进生命周期,触发对应Activity的生命周期 ,这里不浪费篇幅。

总结一下

Application 构建以后,会进入 RootWindowContainer 它通过遍历任务栈、筛选符合条件的 Activity,并构建LifeItem进行页面生命周期启动。

相关推荐
2501_9151063212 分钟前
iOS 打包 IPA 全流程详解,签名配置、工具选择与跨平台上传实战指南
android·macos·ios·小程序·uni-app·cocoa·iphone
超低空18 分钟前
Android MediaSession深度解析:车载音乐播放器完整案例
android·架构·客户端
00后程序员张19 分钟前
iOS 混淆实操指南多工具组合实现 IPA 混淆、加固与发布治理 IPA 加固
android·ios·小程序·https·uni-app·iphone·webview
xiaoshiquan12061 小时前
as强制过滤指定依赖版本库,解决该依赖不同版本冲突
android
2501_929157683 小时前
Switch 20.5.0系统最新PSP模拟器懒人包
android·游戏·ios·pdf
用户095 小时前
Kotlin Flow的6个必知高阶技巧
android·面试·kotlin
用户095 小时前
Flutter插件与包的本质差异
android·flutter·面试
用户095 小时前
Jetpack Compose静态与动态CompositionLocal深度解析
android·面试·kotlin
聆风吟º7 小时前
【Spring Boot 报错已解决】别让端口配置卡壳!Spring Boot “Binding to target failed” 报错解决思路
android·java·spring boot
非专业程序员Ping15 小时前
HarfBuzz概览
android·ios·swift·font