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进行页面生命周期启动。

相关推荐
HLC++8 小时前
Linux的进程间通信
android·linux·服务器
爱笑鱼11 小时前
Binder(二):AIDL 生成的 Proxy、Stub 和 Parcel 到底在做什么?
android
爱笑鱼12 小时前
Binder(一):一次方法调用,究竟怎样跨进程执行?
android
壮哥_icon12 小时前
【Android 系统开发】使用 BAT 脚本高效自动化管理 /system/priv-app/ 系统应用(安装与卸载)
android·运维·自动化
用户693717500138414 小时前
Claude Code终端日志Token占用实测:一个过滤器砍掉60%-90%
android·前端·后端
蝉蜕日记14 小时前
AccumuPDF高级版 v2.57 | 视图文转换PDF,合并分割压缩
android·智能手机·pdf·生活·软件需求
吐了啊取名字太难14 小时前
美颜系统AI修图本地跑并支持Mac、win、安卓、iOS不卡顿
android·人工智能·windows·数码相机·mac·ai编程
阿pin15 小时前
Android随笔-Retrofit
android·retrofit
想取一个与众不同的名字好难16 小时前
安卓自定义颜色选择器
android
我命由我1234516 小时前
Android 开发问题:java.lang.NoSuchMethodError:No virtual method readAllBytes()
android·java·java-ee·kotlin·android studio·android-studio·android runtime