Android 进阶 - 进程启动分析

在Android系统里,做应用开发基本没什么进程的概念,Activity,Service都属于应用,在传统的程序开发者,我们都能清楚的知道进程是什么,进程什么时候启动,什么时候结束,但在Android应用里,设计者们屏蔽了这个概念,让你感觉到Android就是由一引起APK包组成,这对一个程序员来说,非常难受,就如WIN32安装程序一样,做了那么多事情,你却不知道他做了什么,将程序拷贝到哪去了,又在注册表里做了些什么,删除时你还必须用卸载程序。所以,要想真正了解Android系统,我们必需要深入,Android属于Linux内核,是一定有进程的概念,而且,无论Unix,Windows,Linux都有进程,Android当然也有。

其实,在Android里,不论是Activity应用,还是Service应用,最终都需要先启动进程,再在进程里做真正的处理。

1. 启动流程图

进程的启动流程如下图:

上图清晰的描述了进程启动至最后加载Activity或Service之前的过程,fork进程之后,走父进程和子进程两个分支,这在Unix和Linux的C程序里很常用,但在Java里没有这个概念。由于最终采用了Zygote的native本地化接口(C,C++)来Fork进程,所以,这里延续了C的处理风格,相信这对Java程序员非常难以理解。

2. 关键流程说明

2.1 Zygote服务器Fork进程

Caller经由ActivityManagerService.startProcessLocked->Process.start -> Process.startViaZygote->Process.zygoteSendArgsAndGetResult,向zygote服务器发送进程创建的参数,并从zygote服务器获取进程创建的返回结果。中间的过程并不复杂,不分析此部分的源代码。
Zygote是Android大名鼎鼎的Java进程孵化器,Zygote在启动后,会注册Zygote套接字服务器,并运行runSelectLoop来侦听客户端的请求。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public ZygoteInit{
	public static void main(String argv[]) {
	    try {
	        // Start profiling the zygote initialization.
	        SamplingProfilerIntegration.start();
		// 注册套接字服务器
	        registerZygoteSocket();
	        ...
		// 启动SystemServer
	        if (argv[1].equals("start-system-server")) {
	            startSystemServer();
	        } else if (!argv[1].equals("")) {
	            throw new RuntimeException(argv[0] + USAGE_STRING);
	        }
	
	        Log.i(TAG, "Accepting command socket connections");
		// 循环监听Socket请求
	        runSelectLoop();
	
	        closeServerSocket();
	    } catch (MethodAndArgsCaller caller) {
	        caller.run();
	    } catch (RuntimeException ex) {
	        Log.e(TAG, "Zygote died with exception", ex);
	        closeServerSocket();
	        throw ex;
	    }
	}
	...
	private static void runSelectLoop() throws MethodAndArgsCaller {
	    ArrayList<filedescriptor> fds = new ArrayList<filedescriptor>();
	    ArrayList<zygoteconnection> peers = new ArrayList<zygoteconnection>();
	    FileDescriptor[] fdArray = new FileDescriptor[4];
		// server socket的index为0,由于不是client,所以,放peers里往一个null,保持index索引的同步。
		// 其实,这个没有必要,后面的peers.get(index-1)就行。
	    fds.add(sServerSocket.getFileDescriptor());
	    peers.add(null);
	
	    int loopCount = GC_LOOP_COUNT;
	    while (true) {
	        int index;
			...
	        try {
	            fdArray = fds.toArray(fdArray);
	            index = selectReadable(fdArray); // 阻塞等待所有的套接字
	        } catch (IOException ex) {
	            throw new RuntimeException("Error in select()", ex);
	        }
	
	        if (index < 0) {
	            throw new RuntimeException("Error in select()");
	        } else if (index == 0) {
	        	// 如果索引为0,表示有新的client请求连接,创建一个新的ZygoteConnection,并加入peers和fds列表中
	            ZygoteConnection newPeer = acceptCommandPeer();
	            peers.add(newPeer);
	            fds.add(newPeer.getFileDesciptor());
	        } else {
	        	// 如果是客户端Socket有数据,则执行一次操作
	            boolean done;
	            done = peers.get(index).runOnce();
	
	            if (done) {
	                peers.remove(index);
	                fds.remove(index);
	            }
	        }
	    }
	}
}
	
当Zygote服务器通过selectReadable获取到有客户端连接,立即new一个ZygoteConnection连接,并监听到此连接有数据后,调用此连接的runOnce来处理客户端的请求。
frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
    
