1. 程式人生 > >Android9.0 Activity啟動流程分析(二)

Android9.0 Activity啟動流程分析(二)

文章目錄


  在Android9.0 Activity啟動流程分析(一)中,我們最終分析到AMS通過zygote啟動Activity對應的程序,接下來繼續分析。

1、ActivityThread的main函式

  通過zygote啟動程序時,傳入的className為android.app.ActivityThread。
因此,當zygote通過反射呼叫程序的main函式時,ActivityThread的main函式將被呼叫

// frameworks/base/core/java/android/app/ActivityThread.java	
public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

    // CloseGuard defaults to true and can be quite spammy.  We
    // disable it here, but selectively enable it later (via
    // StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false); Environment.initForCurrentUser(); // Set the reporter for event logging in libcore EventLogger.setReporter(new EventLoggingReporter()); // Make sure TrustedCertificateStore looks in the right place for CA certificates final File configDir = Environment.
getUserConfigDirectory(UserHandle.myUserId()); TrustedCertificateStore.setDefaultUserDirectory(configDir); Process.setArgV0("<pre-initialized>"); //準備主執行緒的Looper Looper.prepareMainLooper(); // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line. // It will be in the format "seq=114" long startSeq = 0; if (args != null) { for (int i = args.length - 1; i >= 0; --i) { if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) { startSeq = Long.parseLong( args[i].substring(PROC_START_SEQ_IDENT.length())); } } } // 建立當前程序的ActivityThread ActivityThread thread = new ActivityThread(); //呼叫attach函式 thread.attach(false, startSeq); if (sMainThreadHandler == null) { // 儲存程序對應的主執行緒Handler sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); // 進入主執行緒的訊息迴圈 Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }

  從上述程式碼可以看出,ActivityThread的main函式最主要工作是:

  1. 創建出一個Looper,並將主執行緒加入到訊息迴圈中。
  2. 創建出ActivityThread,並呼叫其attach函式。

  接下來看ActivityThread的attach函式

// frameworks/base/core/java/android/app/ActivityThread.java	
private void attach(boolean system, long startSeq) {
    sCurrentActivityThread = this;
    mSystemThread = system;
    if (!system) {
        ViewRootImpl.addFirstDrawHandler(new Runnable() {
            @Override
            public void run() {
                //JIT-Just in time 
                //JIT技術主要是對多次執行的程式碼進行編譯,當再次呼叫時使用編譯之後的機器碼,而不是每次都解釋,以節約時間
                //JIT原理: 
                //每啟動一個應用程式,都會相應地啟動一個dalvik虛擬機器,啟動時會建立JIT執行緒,一直在後臺執行。
                //當某段程式碼被呼叫時,虛擬機器會判斷它是否需要編譯成機器碼,如果需要,就做一個標記。 
                //JIT執行緒在後臺檢測該標記,如果發現標記被設定,就把對應程式碼編譯成機器碼,並將其機器碼地址及相關資訊儲存起來
                //當程序下次執行到此段程式碼時,就會直接跳到機器碼執行,而不再解釋執行,從而提高執行速度 
                //這裡開啟JIT,應該是為了提高android繪製的速度
                ensureJitEnabled();
            }
        });
        // 設定在DDMS中看到的程序名為"<pre-initialized>"
        android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                                                UserHandle.myUserId());

        //設定RuntimeInit的mApplicationObject引數
        RuntimeInit.setApplicationObject(mAppThread.asBinder());
        final IActivityManager mgr = ActivityManager.getService();
        try {
            // 與AMS通訊,呼叫其attachApplication介面
            mgr.attachApplication(mAppThread, startSeq);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
        // 監控GC操作; 當程序內的一些Activity發生變化,同時記憶體佔用量較大時
        // 通知AMS釋放一些Activity
        // Watch for getting close to heap limit.
        BinderInternal.addGcWatcher(new Runnable() {
            @Override public void run() {
                if (!mSomeActivitiesChanged) {
                    return;
                }
                Runtime runtime = Runtime.getRuntime();
                long dalvikMax = runtime.maxMemory();
                long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();
                // 判斷記憶體佔用量是否過大
                if (dalvikUsed > ((3*dalvikMax)/4)) {
                    if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024)
                            + " total=" + (runtime.totalMemory()/1024)
                            + " used=" + (dalvikUsed/1024));
                    mSomeActivitiesChanged = false;
                    try {
                        // 通知AMS釋放一些Activity,以緩解記憶體緊張
                        mgr.releaseSomeActivities(mAppThread);
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
            }
        });
    } else {
        // SystemServer程序為了融入Android體系,呼叫createSystemContext函式。
        // 在createSystemContext函式中,SystemServer程序建立了自己的ActivityThread,並呼叫了attach函式。
        // 下面處理的是SystemServer程序
        // Don't set application object here -- if the system crashes,
        // we can't display an alert, we just want to die die die.
        android.ddm.DdmHandleAppName.setAppName("system_process",
                UserHandle.myUserId());
        try {
            mInstrumentation = new Instrumentation();
            mInstrumentation.basicInit(this);
            ContextImpl context = ContextImpl.createAppContext(
                    this, getSystemContext().mPackageInfo);
            mInitialApplication = context.mPackageInfo.makeApplication(true, null);
            mInitialApplication.onCreate();
        } catch (Exception e) {
            throw new RuntimeException(
                    "Unable to instantiate Application():" + e.toString(), e);
        }
    }
    ....
}

