Android Learning Road (14) コンテキスト詳細説明

1. はじめに

Android 開発やインタビューでは、4 つの主要なコンポーネントは切り離せません。これらのコンポーネントを作成または開始するとき、新しいキーワードに続いてクラス名を使用してインスタンス オブジェクトを直接作成することはできません。代わりに、独自のコンテキスト環境が必要です。コンテキスト環境はコンテキストです。この記事で説明します。

1.1 コンテキストの概要

コンテキストとは、文字通り: コンテキスト、環境、コンテキストを意味します。Android システムでは、アプリケーション内の現在のオブジェクトの作業環境として理解できます。アプリケーション環境のグローバル情報にアクセスするための多くのインターフェイスが内部的に定義されており、これを通じて、Resources、AssetManager、Package、権限関連情報などのアプリケーションのリソースに関連するクラスにアクセスできます。また、アクティビティやサービスの開始、ブロードキャストの送信など、アプリケーション レベルの操作を呼び出すために使用することもできます。

Context クラスに関する公式のコメントを見てみましょう。

/**
 * Interface to global information about an application environment.  This is
 * an abstract class whose implementation is provided by
 * the Android system.  It
 * allows access to application-specific resources and classes, as well as
 * up-calls for application-level operations such as launching activities,
 * broadcasting and receiving intents, etc.
 */
public abstract class Context {...}

翻訳: コンテキストは、アプリケーション環境に関するグローバル情報のインターフェイスを提供します。これは抽象クラスであり、その実装は Android システムによって提供されます。これにより、アプリケーションを特徴付けるリソースとタイプへのアクセスが可能になり、一部のリソース (アプリケーション環境変数など) を制御するコンテキストになります。

1.2 コンテキストアーキテクチャ

  • Context: システムと対話するための一連のインターフェイスを定義する抽象クラスです。
  • ContextWrapper: Context 抽象クラスから継承され、Context クラスのラッパー クラス (デコレータ モード) です。ContextImpl オブジェクトを指す Context 型のメンバ変数 mBase を内部的に保持します。ContextWrapper のメソッド呼び出しは、ContextImpl のメソッドを呼び出します。ここではプロキシ モードが使用されます。
  • ContextImpl: Context 抽象クラスから継承し、Context クラスの抽象メソッドを実装します。Context クラスの固有の実装クラスです。これは、アクティビティおよび他のアプリケーション コンポーネントにコンテキスト環境を提供し、アプリケーションで使用される Context メソッドはそれによって実装されます。
  • ContextThemeWrapper: ContextWrapper クラスから継承され、ContextWrapper に基づいてテーマに関連するロジックを追加します。つまり、ビューの構築時にビューに設定された Theme 属性を提供するために使用される Theme の Context ラッパー クラスを指定できます。

ContextImpl 実装クラスに関与する主なコア クラスは、ActivityThread、LoadedApk、PackageManager、および ResourcesManager です。これらのクラスはすべてシングルトンであり、アプリケーション プロセスで同じオブジェクトを共有します。
Contextlmpl は軽量クラスですが、LoadedApk は重量クラスです。Contextlmpl のパッケージ操作のための重量級関数のほとんどは、実際には LoadedApk オブジェクトの対応するメソッドに依存します。

アクティビティは ContextThemeWrapper から継承し、アプリケーションとサービスは ContextWrapper から継承します。これらは ContextWrapper クラスから直接的または間接的に継承するため、ContextImpl オブジェクトを指す Context 型のメンバー変数 mBase も持ちます。ContextImpl は、ContextImpl の特定の実装クラスです。 Context クラスなので、それらはすべて Context によって提供されるすべての機能を持ちます。

プロキシ モード: このオブジェクトへのアクセスを制御するために他のオブジェクトにプロキシを提供することを指す構造モードに属し、静的プロキシと動的プロキシに分けられます。
デコレータ パターン: パッケージング パターンとも呼ばれ、構造パターンでもあり、既存のオブジェクトの構造を変更せずに、オブジェクトに何らかの責任を動的に追加する (つまり、機能を追加する) パターンを指します。

1.3 コンテキストのスコープ

注: レイアウト表示インターフェイスをロードする必要がある場合は、できるだけ「コンテキスト」フィールドとして「アクティビティ」を使用してください。「アプリケーション」と「サービス」を「コンテキスト」フィールドとして使用して、レイアウトをロードしてアクティビティを開始することはできますが、エラーを避けることはお勧めできません。または、UI が不可解にもシステムのデフォルトのテーマを使用しています。

1.4 概要

このセクションの分析を通じて、Context の概念、アーキテクチャ、範囲を簡単に理解できたので、詳しく見てみましょう。

2. コンテキストの詳細な説明

前に Context のアーキテクチャについて話したとき、その最終実装クラスは Application、Service、および Activity であることがわかりました。これらはすべて、Context 抽象クラス ContextImpl の実際の実装を保持します。次に、これら 3 つの実装クラスについてそれぞれ説明し、分析します。 。

2.1 アプリケーションのコンテキスト

アプリケーションは Android システム フレームワークのシステム コンポーネントです。Android アプリケーションが開始されると、システムはシステム情報を保存するための Application クラス オブジェクトを 1 つだけ作成します。つまり、Application はシングルトンです。

通常、開発時にApplicationを指定する必要はなく、開発者はシステムが自動的に作成します アプリケーションにカスタマイズしたApplicationを作成したい場合は、Applicationを継承したクラスを作成し、applicationタグに登録するだけで済みますAndroidManifest.xml ファイル内 (名前属性を application タグに追加し、カスタム アプリケーション名を追加するだけです)。