public class ZygoteConnection ...{
	boolean runOnce() throws ZygoteInit.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;
	    }
		...
	    try {
	        ...
	        // fork进程
	        pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
	                parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
	                parsedArgs.niceName);
	    } catch (IOException ex) {
	        logAndPrintError(newStderr, "Exception creating pipe", ex);
	    } 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
	            IoUtils.closeQuietly(serverPipeFd);
	            serverPipeFd = null;
	            handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
	
	            // should never get here, the child is expected to either
	            // throw ZygoteInit.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);
	    }
	}
}
Fork完进程之后,如果是父进程,则执行handleParentProc处理,并返回pid等信息给ActivityManagerService保存;如果是子进程,则继续执行,将进程与ActivityThread连接,并处理后续的Activity或Service的启动。

2.2 父进程处理

见源代码:
private boolean handleParentProc(int pid,
            FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) {
        ...
        try {
            // 向套接字里回写pid和usingWrapper
            mSocketOutStream.writeInt(pid);
            mSocketOutStream.writeBoolean(usingWrapper);
        } catch (IOException ex) {
            Log.e(TAG, "Error reading from command socket", ex);
            return true;
        }

        return false;
    }
从上面的代码可以看出,父进程结束会向客户端套接字回写pid的信息,这样ActivityManagerService就可以收到此回复。上面的套接字是同步通信的,Process.start会等待zygote服务器的回复,并返回给ActivityManagerService.startProcessLocked。
public class ActivityManagerService ...{
    private final void startProcessLocked(ProcessRecord app,
            String hostingType, String hostingNameStr) {
            ...
            Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, null);

            ...
            Slog.i(TAG, buf.toString());
            app.setPid(startResult.pid);
            app.usingWrapper = startResult.usingWrapper;
            app.removed = false;
            synchronized (mPidsSelfLocked) {
                // 保存进程信息
                this.mPidsSelfLocked.put(startResult.pid, app);
                Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
                msg.obj = app;
                mHandler.sendMessageDelayed(msg, startResult.usingWrapper
                        ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
            }
        } catch (RuntimeException e) {
            // XXX do better error recovery.
            app.setPid(0);
            Slog.e(TAG, "Failure starting process " + app.processName, e);
        }
    }
}

2.3 子进程处理