2. AMS的attachApplication函式

  接下來通過Binder呼叫AMS的attachApplication函式,我們從應用程序又回到了SystemServer的AMS中.由於該函式比較長我們分段分析。

2.1 Part-I

// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService
public final void attachApplication(IApplicationThread thread, long startSeq) {
    synchronized (this) {
        int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        // 轉呼叫
        attachApplicationLocked(thread, callingPid, callingUid, startSeq);
        Binder.restoreCallingIdentity(origId);
    }
}
----------------------------------------------------------------------------------------------------------------------------------------------------------
private final boolean attachApplicationLocked(IApplicationThread thread,
        int pid, int callingUid, long startSeq) {

    // 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;
    long startTime = SystemClock.uptimeMillis();
    if (pid != MY_PID && pid >= 0) {
        synchronized (mPidsSelfLocked) {
            // 根據pid查詢對應的ProcessRecord物件
            app = mPidsSelfLocked.get(pid);
        }
    } else {
        app = null;
    }

    // 如果無法根據pid查詢到對應的ProcessRecord則從mPendingStarts列表中查詢
    // It's possible that process called attachApplication before we got a chance to
    // update the internal state.
    if (app == null && startSeq > 0) {
        final ProcessRecord pending = mPendingStarts.get(startSeq);
        if (pending != null && pending.startUid == callingUid
                && handleProcessStartedLocked(pending, pid, pending.usingWrapper,
                        startSeq, true)) {
            app = pending;
        }
    }

    // 如果程序由AMS啟動,則它在AMS中一定有對應的ProcessRecord
    // 此處app為null,則表示AMS沒有該程序的記錄,故需要kill掉此異常程序
    if (app == null) {
        Slog.w(TAG, "No pending application record for pid " + pid
                + " (IApplicationThread " + thread + "); dropping process");
        EventLog.writeEvent(EventLogTags.AM_DROP_PROCESS, pid);
        if (pid > 0 && pid != MY_PID) {
            killProcessQuiet(pid);
            //TODO: killProcessGroup(app.info.uid, pid);
        } else {
            // pid < 0時,fork程序失敗,因此僅上層完成清理工作即可
            // 呼叫ApplicationThread的scheduleExit函式 
            // 應用程序將進行一些掃尾工作,例如結束訊息迴圈,然後退出執行
            try {
                thread.scheduleExit();
            } catch (Exception e) {
                // Ignore exceptions.
            }
        }
        return false;
    }
    // 判斷pid對應processRecord的IApplicationThread是否為null 
    // AMS建立ProcessRecord後,在attach之前,正常情況下IApplicationThread應該為null 
    // 特殊情況下:如果舊應用程序被殺死,底層對應的pid被釋放,在通知到達AMS之前(AMS在下面的程式碼裡註冊了“訃告”接收物件), 
    // 使用者又啟動了一個新的程序,新程序剛好分配到舊程序的pid時 
    // 此處得到的processRecord可能就是舊程序的,於是app.thread可能不為null,因此需要作判斷和處理
    // If this application record is still attached to a previous
    // process, clean it up now.
    if (app.thread != null) {
        handleAppDiedLocked(app, true, true);
    }

    // Tell the process all about itself.

    if (DEBUG_ALL) Slog.v(
            TAG, "Binding process pid " + pid + " to record " + app);

    // 建立一個“訃告”接收物件,註冊到應用程序的ApplicationThread中 
    // 當應用程序退出時,該物件的binderDied將被呼叫,這樣AMS就能做相應的處理 
    // binderDied函式將在另一個執行緒中被呼叫,其內部也會呼叫handleAppDiedLocked函式
    final String processName = app.processName;
    try {
        AppDeathRecipient adr = new AppDeathRecipient(
                app, pid, thread);
        thread.asBinder().linkToDeath(adr, 0);
        app.deathRecipient = adr;
    } catch (RemoteException e) {
        app.resetPackageList(mProcessStats);
        startProcessLocked(app, "link fail", processName);
        return false;
    }

    EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);
    // 設定app的一些變數,例如排程優先順序和oom_adj相關的成員
    app.makeActive(thread, mProcessStats);
    app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;
    app.curSchedGroup = app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
    app.forcingToImportant = null;
    updateProcessForegroundLocked(app, false, false);
    app.hasShownUi = false;
    app.debugging = false;
    app.cached = false;
    app.killedByAm = false;
    app.killed = false;


    // We carefully use the same state that PackageManager uses for
    // filtering, since we use this flag to decide if we need to install
    // providers when user is unlocked later
    app.unlocked = StorageManager.isUserKeyUnlocked(app.userId);

    mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);

  至此,attachApplicationLocked的第一部分介紹完畢。
  這部分程式碼的核心功能比較簡單,其實就是:
  1. 判斷程序的有效性,同時註冊觀察者監聽程序的死亡訊號。
  2. 設定pid對應的ProcessRecord物件的一些成員變數,例如和應用程序互動的IApplicationThread物件、程序排程的優先順序等。

