最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Android應(yīng)用main函數(shù)的調(diào)用

 更新時(shí)間:2019年06月03日 08:40:50   作者:展翅而飛  
Android常識(shí),App主線程初始化了Looper,調(diào)用prepare的地方是ActivityThread.main函數(shù)。問題來了,App的main函數(shù)在哪兒調(diào)用,下面我們來一起學(xué)習(xí)一下吧

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

Activity啟動(dòng)過程的一環(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),后文用到再說。

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

Zygote

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

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,無限循環(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)行清理的過程,比較簡(jiǎn)單。我們重點(diǎn)來看新進(jìn)程開展工作的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)越來越近了,最終調(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)類來自ActivityManagerService.startProcessLocked里的變量entryPoint,前面有說過。

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) {
//...
}
}
}

通過上面的分析,容易知道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ù),使用了拋出異常的方式,沒有捕獲異常的函數(shù)會(huì)馬上結(jié)束,ZytoteInit.main之上的函數(shù)都會(huì)結(jié)束,達(dá)到清理的目的。

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

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Flutter使用RepositoryProvider解決跨組件傳值問題

    Flutter使用RepositoryProvider解決跨組件傳值問題

    在實(shí)際開發(fā)過程中,經(jīng)常會(huì)遇到父子組件傳值的情況。本文將利用RepositoryProvider解決跨組件傳值的問題,感興趣的小伙伴可以了解一下
    2022-04-04
  • Android省市區(qū)三級(jí)聯(lián)動(dòng)控件使用方法實(shí)例講解

    Android省市區(qū)三級(jí)聯(lián)動(dòng)控件使用方法實(shí)例講解

    最近有需求需要實(shí)現(xiàn)省市區(qū)三級(jí)聯(lián)動(dòng),但是發(fā)現(xiàn)之前的實(shí)現(xiàn)不夠靈活,自己做了一些優(yōu)化。下面通過實(shí)例代碼給大家介紹下Android省市區(qū)三級(jí)聯(lián)動(dòng)控件使用方法
    2017-01-01
  • Android仿微信布局的實(shí)現(xiàn)示例

    Android仿微信布局的實(shí)現(xiàn)示例

    本文主要介紹了Android仿微信布局的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • Handler實(shí)現(xiàn)倒計(jì)時(shí)功能

    Handler實(shí)現(xiàn)倒計(jì)時(shí)功能

    這篇文章主要為大家詳細(xì)介紹了Handler實(shí)現(xiàn)倒計(jì)時(shí)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-04-04
  • Android編程繪圖操作之弧形繪制方法示例

    Android編程繪圖操作之弧形繪制方法示例

    這篇文章主要介紹了Android編程繪圖操作之弧形繪制方法,結(jié)合實(shí)例形式分析了Android圖形繪制的相關(guān)組件調(diào)用、屬性設(shè)置與功能實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2017-08-08
  • Android開發(fā)之滑動(dòng)圖片輪播標(biāo)題焦點(diǎn)

    Android開發(fā)之滑動(dòng)圖片輪播標(biāo)題焦點(diǎn)

    這篇文章主要介紹了Android開發(fā)之滑動(dòng)圖片輪播標(biāo)題焦點(diǎn)的相關(guān)資料,需要的朋友可以參考下
    2016-05-05
  • Android登陸界面用戶名檢測(cè)功能

    Android登陸界面用戶名檢測(cè)功能

    這篇文章主要為大家詳細(xì)介紹了Android登陸界面用戶名檢測(cè)功能的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • Android使用RecycleView實(shí)現(xiàn)拖拽交換item位置

    Android使用RecycleView實(shí)現(xiàn)拖拽交換item位置

    這篇文章主要為大家詳細(xì)介紹了Android使用RecycleView實(shí)現(xiàn)拖拽交換item位置,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • RxJava入門之介紹與基本運(yùn)用

    RxJava入門之介紹與基本運(yùn)用

    對(duì)于Android開發(fā)者來說,當(dāng)有一天打開技術(shù)論壇、博客滿屏都是各種Rx的時(shí)候,心里是很慌的。所以趁著現(xiàn)在跟著小編通過這篇文章先來簡(jiǎn)單認(rèn)識(shí)下RxJava,以及RxJava的基本運(yùn)用。對(duì)這感興趣的朋友下面來一起看看吧。
    2016-09-09
  • Android Activity與Intent詳解及示例代碼

    Android Activity與Intent詳解及示例代碼

    本文主要講解Android Activity與Intent的知識(shí),這里整理了相關(guān)資料并附有示例代碼,有興趣的小伙伴可以參考下
    2016-08-08

最新評(píng)論

松阳县| 武宁县| 侯马市| 谷城县| 宜章县| 砀山县| 蒙自县| 巴里| 安康市| 黑河市| 辛集市| 乐安县| 衡阳县| 田东县| 阜南县| 石河子市| 黑河市| 贺兰县| 肃南| 韩城市| 三台县| 汪清县| 昭通市| 德保县| 鸡泽县| 阳信县| 八宿县| 新河县| 东港市| 平顶山市| 抚顺市| 清丰县| 阿瓦提县| 宜春市| 海南省| 西吉县| 宣化县| 定陶县| 织金县| 潼关县| 台北市|