zoukankan      html  css  js  c++  java
  • Android 源码分析(七) Launcher 桌面程序启动分析

    一.前言:

        init进程 –> Zygote进程 –> SystemServer进程 –> Launcher桌面程序 -> 我们的App应用

        init进程:linux的根进程,android系统是基于linux系统的,因此可以算作是整个android操作系统的第一个进程;

        Zygote进程:android系统的根进程,主要作用:可以作用Zygote进程fork出SystemServer进程和各种应用进程;

        SystemService进程:主要是在这个进程中启动系统的各项服务,比如ActivityManagerService,PackageManagerService,WindowManagerService服务等等;
        
        Launcher桌面程序:就是我们平时看到的桌面程序,它其实也是一个android应用程序,只不过这个应用程序是系统默认第一个启动的应用程序.在Zygote进程里等待SystemService进程启动后,创建.
       

      二. Launcher 桌面程序启动

            Launcher程序就是我们平时看到的桌面程序,它其实也是一个android应用程序,只不过这个应用程序是系统默认第一个启动的应用程序。

      首先了解下,Zygote进程是如何启动一个应用的。

    //ZygoteServer.java
     void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
            ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
            ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
    
            fds.add(mServerSocket.getFileDescriptor());
            peers.add(null);
    
            while (true) {
                StructPollfd[] pollFds = new StructPollfd[fds.size()];
                for (int i = 0; i < pollFds.length; ++i) {
                    pollFds[i] = new StructPollfd();
                    pollFds[i].fd = fds.get(i);
                    pollFds[i].events = (short) POLLIN;
                }
                try {
                    Os.poll(pollFds, -1);
                } catch (ErrnoException ex) {
                    throw new RuntimeException("poll failed", ex);
                }
                for (int i = pollFds.length - 1; i >= 0; --i) {
                    if ((pollFds[i].revents & POLLIN) == 0) {
                        continue;
                    }
                    if (i == 0) {
                        ZygoteConnection newPeer = acceptCommandPeer(abiList);
                        peers.add(newPeer);
                        fds.add(newPeer.getFileDesciptor());
                    } else {
                        boolean done = peers.get(i).runOnce(this);
                        if (done) {
                            peers.remove(i);
                            fds.remove(i);
                        }
                    }
                }
            }
        }
    }

      /**
         * Waits for and accepts a single command connection. Throws
         * RuntimeException on failure.
         */
        private ZygoteConnection acceptCommandPeer(String abiList) {
            try {
                return createNewConnection(mServerSocket.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);
        }
    //ZygoteConnection.java
        /**
         * Constructs instance from connected socket.
         *
         * @param socket non-null; connected socket
         * @param abiList non-null; a list of ABIs this zygote supports.
         * @throws IOException
         */
        ZygoteConnection(LocalSocket socket, String abiList) throws IOException {
            mSocket = socket;
            this.abiList = abiList;
    
            mSocketOutStream
                    = new DataOutputStream(socket.getOutputStream());
    
            mSocketReader = new BufferedReader(
                    new InputStreamReader(socket.getInputStream()), 256);
    
            mSocket.setSoTimeout(CONNECTION_TIMEOUT_MILLIS);
    
            try {
                peer = mSocket.getPeerCredentials();
            } catch (IOException ex) {
                Log.e(TAG, "Cannot read peer credentials", ex);
                throw ex;
            }
        }
        
        /**
         * Reads one start command from the command socket. If successful,
         * a child is forked and a {@link Zygote.MethodAndArgsCaller}
         * exception is thrown in that child while in the parent process,
         * the method returns normally. On failure, the child is not
         * spawned and messages are printed to the log and stderr. Returns
         * a boolean status value indicating whether an end-of-file on the command
         * socket has been encountered.
         *
         * @return false if command socket should continue to be read from, or
         * true if an end-of-file has been encountered.
         * @throws Zygote.MethodAndArgsCaller trampoline to invoke main()
         * method in child process
         */
        boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {
    
            String args[];
            Arguments parsedArgs = null;
            FileDescriptor[] descriptors;
    
            try {
                args = readArgumentList();
                descriptors = mSocket.getAncillaryFileDescriptors();
            } catch (IOException ex) {
                Log.w(TAG, "IOException on command socket " + ex.getMessage());
                closeSocket();
                return true;
            }
    
            if (args == null) {
                // EOF reached.
                closeSocket();
                return true;
            }
    
            /** the stderr of the most recent request, if avail */
            PrintStream newStderr = null;
    
            if (descriptors != null && descriptors.length >= 3) {
                newStderr = new PrintStream(
                        new FileOutputStream(descriptors[2]));
            }
    
            int pid = -1;
            FileDescriptor childPipeFd = null;
            FileDescriptor serverPipeFd = null;
    
            try {
                parsedArgs = new Arguments(args);
    
                if (parsedArgs.abiListQuery) {
                    return handleAbiListQuery();
                }
    
                if (parsedArgs.preloadDefault) {
                    return handlePreload();
                }
    
                if (parsedArgs.preloadPackage != null) {
                    return handlePreloadPackage(parsedArgs.preloadPackage,
                            parsedArgs.preloadPackageLibs, parsedArgs.preloadPackageCacheKey);
                }
    
                if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
                    throw new ZygoteSecurityException("Client may not specify capabilities: " +
                            "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
                            ", effective=0x" + Long.toHexString(parsedArgs.effectiveCapabilities));
                }
    
                applyUidSecurityPolicy(parsedArgs, peer);
                applyInvokeWithSecurityPolicy(parsedArgs, peer);
    
                applyDebuggerSystemProperty(parsedArgs);
                applyInvokeWithSystemProperty(parsedArgs);
    
                int[][] rlimits = null;
    
                if (parsedArgs.rlimits != null) {
                    rlimits = parsedArgs.rlimits.toArray(intArray2d);
                }
    
                int[] fdsToIgnore = null;
    
                if (parsedArgs.invokeWith != null) {
                    FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
                    childPipeFd = pipeFds[1];
                    serverPipeFd = pipeFds[0];
                    Os.fcntlInt(childPipeFd, F_SETFD, 0);
                    fdsToIgnore = new int[] { childPipeFd.getInt$(), serverPipeFd.getInt$() };
                }
    
                /**
                 * In order to avoid leaking descriptors to the Zygote child,
                 * the native code must close the two Zygote socket descriptors
                 * in the child process before it switches from Zygote-root to
                 * the UID and privileges of the application being launched.
                 *
                 * In order to avoid "bad file descriptor" errors when the
                 * two LocalSocket objects are closed, the Posix file
                 * descriptors are released via a dup2() call which closes
                 * the socket and substitutes an open descriptor to /dev/null.
                 */
    
                int [] fdsToClose = { -1, -1 };
    
                FileDescriptor fd = mSocket.getFileDescriptor();
    
                if (fd != null) {
                    fdsToClose[0] = fd.getInt$();
                }
    
                fd = zygoteServer.getServerSocketFileDescriptor();
    
                if (fd != null) {
                    fdsToClose[1] = fd.getInt$();
                }
    
                fd = null;
    
                pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                        parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                        parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
                        parsedArgs.appDataDir);
            } catch (ErrnoException ex) {
                logAndPrintError(newStderr, "Exception creating pipe", ex);
            } catch (IllegalArgumentException ex) {
                logAndPrintError(newStderr, "Invalid zygote arguments", ex);
            } catch (ZygoteSecurityException ex) {
                logAndPrintError(newStderr,
                        "Zygote security policy prevents request: ", ex);
            }
    
            try {
                if (pid == 0) {
                    // in child
                    zygoteServer.closeServerSocket();
                    IoUtils.closeQuietly(serverPipeFd);
                    serverPipeFd = null;
                    handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
    
                    // should never get here, the child is expected to either
                    // throw Zygote.MethodAndArgsCaller or exec().
                    return true;
                } else {
                    // in parent...pid of < 0 means failure
                    IoUtils.closeQuietly(childPipeFd);
                    childPipeFd = null;
                    return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
                }
            } finally {
                IoUtils.closeQuietly(childPipeFd);
                IoUtils.closeQuietly(serverPipeFd);
            }
        }
        
    View Code
    //Zygote.java
        public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
              int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
              int[] fdsToIgnore, String instructionSet, String appDataDir) {
            VM_HOOKS.preFork();
            // Resets nice priority for zygote process.
            resetNicePriority();
            int pid = nativeForkAndSpecialize(
                      uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
                      fdsToIgnore, instructionSet, appDataDir);
            // Enable tracing as soon as possible for the child process.
            if (pid == 0) {
                Trace.setTracingEnabled(true);
    
                // Note that this event ends at the end of handleChildProc,
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
            }
            VM_HOOKS.postForkCommon();
            return pid;
        }
    View Code
    //ZygoteConnection.java
        /**
         * Handles post-fork setup of child proc, closing sockets as appropriate,
         * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
         * if successful or returning if failed.
         *
         * @param parsedArgs non-null; zygote args
         * @param descriptors null-ok; new file descriptors for stdio if available.
         * @param pipeFd null-ok; pipe for communication back to Zygote.
         * @param newStderr null-ok; stream to use for stderr until stdio
         * is reopened.
         *
         * @throws Zygote.MethodAndArgsCaller on success to
         * trampoline to code that invokes static main.
         */
        private void handleChildProc(Arguments parsedArgs,
                FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
                throws Zygote.MethodAndArgsCaller {
            /**
             * 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();
            if (descriptors != null) {
                try {
                    Os.dup2(descriptors[0], STDIN_FILENO);
                    Os.dup2(descriptors[1], STDOUT_FILENO);
                    Os.dup2(descriptors[2], STDERR_FILENO);
    
                    for (FileDescriptor fd: descriptors) {
                        IoUtils.closeQuietly(fd);
                    }
                    newStderr = System.err;
                } catch (ErrnoException ex) {
                    Log.e(TAG, "Error reopening stdio", ex);
                }
            }
    
            if (parsedArgs.niceName != null) {
                Process.setArgV0(parsedArgs.niceName);
            }
    
            // End of the postFork event.
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            if (parsedArgs.invokeWith != null) {
                WrapperInit.execApplication(parsedArgs.invokeWith,
                        parsedArgs.niceName, parsedArgs.targetSdkVersion,
                        VMRuntime.getCurrentInstructionSet(),
                        pipeFd, parsedArgs.remainingArgs);
            } else {
                ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion,
                        parsedArgs.remainingArgs, null /* classLoader */);
            }
        }
    View Code

      前一篇文章我们知道在SystemServer进程的启动过程中会调用其main静态方法,开始执行整个SystemServer的启动流程。在调用startOtherService方法中就会通过调用mActivityManagerService.systemReady()方法。

    //SystemServer.java
    private void startOtherServices() {
        ......
             // We now tell the activity manager it is okay to run third party
            // code.  It will call back into us once it has gotten to the state
            // where third party code can really run (but before it has actually
            // started launching the initial applications), for us to complete our
            // initialization.
            mActivityManagerService.systemReady()
        ......
    }
    //ActivityManagerService.java
    public void systemReady(final Runnable goingCallback, BootTimingsTraceLog traceLog) {
            ......
        startHomeActivityLocked(currentUserId, "systemReady");
            ......
    
    }
    
    boolean startHomeActivityLocked(int userId, String reason) {
            if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
                    && mTopAction == null) {
                // We are running in factory test mode, but unable to find
                // the factory test app, so just sit around displaying the
                // error message and don't try to start anything.
                return false;
            }
            Intent intent = getHomeIntent();
            ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
            if (aInfo != null) {
                intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
                // Don't do this if the home app is currently being
                // instrumented.
                aInfo = new ActivityInfo(aInfo);
                aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
                ProcessRecord app = getProcessRecordLocked(aInfo.processName,
                        aInfo.applicationInfo.uid, true);
                if (app == null || app.instr == null) {
                    intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
                    final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
                    // For ANR debugging to verify if the user activity is the one that actually
                    // launched.
                    final String myReason = reason + ":" + userId + ":" + resolvedUserId;
                    mActivityStarter.startHomeActivityLocked(intent, aInfo, myReason);
                }
            } else {
                Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
            }
    
            return true;
        }
    
         Intent getHomeIntent() {
            Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
            intent.setComponent(mTopComponent);
            intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
            if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
                intent.addCategory(Intent.CATEGORY_HOME);
            }
            return intent;
        }
    //ActivityStarter.java
        void startHomeActivityLocked(Intent intent, ActivityInfo aInfo, String reason) {
            mSupervisor.moveHomeStackTaskToTop(reason);
            mLastHomeActivityStartResult = startActivityLocked(null /*caller*/, intent,
                    null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/,
                    null /*voiceSession*/, null /*voiceInteractor*/, null /*resultTo*/,
                    null /*resultWho*/, 0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/,
                    null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/,
                    0 /*startFlags*/, null /*options*/, false /*ignoreTargetSecurity*/,
                    false /*componentSpecified*/, mLastHomeActivityStartRecord /*outActivity*/,
                    null /*container*/, null /*inTask*/, "startHomeActivity: " + reason);
            if (mSupervisor.inResumeTopActivity) {
                // If we are in resume section already, home activity will be initialized, but not
                // resumed (to avoid recursive resume) and will stay that way until something pokes it
                // again. We need to schedule another resume.
                mSupervisor.scheduleResumeTopActivities();
            }
        }

      后面就进入了Activity的启动流程了。

  • 相关阅读:
    玩家上线
    小退
    GS发包到MS
    share初始化
    3 水仙花数
    The left-hand side of an assignment must be a variable,代码中使用了中文的字符
    Mac Sublime Text 浏览器 设置快捷键 让html文件在浏览器打开
    2 质数求解
    1 斐波那契的兔子
    18 赛手的名单
  • 原文地址:https://www.cnblogs.com/bugzone/p/Launcher.html
Copyright © 2011-2022 走看看