日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

簡要回顧

先回顧一下Android系統(tǒng)的啟動過程:

init進(jìn)程fork出Zygote進(jìn)程后,Zygote進(jìn)程會創(chuàng)建一個服務(wù)端socket,等待AMS發(fā)起socket請求。

同時,由Zygote進(jìn)程fork出的SystemServer進(jìn)程會啟動各項系統(tǒng)服務(wù),其中就包含了AMS,AMS會啟動Launcher桌面,此時就可以等待用戶點擊App圖標(biāo)來啟動應(yīng)用進(jìn)程了。

搞懂Android應(yīng)用啟動過程,再也不怕面試官了

 

然后看下系統(tǒng)服務(wù)的啟動,不管是由init進(jìn)程啟動的獨(dú)立進(jìn)程的系統(tǒng)服務(wù)如SurfaceFlinger,還是由SystemServer進(jìn)程啟動的非獨(dú)立進(jìn)程的系統(tǒng)服務(wù)如AMS,都是在ServiceManager進(jìn)程中完成注冊和獲取的,在跨進(jìn)程通信上使用了Android的binder機(jī)制。

搞懂Android應(yīng)用啟動過程,再也不怕面試官了

 

ServiceManager進(jìn)程本身也是一個系統(tǒng)服務(wù),經(jīng)過啟動進(jìn)程、啟動binder機(jī)制、發(fā)布自己和等待請求4個步驟,就可以處理其他系統(tǒng)服務(wù)的獲取和注冊需求了。

AMS發(fā)送socket請求

Android應(yīng)用進(jìn)程的啟動是被動式的,在Launcher桌面點擊圖標(biāo)啟動一個應(yīng)用的組件如Activity時,如果Activity所在的進(jìn)程不存在,就會創(chuàng)建并啟動進(jìn)程。

點擊App圖標(biāo)后經(jīng)過層層調(diào)用會來到ActivityStackSupervisor的startSpecificActivityLocked方法。

//ActivityStackSupervisor.JAVA
final ActivityManagerService mService;

void startSpecificActivityLocked(...) {
    //查找Activity所在的進(jìn)程,ProcessRecord是用來封裝進(jìn)程信息的數(shù)據(jù)結(jié)構(gòu)
    ProcessRecord app = mService.getProcessRecordLocked(...);
    //如果進(jìn)程已啟動,并且binder句柄IApplicationThread也拿到了,那就直接啟動Activity
    if (app != null && app.thread != null) {
        realStartActivityLocked(r, app, andResume, checkConfig);
        return;
    }
    //否則,讓AMS啟動進(jìn)程
    mService.startProcessLocked(...);
} 

app.thread并不是線程,而是一個binder句柄。應(yīng)用進(jìn)程使用AMS需要拿到AMS的句柄IActivityManager,而系統(tǒng)需要通知應(yīng)用和管理應(yīng)用的生命周期,所以也需要持有應(yīng)用進(jìn)程的binder句柄IApplicationThread。

也就是說,他們互相持有彼此的binder句柄,來實現(xiàn)雙向通信。

搞懂Android應(yīng)用啟動過程,再也不怕面試官了

 

那IApplicationThread句柄是怎么傳給AMS的呢?Zygote進(jìn)程收到socket請求后會處理請求參數(shù),執(zhí)行ActivityThread的入口函數(shù)main。

//ActivityThread.java
public static void main(String[] args) {
    //創(chuàng)建主線程的looper
    Looper.prepareMainLooper();
    //ActivityThread并不是線程,只是普通的java對象
    ActivityThread thread = new ActivityThread();
    //告訴AMS,應(yīng)用已經(jīng)啟動好了
    thread.attach(false);
    //運(yùn)行l(wèi)ooper,啟動消息循環(huán)
    Looper.loop();
}

private void attach(boolean system) {
    //獲取AMS的binder句柄IActivityManager
    final IActivityManager mgr = ActivityManager.getService();
    //告訴AMS應(yīng)用進(jìn)程已經(jīng)啟動,并傳入應(yīng)用進(jìn)程自己的binder句柄IApplicationThread
    mgr.attachApplication(mAppThread);
} 

所以對于AMS來說:

1.AMS向Zygote發(fā)起啟動應(yīng)用的socket請求,Zygote收到請求fork出進(jìn)程,返回進(jìn)程的pid給AMS;

2.應(yīng)用進(jìn)程啟動好后,執(zhí)行入口main函數(shù),通過attachApplication方法告訴AMS已經(jīng)啟動,同時傳入應(yīng)用進(jìn)程的binder句柄IApplicationThread。