通常、アプリケーションをカスタマイズする目的は、アプリケーションの起動時にグローバルな初期化作業を行うことです。アプリケーションが起動すると、アプリケーションが作成され、同期的に起動されます。システムはプロセス ID である PID を作成し、すべてのアクティビティが実行時に、これらの初期化されたグローバル変数の値を取得できますが、アプリケーション オブジェクトはアプリケーション全体の実行中に常に存在するため、一部の開発者は、それらをグローバルに取得して使用するために、アプリケーション内にいくつかのツール メソッドを記述します。このように、Application はツール クラスとして使用されます。注: これは、アプリケーション設計に関する Google の原則に重大な違反であり、設計パターンにおける単一責任の原則にも違反します。

2.1.1 カスタム アプリケーション インスタンス

open class TestApplication : Application() {
    // 全局 context
    companion object{
        lateinit var context: Context
    }
    override fun onCreate() {
        super.onCreate()
        context = this
        initSDKs() // 全局初始化
    }

    private fun initSDKs() {...}
}

アプリケーションを継承し、アプリケーションの作成時に呼び出される onCreate() メソッドをオーバーライドします。これは通常、サードパーティ SDK の初期化、環境構成などのグローバル初期化に使用されます。 Application タイプのオブジェクトは、TestApplication # context を通じて取得できます。

2.1.2 アプリケーションインスタンスの取得

class TestActivity : Activity() {
    private val TAG: String = TestActivity::class.java.simpleName
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test)

        val applicationContext = [email protected]()
        val application = [email protected]()

        Log.e(TAG, "application: $application")
        Log.e(TAG, "applicationContext: $applicationContext")
    }
}

アプリケーションを取得するには、通常、次の 2 つの方法があります。

  • アクティビティ # getApplication() またはサービス # getApplication()
  • コンテキスト # getApplicationContext()

アプリケーションは getApplication() と getApplicationContext() の両方で取得できますが、両者の違いは何でしょうか?

ログ出力から、同じオブジェクトを取得していることがわかりますが、なぜ同じ機能を持つ 2 つのメソッドを提供する必要があるのか​​と尋ねる学生もいます。getApplication() メソッドはより直感的ですが、アクティビティ シナリオとサービス シナリオでのみ呼び出すことができるためです。getApplicationContext() メソッドの適用範囲は広く、どのシナリオでも Context オブジェクトを通じてこのメソッドを呼び出すことができます。

2.1.3 アプリケーションコンテキストの作成プロセス

アプリケーションのコンテキストは、アプリケーションの作成時に作成されます。その作成を追跡するには、アプリケーションの起動プロセス、つまり、デスクトップ アプリケーション アイコンをクリックしてから最初のプロセスの特定のステップまで、コンテキストを探索する必要があります。アプリケーションのインターフェイスが表示されます。作成の具体的な手順については、この記事の詳細な分析 ( Android R (11.0) アクティビティの起動プロセスの詳細な説明)を参照してください。

プロセスを簡単に説明しましょう。

  • アプリケーションの初期化クラスとなるActivityThreadクラスは、そのエントリメソッドmain()メソッド #attach()メソッドでActivityThreadを呼び出し、Binder通信によりプロセスをまたいでsystem_serverプロセス内でAMSのattachApplication()メソッドを呼び出し、ApplicationThreadを渡します。パラメータとして。
  • 渡された ApplicationThread を通じて、クロスプロセス通信はアプリケーション プロセスで ApplicationThread #bindApplication() メソッドを呼び出し、アプリケーションをバインドします。
  • ApplicationThread # bindingApplication() メソッドを呼び出して、AppBindData オブジェクトを構築し、内部クラス H を通じて BIND_APPLICATION タイプの Handler メッセージを送信してから、ActivityThread # handleBindApplication() メソッドを呼び出してアプリケーションを作成してバインドします。

2.1.4 タイミング図

面接の質問: ActivityThread はスレッドですか?
ActivityThread クラスはアプリケーションの初期化クラスです。その main() メソッドはアプリケーションのエントリ メソッドです。これは「メイン スレッド」とも呼ばれます。ただし、ActivityThread 自体はスレッドではありません。 「メインスレッド」はメインスレッドで実行されるためです。つまり、ActivityThread はメイン スレッドの一部ですが、メイン スレッドを表すものではありません。その機能は次のとおりです。

  • ActivityThread は、Application オブジェクトの作成とそのライフサイクル メソッド呼び出しの管理を担当します。
  • ActivityThread は、4 つの主要コンポーネントのライフサイクル メソッド呼び出しを管理します。

2.1.5 ソースコード解析

プロセスの説明とシーケンス図から、アプリケーションのコンテキストが ActivityThread # handleBindApplication() メソッドで作成されていることがわかります。詳細な分析のためにソース コードを追跡および表示します。

2.1.5.1 アクティビティスレッド # handleBindApplication()

ActivityThread.class (api 30)
public final class ActivityThread extends ClientTransactionHandler
        implements ActivityThreadInternal {
    ......
 	@UnsupportedAppUsage
    private void handleBindApplication(AppBindData data) {
		......
        final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
        updateLocaleListFromAppContext(appContext,
                mResourcesManager.getConfiguration().getLocales());
        ......
        if (ii != null) {
            ......
            final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
                    appContext.getClassLoader(), false, true, false);
            final ContextImpl instrContext = ContextImpl.createAppContext(this, pi,
                    appContext.getOpPackageName());
            try {
            	// 获取 ClassLoader 加载类文件
                final ClassLoader cl = instrContext.getClassLoader();
                // 获取 Instrumentation 类并构建实例对象
                mInstrumentation = (Instrumentation)
                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
            }
			......
            final ComponentName component = new ComponentName(ii.packageName, ii.name);
            mInstrumentation.init(this, instrContext, appContext, component,
                    data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
			......
        } 
        ......
        Application app;
		......
        try {
        	// 创建 Application
            app = data.info.makeApplication(data.restrictedBackupMode, null);
			......
            mInitialApplication = app;
			......
            try {
                mInstrumentation.onCreate(data.instrumentationArgs);
            }
            ......
            try {
           		// 内部调用 Application # onCreate() 的方法
                // 故 Application # onCreate() 比 ActivityThread 的 main() 方法慢执行
                // 但是会比所有该应用 Activity 的生命周期先调用,因为此时的 Activity 还没启动
                mInstrumentation.callApplicationOnCreate(app);
            }
            ......
        }
    }
    ......
}