2.2 Part-II

  下面我們看看attachApplicationLocked第二部分的程式碼:

    // frameworks/base/services/core/java/com/android/server/am/ActivityManagerService
    // AMS正常啟動後,mProcessesReady就已經變為true了
    boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);

    // generateApplicationProvidersLocked將通過PKMS查詢定義在程序中的ContentProvider,並將其儲存在AMS的資料結構中
    List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
    
    //這裡應該是處理:載入ContentProvider時,啟動程序的場景
    //checkAppInLaunchingProvidersLocked主要將當前啟動程序的ProcessRecord,和AMS中mLaunchingProviders的ProcessRecord進行比較
    //當判斷出該程序是由於啟動ContentProvider而被載入的,那麼就傳送一個延遲訊息(10s)
    //通過這裡可以看出,當由於載入ContentProvider啟動程序時,在程序啟動後,ContentProvider在10s內要完成釋出
    if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
        Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
        msg.obj = app;
        mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
    }

    checkTime(startTime, "attachApplicationLocked: before bindApplication");

    // 獲取App的資訊,準備bindApplication的引數
    ...

        if (app.isolatedEntryPoint != null) {
            // This is an isolated process which should just call an entry point instead of
            // being bound to an application.
            thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs);
        } else if (app.instr != null) {
            // 回撥程序ApplicationThread的bindApplication介面
            thread.bindApplication(processName, appInfo, providers,
                    app.instr.mClass,
                    profilerInfo, app.instr.mArguments,
                    app.instr.mWatcher,
                    app.instr.mUiAutomationConnection, testMode,
                    mBinderTransactionTrackingEnabled, enableTrackAllocation,
                    isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(getGlobalConfiguration()), app.compat,
                    getCommonServicesLocked(app.isolated),
                    mCoreSettingsObserver.getCoreSettingsLocked(),
                    buildSerial, isAutofillCompatEnabled);
        } else {
            thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
                    null, null, null, testMode,
                    mBinderTransactionTrackingEnabled, enableTrackAllocation,
                    isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(getGlobalConfiguration()), app.compat,
                    getCommonServicesLocked(app.isolated),
                    mCoreSettingsObserver.getCoreSettingsLocked(),
                    buildSerial, isAutofillCompatEnabled);
        }
        if (profilerInfo != null) {
            profilerInfo.closeFd();
            profilerInfo = null;
        }
        checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
        // 更新程序排程策略
        updateLruProcessLocked(app, false, null);
        checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
        app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
    } catch (Exception e) {
        ...
    }

  從程式碼來看,第二階段最核心工作就是:
  呼叫程序ApplicationThread的bindApplication函式,接下來我們分析一下該函式。