完成這兩步,應(yīng)用進(jìn)程的啟動過程才算完成。

下面看AMS的startProcessLocked啟動應(yīng)用進(jìn)程時都做了些什么。

//ActivityManagerService.java
final ProcessRecord startProcessLocked(...){
    ProcessRecord app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
    //如果進(jìn)程信息不為空,并且已經(jīng)拿到了Zygote進(jìn)程返回的應(yīng)用進(jìn)程pid
    //說明AMS已經(jīng)請求過了,并且Zygote已經(jīng)響應(yīng)請求然后fork出進(jìn)程了
    if (app != null && app.pid > 0) {
        //但是app.thread還是空,說明應(yīng)用進(jìn)程還沒來得及注冊自己的binder句柄給AMS
        //即此時進(jìn)程正在啟動,那就直接返回,避免重復(fù)創(chuàng)建
        if (app.thread == null) {
            return app;
        }
    }
    //調(diào)用重載方法
    startProcessLocked(...);
} 

之所以要判斷app.thread,是為了避免當(dāng)應(yīng)用進(jìn)程正在啟動的時候,假如又有另一個組件需要啟動,導(dǎo)致重復(fù)拉起(創(chuàng)建)應(yīng)用進(jìn)程。

繼續(xù)看重載方法startProcessLocked:

//ActivityManagerService.java
private final void startProcessLocked(...){
    //應(yīng)用進(jìn)程的主線程的類名
    if (entryPoint == null) entryPoint = "android.app.ActivityThread";
    ProcessStartResult startResult = Process.start(entryPoint, ...);
}

//Process.java
public static final ProcessStartResult start(...){
    return zygoteProcess.start(...);
} 

來到ZygoteProcess。

//ZygoteProcess.java
public final Process.ProcessStartResult start(...){
    return startViaZygote(...);
}

private Process.ProcessStartResult startViaZygote(...){
    ArrayList<String> argsForZygote = new ArrayList<String>();
    //...處理各種參數(shù)
    return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
} 

其中:

  1. openZygoteSocketIfNeeded打開本地socket
  2. zygoteSendArgsAndGetResult發(fā)送請求參數(shù),其中帶上了ActivityThread類名
  3. return返回的數(shù)據(jù)結(jié)構(gòu)ProcessStartResult中會有pid字段

梳理一下:

搞懂Android應(yīng)用啟動過程,再也不怕面試官了

 

注意:Zygote進(jìn)程啟動時已經(jīng)創(chuàng)建好了虛擬機(jī)實例,所以由他fork出的應(yīng)用進(jìn)程可以直接繼承過來用而無需創(chuàng)建。

下面來看Zygote是如何處理socket請求的。

Zygote處理socket請求

從 圖解Android系統(tǒng)的啟動 一文可知,在ZygoteInit的main函數(shù)中,會創(chuàng)建服務(wù)端socket。

//ZygoteInit.java
public static void main(String argv[]) {
    //Server類,封裝了socket
    ZygoteServer zygoteServer = new ZygoteServer();
    //創(chuàng)建服務(wù)端socket,名字為socketName即zygote
    zygoteServer.registerServerSocket(socketName);
    //進(jìn)入死循環(huán),等待AMS發(fā)請求過來
    zygoteServer.runSelectLoop(abiList);
} 

看到ZygoteServer。

//ZygoteServer.java
void registerServerSocket(String socketName) {
    int fileDesc;
    //socket真正的名字被加了個前綴,即 "ANDROID_SOCKET_" + "zygote"
    final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;

    String env = System.getenv(fullSocketName);
    fileDesc = Integer.parseInt(env);

    //創(chuàng)建文件描述符fd
    FileDescriptor fd = new FileDescriptor();
    fd.setInt$(fileDesc);
    //創(chuàng)建LocalServerSocket對象
    mServerSocket = new LocalServerSocket(fd);
}

void runSelectLoop(String abiList){
    //進(jìn)入死循環(huán)
    while (true) {
        for (int i = pollFds.length - 1; i >= 0; --i) {
            if (i == 0) {
                //...
            } else {
                //得到一個連接對象ZygoteConnection,調(diào)用他的runOnce
                boolean done = peers.get(i).runOnce(this);
            }
        }
    }
} 

來到ZygoteConnection的runOnce。

boolean runOnce(ZygoteServer zygoteServer){
    //讀取socket請求的參數(shù)列表
    String args[] = readArgumentList();
    //創(chuàng)建應(yīng)用進(jìn)程
    int pid = Zygote.forkAndSpecialize(...);
    if (pid == 0) {
        //如果是應(yīng)用進(jìn)程(Zygote fork出來的子進(jìn)程),處理請求參數(shù)
        handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
        return true;
    } else {
        return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
    }
} 

handleChildProc方法調(diào)用了ZygoteInit的zygoteInit方法,里邊主要做了3件事:

  1. 啟動binder線程池(后面分析)
  2. 讀取請求參數(shù)拿到ActivityThread類并執(zhí)行他的main函數(shù),執(zhí)行thread.attach告知AMS并回傳自己的binder句柄
  3. 執(zhí)行Looper.loop()啟動消息循環(huán)(代碼前面有)

這樣應(yīng)用進(jìn)程就啟動起來了。梳理一下:

搞懂Android應(yīng)用啟動過程,再也不怕面試官了

 

下面看下binder線程池是怎么啟動的。

啟動binder線程池

Zygote的跨進(jìn)程通信沒有使用binder,而是socket,所以應(yīng)用進(jìn)程的binder機(jī)制不是繼承而來,而是進(jìn)程創(chuàng)建后自己啟動的。

前邊可知,Zygote收到socket請求后會得到一個ZygoteConnection,他的runOnce會調(diào)用handleChildProc。

//ZygoteConnection.java
private void handleChildProc(...){
    ZygoteInit.zygoteInit(...);
}

//ZygoteInit.java
public static final void zygoteInit(...){
    RuntimeInit.commonInit();
    //進(jìn)入native層
    ZygoteInit.nativeZygoteInit();
    RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
} 

來到AndroidRuntime.cpp:

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

來到app_main.cpp:

//app_main.cpp
virtual void onZygoteInit() {
    //獲取單例
    sp<ProcessState> proc = ProcessState::self();
    //在這里啟動了binder線程池
    proc->startThreadPool();
} 

看下ProcessState.cpp:

//ProcessState.cpp
sp<ProcessState> ProcessState::self()
{
    //單例模式,返回ProcessState對象
    if (gProcess != NULL) {
        return gProcess;
    }
    gProcess = new ProcessState("/dev/binder");
    return gProcess;
}

//ProcessState構(gòu)造函數(shù)
ProcessState::ProcessState(const char *driver)
    : mDriverName(String8(driver))
        , mDriverFD(open_driver(driver)) //打開binder驅(qū)動
        ,//...
{
    if (mDriverFD >= 0) {
        //mmap是一種內(nèi)存映射文件的方法,把mDriverFD映射到當(dāng)前的內(nèi)存空間
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, 
                        MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
    }
}

//啟動了binder線程池
void ProcessState::startThreadPool()
{
    if (!mThreadPoolStarted) {
        mThreadPoolStarted = true;
        spawnPooledThread(true);
    }
}

void ProcessState::spawnPooledThread(bool isMain)
{
    if (mThreadPoolStarted) {
        //創(chuàng)建線程名字"Binder:${pid}_${自增數(shù)字}"
        String8 name = makeBinderThreadName();
        sp<Thread> t = new PoolThread(isMain);
        //運(yùn)行binder線程
        t->run(name.string());
    }
} 

ProcessState有兩個宏定義值得注意一下:

//ProcessState.cpp
//一次Binder通信最大可以傳輸?shù)拇笮∈?1MB-4KB*2
#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
//binder驅(qū)動的文件描述符fd被限制了最大線程數(shù)15
#define DEFAULT_MAX_BINDER_THREADS 15

我們看下binder線程PoolThread長啥樣:

class PoolThread : public Thread {
public:
    explicit PoolThread(bool isMain)
        : mIsMain(isMain){}
protected:
    virtual bool threadLoop()
    {    //把binder線程注冊進(jìn)binder驅(qū)動程序的線程池中
        IPCThreadState::self()->joinThreadPool(mIsMain);
        return false;
    }

    const bool mIsMain;
}; 

來到IPCThreadState.cpp:

//IPCThreadState.cpp
void IPCThreadState::joinThreadPool(bool isMain)
{
    //向binder驅(qū)動寫數(shù)據(jù):進(jìn)入死循環(huán)
    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
    status_t result;
    do {
        //進(jìn)入死循環(huán),等待指令的到來
        result = getAndExecuteCommand();
    } while (result != -ECONNREFUSED && result != -EBADF);
    //向binder驅(qū)動寫數(shù)據(jù):退出死循環(huán)
    mOut.writeInt32(BC_EXIT_LOOPER);
}

