1. 程式人生 > >ContentProvider啟動流程分析(三)

ContentProvider啟動流程分析(三)

## 0x01 扯東扯西的前言&概述

## 0x02 ContentProvider啟動流程分析

step15: ApplicationThread#bindApplication()

上一步,在ApplicationThreadProxy類的bindApplication()函式中,通過Binder物件mRemote發出了一個程序間通訊請求!

反查原始碼很容易知道,bindApplication()函式,其實是IApplicationThread介面類的介面函式,而ActivityThread類的內部類ApplicationThread實現了這一介面,所以自然也就實現了介面函式bindApplication(),我們來看ApplicationThread類的函式bindApplication()原始碼刪減如下:

public final void bindApplication(String processName, ApplicationInfo appInfo,
            List<ProviderInfo> providers, ComponentName instrumentationName,
            ProfilerInfo profilerInfo, Bundle instrumentationArgs,
            IInstrumentationWatcher instrumentationWatcher,
            IUiAutomationConnection instrumentationUiConnection, int debugMode,
            boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,
            Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
            Bundle coreSettings) {
    AppBindData data = new AppBindData();
    data.processName = processName;
    data.appInfo = appInfo;
    data.providers = providers;
    data.instrumentationName = instrumentationName;
    data.instrumentationArgs = instrumentationArgs;
    data.instrumentationWatcher = instrumentationWatcher;
    data.instrumentationUiAutomationConnection = instrumentationUiConnection;
    data.debugMode = debugMode;
    data.enableOpenGlTrace = enableOpenGlTrace;
    data.restrictedBackupMode = isRestrictedBackupMode;
    data.persistent = persistent;
    data.config = config;
    data.compatInfo = compatInfo;
    data.initProfilerInfo = profilerInfo;
    sendMessage(H.BIND_APPLICATION, data);
}
  • 沒錯!ApplicationThread類的成員函式bindApplication(),就是型別為BIND_APPLICATION_TRANSACTION的程序間通訊請求的真正處理者!處理的邏輯是:先把將要啟動的ContentProvider元件列表,封裝成一個AppBindData物件;
  • 然後再呼叫外部類ActivityThread的成員函式sendMessage(),再次將這個AppBindData物件封裝成一個型別為BIND_APPLICATION的訊息,以便可以傳送到新建應用程式程序的主執行緒的訊息佇列中!

step16: ActivityThread#sendMessage()

ActivityThread類持有一個mH成員變數,mH實際上是一個Handler物件,通過mH.sendMessage()傳送訊息到新建應用程式程序的主執行緒的訊息佇列中!所以,這個訊息最後是最終是在mH.handleMessage()函式中處理的。

step17: ActivityThread#handleMessage()

ActivityThread內部類H的程豔方法handleMessage()原始碼刪減如下:

public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
        ......
    case BIND_APPLICATION:
        AppBindData data = (AppBindData)msg.obj;
        handleBindApplication(data);
        break;
        ......
    }
    if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
  • 在handleMessage()函式中,我們只考慮BIND_APPLICATION的情況,首先將Message物件msg的成員變數obj強轉成個AppBindData物件,它包含了要啟動的ContentProvider列表。
  • 然後,呼叫handleBindApplication()函式把這些ContentProvider元件啟動起來!

接下來,關注handleBindApplication()函式,是怎樣把這些ContentProvider元件啟動起來的?

step18: ActivityThread#handleBindApplication()

ActivityThread類的成員函式handleBindApplication()原始碼如下:

private void handleBindApplication(AppBindData data) {
    ......
    // don't bring up providers in restricted mode; they may depend on the
    // app's custom Application class
    if (!data.restrictedBackupMode) {
        List<ProviderInfo> providers = data.providers;
        if (providers != null) {
            installContentProviders(app, providers);
            ......
        }
    }
}

引數data是一個AppBindData物件,data的成員變數providers儲存了,需要在當前程序中啟動的ContentProvider元件,接下來則會呼叫ActivityThread類的成員函式installContentProviders()來啟動這些元件!

step19: ActivityThread#installContentProviders()

