溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么在Android中調(diào)用main函數(shù)

發(fā)布時(shí)間:2021-03-10 15:18:45 來(lái)源:億速云 閱讀:203 作者:Leah 欄目:移動(dòng)開(kāi)發(fā)

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)怎么在Android中調(diào)用main函數(shù),文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

啟動(dòng)App進(jìn)程

Activity啟動(dòng)過(guò)程的一環(huán)是調(diào)用ActivityStackSupervisor.startSpecificActivityLocked,如果App所在進(jìn)程還不存在,首先調(diào)用AMS的startProcessLocked:

void startSpecificActivityLocked(ActivityRecord r,
boolean andResume, boolean checkConfig) {
// Is this activity's application already running?
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);
r.task.stack.setLaunchTime(r);
if (app != null && app.thread != null) {
//...
}
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true);
}

startProcessLocked函數(shù)有多個(gè)重載,看最長(zhǎng)的那個(gè),最重要是調(diào)用了Process.start。

if (entryPoint == null) entryPoint = "android.app.ActivityThread";
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);

第一個(gè)參數(shù)是android.app.ActivityThread,執(zhí)行的目標(biāo),后文用到再說(shuō)。

Process.start簡(jiǎn)單地調(diào)用了startViaZygote,封裝一些參數(shù),再調(diào)用zygoteSendArgsAndGetResult。顧名思義,接下來(lái)進(jìn)程的啟動(dòng)工作交給Zygote。

Zygote

Zygote翻譯過(guò)來(lái)意思是“受精卵”,這也是Zygote的主要工作——孵化進(jìn)程。概括Zygote的主要工作有以下三點(diǎn),ZygoteInit的main函數(shù)也清晰地體現(xiàn)了。Zygote的啟動(dòng)和其他作用另文分析,這次關(guān)注Zygote對(duì)Socket的監(jiān)聽(tīng)。

1.registerZygoteSocket:?jiǎn)?dòng)Socket的Server端

2.preload:預(yù)加載資源

3.startSystemServer:?jiǎn)?dòng)system_server進(jìn)程

public static void main(String argv[]) {
// Mark zygote start. This ensures that thread creation will throw
// an error.
ZygoteHooks.startZygoteNoThreadCreation();
try {
//...
registerZygoteSocket(socketName);
//...
preload();
//...
if (startSystemServer) {
startSystemServer(abiList, socketName);
}
//...
runSelectLoop(abiList);
//...
} catch (MethodAndArgsCaller caller) {
caller.run();
} catch (Throwable ex) {
//...
}
}

最后Zygote執(zhí)行runSelectLoop,無(wú)限循環(huán)等待處理進(jìn)程啟動(dòng)的請(qǐng)求。

private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
fds.add(sServerSocket.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();
if (done) {
peers.remove(i);
fds.remove(i);
}
}
}
}
}

與Zygote通信使用的IPC方式是socket,類型是LocalSocket,實(shí)質(zhì)是對(duì)Linux的LocalSocket的封裝。與記憶中socket綁定需要IP和端口不同,LocalSocket使用FileDescriptor文件描述符,它可以表示文件,也可以表示socket。

runSelectLoop維護(hù)了兩個(gè)列表,分別保存文件描述符FileDescriptor和ZygoteConnection,兩者一一對(duì)應(yīng)。index=0的FileDescriptor表示ServerSocket,因此index=0的ZygoteConnection用null填充。

在每次循環(huán)中,判斷fds里哪個(gè)可讀:

  • 當(dāng)i=0時(shí),表示有新的client,調(diào)用acceptCommandPeer創(chuàng)建ZygoteConnection并保存

  • 當(dāng)i>0時(shí),表示已建立連接的socket中有新的命令,調(diào)用runOnce函數(shù)執(zhí)行

fork進(jìn)程

runOnce函數(shù)非常長(zhǎng),我們只關(guān)注進(jìn)程創(chuàng)建部分。

boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {

//...
try {
//...
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
parsedArgs.appDataDir);
} catch (ErrnoException 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 {
//...
}
}

首先調(diào)用了forkAndSpecialize函數(shù),創(chuàng)建進(jìn)程返回一個(gè)pid。

public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
String instructionSet, String appDataDir) {
VM_HOOKS.preFork();
int pid = nativeForkAndSpecialize(
uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
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;
}

在forkAndSpecialize中核心就是利用JNI調(diào)用native的fork函數(shù),調(diào)用之前會(huì)執(zhí)行VM_HOOKS.preFork(),調(diào)用之后執(zhí)行VM_HOOKS.postForkCommon()。

VM_HOOKS.preFork()的功能是停止Zygote的4個(gè)Daemon子線程的運(yùn)行,確保Zygote是單線程,提升fork效率。當(dāng)線程停止之后初始化gc堆。VM_HOOKS.postForkCommon()可以看作是逆操作,關(guān)于虛擬機(jī)更加深入的內(nèi)容暫不討論。

native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);

nativeForkSystemServer是一個(gè)native函數(shù),對(duì)應(yīng)com_android_internal_os_Zygote.cpp的com_android_internal_os_Zygote_nativeForkAndSpecialize,繼續(xù)調(diào)用了ForkAndSpecializeCommon,最核心一句則是調(diào)用fork函數(shù)。