2.2.1 ApplicationThread的bindApplication函式

  從之前的程式碼,我們知道應用程序由zygote fork得到,然後呼叫ActivityThread的main函式,進入到Java世界。在Process.java通知zygote fork程序的時候並沒有包含任何與Activity相關的資訊,也就是說該程序並沒有融入到Android的體系中,因此僅能被稱為一個Java程序,甚至連程序名也只是“敷衍”地定義為“pre-initialized”。
此處的bindApplication函式,就是在新程序中建立並初始化對應的Android執行環境。

現在,我們看看bindApplication函式的主要流程:

// frameworks/base/core/java/android/app/ActivityThread.java	
public final void bindApplication(String processName, ApplicationInfo appInfo,
      List<ProviderInfo> providers, ComponentName instrumentationName,
      ProfilerInfo profilerInfo, Bundle instrumentationArgs,
      IInstrumentationWatcher instrumentationWatcher,
      IUiAutomationConnection instrumentationUiConnection, int debugMode,
      boolean enableBinderTracking, boolean trackAllocation
            
           

相關推薦

Android9.0 Activity啟動流程分析

文章目錄 1、ActivityThread的main函式 2. AMS的attachApplication函式 2.1 Part-I 2.2 Part-II 2.2.1 ApplicationThread的bindApp

Android9.0 Activity啟動流程分析

1、ActivityRecord、TaskRecord、ActivityStack和ActivityDisplay介紹   本篇文章是基於Android refs/tags/android-9.0.0_r8分支的程式碼進行分析的   在分析Activity啟動的原始碼之前先介紹一下Act

ContentProvider啟動流程分析

## 0x01 扯東扯西的前言&概述 ## 0x02 ContentProvider啟動流程分析 step6: ActivityManagerProxy#getContentProvider() 代理類ActivityManagerProxy位於ActivityManagerNative.j

springboot啟動流程分析

現在繼續看啟動過程的詳情,詳細描述下SpringApplication建構函式: 1.載入過程中的SpringApplication初始化如下: public SpringApplication(ResourceLoader resourceLoader

SpringBoot啟動流程分析:SpringApplication的run方法

SpringBoot系列文章簡介 SpringBoot原始碼閱讀輔助篇:   Spring IoC容器與應用上下文的設計與實現 SpringBoot啟動流程原始碼分析: SpringBoot啟動流程分析(一):SpringApplication類初始化過程 SpringBoot啟動流程分析(二)

Activity啟動流程分析基於android 5.1

最近由於工作需要,需要深入瞭解AMS的內部實現。說老實話,雖然已經經過了幾輪重構,AMS的程式碼還是又臭又長。。。 萬事開頭難,先找個入口開始看吧。當從Launcher介面點選啟動一個app時,會啟動一個新的activity。所以就從startActivity()看起,研究

[Android6.0][RK3399] 雙屏異顯程式碼實現流程分析

Platform: RK3399 OS: Android 6.0 Version: v2016.08 LCD interface: eDP + mipi Patch Code Date: Fri, 09 Dec 2016 10:53:11

AliOS Things的啟動過程分析

AliOS Things的啟動過程分析(二) 在AliOS Things的啟動過程分析(一)中分析了developerkit從系統上電到呼叫main函式所經歷的一些步驟,接下來詳細分析一下main函式的一些工作,主要是核心的相關初始化工作。main函式所處的位置位於    

HBase原始碼分析之HRegion上compact流程分析

  2016年03月03日 21:38:04 辰辰爸的技術部落格 閱讀數:2767 版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/lipeng_bigdata/article/details/50791205

dart 非同步事件執行流程分析

// use two list to test the async envet exe order. // one record the emitted order; // and the other record the captured order; import 'dart:

nu-lb-nuc140 RTX 流程 分析

0 參考資料 http://www.stmcu.org.cn/module/forum/thread-605101-1-1.html 【安富萊】【RTX作業系統教程】第18章 記憶體管理 1 巨集定義 __TARGET_ARCH_6S_M __USE_EXCLUSIVE_AC

ContentProvider啟動流程分析

## 0x01 扯東扯西的前言&概述 ## 0x02 ContentProvider啟動流程分析 step15: ApplicationThread#bindApplication() 上一步,在ApplicationThreadProxy類的bindApplication()函式中,通過B

ContentProvider啟動流程分析

## 0x01 扯東扯西的前言&概述 作為安卓設計的四大元件之一,是跨程序共享資料的一把利器,所謂跨程序共享資料,通俗理解就是,應用程式A可以訪問操作應用程式B共享出來的資料,這些共享出來的資料一般都有其對應的URI(統一資源識別符號),那麼就涉及到兩個過程: 提供資料內容的過程: A應用(

springboot啟動流程分析

以springboot 2.0.2.RELEASE版本為例 1.pom.xml引入依賴 <parent> <groupId>org.springframework.boot</groupId>

Oracle11g RAC 啟動流程梳理OHASD簡析和啟停實驗

簡單說明: 11gRAC啟動分為四個層次,第一個層次是OHASD和子代理程序啟動: init——>init.ohasd——>ohasd——>agent子程序啟動 即: OS啟動——>/etc/rc.d/init.d/init.o

OTelephony分析之通話流程分析撥打電話流程分析

撥打電話,是從通話的撥號盤開始的,而撥號盤的介面是在DialpadFragment,因此,需要從這個地方進行分析一.撥號盤介面撥號流程 public void onClick(View view) { ...... if (resId == R.id.dia

Activity啟動流程筆記

從startActivity開始說起: public void startActivity(Intent intent, Bundle options) { if (options != null) { startA

Android 呼吸燈流程分析

一、Android呼吸燈Driver實現       1、註冊驅動       程式碼位置:mediatek/kernel/drivers/leds/leds_drv.c 602static struct platform_driver mt65xx_leds_drive

Quartz原始碼——scheduler.start()啟動原始碼分析

scheduler.start()是Quartz的啟動方式!下面進行分析,方便自己檢視! 我都是分析的jobStore 方式為jdbc的SimpleTrigger!RAM的方式類似分析方式! 解釋: {0} : 表的字首 ,如表qrtz_

Android OTA升級原理和流程分析---update.zip差分包問題的解決

Android OTA升級原理和流程分析(二)—update.zip差分包問題的解決 在上一篇末尾提到的生成差分包時出現的問題,現已解決,由於最近比較忙,相隔的時間也比較長,所以單列一個篇幅提示大家。這個問題居然是原始碼中的問題,可能你已經制作成功了,不過我的