ActivityThread類的成員函式installContentProviders()原始碼如下:

private void installContentProviders(
    Context context, List<ProviderInfo> providers) {
    final ArrayList<IActivityManager.ContentProviderHolder> results =
        new ArrayList<IActivityManager.ContentProviderHolder>();

    for (ProviderInfo cpi : providers) {
        IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi, false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
        if (cph != null) {
            cph.noReleaseNeeded = true;
            results.add(cph);
        }
    }

    try {
        ActivityManagerNative.getDefault().publishContentProviders(
            getApplicationThread(), results);
    } catch (RemoteException ex) {
    }
}
  • for-each迴圈得到儲存在providers中的每一個元件ProviderInfo物件cpi,然後呼叫installProvider()函式啟動每個cpi對應的ContnetProvider元件;
  • 這些元件啟動之後,就可以獲得其對應的一個IContentProvider介面;然後,把這個介面封裝成一個ContentProviderHolder物件;
  • 最後呼叫AMS代理物件(也即,ActivityManagerProxy)的成員函式publishContentProviders(),將這些ContentProviderHolder物件傳遞給AMS;AMS正是通過這些ContentProviderHolder物件獲取它所描述的ContentProvider元件的一個訪問介面;

接下來,先分析ActivityThread類成員函式installProvider()在當前應用程式程序中啟動一個ContentProvider元件的過程;然後,分析AMS代理物件ActivityManagerProxy成員函式publishContentProviders()將啟動完成的元件釋出到AMS的過程!

step20: ActivityThread#installProvider()

ActivityThread類成員函式installProvider()原始碼如下:

/**
 * Installs the provider.
 *
 * Providers that are local to the process or that come from the system server
 * may be installed permanently which is indicated by setting noReleaseNeeded to true.
 * Other remote providers are reference counted.  The initial reference count
 * for all reference counted providers is one.  Providers that are not reference
 * counted do not have a reference count (at all).
 *
 * This method detects when a provider has already been installed.  When this happens,
 * it increments the reference count of the existing provider (if appropriate)
 * and returns the existing provider.  This can happen due to concurrent
 * attempts to acquire the same provider.
 */
private IActivityManager.ContentProviderHolder installProvider(Context context,
        IActivityManager.ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ContentProvider localProvider = null;
    IContentProvider provider;
    // holder為空,表示引數info所描述的ContentProvider元件需要在當前程序中啟動
    if (holder == null || holder.provider == null) {
        Context c = null;
        ApplicationInfo ai = info.applicationInfo;
        if (context.getPackageName().equals(ai.packageName)) {
            c = context;
        }
        ......
        final java.lang.ClassLoader cl = c.getClassLoader();
        localProvider = (ContentProvider)cl.
                        loadClass(info.name).newInstance();
        provider = localProvider.getIContentProvider();
        if (provider == null) {
            return null;
        }
        // XXX Need to create the correct context for this provider.
        localProvider.attachInfo(c, info);
    } else {
        provider = holder.provider;
    }

    IActivityManager.ContentProviderHolder retHolder;
    synchronized (mProviderMap) {
        IBinder jBinder = provider.asBinder();
        if (localProvider != null) {
            ComponentName cname = new ComponentName(info.packageName, info.name);
            ProviderClientRecord pr = mLocalProvidersByName.get(cname);
            if (pr != null) {
                provider = pr.mProvider;
            } else {
                holder = new IActivityManager.ContentProviderHolder(info);
                holder.provider = provider;
                holder.noReleaseNeeded = true;
                pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                mLocalProviders.put(jBinder, pr);
                mLocalProvidersByName.put(cname, pr);
            }
            retHolder = pr.mHolder;
        } else {
            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
            if (prc != null) {
                ......
            } else {
                ProviderClientRecord client = installProviderAuthoritiesLocked(
                                                  provider, localProvider, holder);
                if (noReleaseNeeded) {
                    prc = new ProviderRefCount(holder, client, 1000, 1000);
                } else {
                    prc = stable ? new ProviderRefCount(holder, client, 1, 0)
                          : new ProviderRefCount(holder, client, 0, 1);
                }
                mProviderRefCountMap.put(jBinder, prc);
            }
            retHolder = prc.holder;
        }
    }
    return retHolder;
}
  • 對傳進來的引數holder判空,如果為null,表示要在當前應用程式程序中,將引數info所描述的ContentProvider元件啟動起來;另一個引數context描述了將要啟動的ContentProvider元件執行的上下文環境;
  • localProvider表示一個ContentProvider元件物件,通過localProvider.getIContentProvider(),也即呼叫localProvider的成員函式getIContentProvider()來獲得該元件對應的一個IContentProvider介面provider;這個IContentProvider最終需要釋出到AMS,AMS會將它返回給那些需要訪問它所描述的ContentProvider元件的呼叫方應用程式程序;
  • 接下來通過localProvider.attachInfo()來進一步初始化前面所建立的ContentProvider元件;
  • 接下來對全域性變數mProviderMap加同步鎖,然後對localProvider判空,正常情況下此時的localProvider應該是非空的,所以,自然要把它儲存到mLocalProviders和mLocalProvidersByName中;
  • 但是如果localProvider仍然為空,接下來會出現一個ProviderRefCount物件prc,用來表示和記錄ContentProvider元件的引用計數,如果prc為空,則把這個localProvider封裝成一個ProviderClientRecord物件,並儲存在prc變數中;
  • 最後,返回這個ContentProviderHolder物件!