ActivityThread # handleBindApplication() メソッドのパラメータ AppBindData は、AMS によってアプリケーションに渡される起動情報です。これには、LoadedApk、ApplicationInfo などが含まれ、LoadedApk インスタンス オブジェクトを通じて ContextImpl および Application インスタンス オブジェクトが作成されます。

2.1.5.2 LoadedApk # makeApplication()

LoadedApk.java  (api 30)
public final class LoadedApk {
	......
   @UnsupportedAppUsage
    public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        if (mApplication != null) {
       		// 如果 mApplication 已经存在则直接返回
            return mApplication;
        }
		......
        Application app = null;
        // 获取 AndroidMenifest 中 application 标签指定的 Application 类
        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }

        try {
        	// 获取 ClassLoader
            final java.lang.ClassLoader cl = getClassLoader();
            ......
            // 创建 ContextImpl 实例
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            ......
            // 利用类加载器 ClassLoader 创建 AndroidMenifest 指定的 Application 类
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
			// 将 Application 实例赋值给 ContextImpl,以便 ContextImpl 可以访问 Application
            appContext.setOuterContext(app);
        }
        ......
        mActivityThread.mAllApplications.add(app);
        // app 赋值给 mApplication,当我们调用 Context.getApplicationContext() 就是获取这个对象
        mApplication = app;
        if (instrumentation != null) {
            try {
            	// 由于 instrumentation 此时为空所以不会回调 Application 的 onCreate 方法
                instrumentation.callApplicationOnCreate(app);
            }
            ......
        }
		......
        return app;
    }
    ......
}

実行プロセスは次のとおりです。

  • まず、LoadedApk オブジェクト内の mApplication が存在するかどうかを確認し、既に存在する場合は、それを直接返します。存在しない場合は、まず AndroidMenifest の application タグの name 属性で指定された Application クラス名を取得します。次に、ClassLoader を取得し、ContextImpl インスタンスを作成します。
  • クラスローダー ClassLoader を通じて Application クラスのインスタンスを作成し、その Application インスタンスを ContextImpl のメンバー変数 mOuterContext に割り当てて、ContextImpl が mOuterContext を通じて Application インスタンスにアクセスできるようにします。同時に、Application インスタンスを mApplication に割り当て、Context # getApplicationContext() メソッドを呼び出してインスタンス オブジェクトを取得します。

2.1.5.3 ContextImplの作成

ContextImpl.java (api 30)
class ContextImpl extends Context {
	......
    @UnsupportedAppUsage
    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
        return createAppContext(mainThread, packageInfo, null);
    }

    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo,
            String opPackageName) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, null,
                0, null, opPackageName);
        context.setResources(packageInfo.getResources());
        context.mIsSystemOrSystemUiContext = isSystemOrSystemUI(context);
        return context;
    }
    ......
}

ContextImpl インスタンス オブジェクトを作成し、システム リソース (ActivityThread、LoadedApk など) へのアクセスに関連する「権限」オブジェクトを ContextImpl に割り当てます。

2.1.5.4 アプリケーションの作成

戻って、LoadedApk # makeApplication() の Application クラス インスタンスの作成を見てみましょう。コードは次のとおりです。

Instrumentation.java (api 30)
public class Instrumentation {
	......
    public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
		// 获取 LoadedApk 的 AppComponentFactory,然后通过 ClassLoader 
		// 加载 Application 类,并创建类的实例对象
        Application app = getFactory(context.getPackageName())
                .instantiateApplication(cl, className);
		// 将 ContextImpl 实例绑定到 Application 实例对象的 mBase 成员变量
        app.attach(context);
        return app;
    }

    private AppComponentFactory getFactory(String pkg) {
        ......
        LoadedApk apk = mThread.peekPackageInfo(pkg, true);
        // This is in the case of starting up "android".
        if (apk == null) apk = mThread.getSystemContext().mPackageInfo;
        return apk.getAppFactory();
    }
    ......
}

AppComponentFactory.java (api 30)
public class AppComponentFactory {
	......
    public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
            @NonNull String className)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
		// 通过 ClassLoader 加载 Application 类,并创建类的实例对象
        return (Application) cl.loadClass(className).newInstance();
    }
    ......
}

LoadedApkのAppComponentFactoryを取得し、ClassLoaderでApplicationクラスを読み込み、そのクラスのインスタンスオブジェクトを作成します。次に、作成した Application インスタンス オブジェクトの mBase メンバー変数に ContextImpl インスタンスを割り当てます。

2.1.5.5 アプリケーションバインディング ContextImpl

Application.java (api 30)
public class Application extends ContextWrapper implements ComponentCallbacks2 {
	......
    @UnsupportedAppUsage
    /* package */ final void attach(Context context) {
    	// 这里 context 为 ContextImpl 实例对象
    	// 调用 ContextWrapper # attachBaseContext() 方法
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }
    ......
}

ContextWrapper.java (api 30)
public class ContextWrapper extends Context {
	......
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        // 将 ContextImpl 实例对象赋值给成员变量 mBase
        mBase = base;
    }
    ......
}

Application のattach() メソッドで、ContextWrapper のattachBaseContext() メソッドを呼び出し、ContextImpl インスタンス オブジェクトをそのメンバー変数 mBase に割り当てます。

2.2 サービスコンテキスト

サービスは、Android システム フレームワークの 4 つの主要コンポーネントの 1 つで、ユーザー インターフェイスなしでバックグラウンドで長時間実行される操作を実行できるアプリケーション コンポーネントです。他のアプリケーションコンポーネント(Activityなど)からも起動できます。サービスは一度起動すると常にバックグラウンドで動作します。サービスを起動しているコンポーネント(Activity)が破壊されても影響を受けません。 Context インスタンスの起源を分析します。