子进程进入了handleChildProc,主要是调用了ActivityThread.main函数,创建了一个新的ActivityThread对象,并连接应用。
2.3.1 ActivityThread.attach
frameworks/base/core/java/android/app/ActivityThread.java
public class ActivityThread{
	// ApplicationThread继承自ApplicationThreadNative,是一个Binder服务端
    private class ApplicationThread extends ApplicationThreadNative {
    	...
    }
    ...
    // 创建应用服务端Binder对象mAppThread
    final ApplicationThread mAppThread = new ApplicationThread();
    ...
    private void attach(boolean system) {
        sCurrentActivityThread = this;
        mSystemThread = system;
        if (!system) {
        	// 非系统应用
            ViewRootImpl.addFirstDrawHandler(new Runnable() {
                @Override
                public void run() {
                    ensureJitEnabled();
                }
            });
            android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                                                    UserHandle.myUserId());
            RuntimeInit.setApplicationObject(mAppThread.asBinder());
            IActivityManager mgr = ActivityManagerNative.getDefault();
            try {
            	// 通过Binder,通知ActivityManagerService,连接应用
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                // Ignore
            }
        } else {
            ...
        }
        ...
    }
    ...
}
通过ActivityServiceProxy,经由Binder,进入ActivityManagerService服务。
frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
public class ActivityManagerService ...{
    ...
    // 参数thread,经过Binder调用,已经转换成远程接口,此接口对象可以在ActivityManagerService内部远程访问Activity应用里的thread服务对象
    public final void attachApplication(IApplicationThread thread) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid);
            Binder.restoreCallingIdentity(origId);
        }
    }
    ...
    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) {
                // 根据pid,获取app信息,此信息在父进程Fork完后会保存。
                app = mPidsSelfLocked.get(pid);
            }
        } else {
            app = null;
        }

        ...
        // 保存app.thread=thread
        app.makeActive(thread, mProcessStats);
        ...
        try {
            int testMode = IApplicationThread.DEBUG_OFF;
            if (mDebugApp != null && mDebugApp.equals(processName)) {
                testMode = mWaitForDebugger
                    ? IApplicationThread.DEBUG_WAIT
                    : IApplicationThread.DEBUG_ON;
                app.debugging = true;
                if (mDebugTransient) {
                    mDebugApp = mOrigDebugApp;
                    mWaitForDebugger = mOrigWaitForDebugger;
                }
            }
            String profileFile = app.instrumentationProfileFile;
            ParcelFileDescriptor profileFd = null;
            boolean profileAutoStop = false;
            if (mProfileApp != null && mProfileApp.equals(processName)) {
                mProfileProc = app;
                profileFile = mProfileFile;
                profileFd = mProfileFd;
                profileAutoStop = mAutoStopProfiler;
            }
            boolean enableOpenGlTrace = false;
            if (mOpenGlTraceApp != null && mOpenGlTraceApp.equals(processName)) {
                enableOpenGlTrace = true;
                mOpenGlTraceApp = null;
            }

            // If the app is being launched for restore or full backup, set it up specially
            boolean isRestrictedBackupMode = false;
            if (mBackupTarget != null && mBackupAppName.equals(processName)) {
                isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE)
                        || (mBackupTarget.backupMode == BackupRecord.RESTORE_FULL)
                        || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL);
            }

            ensurePackageDexOpt(app.instrumentationInfo != null
                    ? app.instrumentationInfo.packageName
                    : app.info.packageName);
            if (app.instrumentationClass != null) {
                ensurePackageDexOpt(app.instrumentationClass.getPackageName());
            }
            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Binding proc "
                    + processName + " with config " + mConfiguration);
            ApplicationInfo appInfo = app.instrumentationInfo != null
                    ? app.instrumentationInfo : app.info;
            app.compat = compatibilityInfoForPackageLocked(appInfo);
            if (profileFd != null) {
                profileFd = profileFd.dup();
            }
            thread.bindApplication(processName, appInfo, providers,
                    app.instrumentationClass, profileFile, profileFd, profileAutoStop,
                    app.instrumentationArguments, app.instrumentationWatcher,
                    app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
                    isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(mConfiguration), app.compat, getCommonServicesLocked(),
                    mCoreSettingsObserver.getCoreSettingsLocked());
            updateLruProcessLocked(app, false, null);
            app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
        } catch (Exception e) {
            // todo: Yikes!  What should we do?  For now we will try to
            // start another process, but that could easily get us in
            // an infinite loop of restarting processes...
            Slog.w(TAG, "Exception thrown during bind!", e);

            app.resetPackageList(mProcessStats);
            app.unlinkDeathRecipient();
            startProcessLocked(app, "bind fail", processName);
            return false;
        }
        ...
        if (normalMode) {
            try {
                // 尝试启动应用
                if (mStackSupervisor.attachApplicationLocked(app, mHeadless)) {
                    didSomething = true;
                }
            } catch (Exception e) {
                // 启动应用失败
                badApp = true;
            }
        }

        // Find any services that should be running in this process...
        if (!badApp) {
            //尝试启动服务
            try {
                didSomething |= mServices.attachApplicationLocked(app, processName);
            } catch (Exception e) {
                badApp = true;
            }
        }

        // Check if a next-broadcast receiver is in this process...
        if (!badApp && isPendingBroadcastProcessLocked(pid)) {
            // 服务启动失败,则尝试发送广播
            try {
                didSomething |= sendPendingBroadcastsLocked(app);
            } catch (Exception e) {
                // If the app died trying to launch the receiver we declare it 'bad'
                badApp = true;
            }
        }

        // Check whether the next backup agent is in this process...
        if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.uid) {
            // 尝试发送广播失败,则尝试启动备份代理
            if (DEBUG_BACKUP) Slog.v(TAG, "New app is backup target, launching agent for " + app);
            ensurePackageDexOpt(mBackupTarget.appInfo.packageName);
            try {
                thread.scheduleCreateBackupAgent(mBackupTarget.appInfo,
                        compatibilityInfoForPackageLocked(mBackupTarget.appInfo),
                        mBackupTarget.backupMode);
            } catch (Exception e) {
                Slog.w(TAG, "Exception scheduling backup agent creation: ");
                e.printStackTrace();
            }
        }
        ...

        return true;
    }
}
此函数,会先通过pid获取进程的信息,再依次尝试启动应用,启动服务,发送广播和启动备份代理,从上至少,只要有一个启动成功,则后面的动作不再继续处理。由此,我们也知道了,应用、服务、广播放、备份等都与进程启动相关。
到此进程启动完成,接下来就是讨论如何启动应用或服务等了,将在后面的文章中描述。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值