【安卓源码】安卓app应用进程启动原理

news2025/1/20 1:41:10

 

目录

0. 应用app 调用 startActivity

1. AMS 调用 startActivity 方法

2. zygote socket 通信,通知 zygote 创建应用进程

1-1) 去fork 一个子进程 Zygote.forkAndSpecialize

1-2)执行子进程方法 handleChildProc

1-3)执行父进程方法 handleParentProc,通知子进程的pid 号

3. 应用进程初始化流程


  1. App发起进程:当从桌面启动应用,则发起进程便是Launcher所在进程;当从某App内启动远程进程,则发送进程便是该App所在进程。发起进程先通过binder发送消息给system_server进程;
  2. system_server进程:调用Process.start()方法,通过socket向zygote进程发送创建新进程的请求;
  3. zygote进程:在执行ZygoteInit.main()后便进入runSelectLoop()循环体内,当有客户端连接时便会执行ZygoteConnection.runOnce()方法,再经过层层调用后fork出新的应用进程;
  4. 新进程:执行handleChildProc方法,最后调用ActivityThread.main()方法。

0. 应用app 调用 startActivity

/frameworks/base/core/java/android/app/ContextImpl.java

    public void startActivity(Intent intent) {
        warnIfCallingFromSystemProcess();
        startActivity(intent, null);
    }

=========
    @Override
    public void startActivity(Intent intent, Bundle options) {
        warnIfCallingFromSystemProcess();

。。。。

// mMainThread.getApplicationThread() 为:ApplicationThread 用于进程间通信
// final ApplicationThread mAppThread = new ApplicationThread()
// 可以系统进程与 应用进程交互
        mMainThread.getInstrumentation().execStartActivity(
                getOuterContext(), mMainThread.getApplicationThread(), null,
                (Activity) null, intent, -1, options);
    }

Instrumentation().execStartActivity(

/frameworks/base/core/java/android/app/Instrumentation.java

    @UnsupportedAppUsage
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {

// 将 app 进程设置为:whoThread 
        IApplicationThread whoThread = (IApplicationThread) contextThread;
。。。

// 关于 activity 的监控类
        if (mActivityMonitors != null) {
。。。
        try {
            intent.migrateExtraStreamToClipData(who);
            intent.prepareToLeaveProcess(who);

// 调用系统进程的 ActivityTaskManagerService 的 startActivity
            int result = ActivityTaskManager.getService().startActivity(whoThread,
                    who.getOpPackageName(), who.getAttributionTag(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()), token,
                    target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);

// 检查返回来的值
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }

1. AMS 调用 startActivity 方法

 调用系统进程的 ActivityTaskManagerService 的 startActivity

/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java

    @Override
    public final int startActivity(IApplicationThread caller, String callingPackage,
            String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
            String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
            Bundle bOptions) {
        return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
                UserHandle.getCallingUserId());
    }

=======
    private int startActivityAsUser(IApplicationThread caller, String callingPackage,
            @Nullable String callingFeatureId, Intent intent, String resolvedType,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
        assertPackageMatchesCallingUid(callingPackage);
        enforceNotIsolatedCaller("startActivityAsUser");

        userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");

        // TODO: Switch to user app stacks here.
        return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
// caller 为:ApplicationThread
                .setCaller(caller)
                .setCallingPackage(callingPackage)
                .setCallingFeatureId(callingFeatureId)
                .setResolvedType(resolvedType)

// resultTo 为null, resultWho为 null
                .setResultTo(resultTo)
                .setResultWho(resultWho)

// requestCode 为 -1
                .setRequestCode(requestCode)
                .setStartFlags(startFlags)
                .setProfilerInfo(profilerInfo)
                .setActivityOptions(bOptions)
                .setUserId(userId)
                .execute();

    }

执行如下,省略:

 /frameworks/base/services/core/java/com/android/server/wm/ActivityStartController.java

    ActivityStartController(ActivityTaskManagerService service) {
        this(service, service.mTaskSupervisor,
                new DefaultFactory(service, service.mTaskSupervisor,
                    new ActivityStartInterceptor(service, service.mTaskSupervisor)));
    }
-----
    ActivityStarter obtainStarter(Intent intent, String reason) {

// DefaultFactory
        return mFactory.obtain().setIntent(intent).setReason(reason);
    }

==============
/frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java

execute() ==> executeRequest ==> startActivityUnchecked ==> startActivityInner
==> .....

==============
/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java

startSpecificActivity(

/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java

    void startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop,
            String hostingType) {
        try {
            if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "dispatchingStartProcess:"
                        + activity.processName);
            }
            // Post message to start process to avoid possible deadlock of calling into AMS with the
            // ATMS lock held.

// 相当于设置了一个 runnable
            final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::startProcess,
                    mAmInternal, activity.processName, activity.info.applicationInfo, knownToBeDead,
                    isTop, hostingType, activity.intent.getComponent());
            mH.sendMessage(m);
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }

mH.sendMessage(m) 看下是跑在什么线程