2.2.1 サービスコンテキストの作成手順

サービスのコンテキストは、サービスが他のアプリケーション コンポーネントによって開始されるときに作成されます (アクティビティでの開始など)。サービスの開始プロセスについてはここでは詳細に分析しませんし、この記事の焦点でもありません。詳細については、この記事の詳細な分析 - Android R (11.0) サービスの起動プロセスの詳細な説明 を参照してください。

プロセスを簡単に説明しましょう。

  • 通常、Service を開始するには Context # startService() メソッドが呼び出されますが、上記の分析からわかるように、ここではその実装クラスの ContextImpl # startService() メソッドが呼び出され、次に AMS の startService() メソッドが呼び出されます。 system_server プロセスは、Binder 通信を通じてプロセス間で呼び出されます。システム プロセスで一連の呼び出しが行われた後、プロセスは ApplicationThread のscheduleCreateService() メソッドに進み、AMS プロセスで作成された ServiceInfo が CreateServiceData オブジェクトにカプセル化されます。
  • ApplicationThread #scheduleCreateService() メソッドでは、CreateServiceData インスタンス オブジェクトが内部クラス H を介して CREATE_SERVICE タイプの Handler メッセージを送信し、ActivityThread # handleCreateService() メソッドを呼び出してサービスを作成し、サービスのコンテキストを作成します。

2.2.2 タイミング図

2.2.3 ソースコード解析

プロセスの説明とシーケンス図から、サービスのコンテキストが ActivityThread # handleCreateService() メソッドで作成されていることがわかります。詳細な分析のためにソース コードを追跡して表示します。

2.2.3.1 アクティビティスレッド # handleCreateService()

ActivityThread.class (api 30)
public final class ActivityThread extends ClientTransactionHandler
        implements ActivityThreadInternal {
    ......
    @UnsupportedAppUsage
    private void handleCreateService(CreateServiceData data) {
		......
		// 获取 LoadedApk 实例对象,用于获取类加载器 ClassLoader 等
        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
  			......
  			// 创建 ContextImpl 实例,即 Service 的上下文环境 Context[参见 2.1.5.3 节]
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            // 创建 Application 实例[参见 2.1.5.2 节]
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            // 获取 ClassLoader 实例对象
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            // 加载 Service 类并创建实例对象
            service = packageInfo.getAppFactory()
                    .instantiateService(cl, data.info.name, data.intent);
			......
            context.getResources().addLoaders(
                    app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
			// 将 Service 实例赋值给 ContextImpl,以便 ContextImpl 可以访问 Service
            context.setOuterContext(service);
            // 将 ContextImpl 实例绑定到 Service 实例对象的 mBase 成员变量
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
			// 回调 Service # onCreate() 方法
            service.onCreate();
            // 将 service 对象加入到 mService 集合中,key 值为 data.token
            mServices.put(data.token, service);
            try {
            	// 跨进程调用 AMS # serviceDoneExecuting() 方法通知 AMS,Service 已经启动完毕
                ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } 
        ......
    }
    ......
}

実行プロセスは次のとおりです。

  • クラスローダClassLoaderなどの取得に使用されるLoadedApkインスタンスオブジェクトを取得し、Serviceのコンテキスト環境であるContextImplインスタンスオブジェクトを作成し、ContextImplにシステムリソースへのアクセスに関する「権限」オブジェクトを付与します。 - ActivityThread、LoadedApk など
  • ClassLoader インスタンス オブジェクトを取得し、それを使用して Service クラスをロードし、インスタンス オブジェクトを作成します。作成した Service インスタンスを ContextImpl のメンバー変数 mOuterContext に代入し、ContextImpl が mOuterContext 経由で Service インスタンスにアクセスできるようにします。
  • Service #attach() メソッドを呼び出して ContextImpl インスタンスを Service インスタンス オブジェクトの mBase メンバー変数にバインドし、次に Service #onCreate() メソッドをコールバックし、最後に AMS #プロセス全体にわたる serviceDoneExecuting() メソッド。

2.2.3.2 サービスバインディング ContextImpl

Service.java (api 30)
public abstract class Service extends ContextWrapper implements ComponentCallbacks2,
        ContentCaptureManager.ContentCaptureClient {
	......
    @UnsupportedAppUsage
    public final void attach(
            Context context,
            ActivityThread thread, String className, IBinder token,
            Application application, Object activityManager) {
		// 继续调用 Service # attachBaseContext() 方法
        attachBaseContext(context);
        ......
        setContentCaptureOptions(application.getContentCaptureOptions());
    }
    
    @Override
    protected void attachBaseContext(Context newBase) {
    	// 调用父类 ContextWrapper # attachBaseContext() 方法
        super.attachBaseContext(newBase);
        if (newBase != null) {
            newBase.setContentCaptureOptions(getContentCaptureOptions());
        }
    }
    ......
}

ContextWrapper.java (api 30)
public class ContextWrapper extends Context {
	......
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        // 将 ContextImpl 实例对象赋值给成员变量 mBase
        mBase = base;
    }
    ......
}

サービスのattach() メソッドでサービス #attachBaseContext() メソッドの呼び出しを続けてから、親クラスの ContextWrapper #attachBaseContext() メソッドの呼び出しを続けて、ContextImpl インスタンス オブジェクトをメンバー変数 mBase に割り当てます。

2.3 アクティビティのコンテキスト

アクティビティは、Android システム フレームワークの 4 つの主要コンポーネントの中で最も頻繁に使用されるコンポーネントであり、ユーザーにコンテンツを表示したり、ユーザーと直接対話したりするために使用されるインターフェイスです。Activity の Context は、新しい Activity の開始、Service の開始、Context を介したブロードキャスト受信機の登録など、日常の開発でよく使用されます。Activity の Context インスタンスの起源を見てみましょう。

2.3.1 アクティビティコンテキストの作成プロセス

Activity のコンテキストは、Activity コンポーネントが開始されるときに作成されます。ここでは、Activity の起動プロセスを詳細に分析しませんし、この記事の焦点でもありません。興味のある学生は、この記事の詳細な分析を参照してください - 詳細Android R( 11.0)Activity起動処理の説明

プロセスを簡単に説明しましょう。

  • 通常、Activity # startActivity() メソッドが呼び出されてアクティビティが開始され、次に system_server プロセス内の ATMS の startActivity() メソッドが Binder 通信を通じてプロセス間で呼び出され、システム プロセス内で一連の呼び出しが行われた後、プロセスは に進みます。 ApplicationThread のscheduleTransaction() メソッド。
  • ApplicationThread #scheduleTransaction() メソッドは、ライフサイクル ステータスに従ってアクティビティを開始するトランザクション LaunchActivityItem をスケジュールします。LaunchActivityItem #execute() メソッドでは、ActivityThread #handleLaunchActivity() メソッドが呼び出され、アクティビティの作成と開始が行われます。アクティビティのコンテキストを作成します。

Android P (9.0) は、アクティビティの起動とライフ サイクル関連のロジックを開始します。これは複数のトランザクション トランザクション (LaunchActivityItem、ResumeActivityItem など) に分離され、トランザクションの実行は ClientLifecycleManager を通じてスケジュールされます。

2.3.2 タイミング図

2.3.3 ソースコード解析

プロセスの概要とシーケンス図から、アクティビティのコンテキストが ActivityThread # handleLaunchActivity() メソッドで作成されていることがわかります。詳細な分析のためにソース コードを追跡して表示します。

2.3.3.1 アクティビティスレッド # handleLaunchActivity()

ActivityThread.java (api 30)
public final class ActivityThread extends ClientTransactionHandler {
	......
    @Override
    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
        ......
        final Activity a = performLaunchActivity(r, customIntent);
        ......
        return a;
    }
    ......
}

引き続き、ActivityThread # PerformLaunchActivity() を呼び出してアクティビティの起動を実行し、起動プロセスの追跡を続けます。

2.3.3.2 アクティビティスレッド # PerformLaunchActivity()

ActivityThread.java (api 30)
public final class ActivityThread extends ClientTransactionHandler {
	......
     /**  Core implementation of activity launch. */
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
        	// 获取 LoadedApk 实例对象,用于获取类加载器 ClassLoader 等
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }
    	......
		// 创建 ContextImpl 实例,即 Activity 的上下文环境 Context
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
        	// 通过类加载器 ClassLoader 加载并新建 Activity 的实例
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            ......
        } catch (Exception e) {
            ......
        }
        try {
        	// 创建 Application,注意 r.packageInfo 是前面获取的 LoadedApk 实例对象
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
			......
            if (activity != null) {
                ......
                appContext.getResources().addLoaders(
                        app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
                // 将 Activity 实例赋值给 ContextImpl,以便 ContextImpl 可以访问 Activity
                appContext.setOuterContext(activity);
                // 执行 Activity 的 attach、初始化 Window 等并把 ContextImpl 实例设置给 Activity
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback,
                        r.assistToken);
                ......
                activity.mCalled = false;
                // 执行 Activity 的 onCreate
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                ......
            }
            // 设置生命周期的状态为 ON_CREATE
            r.setState(ON_CREATE);
            synchronized (mResourcesManager) {
            	// 将包含 Activity 信息集的 r 对象,也就是 ActivityClientRecord
            	// 加入到 mActivities 中,r.token 为 key 值
                mActivities.put(r.token, r);
            }
        }
		......
        return activity;
    }
    ......
}

実行プロセスは次のとおりです。

  • クラスローダー ClassLoader などを取得するために使用される LoadedApk インスタンス オブジェクトを取得し、引き続き ActivityThread # createBaseContextForActivity() メソッドを呼び出します。このメソッドは ContextImpl # createActivityContext() メソッドを呼び出して、ContextImpl インスタンス オブジェクトを作成します。 context 環境 アクティビティのコンテキスト。
  • Instrumentation # newActivity() メソッドを呼び出して新しいアクティビティ インスタンス オブジェクトをロードして作成します。このオブジェクトは AppComponentFactory # instantiateActivity() メソッドを呼び出します。次に、ActivityThread で取得したクラス ローダー ClassLoader を通じて新しいアクティビティ インスタンス オブジェクトをロードして作成します。 ) 方法。
  • LoadApk # makeApplication() メソッドを通じて Application オブジェクトを作成します。このプロセスは、クラス ローダー ClassLoader を使用して、新しいアクティビティをロードして作成するのと似ています。
  • Activity #attach() メソッドを実行すると、ContextImpl インスタンスがアクティビティに設定されます。さらに、このメソッドは、Window インスタンスの作成も完了し、Window との関連付けを確立します。これにより、Window が外部入力を受け取ったときに、イベントをアクティビティに渡すことができます。
  • Instrumentation # callActivityOnCreate() メソッドを実行すると、Activity # PerformCreate() メソッドが呼び出され、Activity # PerformCreate() メソッドが Activity # onCreate() メソッドを呼び出します。

2.3.3.3 アクティビティスレッド # createBaseContextForActivity()

ActivityThread.java (api 30)
public final class ActivityThread extends ClientTransactionHandler {
	......
	private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
        ......
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
		......
        return appContext;
    }
    ......
}

ContextImpl.java (api 30)
class ContextImpl extends Context {
	......
    @UnsupportedAppUsage
    static ContextImpl createActivityContext(ActivityThread mainThread,
            LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
            Configuration overrideConfiguration) {
		......
		// 通过 LoadedApk 获取类加载器 ClassLoader
        ClassLoader classLoader = packageInfo.getClassLoader();
        ......
        // 创建 Activity 的 ContextImpl 实例对象
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null,
                activityInfo.splitName, activityToken, null, 0, classLoader, null);
        ......
        return context;
    }
    ......
}

メインプロセスのソースコードを見て、ContextImpl # createActivityContext() を呼び出してアクティビティのコンテキストを作成するだけです。

メインプロセスにおけるActivityとApplicationの読み込みと作成の処理については、興味のある方はソースコードをたどってご覧いただければと思いますが、主な目的は、クラスローダClassLoaderによって読み込まれた後、新しいインスタンスオブジェクトを作成することです。主にアクティビティをバインドするコンテキストのプロセスを表示します。

2.3.3.4 アクティビティ #attach()

ContextImpl.java (api 30)
public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2, ...... {
	......
	@UnsupportedAppUsage
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,...) {
        // 将 ContextImpl 实例绑定到 Activity 实例对象的 mBase 成员变量
        attachBaseContext(context);
		......
		// 新建 PhoneWindow 实例
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        ......
        mUiThread = Thread.currentThread();
        ......
        mWindowManager = mWindow.getWindowManager();
		......
    }
	......
    @Override
    protected void attachBaseContext(Context newBase) {
    	// 继续调用父类的 attachBaseContext() 方法
        super.attachBaseContext(newBase);
        if (newBase != null) {
            newBase.setAutofillClient(this);
            newBase.setContentCaptureOptions(getContentCaptureOptions());
        }
    }
    ......
}

ContextThemeWrapper.java (api 30)
public class ContextThemeWrapper extends ContextWrapper {
	......
    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
    }
    ......
}

ContextWrapper.java (api 30)
public class ContextWrapper extends Context {
	......
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
    ......
}

アクティビティのattach() メソッドでアクティビティ #attachBaseContext() メソッドを呼び出し続け、親クラスの ContextThemeWrapper #attachBaseContext() メソッドを呼び出し続けます。ContextThemeWrapper は ContextWrapper から継承しているため、引き続き ContextWrapper #attachBaseContext() メソッドを呼び出して、 ContextImpl インスタンス オブジェクトをメンバー変数 mBase に代入します。

2.4 概要

ソース コードを詳細に分析すると、Application、Service、Activity が ContextWrapper クラスから直接的または間接的に継承していることがわかり、それらはすべて ContextImpl オブジェクトを指す Context 型のメンバー変数 mBase を持っています。 Context クラスのクラスなので、これらはすべて、アプリケーション環境に関するグローバル情報を取得するために Context によって提供されるインターフェイス関数を備えています。

3. 文脈に関する補足知識

前の章では、Application、Service、Activity などのコンポーネントの作成と、ContextImpl インスタンス オブジェクトのバインドのプロセスを分析しましたが、学生の中には、4 つの主要コンポーネントのうち、BroadcastReceiver と ContentProvider の作成プロセス中に ContextImpl インスタンス オブジェクトがバインドされないのではないかと疑問に思う人もいるかもしれません。

実はバインドもされていますが、Contextを継承しているわけではなく、Contextインスタンスは上記3つで提供する必要があるので、ソースコードをざっと見てみましょう。

3.1 BroadcastReceiverがContextインスタンスを取得する

開発時、ブロードキャストレシーバーの登録は通常Context # registerReceiver() メソッドを呼び出して行いますが、ここでのContextはブロードキャストレシーバーを登録する際のシナリオに応じて上記3つのいずれかを提供することができます。たとえば、Context # registerReceiver() メソッドを呼び出して登録すると、上記の分析からわかるように、Context 実装クラスの ContextImpl # registerReceiver() メソッドが呼び出されます。コードは次のとおりです。

ContextImpl.java (api 30)
class ContextImpl extends Context {
	......
    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        return registerReceiver(receiver, filter, null, null);
    }
    
    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
            String broadcastPermission, Handler scheduler) {
        return registerReceiverInternal(receiver, getUserId(),
                filter, broadcastPermission, scheduler, getOuterContext(), 0);
    }
    ......
    @UnsupportedAppUsage
    final Context getOuterContext() {
        return mOuterContext;
    }
    ......
}

ブロードキャスト レシーバーを登録するときは、引き続き ContextImpl # registerReceiverInternal() メソッドを呼び出し、現在のコンテキスト、つまり Context を渡します。ここで、Context インスタンスは ContextImpl # getOuterContext() を通じて取得されます。このメソッドは見覚えがあるでしょうか? 2.1.5.2、2.2.3.1、および 2.3.3.2 では、値は ContextImpl # setOuterContext() メソッドを通じて割り当てられ、上記の分析も検証されます。Context インスタンスは、ブロードキャスト受信機を登録するときにシナリオに基づいて取得されます。前述の 3 つが取得されます。

3.2 ContentProvider が Context インスタンスを取得する

ContentProvider は、4 つのコンポーネントの中で最も使用頻度が低いです。通常、プロセス間でデータを共有するために使用されます。アプリケーションの開始時にシステムによって作成されますが、コンテキスト アーキテクチャ自体には属していないため、作成時に使用されますContentProvider インスタンス。Context インスタンスは別の場所で取得する必要があります。この場合、まずアプリケーションの起動プロセス中に ContentProvider インスタンスが作成される場所を見てみましょう。

3.2.1 タイミング図

アプリケーションの起動プロセスについてはここでは詳しく説明しませんが、Android R (11.0) のアクティビティ起動プロセスの詳細な説明については、この記事の詳細な分析を参照してください。アプリケーションが Application を作成してバインドした後、ActivityThread # installContentProviders() メソッドを通じて Context のインスタンスを作成してバインドします。ソース コードを調べて確認してみましょう。

3.2.2 ソースコード解析

3.2.2.1 アクティビティスレッド # handleBindApplication()