pid_t pid = fork();

簡(jiǎn)單回憶fork函數(shù)作用,它復(fù)制當(dāng)前進(jìn)程,屬性和當(dāng)前進(jìn)程相同,使用copy on write(寫時(shí)復(fù)制)。執(zhí)行函數(shù)后,新進(jìn)程已經(jīng)創(chuàng)建,返回的pid=0;對(duì)于被復(fù)制的進(jìn)程,返回新進(jìn)程的pid;出現(xiàn)錯(cuò)誤時(shí),返回-1。

因此,執(zhí)行forkAndSpecialize函數(shù)后,runOnce后續(xù)的代碼分別在兩個(gè)進(jìn)程中執(zhí)行,判斷當(dāng)前的pid,區(qū)分是在當(dāng)前進(jìn)程還是新進(jìn)程。

  • pid == 0:新進(jìn)程,調(diào)用handleChildProc

  • pid != 0:當(dāng)前進(jìn)程,調(diào)用handleParentProc

handleParentProc函數(shù)是當(dāng)前進(jìn)程進(jìn)行清理的過(guò)程,比較簡(jiǎn)單。我們重點(diǎn)來(lái)看新進(jìn)程開(kāi)展工作的handleChildProc函數(shù)。

新進(jìn)程的初始化

private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller {
//...
if (parsedArgs.invokeWith != null) {
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
pipeFd, parsedArgs.remainingArgs);
} else {
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
parsedArgs.remainingArgs, null /* classLoader */);
}
}

兩種啟動(dòng)方式:

  • WrapperInit.execApplication

  • RuntimeInit.zygoteInit

第一種的目的不太懂,先掛起,后續(xù)分析。

第二種RuntimeInit.zygoteInit,繼續(xù)調(diào)用applicationInit,離我們的目標(biāo)越來(lái)越近了,最終調(diào)用到invokeStaticMain。

private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
Class<?> cl;
try {
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
//...
}
Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
//...
}
int modifiers = m.getModifiers();
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException(
"Main method is not public and static on " + className);
}
throw new ZygoteInit.MethodAndArgsCaller(m, argv);
}

在這里使用反射獲得需要被執(zhí)行的類和函數(shù),目標(biāo)函數(shù)當(dāng)然就是main,目標(biāo)類來(lái)自ActivityManagerService.startProcessLocked里的變量entryPoint,前面有說(shuō)過(guò)。

entryPoint = "android.app.ActivityThread"

最后一句執(zhí)行throw new ZygoteInit.MethodAndArgsCaller(m, argv),讓人有些費(fèi)解。

public static class MethodAndArgsCaller extends Exception
implements Runnable {
//...
public void run() {
try {
mMethod.invoke(null, new Object[] { mArgs });
} catch (IllegalAccessException ex) {
//...
}
}
}

通過(guò)上面的分析,容易知道MethodAndArgsCaller就是App的主線程,里面的run方法實(shí)現(xiàn)了反射的調(diào)用。什么時(shí)候觸發(fā)執(zhí)行,為什么要這樣設(shè)計(jì)?

既然MethodAndArgsCaller是異常,拋出它肯定某個(gè)地方會(huì)接收,回顧一路的調(diào)用鏈:

  • ZytoteInit.main

  • ZytoteInit.runSelectLoop

  • ZygoteConnection.runOnce

  • ZygoteConnection.handleChildProc

  • RuntimeInit.zygoteInit

  • RuntimeInit.applicationInit

  • RuntimeInit.invokeStaticMain

看前面的ZytoteInit.main函數(shù),catch了MethodAndArgsCaller異常,直接調(diào)用了run函數(shù)。注釋里解釋了為什么要這樣做:

This throw gets caught in ZygoteInit.main(), which responds by invoking the exception's run() method. This arrangement clears up all the stack frames that were required in setting up the process.

函數(shù)在虛擬機(jī)是保存在棧中,每調(diào)用一個(gè)函數(shù),就將函數(shù)相關(guān)數(shù)據(jù)壓入棧;執(zhí)行完函數(shù),將函數(shù)從棧中彈出。因此,棧底的就是main函數(shù)。

在上面的研究中,新進(jìn)程創(chuàng)建后,經(jīng)歷一系列函數(shù)的調(diào)用才到main函數(shù),如果直接調(diào)用main函數(shù),調(diào)用鏈中關(guān)于初始化的函數(shù)會(huì)一直存在。為了清理這部分函數(shù),使用了拋出異常的方式,沒(méi)有捕獲異常的函數(shù)會(huì)馬上結(jié)束,ZytoteInit.main之上的函數(shù)都會(huì)結(jié)束,達(dá)到清理的目的。

最后補(bǔ)充一點(diǎn),從handleChildProc函數(shù)開(kāi)始,一系列過(guò)程調(diào)用了ActivityThread的main函數(shù),這不是啟動(dòng)App獨(dú)有的,后續(xù)研究啟動(dòng)SystemServer進(jìn)程時(shí),你會(huì)發(fā)現(xiàn)邏輯都是一樣。

上述就是小編為大家分享的怎么在Android中調(diào)用main函數(shù)了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI