Contextを深く理解する

5907 ワード

Contextとは?
Android開発ではContext呼び出しから離れられないシステム関連のAPIがContextを使用しなければならないことはよく知られていると思います.私たちは彼女をコンテキスト環境と理解することができます.おそらく、システムAPIを呼び出すときに使用する必要があるグローバル変数の山が格納されています.文字はずっと私の言いたいことを表現しにくいので、原理を分析しましょう.
Contextはどこから来ましたか?
Androidアプリケーションの開発にはActivityが必要で、ActivityでさまざまなシステムAPIを呼び出す必要があります.例えば、
String string = getString(R.string.app_name);

この行のコードは簡単に見えますが、Activityで呼び出さないとどうやってstringを取得しますか?
Context context;
context.getString(R.string.app_name);

Contextオブジェクトが必要ですが、Activityではなぜ必要ありませんか?
答えは、ActivityはもともとContextであり、Contextから継承されています.Activityを開くと、彼の継承順序は次のようにわかります.
Activity -> ContextThemeWrapper -> ContextWrapper -> Context
Contextは抽象クラスで、すべての方法は実装されていません.ContextWrapperはすべての方法を実装していますが、彼はエージェントにすぎません.ソースコードを開くと、彼はこのように見えます.
    @Override
    public AssetManager getAssets() {
        return mBase.getAssets();
    }

    @Override
    public Resources getResources()
    {
        return mBase.getResources();
    }

彼はmBaseの方法を呼び出すだけで、(この設計モードではエージェントモードProxyと呼ばれています)、私たちはこのmBaseがどこから来たのかを明らかにしなければなりません!
ソースコードを見ると2つの場所にmBaseが設置されているだけで、
    public ContextWrapper(Context base) {
        mBase = base;
    }
    
    /**
     * Set the base context for this ContextWrapper.  All calls will then be
     * delegated to the base context.  Throws
     * IllegalStateException if a base context has already been set.
     * 
     * @param base The new base context for this wrapper.
     */
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

1つはコンストラクション関数、1つはattachBaseContextです.
まず構造関数を分析しましょう.Activityは彼女から受け継がれているので、Activityがどのように作成されたのかを分析しました.Activityの作成プロセスは複雑なので、このポイントは彼ではありません.私はここで直接結果を出しました.Activityが作成されたときに呼び出されたのはパラメータなし構造関数です.したがって、mBaseはコンストラクション関数ではなく、attachBaseContextから来ています.次に、ActivityがattachBaseContext()メソッドを呼び出した場所を分析します.
Activityソースコードを確認すると呼び出し先が見つかります
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config) {
        attachBaseContext(context);

Activity.attachメソッドframeworkがActivityを作成するときに呼び出されます.このcontextパラメータが何であるかを知るにはframeworkコードから見なければなりません.
コード:android.app.ActivityThread 
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);           
        } catch (Exception e) {
        }

        try {
            if (activity != null) {
                ContextImpl appContext = new ContextImpl();
                appContext.init(r.packageInfo, r.token, this);
                appContext.setOuterContext(activity);
				
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
             
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config);

見て、contextはここのappContextです.彼はContextImplオブジェクトです.はい、ContextImplが本当の実装者であることを知っています.
だからgetString()の本当の呼び出しはContextImplです.getResource.getString()、getString()の真の実装はResource(android.content.res.Resources)にある.
Contextは何種類ありますか?
  • Activity:
  • を分析しました
  • Service:ContextWrapperからActivityと同じ
  • を継承
  • BroadcastReceiver : onReceiver(Context context )
  • Application:

  • すべてのContextが実際に実装されていることを覚えておいてください.
    Activity、ServiceのContextは、グローバル一意ではなく作成されるたびに作成されるので、Activity、ServiceをグローバルContext参照としないでください.これにより、Activityは破棄されず、常に参照されています.
    では、どのようなContextがグローバルリファレンスを行うことができますか?
    答えは:Application、もしあなたがカスタマイズしたApplicationがなければどのようにこのContextを取得しますか?Contextには
    getApplicationContext()メソッドは、ContextImplで次のように実装されています.
    android.app.ContextImpl
        @Override
        public Context getApplicationContext() {
            return (mPackageInfo != null) ?
                    mPackageInfo.getApplication() : mMainThread.getApplication();
        }

    android.app.LoadedApk
        Application getApplication() {
            return mApplication;
        }

    このmApplicationオブジェクトがプログラムのApplicationです.彼をカスタマイズしたアプリケーションに変えることができます
    BroadcastReceiverのContext
                Application app = packageInfo.makeApplication(false, mInstrumentation);
    
                if (localLOGV) Slog.v(
                    TAG, "Performing receive of " + data.intent
                    + ": app=" + app
                    + ", appName=" + app.getPackageName()
                    + ", pkg=" + packageInfo.getPackageName()
                    + ", comp=" + data.intent.getComponent().toShortString()
                    + ", dir=" + packageInfo.getAppDir());
    
                ContextImpl context = (ContextImpl)app.getBaseContext();
                sCurrentBroadcastIntent.set(data.intent);
                receiver.setPendingResult(data);
                receiver.onReceive(context.getReceiverRestrictedContext(),
                        data.intent);

    コードから、彼はアプリケーションのContextImplオブジェクトなので、グローバルな