ActivityThread.class (api 30)
public final class ActivityThread extends ClientTransactionHandler
        implements ActivityThreadInternal {
    ......
 	@UnsupportedAppUsage
    private void handleBindApplication(AppBindData data) {
        ......
        Application app;
		......
        try {
        	// 创建 Application
            app = data.info.makeApplication(data.restrictedBackupMode, null);
            ......
            if (!data.restrictedBackupMode) {
                if (!ArrayUtils.isEmpty(data.providers)) {
                	// 创建 ContentProvider 实例,注意入参是 Application 实例
                    installContentProviders(app, data.providers);
                }
            }
            ......
            try {
           		// 内部调用 Application # onCreate() 的方法
                mInstrumentation.callApplicationOnCreate(app);
            }
            ......
        }
    }
    
    @UnsupportedAppUsage
    private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();

        for (ProviderInfo cpi : providers) {
            ......
            // 继续调用 installProvider() 方法创建 ContentProvider 实例
            ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }
        try {
        	// 将 ContentProvider 列表发布到 AMS 中目的是进行缓存
        	// 其它应用进程想要获取它的 ContentProvider 的时候可以直接在缓存中遍历获取
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

	@UnsupportedAppUsage
    private ContentProviderHolder installProvider(Context context,
            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)) {
            	// 包名相同即同一应用内,则使用入参 Application 作为 Context
                c = context;
            }
			......// 根据不同使用场景,获取对应场景下的 Context 实例
            try {
                final java.lang.ClassLoader cl = c.getClassLoader();
                LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
                ......
                // 内部通过 ClassLoader 加载并新建 ContentProvider 实例对象
                localProvider = packageInfo.getAppFactory()
                        .instantiateProvider(cl, info.name);
                provider = localProvider.getIContentProvider();
                ......
                // XXX Need to create the correct context for this provider.
                // 为 ContentProvider 设置合适的上下文环境 - Context
                localProvider.attachInfo(c, info);
            }
            ......
        } 
        ......
    }
    ......
}

ActivityThread # handleBindApplication() メソッドで、ActivityThread # installContentProviders() メソッドを呼び出し、作成された Application インスタンス オブジェクトを渡します。引き続き、ActivityThread # installProvider() メソッドを呼び出して、ContentProvider インスタンス オブジェクトを作成します。作成プロセスは、クラスを通じて、ローダー ClassLoader は、新しい ContentProvider インスタンス オブジェクトをロードして作成し、最後に ContentProvider #attachInfo() メソッドを呼び出して、適切なコンテキスト環境 (ContentProvider の Context) を設定します。

3.2.2.2 ContentProvider #attachInfo()

ContentProvider.class (api 30)
public abstract class ContentProvider implements ContentInterface, ComponentCallbacks2 {
    ......
    public void attachInfo(Context context, ProviderInfo info) {
        attachInfo(context, info, false);
    }

    private void attachInfo(Context context, ProviderInfo info, boolean testing) {
        mNoPerms = testing;
        mCallingPackage = new ThreadLocal<>();
        // 这里只允许设置一次,因此 ContentProvider 创建交付使用后,客户端不能再更改它
        if (mContext == null) {
            mContext = context;
            ......
            // 回调 ContentProvider # onCreate() 方法
            ContentProvider.this.onCreate();
        }
    }
    ......
}

ContentProvider #attachInfo() メソッドは、Context インスタンス オブジェクトを ContentProvider のメンバー変数 mContext に割り当てます。これにより、ContextProvider は、Context によって提供されるインターフェイス関数を使用してアプリケーション環境に関するグローバル情報を取得できます。また、この Context は初期の ActivityThread # handleBindApplication( ) メソッド 渡される Application インスタンス オブジェクト (注: メンバー変数 mContext は 1 回だけ設定できます)。

注: ContentProvider # installProvider() メソッドでは、さまざまな使用シナリオに従って、対応するシナリオの Context インスタンス オブジェクトが取得されます。ここで分析しているのは同じアプリケーション内であるため、ContentProvider のメンバー変数 mContext には値が割り当てられます渡されたアプリケーション インスタンス オブジェクトの。他のアプリケーションのパッケージ名がプロセス間またはインテント # setPackage() を通じて指定されている場合は、対応するシナリオで Context インスタンス オブジェクトを取得する必要があります。

3.3 概要

このセクションでは、4 つの主要なコンポーネントのうち、BroadcastReceiver と ContentProvider がどのように Context インスタンス オブジェクトを取得するかを補足します。これらはシステム コンポーネントですが、Context アーキテクチャのメンバーではありません。ただし、システム コンポーネントとして、Context を使用する必要もあります。インターフェース機能を提供します。アプリケーション環境のグローバルな情報を取得するため、深く学ぶ姿勢でソースコードのプロセスを熟読し、Context インスタンス オブジェクトの作成と取得のプロセスを整理しました。

4. まとめ

この記事の解説やソースコード分析と合わせて、インタビューで聞かれた質問を見て理解を深めてみましょう。

質問 1: Android システムのアプリケーションにはコンテキストがいくつありますか?

この記事の詳細な説明から、Context アーキテクチャでは、Application、Activity、および Service がインスタンス オブジェクトを作成するときに、ContextImpl インスタンス オブジェクトを作成し、それを親クラス ContextWrapper のメンバー変数 mBase に割り当てることがわかります。サブクラス オブジェクトには親クラスの All プロパティとメソッドがオブジェクト内にあるため、ContextImpl インスタンス オブジェクトは、Application、Activity、および Service インスタンス オブジェクトのメンバー変数 mBase を通じて取得できます。つまり、各アクティビティとサービスにはコンテキストがあり、各アプリケーション内のアプリケーションは一意であるため、Android システムのアプリケーション内のコンテキストの数 = アクティビティの数 + サービスの数 + 1 となります。

質問 2: コンテキストによってメモリ リークが発生しますか?