/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java

    public void initialize(IntentFirewall intentFirewall, PendingIntentController intentController,
            Looper looper) {

// 这里传入了 looper 对象
        mH = new H(looper);

looper 执行在那个线程,消息处理就是在那个线程

/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

2226      public ActivityManagerService(Context systemContext, ActivityTaskManagerService atm) {
2227          LockGuard.installLock(this, LockGuard.INDEX_ACTIVITY);
2228          mInjector = new Injector(systemContext);

。。。。。。。
2322          mActivityTaskManager.initialize(mIntentFirewall, mPendingIntentController,
2323                  DisplayThread.get().getLooper());

looper 是 DisplayThread.get().getLooper()

/frameworks/base/services/core/java/com/android/server/DisplayThread.java

DisplayThread 是个handlerThread

public final class DisplayThread extends ServiceThread {
    private static DisplayThread sInstance;
    private static Handler sHandler;

。。。
    private static void ensureThreadLocked() {
        if (sInstance == null) {
            sInstance = new DisplayThread();
            sInstance.start();
            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
            sHandler = new Handler(sInstance.getLooper());
        }
    }

    public static DisplayThread get() {
        synchronized (DisplayThread.class) {
            ensureThreadLocked();
            return sInstance;
        }
    }

==========
// ServiceThread  继承了 HandlerThread 
26  public class ServiceThread extends HandlerThread {

所以是跑在 DisplayThread 线程中

/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

16020          @Override
16021          public void startProcess(String processName, ApplicationInfo info, boolean knownToBeDead,
16022                  boolean isTop, String hostingType, ComponentName hostingName) {
16023              try {
16024                  if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
16025                      Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "startProcess:"
16026                              + processName);
16027                  }
16028                  synchronized (ActivityManagerService.this) {
16029                      // If the process is known as top app, set a hint so when the process is
16030                      // started, the top priority can be applied immediately to avoid cpu being
16031                      // preempted by other processes before attaching the process of top app.
16032                      startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
16033                              new HostingRecord(hostingType, hostingName, isTop),
16034                              ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE, false /* allowWhileBooting */,
16035                              false /* isolated */);
16036                  }
16037              } finally {
16038                  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
16039              }
16040          }

===============
2682      final ProcessRecord startProcessLocked(String processName,
2683              ApplicationInfo info, boolean knownToBeDead, int intentFlags,
2684              HostingRecord hostingRecord, int zygotePolicyFlags, boolean allowWhileBooting,
2685              boolean isolated) {
2686          return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
2687                  hostingRecord, zygotePolicyFlags, allowWhileBooting, isolated, 0 /* isolatedUid */,
2688                  null /* ABI override */, null /* entryPoint */,
2689                  null /* entryPointArgs */, null /* crashHandler */);
2690      }

/frameworks/base/services/core/java/com/android/server/am/ProcessList.java

// 要启动的对象
2023              final String entryPoint = "android.app.ActivityThread";

2043      @GuardedBy("mService")
2044      boolean startProcessLocked(HostingRecord hostingRecord, String entryPoint, ProcessRecord app,
2045              int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags, int mountExternal,
2046              String seInfo, String requiredAbi, String instructionSet, String invokeWith,
2047              long startTime) {
2048          app.setPendingStart(true);
2049          app.setRemoved(false);
2050          synchronized (mProcLock) {
2051              app.setKilledByAm(false);
2052              app.setKilled(false);
2053          }

。。。。
2080          } else {
2081              try {
2082                  final Process.ProcessStartResult startResult = startProcess(hostingRecord,
2083                          entryPoint, app,
2084                          uid, gids, runtimeFlags, zygotePolicyFlags, mountExternal, seInfo,
2085                          requiredAbi, instructionSet, invokeWith, startTime);
2086                  handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
2087                          startSeq, false);

// ProcessList.java
// ===== startProcessLocked 在fork 一个应用进程之后,保存这个 pid 值:handleProcessStartedLocked =====
2086                  handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
2087                          startSeq, false);