status_t IPCThreadState::getAndExecuteCommand()
{
    //從binder驅(qū)動讀數(shù)據(jù),得到指令
    cmd = mIn.readInt32();
    //執(zhí)行指令
    result = executeCommand(cmd);
    return result;
} 

梳理一下binder的啟動過程:

  1. 打開binder驅(qū)動
  2. 映射內(nèi)存,分配緩沖區(qū)
  3. 運(yùn)行binder線程,進(jìn)入死循環(huán),等待指令

總結(jié)

綜上,Android應(yīng)用進(jìn)程的啟動可以總結(jié)成以下步驟:

  1. 點擊Launcher桌面的App圖標(biāo)
  2. AMS發(fā)起socket請求
  3. Zygote進(jìn)程接收請求并處理參數(shù)
  4. Zygote進(jìn)程fork出應(yīng)用進(jìn)程,應(yīng)用進(jìn)程繼承得到虛擬機(jī)實例
  5. 應(yīng)用進(jìn)程啟動binder線程池、運(yùn)行ActivityThread類的main函數(shù)、啟動Looper循環(huán)

完整流程圖:

搞懂Android應(yīng)用啟動過程,再也不怕面試官了

 

面試前的知識梳理,儲備提升

自己的知識準(zhǔn)備得怎么樣,這直接決定了你能否順利通過一面和二面,所以在面試前來一個知識梳理,看需不需要提升自己的知識儲備是很有必要的。

關(guān)于知識梳理,這里再分享一下我面試這段時間的復(fù)習(xí)路線:(以下體系的復(fù)習(xí)資料是我從各路大佬收集整理好的)

  • 架構(gòu)師筑基必備技能:深入Java泛型+注解深入淺出+并發(fā)編程+數(shù)據(jù)傳輸與序列化+Java虛擬機(jī)原理+反射與類加載+動態(tài)代理+高效IO
  • Android高級UI與FrameWork源碼:高級UI晉升+Framework內(nèi)核解析+Android組件內(nèi)核+數(shù)據(jù)持久化
  • 360°全方面性能調(diào)優(yōu):設(shè)計思想與代碼質(zhì)量優(yōu)化+程序性能優(yōu)化+開發(fā)效率優(yōu)化
  • 解讀開源框架設(shè)計思想:熱修復(fù)設(shè)計+插件化框架解讀+組件化框架設(shè)計+圖片加載框架+網(wǎng)絡(luò)訪問框架設(shè)計+RXJava響應(yīng)式編程框架設(shè)計+IOC架構(gòu)設(shè)計+Android架構(gòu)組件Jetpack
  • NDK模塊開發(fā):NDK基礎(chǔ)知識體系+底層圖片處理+音視頻開發(fā)
  • 微信小程序:小程序介紹+UI開發(fā)+API操作+微信對接
  • Hybrid 開發(fā)與Flutter:html5項目實戰(zhàn)+Flutter進(jìn)階
搞懂Android應(yīng)用啟動過程,再也不怕面試官了

 

知識梳理完之后,就需要進(jìn)行查漏補(bǔ)缺,所以針對這些知識點,我手頭上也準(zhǔn)備了不少的電子書和筆記,這些筆記將各個知識點進(jìn)行了完美的總結(jié)。

搞懂Android應(yīng)用啟動過程,再也不怕面試官了

《507頁Android開發(fā)相關(guān)源碼解析》


搞懂Android應(yīng)用啟動過程,再也不怕面試官了

《379頁Android開發(fā)面試寶典》

3.項目復(fù)盤

實際上,面試的一二輪所問到的技術(shù)問題,很多都是圍繞著你的項目展開,因此在面試前最后要做好的一件事情就是項目復(fù)盤。關(guān)于項目復(fù)盤,我個人的思路如下,可供參考:

  • 你在這個項目中承擔(dān)了什么樣的角色?
  • 這個項目的背景是什么,如果是技術(shù)項目,為什么要做?
  • 有哪些技術(shù)難點,是怎么解決的,是否還有更好的方案?
  • 你認(rèn)為項目中是否有可以改進(jìn)的點?
  • 這個項目解決了什么問題,最好用數(shù)據(jù)說話,這個數(shù)據(jù)又是怎么得出來的?

提前把思路捋一捋,上面這些問題好好思考或準(zhǔn)備一下,做到心中有譜以后,自然能夠面試官聊得融洽,保持一個好的心態(tài),通過的幾率就會更大一些。

以上文章中的資料,均可以免費(fèi)分享給大家來學(xué)習(xí),

資料太多,全部展示會影響篇幅,暫時就先列舉這些部分截圖;

分享到:
標(biāo)簽:Android
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定