一般的に Context によるメモリリークは Context が破棄される場合がほとんどですが、GC 破棄は Context が参照されているため失敗します Application の Context オブジェクトはアプリケーションのプロセスとともに存在すると理解できるため、いくつかの問題点をまとめますContext.提案を使用する場合:

  • アプリケーションのコンテキストが処理できる場合、およびライフサイクルが長いオブジェクトの場合、アプリケーションのコンテキストが最初に使用されます。
  • アクティビティよりも長いライフサイクルを持つオブジェクトにアクティビティへの参照を保持させないでください。
  • 非静的内部クラスは外部クラス インスタンスへの参照を暗黙的に保持するため、Activity では非静的内部クラスを使用しないようにしてください。静的内部クラスを使用し、外部インスタンス参照を弱参照として保持することをお勧めします。

質問 3: getContext()、getBaseContxet()、getApplication()、および getApplicationContext() の違いは何ですか?

記事で分析されているように、getApplication() と getApplicationContext() の 2 つのメソッドは同じインスタンス オブジェクトを取得しますが、使用シナリオの範囲が異なります。getApplication() メソッドはより直感的ですが、アクティビティ シナリオとサービス シナリオでのみ呼び出すことができます。getApplicationContext() メソッドは適用範囲が広く、どのようなシナリオでも Context オブジェクトを通じて呼び出すことができます。では、なぜ同じオブジェクトなのでしょうか? ソースコードをざっと見てみましょう。

Activity.class (api 30)
public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,... {
	......
	@UnsupportedAppUsage
    private Application mApplication;
    
    public final Application getApplication() {
        return mApplication;
    }
    @UnsupportedAppUsage
    final void attach(Context context, ActivityThread aThread,...
            Application application, ...) {
        ......
        mApplication = application;
		......
    }
}

Service.class (api 30)
public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
	......
	@UnsupportedAppUsage
    private Application mApplication;
    
    public final Application getApplication() {
        return mApplication;
    }
    @UnsupportedAppUsage
    public final void attach(
            Context context,...
            Application application, Object activityManager) {
        ......
        mApplication = application;
        ......
    }
}

まず、getApplication() メソッドは、アクティビティとサービスがattach() メソッドを呼び出すときに渡された Application インスタンス オブジェクトを返すことがわかります。この記事の前半の分析を覚えておいてください。Activity #attach() メソッドの呼び出しについては、2.3.3.2 ActivityThread # PerformLaunchActivity() を参照し、Service #attach() メソッドの呼び出しについては、2.2.3.1 ActivityThread #を参照してください。 handleCreateService(): これら 2 つの間 各メソッドのattach() メソッドに渡される Application インスタンス オブジェクトは、LoadedApk # makeApplication() メソッドを通じて作成および取得されます。

getApplicationContext() メソッドの戻り値を見てみましょう。ContextWrapper が呼び出されますが、最終的には実装クラス ContextImpl に委譲されます。ソース コードは次のとおりです。

ContextImpl.java (api 30)
class ContextImpl extends Context {
	......
	@UnsupportedAppUsage
    final @NonNull ActivityThread mMainThread;
    @UnsupportedAppUsage
    final @NonNull LoadedApk mPackageInfo;
    
    @Override
    public Context getApplicationContext() {
        return (mPackageInfo != null) ?
                mPackageInfo.getApplication() : mMainThread.getApplication();
    }
    ......
}

ここでは、mPackageInfo が空かどうかに応じて、mPackageInfo # getApplication() メソッドと mMainThread # getApplication() メソッドがそれぞれ呼び出されます。この 2 つのメソッドについて見てみましょう。まず、getApplication() メソッドの戻り値を見てみましょう。 LoadedApk のコードは次のとおりです。


LoadedApk.java (api 30)
public final class LoadedApk {
	......
	@UnsupportedAppUsage
    private Application mApplication;

    Application getApplication() {
        return mApplication;
    }

	@UnsupportedAppUsage
    public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        if (mApplication != null) {
            return mApplication;
        }
		......
        Application app = null;
		......
        try {
        	......
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
        }
        ......
        mApplication = app;
		......
        return app;
    }
    ......
}

まず、LoadedApk の mApplication が空かどうかを確認します (オブジェクトのシングルトンであることが保証されています)。空でない場合は、直接戻ります。空の場合は、新しい Application インスタンス オブジェクトを作成して mApplication に割り当てます。次に、ActivityThread の getApplication() メソッドの戻り値を見てみましょう。コードは次のとおりです。

ActivityThread.java (api 30)
public final class ActivityThread extends ClientTransactionHandler {
	......
    @UnsupportedAppUsage
    Application mInitialApplication;
    
    @UnsupportedAppUsage
    public Application getApplication() {
        return mInitialApplication;
    }
    ......
    @UnsupportedAppUsage
    private void handleBindApplication(AppBindData data) {
		......
        Application app;
        try {
        	// data.info 是 LoadedApk 类
            app = data.info.makeApplication(data.restrictedBackupMode, null);
			......
            mInitialApplication = app;
			......
        }
	......
}

ActivityThread # getApplication() メソッドによって返される mInitialApplication は、LoadedApk # makeApplication() メソッドによっても返されるため、上記 2 つのケースでは getApplicationContext() メソッドが同じ Application インスタンス オブジェクトを返すと結論付けることができます。

getApplication() メソッドによって返される Application インスタンス オブジェクトも、LoadedApk # makeApplication() メソッドを通じて作成および取得されるため、getApplication() と getApplicationContext() の 2 つのメソッドは同じ Application インスタンス オブジェクトを返します。

getContext()、getBaseContxet()、getApplicationContext() メソッドの違いは何ですか?

まず、getBaseContxet()メソッドは、先ほど解析したmBaseのContextに割り当てられた実装クラスのインスタンスオブジェクトを取得します。getApplicationContext() メソッドによって返される LoadedApk #makeApplication() メソッドによって作成されたアプリケーション インスタンス オブジェクト。また、Application、Activity、Service にはすべて getBaseContxet() と getApplicationContext() という 2 つのメソッドがあります。getContext() メソッドは、Fragment または View でホスト オブジェクトを取得するために使用されます。

五、参考

  1. Contextメカニズムの包括的な分析

おすすめ

転載: blog.csdn.net/qq_32907491/article/details/132789346