接下來,繼續分析ContentProvider元件的成員函式getIContentProvider()和成員函式attachInfo()的實現細節!

step21: ContentProvider#getIContnetProvider()

ContentProvider類成員函式getIContentProvider()原始碼如下:

public abstract class ContentProvider implements ComponentCallbacks2 {
    ......
    private Transport mTransport = new Transport();
    ......
    class Transport extends ContentProviderNative {......}
    ......
    public IContentProvider getIContentProvider() {
        return mTransport;
    }
    ......
}
  • 每一個ContentProvider元件都有一個內部類Transport,其本質是一個Binder本地物件;並且持有一個型別為Transport的全域性變數mTransPort來表示一個Binder本地物件。
  • 通過將這個Binder本地物件傳遞給AMS,然後AMS會將引用了這個Binder本地物件的一個Binder代理物件返回給需要訪問該ContentProvider元件的其他應用程式程序;這樣,其他應用程式程序就可以通過這個Binder代理物件來間接的訪問一個ContentProvider元件中的資料了!
  • ContentProvider類成員函式getIContentProvider()的實現很簡單,只是簡單地將全域性變數mTransPort描述的一個IContentProvider介面返回給呼叫者。

step22: ContentProvider#attachInfo()

ContentProvider類的成員函式attachInfo(),作用是初始化前面所建立的一個ContentProvider元件!attachInfo()原始碼如下:

public abstract class ContentProvider implements ComponentCallbacks2 {
    ......
    /**
     * After being instantiated, this is called to tell the content provider
     * about itself.
     *
     * @param context The context this provider is running in
     * @param info Registered information about this content provider
     */
    public void attachInfo(Context context, ProviderInfo info) {
        attachInfo(context, info, false);
    }

    private void attachInfo(Context context, ProviderInfo info, boolean testing) {
        ......
        /*
         * Only allow it to be set once, so after the content service gives
         * this to us clients can't change it.
         */
        if (mContext == null) {
            mContext = context;
            if (context != null) {
                mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
                        Context.APP_OPS_SERVICE);
            }
            mMyUid = Process.myUid();
            if (info != null) {
                setReadPermission(info.readPermission);
                setWritePermission(info.writePermission);
                setPathPermissions(info.pathPermissions);
                ......
            }
            ContentProvider.this.onCreate();
        }
    }
    ......
}
  • 依然是兩個過載函式,接收兩個引數的attachInfo()函式內部呼叫了接收三個引數的attachInfo()函式,我們直接關注接收三個引數的attachInfo()函式;
  • setReadPermission(info.readPermission),setWritePermission(info.writePermission)和setPathPermissions(info.pathPermissions)這三個函式的作用是,設定ContentProvider元件的讀寫許可權和訪問許可權;
  • 最後ContentProvider.this.onCreate()函式,實際呼叫的是COntentProvider子類的onCreate()函式,以便在子類的onCreate()函式中,執行一些業務相關的初始化操作!
  • 在我們最開始的場景中,ContentProvider元件指的就是BxxApp應用程式程序中SubContentProvider元件,所以上面呼叫的時加上就是SubContentProvider類的onCreate()函式,而我們的業務邏輯相關的一些初始化工作,也正是放在SubContentProvider類的onCreate()函式中執行的!