========
2297      private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
2298              ProcessRecord app, int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags,
2299              int mountExternal, String seInfo, String requiredAbi, String instructionSet,
2300              String invokeWith, long startTime) {
2301          try {

。。。。
2382              } else if (hostingRecord.usesAppZygote()) {

。。。。。。。。
2394              } else {
2395                  regularZygote = true;
2396                  startResult = Process.start(entryPoint,
2397                          app.processName, uid, uid, gids, runtimeFlags, mountExternal,
2398                          app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
2399                          app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
2400                          isTopApp, app.getDisabledCompatChanges(), pkgDataInfoMap,
2401                          allowlistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
2402                          new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
2403              }




 handleProcessStartedLocked

2. zygote socket 通信,通知 zygote 创建应用进程

Process.start 方法

/frameworks/base/core/java/android/os/Process.java

    public static ProcessStartResult start(@NonNull final String processClass,
                                           @Nullable final String niceName,
                                           int uid, int gid, @Nullable int[] gids,
                                           int runtimeFlags,
                                           int mountExternal,
                                           int targetSdkVersion,
                                           @Nullable String seInfo,
                                           @NonNull String abi,
                                           @Nullable String instructionSet,
                                           @Nullable String appDataDir,
                                           @Nullable String invokeWith,
                                           @Nullable String packageName,
                                           int zygotePolicyFlags,
                                           boolean isTopApp,
                                           @Nullable long[] disabledCompatChanges,
                                           @Nullable Map<String, Pair<String, Long>>
                                                   pkgDataInfoMap,
                                           @Nullable Map<String, Pair<String, Long>>
                                                   whitelistedDataInfoMap,
                                           boolean bindMountAppsData,
                                           boolean bindMountAppStorageDirs,
                                           @Nullable String[] zygoteArgs) {
        return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, invokeWith, packageName,
                    zygotePolicyFlags, isTopApp, disabledCompatChanges,
                    pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
                    bindMountAppStorageDirs, zygoteArgs);
    }

===============

606      public static final ZygoteProcess ZYGOTE_PROCESS = new ZygoteProcess();

// 创建了 ZygoteProcess 对象
    public ZygoteProcess() {

// String PRIMARY_SOCKET_NAME = "zygote";
// 设置 SOCKET_NAME 为 "zygote"
        mZygoteSocketAddress =
                new LocalSocketAddress(Zygote.PRIMARY_SOCKET_NAME,
                                       LocalSocketAddress.Namespace.RESERVED);

====================start==========================

java 层的Socket 通信的使用:

客户端:

import android.net.LocalSocket;
import android.net.LocalSocketAddress;

// 连接服务器端
LocalSocket Client = new LocalSocket();  
string name = "socket_name";
Client.connect(new LocalSocketAddress(name));

// 写入数据:
os = new PrintWriter(Client.getOutputStream());  
os.println(data);  
os.flush();  

// 读取服务端数据

BufferedReader is = new BufferedReader(new InputStreamReader(Client.getInputStream()));  
String result = is.readLine();


服务器端:


import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;

// 创建 LocalServerSocket 对象,执行accept 方法
// LocalServerSocket 的名字需要和客户端创建的name 是一样的
string name = "socket_name";
LocalServerSocket server = new LocalServerSocket(name);        
while (!exit) {  
   LocalSocket connect = server.accept(); 

// 获取客户端的数据
InputStream input = socket.getInputStream();
byte[] buffer = new byte[1024];
int len = input.read(buffer);

PrintWriter os = new PrintWriter(socket.getOutputStream());  
os.println("this is server\0");  
os.flush();

====================end==========================

zygote 进程 执行 

/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

    @UnsupportedAppUsage
    public static void main(String[] argv) {
        ZygoteServer zygoteServer = null;

。。。。。。
            final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME);

// 创建了 ZygoteServer 对象
            zygoteServer = new ZygoteServer(isPrimaryZygote);


            // The select loop returns early in the child process after a fork and
            // loops forever in the zygote.

// 执行 runSelectLoop 循环监听客户端的创建进程的消息
            caller = zygoteServer.runSelectLoop(abiList);

// 创建了 ZygoteServer 对象

/frameworks/base/core/java/com/android/internal/os/ZygoteServer.java

    /**
     * Listening socket that accepts new server connections.
     */
    private LocalServerSocket mZygoteSocket;

。。。。。
    ZygoteServer(boolean isPrimaryZygote) {
        mUsapPoolEventFD = Zygote.getUsapPoolEventFD();

        if (isPrimaryZygote) {

// 调用createManagedSocketFromInitSocket 创建LocalServerSocket  对象,socket名字为: PRIMARY_SOCKET_NAME = "zygote";
            mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);
            mUsapPoolSocket =
                    Zygote.createManagedSocketFromInitSocket(
                            Zygote.USAP_POOL_PRIMARY_SOCKET_NAME);

构造函数中调用createManagedSocketFromInitSocket 创建LocalServerSocket  对象,socket名字为: PRIMARY_SOCKET_NAME = "zygote";

    static LocalServerSocket createManagedSocketFromInitSocket(String socketName) {
        int fileDesc;
        final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;

        try {
            String env = System.getenv(fullSocketName);
            fileDesc = Integer.parseInt(env);
        } catch (RuntimeException ex) {
            throw new RuntimeException("Socket unset or invalid: " + fullSocketName, ex);
        }

        try {
            FileDescriptor fd = new FileDescriptor();
            fd.setInt$(fileDesc);

// 获取 LocalServerSocket 对象
            return new LocalServerSocket(fd);
        } catch (IOException ex) {
            throw new RuntimeException(
                "Error building socket from file descriptor: " + fileDesc, ex);
        }
    }

// 执行 runSelectLoop 循环监听客户端的创建进程的消息

/frameworks/base/core/java/com/android/internal/os/ZygoteServer.java

    Runnable runSelectLoop(String abiList) {
        ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
        ArrayList<ZygoteConnection> peers = new ArrayList<>();

// 将 ZygoteSocket 的文件描述符放到 socketFDs 数组中
        socketFDs.add(mZygoteSocket.getFileDescriptor());
        peers.add(null);

        while (true) {

            StructPollfd[] pollFDs;

            } else {
                pollFDs = new StructPollfd[socketFDs.size()];
            }

            int pollIndex = 0;

// 创建 StructPollfd ,并赋值初值
            for (FileDescriptor socketFD : socketFDs) {
                pollFDs[pollIndex] = new StructPollfd();
                pollFDs[pollIndex].fd = socketFD;
                pollFDs[pollIndex].events = (short) POLLIN;
                ++pollIndex;
            }

            final int usapPoolEventFDIndex = pollIndex;

            if (mUsapPoolEnabled) {
。。。。
            }

            int pollTimeoutMs;

            if (mUsapPoolRefillTriggerTimestamp == INVALID_TIMESTAMP) {
                pollTimeoutMs = -1;
            } else {
                long elapsedTimeMs = System.currentTimeMillis() - mUsapPoolRefillTriggerTimestamp;

                if (elapsedTimeMs >= mUsapPoolRefillDelayMs) {

                    pollTimeoutMs = 0;
                    mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
                    mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;

                } else if (elapsedTimeMs <= 0) {

                    pollTimeoutMs = mUsapPoolRefillDelayMs;

                } else {
                    pollTimeoutMs = (int) (mUsapPoolRefillDelayMs - elapsedTimeMs);
                }
            }

            int pollReturnValue;
            try {

// poll 监听 fd 变化
                pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
            } catch (ErrnoException ex) {
                throw new RuntimeException("poll failed", ex);
            }

            if (pollReturnValue == 0) {

                mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
                mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;

            } else {
                boolean usapPoolFDRead = false;

                while (--pollIndex >= 0) {
                    if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
                        continue;
                    }

                    if (pollIndex == 0) {
                        // Zygote server socket

// acceptCommandPeer 创建 ZygoteConnection 对象
                        ZygoteConnection newPeer = acceptCommandPeer(abiList);
                        peers.add(newPeer);
// 将其增加到监听的数组中
                        socketFDs.add(newPeer.getFileDescriptor());
                    } else if (pollIndex < usapPoolEventFDIndex) {
                        // Session socket accepted from the Zygote server socket

                        try {

// 再次循环后,监听到客户端消息,则执行下列方法
                            ZygoteConnection connection = peers.get(pollIndex);
                            boolean multipleForksOK = !isUsapPoolEnabled()
                                    && ZygoteHooks.isIndefiniteThreadSuspensionSafe();

// 执行 ZygoteConnection 对象 的 processCommand 方法
                            final Runnable command =
                                    connection.processCommand(this, multipleForksOK);

                            if (mIsForkChild) {

                                if (command == null) {
                                    throw new IllegalStateException("command == null");
                                }

                                return command;
                            } else {
                                // We're in the server - we should never have any commands to run.
                                if (command != null) {
                                    throw new IllegalStateException("command != null");
                                }

                                if (connection.isClosedByPeer()) {
                                    connection.closeSocket();
                                    peers.remove(pollIndex);
                                    socketFDs.remove(pollIndex);
                                }
                            }
                        } catch (Exception e) {
                            if (!mIsForkChild) {
。。。。。。。。。。
                            } else {
。。。。。。。。
                                throw e;

                    } else {

                        long messagePayload;

                        try {
                            byte[] buffer = new byte[Zygote.USAP_MANAGEMENT_MESSAGE_BYTES];
                            int readBytes =
                                    Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length);

                            if (readBytes == Zygote.USAP_MANAGEMENT_MESSAGE_BYTES) {
                                DataInputStream inputStream =
                                        new DataInputStream(new ByteArrayInputStream(buffer));

                                messagePayload = inputStream.readLong();
                            } else {
。。。

。。。。。。。。
            }
        }
    }
}

// acceptCommandPeer 创建 ZygoteConnection 对象

    private ZygoteConnection acceptCommandPeer(String abiList) {
        try {
            return createNewConnection(mZygoteSocket.accept(), abiList);
        } catch (IOException ex) {
            throw new RuntimeException(
                    "IOException during accept()", ex);
        }
    }

=========
    protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
            throws IOException {
        return new ZygoteConnection(socket, abiList);
    }

startViaZygote 创建应用进程 :system 进程与 zygote 进程 socket 通信

/frameworks/base/core/java/android/os/ZygoteProcess.java

    public final Process.ProcessStartResult start(@NonNull final String processClass,
                                                  final String niceName,
。。。

        try {
            return startViaZygote(processClass, niceName, uid, gid, gids,
                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
                    packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges,
                    pkgDataInfoMap, allowlistedDataInfoList, bindMountAppsData,
                    bindMountAppStorageDirs, zygoteArgs);

=================
    private Process.ProcessStartResult startViaZygote(@NonNull final String processClass,
                                                      @Nullable final String niceName,
                                                      final int uid, final int gid,
                                                      @Nullable final int[] gids,
                                                      int runtimeFlags, int mountExternal,
                                                      int targetSdkVersion,
                                                      @Nullable String seInfo,
                                                      @NonNull String abi,
                                                      @Nullable String instructionSet,
                                                      @Nullable String appDataDir,
                                                      @Nullable String invokeWith,
                                                      boolean startChildZygote,
                                                      @Nullable String packageName,
                                                      int zygotePolicyFlags,
                                                      boolean isTopApp,
                                                      @Nullable long[] disabledCompatChanges,
                                                      @Nullable Map<String, Pair<String, Long>>
                                                              pkgDataInfoMap,
                                                      @Nullable Map<String, Pair<String, Long>>
                                                              allowlistedDataInfoList,
                                                      boolean bindMountAppsData,
                                                      boolean bindMountAppStorageDirs,
                                                      @Nullable String[] extraArgs)
                                                      throws ZygoteStartFailedEx {
        ArrayList<String> argsForZygote = new ArrayList<>();

。。。。

        argsForZygote.add("--target-sdk-version=" + targetSdkVersion);

        // --setgroups is a comma-separated list
        if (gids != null && gids.length > 0) {
            final StringBuilder sb = new StringBuilder();
            sb.append("--setgroups=");

            final int sz = gids.length;
            for (int i = 0; i < sz; i++) {
                if (i != 0) {
                    sb.append(',');
                }
                sb.append(gids[i]);
            }

            argsForZygote.add(sb.toString());
        }
。。。。。
。。。。。
        argsForZygote.add(processClass);


// 将消息封装 再 argsForZygote 中
        if (extraArgs != null) {
            Collections.addAll(argsForZygote, extraArgs);
        }

        synchronized(mLock) {

            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
                                              zygotePolicyFlags,
                                              argsForZygote);
        }
    }

zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi)

openZygoteSocketIfNeeded 方法:返回值是 zygoteState

    @GuardedBy("mLock")
    private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
        try {
            attemptConnectionToPrimaryZygote();

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

// 连接的 socket name 为:PRIMARY_SOCKET_NAME = "zygote";
    public ZygoteProcess() {
        mZygoteSocketAddress =
                new LocalSocketAddress(Zygote.PRIMARY_SOCKET_NAME,
                                       LocalSocketAddress.Namespace.RESERVED);

zygoteSendArgsAndGetResult 方法

    private Process.ProcessStartResult zygoteSendArgsAndGetResult(
            ZygoteState zygoteState, int zygotePolicyFlags, @NonNull ArrayList<String> args)
            throws ZygoteStartFailedEx {
。。。。
         */
        String msgStr = args.size() + "\n" + String.join("\n", args) + "\n";


        return attemptZygoteSendArgsAndGetResult(zygoteState, msgStr);
    }
    private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(
            ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {
        try {
            final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
            final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;

// 将客户端的请求通知到服务器端。阻塞
            zygoteWriter.write(msgStr);
            zygoteWriter.flush();

            // Always read the entire result from the input stream to avoid leaving
            // bytes in the stream for future process starts to accidentally stumble
            // upon.

// 读取服务器端返回的进程号 pid
            Process.ProcessStartResult result = new Process.ProcessStartResult();
            result.pid = zygoteInputStream.readInt();
            result.usingWrapper = zygoteInputStream.readBoolean();

            if (result.pid < 0) {
                throw new ZygoteStartFailedEx("fork() failed");
            }

// 返回这个 result
            return result;

zygote 进程执行 

/frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java

    Runnable processCommand(ZygoteServer zygoteServer, boolean multipleOK) {
        ZygoteArguments parsedArgs;

        try (ZygoteCommandBuffer argBuffer = new ZygoteCommandBuffer(mSocket)) {
            while (true) {
                try {

// 将传过来的参数包装为:ZygoteArguments 
// 调用 parseArgs 函数解析这些参数
                    parsedArgs = ZygoteArguments.getInstance(argBuffer);
                    // Keep argBuffer around, since we need it to fork.
                } catch (IOException ex) {
                    throw new IllegalStateException("IOException on command socket", ex);
                }
                if (parsedArgs == null) {
                    isEof = true;
                    return null;
                }

                int pid;
。。。。。。。


                if (parsedArgs.mInvokeWith != null || parsedArgs.mStartChildZygote
                        || !multipleOK || peer.getUid() != Process.SYSTEM_UID) {
                    // Continue using old code for now. TODO: Handle these cases in the other path.

//1-1) 去fork 一个子进程 Zygote.forkAndSpecialize
                    pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid,
                            parsedArgs.mGids, parsedArgs.mRuntimeFlags, rlimits,
                            parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName,
                            fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
                            parsedArgs.mInstructionSet, parsedArgs.mAppDataDir,
                            parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList,
                            parsedArgs.mAllowlistedDataInfoList, parsedArgs.mBindMountAppDataDirs,
                            parsedArgs.mBindMountAppStorageDirs);

                    try {
                        if (pid == 0) {
// 设置是子进程,退出 runSelectLoop
                            // in child
                            zygoteServer.setForkChild();
// 关闭socket 通信
                            zygoteServer.closeServerSocket();
                            IoUtils.closeQuietly(serverPipeFd);
                            serverPipeFd = null;
// 1-2)执行子进程方法 handleChildProc
                            return handleChildProc(parsedArgs, childPipeFd,
                                    parsedArgs.mStartChildZygote);
                        } else {
                            // In the parent. A pid < 0 indicates a failure and will be handled in
                            // handleParentProc.
                            IoUtils.closeQuietly(childPipeFd);
                            childPipeFd = null;

// 1-3)执行父进程方法 handleParentProc,通知子进程的pid 号
                            handleParentProc(pid, serverPipeFd);
                            return null;
                        }
                    } finally {
                        IoUtils.closeQuietly(childPipeFd);
                        IoUtils.closeQuietly(serverPipeFd);
                    }
                } else {
。。。。。
        throw new AssertionError("Shouldn't get here");
    }

1-1) 去fork 一个子进程 Zygote.forkAndSpecialize

/frameworks/base/core/java/com/android/internal/os/Zygote.java

    static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
            int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
            int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
            boolean isTopApp, String[] pkgDataInfoList, String[] allowlistedDataInfoList,
            boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
        ZygoteHooks.preFork();

// 调用native 层代码 nativeForkAndSpecialize
        int pid = nativeForkAndSpecialize(
                uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
                fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
                pkgDataInfoList, allowlistedDataInfoList, bindMountAppDataDirs,
                bindMountAppStorageDirs);

// 调用native 层代码 nativeForkAndSpecialize

/frameworks/base/core/jni/com_android_internal_os_Zygote.cpp

static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
        JNIEnv* env, jclass, jint uid, jint gid, jintArray gids, jint runtime_flags,
        jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name,
        jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote,
        jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
        jobjectArray pkg_data_info_list, jobjectArray allowlisted_data_info_list,
        jboolean mount_data_dirs, jboolean mount_storage_dirs) {
。。。。

// fork 一个进程
    pid_t pid = zygote::ForkCommon(env, /* is_system_server= */ false, fds_to_close, fds_to_ignore,
                                   true);

// 如果是子进程的话,则执行 SpecializeCommon
    if (pid == 0) {
        SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, capabilities, capabilities,
                         mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE,
                         instruction_set, app_data_dir, is_top_app == JNI_TRUE, pkg_data_info_list,
                         allowlisted_data_info_list, mount_data_dirs == JNI_TRUE,
                         mount_storage_dirs == JNI_TRUE);
    }
    return pid;
}
// Utility routine to fork a process from the zygote.
pid_t zygote::ForkCommon(JNIEnv* env, bool is_system_server,
                         const std::vector<int>& fds_to_close,
                         const std::vector<int>& fds_to_ignore,
                         bool is_priority_fork,
                         bool purge) {
  SetSignalHandlers();

。。。。

// fork 一个子进程
  pid_t pid = fork();

  if (pid == 0) {
    if (is_priority_fork) {
      setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MAX);
    } else {
      setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MIN);
    }

fork采用copy-on-write技术,这是linux创建进程的标准方法,调用一次,返回两次,返回值有3种类型:

  • 在父进程中,fork返回新创建的子进程pid。
  • 在子进程中,fork返回0;
  • 当出现错误时,fork返回负数。

fork的主要工作是寻找空闲的进程号pid,然后从父进程拷贝进程信息,例如数据段和代码段,fork()后子进程要执行的代码等。Zygote进程是所有Android进程的母体,包括system_server和各个App进程。

Zygote利用fork方法生成新进程,对于新进程A复用Zygote进程本身的资源,再加上新进程A相关的资源,构成新的应用进程A。

1-2)执行子进程方法 handleChildProc

    private Runnable handleChildProc(ZygoteArguments parsedArgs,
            FileDescriptor pipeFd, boolean isZygote) {
        /*
         * By the time we get here, the native code has closed the two actual Zygote
         * socket connections, and substituted /dev/null in their place.  The LocalSocket
         * objects still need to be closed properly.
         */

        closeSocket();

        Zygote.setAppProcessName(parsedArgs, TAG);
...
        } else {
            if (!isZygote) {
                return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                        parsedArgs.mDisabledCompatChanges,
                        parsedArgs.mRemainingArgs, null /* classLoader */);
            } else {

/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

    public static Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
            String[] argv, ClassLoader classLoader) {
        if (RuntimeInit.DEBUG) {
            Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
        }

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
        RuntimeInit.redirectLogStreams();

        RuntimeInit.commonInit();

// 连接biander 驱动 nativeZygoteInit
        ZygoteInit.nativeZygoteInit();

// 反射获取 ActivityThread 的main 方法,调用run 方法时执行
        return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
                classLoader);
    }

连接biander 驱动 nativeZygoteInit

/frameworks/base/core/jni/AndroidRuntime.cpp

int register_com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env)
{
    const JNINativeMethod methods[] = {
        { "nativeZygoteInit", "()V",
            (void*) com_android_internal_os_ZygoteInit_nativeZygoteInit },
    };

=====

static AndroidRuntime* gCurRuntime = NULL;

static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
{
    gCurRuntime->onZygoteInit();
}

AndroidRuntime 的 onZygoteInit 方法

/frameworks/base/cmds/app_process/app_main.cpp

class AppRuntime : public AndroidRuntime
{
public:
    AppRuntime(char* argBlockStart, const size_t argBlockLength)
        : AndroidRuntime(argBlockStart, argBlockLength)
        , mClass(NULL)
    {
    }

    virtual void onZygoteInit()
    {
        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.\n");
        proc->startThreadPool();
    }

/frameworks/native/libs/binder/ProcessState.cpp

创建binder 主线程 

void ProcessState::startThreadPool()
{
    AutoMutex _l(mLock);
    if (!mThreadPoolStarted) {
        mThreadPoolStarted = true;
        spawnPooledThread(true);
    }
}

=====
void ProcessState::spawnPooledThread(bool isMain)
{
    if (mThreadPoolStarted) {
        String8 name = makeBinderThreadName();
        ALOGV("Spawning new pooled thread, name=%s\n", name.string());
        sp<Thread> t = sp<PoolThread>::make(isMain);
        t->run(name.string());
    }
}

1-3)执行父进程方法 handleParentProc,通知子进程的pid 号

    private void handleParentProc(int pid, FileDescriptor pipeFd) {
        if (pid > 0) {
            setChildPgid(pid);
        }

        boolean usingWrapper = false;
        if (pipeFd != null && pid > 0) {
            int innerPid = -1;
            try {

。。。。。。

        try {
            mSocketOutStream.writeInt(pid);
            mSocketOutStream.writeBoolean(usingWrapper);
        } catch (IOException ex) {
            throw new IllegalStateException("Error writing to command socket", ex);
        }
    }

3. 应用进程初始化流程

/frameworks/base/core/java/android/app/ActivityThread.java

    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

// 开启应用的主线程
        Looper.prepareMainLooper();

// 创建 ActivityThread 对象,然后执行 attach 方法
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

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


// 循环
        Looper.loop();

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

创建 ActivityThread 对象,然后执行 attach 方法

    @UnsupportedAppUsage
    ActivityThread() {
        mResourcesManager = ResourcesManager.getInstance();
    }

=======
    private void attach(boolean system, long startSeq) {
        sCurrentActivityThread = this;
        mConfigurationController = new ConfigurationController(this);
        mSystemThread = system;

// 不是 system 进程
        if (!system) {
            android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                                                    UserHandle.myUserId());
            RuntimeInit.setApplicationObject(mAppThread.asBinder());
            final IActivityManager mgr = ActivityManager.getService();
            try {
                mgr.attachApplication(mAppThread, startSeq);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }


/// mAppThread 为:
    final ApplicationThread mAppThread = new ApplicationThread();

// ApplicationThread  继承了 Stub 表明可以进程间通信
    private class ApplicationThread extends IApplicationThread.Stub {

ActivityThread 管理着主线程,与 system 进程 的ams 交互。实现方式是:attachApplication(mAppThread,应用进程使用AMS需要拿到AMS的句柄IActivityManager,而系统需要通知应用和管理应用的生命周期,所以也需要持有应用进程的binder句柄IApplicationThread。

/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

4638      public final void attachApplication(IApplicationThread thread, long startSeq) {
4639          if (thread == null) {
4640              throw new SecurityException("Invalid application interface");
4641          }
4642          synchronized (this) {
4643              int callingPid = Binder.getCallingPid();
4644              final int callingUid = Binder.getCallingUid();
4645              final long origId = Binder.clearCallingIdentity();
4646              attachApplicationLocked(thread, callingPid, callingUid, startSeq);
4647              Binder.restoreCallingIdentity(origId);
4648          }

==========

ProcessList.java
===== startProcessLocked 在fork 一个应用进程之后,保存这个 pid 值:handleProcessStartedLocked =====

/frameworks/base/services/core/java/com/android/server/am/ProcessList.java

    @GuardedBy("mService")
    boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper,
            long expectedStartSeq, boolean procAttached) {
        mPendingStarts.remove(expectedStartSeq);
        final String reason = isProcStartValidLocked(app, expectedStartSeq);
        if (reason != null) {
            Slog.w(TAG_PROCESSES, app + " start not valid, killing pid=" +
                    pid
                    + ", " + reason);
            app.setPendingStart(false);

// 如果判断该进程是无效的,则调用native 层方法kill 这个进程
            killProcessQuiet(pid);
            Process.killProcessGroup(app.uid, app.getPid());
            noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
                    ApplicationExitInfo.SUBREASON_INVALID_START, reason);
            return false;
        }
        mService.mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);
        checkSlow(app.getStartTime(), "startProcess: done updating battery stats");

// 打印对应的log
        EventLog.writeEvent(EventLogTags.AM_PROC_START,
                UserHandle.getUserId(app.getStartUid()), pid, app.getStartUid(),
                app.processName, app.getHostingRecord().getType(),
                app.getHostingRecord().getName() != null ? app.getHostingRecord().getName() : "");

        try {
            AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.info.packageName,
                    app.processName, app.uid, app.getSeInfo(), app.info.sourceDir, pid);
        } catch (RemoteException ex) {
            // Ignore
        }

        Watchdog.getInstance().processStarted(app.processName, pid);

        checkSlow(app.getStartTime(), "startProcess: building log message");
        StringBuilder buf = mStringBuilder;
        buf.setLength(0);
        buf.append("Start proc ");
        buf.append(pid);
        buf.append(':');
        buf.append(app.processName);
        buf.append('/');
        UserHandle.formatUid(buf, app.getStartUid());
        if (app.getIsolatedEntryPoint() != null) {
            buf.append(" [");
            buf.append(app.getIsolatedEntryPoint());
            buf.append("]");
        }
        buf.append(" for ");
        buf.append(app.getHostingRecord().getType());
        if (app.getHostingRecord().getName() != null) {
            buf.append(" ");
            buf.append(app.getHostingRecord().getName());
        }
        mService.reportUidInfoMessageLocked(TAG, buf.toString(), app.getStartUid());
        synchronized (mProcLock) {
            app.setPid(pid);
            app.setUsingWrapper(usingWrapper);
            app.setPendingStart(false);
        }
        checkSlow(app.getStartTime(), "startProcess: starting to update pids map");
        ProcessRecord oldApp;
        synchronized (mService.mPidsSelfLocked) {
            oldApp = mService.mPidsSelfLocked.get(pid);
        }
        // If there is already an app occupying that pid that hasn't been cleaned up
        if (oldApp != null && !app.isolated) {
            // Clean up anything relating to this pid first
            Slog.wtf(TAG, "handleProcessStartedLocked process:" + app.processName
                    + " startSeq:" + app.getStartSeq()
                    + " pid:" + pid
                    + " belongs to another existing app:" + oldApp.processName
                    + " startSeq:" + oldApp.getStartSeq());
            mService.cleanUpApplicationRecordLocked(oldApp, pid, false, false, -1,
                    true /*replacingPid*/, false /* fromBinderDied */);
        }

// 给 ActivityManagerService 管理
        mService.addPidLocked(app);
        synchronized (mService.mPidsSelfLocked) {
            if (!procAttached) {
                Message msg = mService.mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
                msg.obj = app;
                mService.mHandler.sendMessageDelayed(msg, usingWrapper
                        ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
            }
        }
        checkSlow(app.getStartTime(), "startProcess: done updating pids map");
        return true;
    }

打印对应的log:

EventLog.writeEvent(EventLogTags.AM_PROC_START,
                UserHandle.getUserId(app.getStartUid()), pid, app.getStartUid(),
                app.processName, app.getHostingRecord().getType(),
                app.getHostingRecord().getName() != null ? app.getHostingRecord().getName() : "");

// 0:看log 全是0
// 1238 是pid进程号
// app.getStartUid 传给zygote 的uid为 :1001
// 应用名字processName:com.android.phone
am_proc_start: [0,1238,1001,com.android.phone,added application,com.android.phone]

// 给 ActivityManagerService 管理
mService.addPidLocked(app);

/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

885      @GuardedBy("this")
886      void addPidLocked(ProcessRecord app) {
887          final int pid = app.getPid();
888          synchronized (mPidsSelfLocked) {
889              mPidsSelfLocked.doAddInternal(pid, app);
890          }
891          synchronized (sActiveProcessInfoSelfLocked) {
892              if (app.processInfo != null) {
893                  sActiveProcessInfoSelfLocked.put(pid, app.processInfo);
894              } else {
895                  sActiveProcessInfoSelfLocked.remove(pid);
896              }
897          }
898          mAtmInternal.onProcessMapped(pid, app.getWindowProcessController());
899      }

深入理解Android之应用程序进程启动流程(Android 10) - 掘金

Android Framework(三)——应用程序进程启动流程 - Sukai's Blog - Android Developer

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/556191.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用户管理 ---MySQL总结(七)

用户管理 对于MySQL的用户也是需要进行管理&#xff0c;这里的东西类似与Linux的多用户管理&#xff0c;基本相同 用户属性 MySQL的用户都是储存在数据库mysql的user之中。 这里使用desc table_name;查看user表的属性。 desc user;//下面的就是输出的结果host:登陆限制 user…

chatgpt赋能Python-python_ai_app

用Python编写AI应用程序 Python是目前最受欢迎的编程语言之一&#xff0c;被广泛用于各种应用程序的开发。其中&#xff0c;人工智能&#xff08;AI&#xff09;应用程序成为Python编程人员最感兴趣和热门的领域之一。这篇文章将重点介绍用Python编写AI应用程序的好处&#xf…

从零开始Vue3+Element Plus后台管理系统(16)——组合式函数hook二次封装el-table

终于写到组合式函数了&#xff0c;它类似vue2的mixin&#xff0c;但优于mixin&#xff0c;用好了可以事半功倍。 在 Vue 应用的概念中&#xff0c;“组合式函数”(Composables) 是一个利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数。 官方文档&#xff1a;https://cn.vu…

chatgpt赋能Python-python_chan

Python的Channel模块&#xff1a;优化你的并发控制 Python是一门优秀的编程语言&#xff0c;在众多优秀的模块中&#xff0c;Channel模块是一个备受喜爱的模块。它是Python并发控制的建议之一&#xff0c;可以被用来在多个协程之间传递和传输消息。这个模块不仅仅是Python 3.5…

chatgpt赋能Python-python_cal

Python编程的神器——Cal 随着人工智能、机器学习等技术的发展&#xff0c;Python语言成为了最热门的编程语言之一。Python可以帮助程序员快速实现自己的想法&#xff0c;让程序的编写变得更加简单和容易。在Python中&#xff0c;有许多高效好用的工具和库&#xff0c;而其中最…

chatgpt赋能Python-python_chi2

Python中的Chi-Squared测试&#xff1a;一种用于统计分析的重要方法 数据分析是当今商业和科学中最重要的工具之一&#xff0c;它可以帮助人们了解他们的业务和科学领域。其中数据分析的技术以Python为代表的编程语言越来越受到欢迎&#xff0c;这些方法可以用于分类、回归、聚…

《数据可视化》课程期末项目_地理交通数据可视化

2022年上海疫情爆发期间交通数据可视化分析 《数据可视化》课程期末项目报告-选题&#xff1a;地理数据可视化 GitHub源码地址(如果有用点个 star 吧~谢谢&#xff01;) 文章目录 1.0 项目简介2.0 数据简介2.1 航线数据2.2 公交路线数据2.1 项目流程 3.0 数据处理3.1 航线数据…

【面试题】如何实现vue虚拟列表,纵享丝滑

大厂面试题分享 面试题库 前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 web前端面试题库 VS java后端面试题库大全 前言 最近在工作中遇到了一个列表的需求&#xff0c;因为做的是C端&#xff0c;所以对性能…

什么是接口测试?接口测试流程有哪些?我来告诉你

目录 首先&#xff0c;什么是接口呢&#xff1f; 一、常见接口&#xff1a; 二、前端和后端&#xff1a; 三、什么是接口测试&#xff1a; 四、接口组成 五、为什么要做接口测试&#xff1a; 六、接口测试怎么测&#xff1a; 七、用什么工具测 八. 接口测试持续集成 九…

chatgpt赋能Python-python_canbus

Python Canbus&#xff1a;如何使用Python编程语言控制Canbus&#xff1f; 介绍 Canbus被广泛地应用于现代汽车中&#xff0c;是一个用于通讯的协议&#xff0c;允许汽车的各个部分进行通信。为了控制Canbus&#xff0c;很多工程师都使用Python编程语言&#xff0c;因为它简单…

爬虫练习-12306自动购票升级版

文章目录 前言代码更新 前言 hello兄弟们&#xff0c;偷懒归来了。别问为啥这么久没更&#xff0c;问就是失踪了 最近一直在学习Django以及爬虫进阶之类的知识&#xff0c;加上快期末了&#xff0c;一直没有想起来自己还有一个账号没有更新&#xff0c;sorry啦 言归正传&…

SpringBoot - Jackson详解

写在前面 JSON 是目前主流的前后端数据传输方式。在 Spring Boot 项目中&#xff0c;只要添加了 WEB依赖&#xff08;spring-boot-starter-web&#xff09;&#xff0c;就可以很方便地实现 JSON 转换。WEB 依赖默认加入了 jackson-databind 作为 JSON 处理器&#xff0c;我们不…

算法小试炼(差不多相当于重新过一遍ACWING,为了夏令营做点准备)

1.最长不重复子串 这个题目的具体意思就不用我说了&#xff0c;我这里给出两种算法 1&#xff09;暴力搜索 只要机器够快&#xff0c;没有什么是暴搜解决不了的^ ^&#xff08;开玩笑 很简单&#xff0c;我们只需要遍历长度&#xff0c;跟左边界就好了&#xff0c;这个应该没…

测试必知必会的Mock数据方法

Mock数据的含义 那么Mock数据是什么意思呢 首先Mock这个英文单词有模拟的意思&#xff0c;模拟数据通俗的理解就是构造假数据&#xff0c;即Mock数据就是通过构造假数据来达到测试的目的&#xff0c;它广泛运用于功能测试、接口测试、单元测试 在功能测试中&#xff0c;可以…

离散数学 | 图论 | 欧拉图 | 哈密顿图 | 割点 | 桥(欧拉图和哈密顿图有没有割点和桥?)

本文主要解决以下几个问题&#xff1a; 1.欧拉图能不能有割点&#xff0c;能不能有桥&#xff1f; 2.哈密顿图能不能有割点&#xff0c;能不能有桥&#xff1f; 首先我们要明白几个定义 割点的定义就是在一个图G中&#xff0c;它本来是连通的&#xff0c;去掉一个点v以后这个…

【firewalld防火墙】

目录 一、firewalld概述二、firewalld 与 iptables 的区别1、firewalld 区域的概念 三、firewalld防火墙默认的9个区域四、Firewalld 网络区域1、区域介绍2、firewalld数据处理流程 五、firewalld防火墙的配置方法1、使用firewall-cmd 命令行工具。2、使用firewall-config 图形…

【计算机网络基础】章节测试4 网络层

R1与R2是一个自治系统中采用RIP路由协议的两个相邻路由器,R1的路由表如图(a)所示。如果R1收到R2发送的如图(b)所示的(V,D)报文,更新之后的R1的4个路由表项的距离从上到下依次为0、4、4、2,那么图 (b)中a、b、c、d 可能的数据一个是( C )。 A. 1、2、2、1 B. 2、2、3、1…

Java 核心技术 卷I 第2章 Java程序设计环境

第2章 Java程序设计环境 2.1 安装Java开发工具包 2.1.1 下载JDK www.oracle.com/technetwork/java/javase/downloads Java术语 2.1.2 设置JDK 下载JDK之后&#xff0c;需要安装这个开发包并明确要在哪里安装&#xff0c;后面还会需要这个信息. 指/opt/jdk1.8.0_31/bin或c…

从零开始Vue3+Element Plus后台管理系统(十四)——PDF预览和打印

其实我常常会纠结今天要写什么内容。 因为希望能够保持每日更新&#xff0c;所以要写的内容不能太难——最好是半天可以搞出demo并且输出文章&#xff0c;所以很多东西浅尝辄止&#xff0c;并没有深入研究&#xff0c;还写出了一些bug &#x1f41b; 今天又浅浅的研究了下在V…

这还只是阿里20K+测试岗面试题,看的我冷汗直流.....

朋友入职已经两周了&#xff0c;整体工作环境还是非常满意的&#xff01;所以这次特意抽空给我写出了这份面试题&#xff0c;而我把它分享给伙伴们&#xff0c;面试&入职的经验&#xff01; 大概是在3月中的时候他告诉我投递了阿里巴巴并且简历已通过&#xff0c;3月23经过…