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

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

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

簡要回顧

先回顧一下Android系統的啟動過程:

init進程fork出Zygote進程后,Zygote進程會創建一個服務端socket,等待AMS發起socket請求。

同時,由Zygote進程fork出的SystemServer進程會啟動各項系統服務,其中就包含了AMS,AMS會啟動Launcher桌面,此時就可以等待用戶點擊App圖標來啟動應用進程了。

搞懂Android應用啟動過程,再也不怕面試官了

 

然后看下系統服務的啟動,不管是由init進程啟動的獨立進程的系統服務如SurfaceFlinger,還是由SystemServer進程啟動的非獨立進程的系統服務如AMS,都是在ServiceManager進程中完成注冊和獲取的,在跨進程通信上使用了Android的binder機制。

搞懂Android應用啟動過程,再也不怕面試官了

 

ServiceManager進程本身也是一個系統服務,經過啟動進程、啟動binder機制、發布自己和等待請求4個步驟,就可以處理其他系統服務的獲取和注冊需求了。

AMS發送socket請求

Android應用進程的啟動是被動式的,在Launcher桌面點擊圖標啟動一個應用的組件如Activity時,如果Activity所在的進程不存在,就會創建并啟動進程。

點擊App圖標后經過層層調用會來到ActivityStackSupervisor的startSpecificActivityLocked方法。

//ActivityStackSupervisor.JAVA
final ActivityManagerService mService;

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

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

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

搞懂Android應用啟動過程,再也不怕面試官了

 

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

//ActivityThread.java
public static void main(String[] args) {
    //創建主線程的looper
    Looper.prepareMainLooper();
    //ActivityThread并不是線程,只是普通的java對象
    ActivityThread thread = new ActivityThread();
    //告訴AMS,應用已經啟動好了
    thread.attach(false);
    //運行looper,啟動消息循環
    Looper.loop();
}

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

所以對于AMS來說:

1.AMS向Zygote發起啟動應用的socket請求,Zygote收到請求fork出進程,返回進程的pid給AMS;

2.應用進程啟動好后,執行入口main函數,通過attachApplication方法告訴AMS已經啟動,同時傳入應用進程的binder句柄IApplicationThread。

完成這兩步,應用進程的啟動過程才算完成。

下面看AMS的startProcessLocked啟動應用進程時都做了些什么。

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

之所以要判斷app.thread,是為了避免當應用進程正在啟動的時候,假如又有另一個組件需要啟動,導致重復拉起(創建)應用進程。

繼續看重載方法startProcessLocked:

//ActivityManagerService.java
private final void startProcessLocked(...){
    //應用進程的主線程的類名
    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>();
    //...處理各種參數
    return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
} 

其中:

  1. openZygoteSocketIfNeeded打開本地socket
  2. zygoteSendArgsAndGetResult發送請求參數,其中帶上了ActivityThread類名
  3. return返回的數據結構ProcessStartResult中會有pid字段

梳理一下:

搞懂Android應用啟動過程,再也不怕面試官了

 

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

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

Zygote處理socket請求

從 圖解Android系統的啟動 一文可知,在ZygoteInit的main函數中,會創建服務端socket。

//ZygoteInit.java
public static void main(String argv[]) {
    //Server類,封裝了socket
    ZygoteServer zygoteServer = new ZygoteServer();
    //創建服務端socket,名字為socketName即zygote
    zygoteServer.registerServerSocket(socketName);
    //進入死循環,等待AMS發請求過來
    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);

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

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

來到ZygoteConnection的runOnce。

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

handleChildProc方法調用了ZygoteInit的zygoteInit方法,里邊主要做了3件事:

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

這樣應用進程就啟動起來了。梳理一下:

搞懂Android應用啟動過程,再也不怕面試官了

 

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

啟動binder線程池

Zygote的跨進程通信沒有使用binder,而是socket,所以應用進程的binder機制不是繼承而來,而是進程創建后自己啟動的。

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

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

//ZygoteInit.java
public static final void zygoteInit(...){
    RuntimeInit.commonInit();
    //進入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構造函數
ProcessState::ProcessState(const char *driver)
    : mDriverName(String8(driver))
        , mDriverFD(open_driver(driver)) //打開binder驅動
        ,//...
{
    if (mDriverFD >= 0) {
        //mmap是一種內存映射文件的方法,把mDriverFD映射到當前的內存空間
        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) {
        //創建線程名字"Binder:${pid}_${自增數字}"
        String8 name = makeBinderThreadName();
        sp<Thread> t = new PoolThread(isMain);
        //運行binder線程
        t->run(name.string());
    }
} 

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

//ProcessState.cpp
//一次Binder通信最大可以傳輸的大小是 1MB-4KB*2
#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
//binder驅動的文件描述符fd被限制了最大線程數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線程注冊進binder驅動程序的線程池中
        IPCThreadState::self()->joinThreadPool(mIsMain);
        return false;
    }

    const bool mIsMain;
}; 

來到IPCThreadState.cpp:

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

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

梳理一下binder的啟動過程:

  1. 打開binder驅動
  2. 映射內存,分配緩沖區
  3. 運行binder線程,進入死循環,等待指令

總結

綜上,Android應用進程的啟動可以總結成以下步驟:

  1. 點擊Launcher桌面的App圖標
  2. AMS發起socket請求
  3. Zygote進程接收請求并處理參數
  4. Zygote進程fork出應用進程,應用進程繼承得到虛擬機實例
  5. 應用進程啟動binder線程池、運行ActivityThread類的main函數、啟動Looper循環

完整流程圖:

搞懂Android應用啟動過程,再也不怕面試官了

 

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

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

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

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

 

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

搞懂Android應用啟動過程,再也不怕面試官了

《507頁Android開發相關源碼解析》


搞懂Android應用啟動過程,再也不怕面試官了

《379頁Android開發面試寶典》

3.項目復盤

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

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

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

以上文章中的資料,均可以免費分享給大家來學習,

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

分享到:
標簽:Android
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

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

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定