step23: SubContentProvider#onCreate()

public class SubContentProvider extends ContentProvider {
    ......
    @Override
    public boolean onCreate() {
        ContentResolver resolver = getContext().getContentResolver();
        DatabaseHelper helper = new DtatabaseHelper(......);
        ......
        return true;
    }
    ......
}
  • 需要注意,由於一個ContentProvider元件再啟動過程中需要執行onCreate()函式,因此,我們應該避免在onCeate()方法中執行耗時操作,例如和IO相關的操作,否則可能造成這個ContentProvider元件啟動超時!

這一步執行完成後,就會返回到前面的step20,然後再次返回到前面的step19,即ActivityThread類成員函式installContentProviders()中,接下來就會呼叫AMS代理物件的成員函式publishContentProviders(),也即ActivityManagerProxy的成員函式publishContentProviders(),將前面所有啟動的ContentProvider元件的一個IContentProvider訪問介面釋出到ActivityManagerService中~

step24: ActivityManagerProxy#publishContentProviders()

ActivityManagerProxy類成員函式publishContentProviders(),會將所有啟動的ContentProvider元件的一個IContentProvider訪問介面釋出到ActivityManagerService中,原始碼如下:

public void publishContentProviders(IApplicationThread caller,
        List<ContentProviderHolder> providers) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    data.writeTypedList(providers);
    mRemote.transact(PUBLISH_CONTENT_PROVIDERS_TRANSACTION, data, reply, 0);
    reply.readException();
    data.recycle();
    reply.recycle();
}
  • 先把接收的引數儲存到Parcel物件data中;
  • 接著再通過ActivityManagerProxy類內部的一個Binder代理物件mRemote向ActivityMnagerService傳送一個型別為PUBLISH_CONTENT_PROVIDERS_TRANSACTION程序間通訊請求;

step25: ActivityManagerService#publishContentProviders()

以上10步都是在新建的應用程式程序中執行的!step25是在ActivityManagerService中執行的,AMS類成員函式publishContentProviders(),用來處理型別為PUBLISH_CONTENT_PROVIDERS_TRANSACTION程序間通訊請求,其原始碼如下:

AMS#publishContentProviders()

  • 引數caller是一個型別為ApplicationThread的Binder代理物件,它引用了執行在新建應用程式程序中的一個ApplicationThread物件,getRecordForAppLocked(caller)方法通過caller來獲取一個用來描述新建應用程式程序的ProcessRecord物件r;
  • 新建應用程式程序在啟動時,會將需要在它裡面執行的ContentProvider元件啟動起來!從前面的step13可知,在AMS中,這些ContentProvider元件使用一個ContentProviderRecord物件來描述,它們儲存在用來描述新建應用程式程序的一個ProcessRecord物件r的一個成員變數pubProviders中;
  • 第10到14行,第一個for迴圈,取出providers中的每一個ContentProvider元件,並且拿到ContentProvider元件對應的ContentProviderRecord物件dst;
  • 第15到22行,第二個for迴圈,通過兩種方式,把ContentProviderRecord物件dst儲存到全域性變數mProviderMap;
  • 引數providers包含了要釋出到AMS中的ContentProvider元件,每一個ContentProvider元件都使用一個ContentProviderHolder物件來描述,它裡面包含了要釋出的ContentProvider元件的一個IContentProvider介面,如圖第34行到40行所示!

從前面的step8可知,一個應用程式程序請求ActivityManagerService返回一個ContentProvider元件的代理物件時,如果這個ContentProvider元件還未啟動起來,那麼AMS就會先建立一個新的應用程式程序來啟動該ContentProvider元件,然後再在一個while迴圈中等待該ContentProvider元件啟動完成,並且將他的一個代理物件釋出到AMS中。

現在既然這個ContentProvider已經啟動完成,並且將它的一個代理物件,即一個型別為Transport的Binder代理物件釋出到AMS,因此,前面正在等待的一個AMS執行緒就可以停止等待,並且將這個型別為Transport的Binder代理物件封裝成一個ContentProvider物件返回給請求它的應用程式程序。

這一步執行完畢,就會使得AMS從前面的step8返回step5,即返回到MainActivity元件所執行的應用程式程序中,即AxxApp應用程式程序!然後,繼續執行ActivityThread類成員函式installProvider(),用來儲存前面從ActivityManagerService獲得的一個ContentProvider元件的一個IContentProvider訪問介面。

step26: ActivityThread#installProvider()

ActivityThread類成員函式installProvider()原始碼如下:

/**
 * Installs the provider.
 *
 * Providers that are local to the process or that come from the system server
 * may be installed permanently which is indicated by setting noReleaseNeeded to true.
 * Other remote providers are reference counted.  The initial reference count
 * for all reference counted providers is one.  Providers that are not reference
 * counted do not have a reference count (at all).
 *
 * This method detects when a provider has already been installed.  When this happens,
 * it increments the reference count of the existing provider (if appropriate)
 * and returns the existing provider.  This can happen due to concurrent
 * attempts to acquire the same provider.
 */
private IActivityManager.ContentProviderHolder installProvider(Context context,
        IActivityManager.ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ContentProvider localProvider = null;
    IContentProvider provider;
    if (holder == null || holder.provider == null) {
        Context c = null;
        ApplicationInfo ai = info.applicationInfo;
        if (context.getPackageName().equals(ai.packageName)) {
            c = context;
        } else if (mInitialApplication != null &&
                   mInitialApplication.getPackageName().equals(ai.packageName)) {
            c = mInitialApplication;
        } else {
            try {
                c = context.createPackageContext(ai.packageName,
                                                 Context.CONTEXT_INCLUDE_CODE);
            } catch (PackageManager.NameNotFoundException e) {
                // Ignore
            }
        }
        if (c == null) {
            return null;
        }
        try {
            final java.lang.ClassLoader cl = c.getClassLoader();
            localProvider = (ContentProvider)cl.
                            loadClass(info.name).newInstance();
            provider = localProvider.getIContentProvider();
            if (provider == null) {
                return null;
            }
            // XXX Need to create the correct context for this provider.
            localProvider.attachInfo(c, info);
        } catch (java.lang.Exception e) {......}
    } else {
        provider = holder.provider;
    }

    IActivityManager.ContentProviderHolder retHolder;

    synchronized (mProviderMap) {
        IBinder jBinder = provider.asBinder();
        if (localProvider != null) {
            ComponentName cname = new ComponentName(info.packageName, info.name);
            ProviderClientRecord pr = mLocalProvidersByName.get(cname);
            if (pr != null) {
                provider = pr.mProvider;
            } else {
                holder = new IActivityManager.ContentProviderHolder(info);
                holder.provider = provider;
                holder.noReleaseNeeded = true;
                pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                mLocalProviders.put(jBinder, pr);
                mLocalProvidersByName.put(cname, pr);
            }
            retHolder = pr.mHolder;
        } else {
            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
            if (prc != null) {
                // We need to transfer our new reference to the existing
                // ref count, releasing the old one...  but only if
                // release is needed (that is, it is not running in the
                // system process).
                if (!noReleaseNeeded) {
                    incProviderRefLocked(prc, stable);
                    try {
                        ActivityManagerNative.getDefault().removeContentProvider(
                            holder.connection, stable);
                    } catch (RemoteException e) {
                        //do nothing content provider object is dead any way
                    }
                }
            } else {
                ProviderClientRecord client = installProviderAuthoritiesLocked(
                                                provider, localProvider, holder);
                if (noReleaseNeeded) {
                    prc = new ProviderRefCount(holder, client, 1000, 1000);
                } else {
                    prc = stable
                          ? new ProviderRefCount(holder, client, 1, 0)
                          : new ProviderRefCount(holder, client, 0, 1);
                }
                mProviderRefCountMap.put(jBinder, prc);
            }
            retHolder = prc.holder;
        }
    }
    return retHolder;
}
  • 這步與前面的step20,都是在ActivityThread類成員函式installProvider()。不過,前面step20是在啟動ContentProvider元件的應用程式程序中執行的;而這步是在AxxApp應用程式的MainActivity中執行的!另一個區別是:這步得到引數provider不等於null,它用來描述一個在其他應用程式程序中啟動的ContentProvider元件的一個IContentProvider訪問介面。

  • 這步執行完成後,就返回到前面的step1,這時在AxxApp應用的MainActivity中,就獲得了與URI值對應的SubContentProvider元件的一個IContentProvider訪問介面,最後就可以通過這個介面訪問另一個應用程式的資料內容了!

## 0x03 參考文獻與簡單的結語

至此,ContentProvider啟動流程分析到此結束!

相關推薦

ContentProvider啟動流程分析

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

ContentProvider啟動流程分析

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

ContentProvider啟動流程分析

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

Android OTA升級原理和流程分析---Android系統的啟動模式

        以下的篇幅開始分析我們在上兩個篇幅中生成的update.zip包在具體更新中所經過的過程,並根據原始碼分析每一部分的工作原理。 一、       系統更新update.zip包的兩種方式 1.  通過上一個文件,我們知道了怎樣製作一個updat

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

nu-lb-nuc140 RTX 流程 分析

nu-lb-nuc140 RTX 流程 分析(三) 開始解析如下的部分: __asm void SVC_Handler (void) 當前的暫存器: 讀取os_tsk typedef struct OS_TSK { P_TCB run;

springboot啟動流程分析

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

springboot啟動流程分析

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

STM32 eCos 啟動程式碼分析系統時鐘滴答

時鐘滴答好比人的心臟一樣,是作業系統必不可少的一個部件,線上程的切換和軟體延時等系統時間相關功能中起著無法替代的角色。 作業系統中的時鐘滴答,需要一個週期性的可配置的訊號源來實現,並且一般都是以中斷的方式在後臺通知系統下一個滴答的到來。 eCos中為了提供移植性,一般會用

Spring Boot 啟動過程分析

private void refreshContext(ConfigurableApplicationContext context) { // 由於這裡需要呼叫父類一系列的refresh操作,涉及到了很多核心操作,因此耗時會比較長,本文不做具體展開 refresh(context);

electron-vue架構解析3-開發環境啟動流程分析

上一節我們介紹了生產環境的打包流程,這一節我們來看開發環境的啟動流程。 該框架主要修改是對開發環境的優化,包括了於開發環境的配置檔案隔離,主程序和渲染程序配置檔案隔離,編譯過程提示等功能,因此這一節內容才是整個框架的核心。 我們從開發人員用到的啟動命令

SpringBoot啟動流程分析:SpringApplication類初始化過程

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

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

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

SpringBoot啟動流程分析:IoC容器的初始化過程

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

SpringBoot啟動流程分析:SpringBoot自動裝配原理實現

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

SpringBoot啟動流程分析:IoC容器依賴注入

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

Android 8.0 系統啟動流程之Linux核心啟動--kernel_init程序

    在上一篇文章中詳細的分析了kthreadd程序的啟動,init程序也是有idle程序去觸發啟動的,init程序分為前後兩部分,前一部分是在核心啟動的,主要是完成建立和核心初始化工作,內容都是跟Linux核心相關的;後一部分是在使用者空間啟動的,主要完成A

Activity啟動流程分析基於android 5.1

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

Netty原始碼分析 ----- 服務端啟動原始碼分析

本文接著前兩篇文章來講,主要講服務端類剩下的部分,我們還是來先看看服務端的程式碼 /** * Created by chenhao on 2019/9/4. */ public final class SimpleServer { public static void main(