ApplicationContextはいつどこでつくられているのか？　~SDKのソースコードを読む~

知った気になってて、実は調べてないってこと意外と結構ありますよね。

今日はHandlerのhadleMessageあたりを調べていると
ふとApplicationContextが気になったので調べてみました。

手がかりはやはり。getApplicationContext()の実装です。
ではまずgetApplicationContext()はどこで実装されているのでしょう？

# Activityの継承関係について

まずActivityの継承関係をまとめてみます。

``` Activity -> ContextThemeWrapper -> ContextWrapper -> Context ``` 

普通に継承関係をたどるとこうなります。

そこで実際にgetApplicationContextが実装してあるのはどこでしょう？


# getApplicationContext()を実装してあるところを探せ！

ありました。意外とあっけないです。すぐでした。
それはどこかというとContextWrapperです。
ContextWrapperの関係のある部分を書き出してみると以下になります。

```java
public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }

    @Override
    public Context getApplicationContext() {
        return mBase.getApplicationContext();
    }
}
```

なるほど、このコードだとmBaseの実装がgetApplicationContext()のきっと中身を渡してくれているのでしょう。

mBaseに代入しているころがキーになりそうです。

# ContextWrapperのmBaseはどこからくる？

ではActivityが生成されるなかでmBaseが代入されるのはContextWrapperをnewしているところでしょうか？
いいえ違います。ContextWrapperをnewしているところは今のところ見つかってません。
そのかわり別のところで代入しているのをみつけました。

ここの答えはContextWrapperのattachBaseContext()メソッドです。

attachBaseContext()メソッドは、親のContextにはなくContextWrapperで定義されたメソッドの様です。
ということは、ActivityかContextThemeWrapperで呼ばれているのでしょう。
これも簡単。attachBaseContext()メソッドはActivityのattach()メソッドの中でよばれていました。

# ここまでのまとめ

ここまでまとめるとこうです。

Activityの継承関係は下記のようになっていて、

``` Activity -> ContextThemeWrapper -> ContextWrapper -> Context ``` 
getApplicationContextはActivityのattach()メソッドからわたされるContext引数のようです。

ここからとうとうActivityがどう生成されてくるのかを追うことになります。


# Activityはどこで生成されるのか？

ではここでActivityが生成される流れについて考えていきます。
みなさんはonCreateがどこから呼ばれているのか知ってますか。
簡単に知ることができます。

プロジェクトを生成して作ったMainActivityの中でonCreateの中で以下のコードを書きましょう。するとログにあるものがでてきます。

```java
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
        for(StackTraceElement e: stacktrace) {
            String className =e.getClassName();
            String methodName = e.getMethodName();
            Log.d("MainActivity", className + "#" + methodName);
        }
    }
```

これを実行すると以下のログが出力されます。

```
      D/MainActivity: dalvik.system.VMStack#getThreadStackTrace
      D/MainActivity: java.lang.Thread#getStackTrace
      D/MainActivity: com.example.MainActivity#printStackTrace
      D/MainActivity: com.example.MainActivity#onCreate
      D/MainActivity: android.app.Activity#performCreate
      D/MainActivity: android.app.Instrumentation#callActivityOnCreate
      D/MainActivity: android.app.ActivityThread#performLaunchActivity
      D/MainActivity: android.app.ActivityThread#handleLaunchActivity
      D/MainActivity: android.app.ActivityThread#-wrap11
      D/MainActivity: android.app.ActivityThread$H#handleMessage
      D/MainActivity: android.os.Handler#dispatchMessage
      D/MainActivity: android.os.Looper#loop
      D/MainActivity: android.app.ActivityThread#main
      D/MainActivity: java.lang.reflect.Method#invoke
      D/MainActivity: com.android.internal.os.ZygoteInit$MethodAndArgsCaller#run
      D/MainActivity: com.android.internal.os.ZygoteInit#main
```

ここでActivityThreadというクラスが出てきました。ログから推測すると
どうやらHandleクラスからMessageを取得して、handleMessageでMessageの内容を振り分け
その結果handleLaunchActivity()メソッドが呼ばれているようです。

handleLaunchActivity()を日本語に置き換えると、Activityの起動をハンドル()
きっとここがActivityを生成するところなのでしょう！

# Activityが生成され初期化される流れ

ActivityThreadクラスのhandleLaunchActivity()ではActivityClientRecordという謎のクラスオブジェクトの参照をhandleMessage()メソッドのMessageオブジェクトからもらってきているようです。

ここで一気にActivityのインスタンスが生成されているところを探してみましょう。

```java
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
```

ありました。performLaunchActivity()メソッドの中に上記の様なコードがありました。

ここでClassLoaderを利用してActivityの参照をデバイスのメモリ上に展開するようです。

そのご

```java
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

```
でApplicationクラスのインスタンス（オブジェクトの参照）が生成されています。

※LoadedApkクラスのmakeApplication()メソッドの実装をみると、すでにApplicationインスタンスが生成されていればそれを返すので、おそらく、一度アプリを起動して、MainActivityから他のActivityを呼ぶときは、二度も生成しないということなんでしょうね。なるほど。

makeApplication()メソッド内をよーくみると
ContextImplクラスのインスタンスが生成されているようにみえます！


```java
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
```

ここです。この部分です。
newApplication()メソッドの実装を追ってみると、ここでApplicationインスタンスからattach()メソッドが呼ばれ、上のappContextをattach()メソッドの引数にとっています。
つまり、まず作られるのはActivity。その次がContextImplで、その次にApplicationインスタンスが生成されていなければ生成し、生成時にはContextImplインスタンスをApplicationインスタンスに渡しています！

getApplicationContextとはこのContext(ContextImpl)なのでしょうか？


でもちょっとまって、落ち着いて次の流れを見ていきましょう。

performLaunchActivity()メソッドまで戻ってくると、

```java
                Context appContext = createBaseContextForActivity(r, activity);
```
と生成したContextを

```java
  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);
```
先ほど生成したactivityのインスタンスに渡しています。
なんと、このContextがActivityクラスの祖父であるContextWrapperクラスのmBaseの正体です！
先ほどのApplicationのattachメソッドで渡されているContextの実装は別ものだったようです。
（注意がありますActivityはContextWrapperの孫にあたりますが、ApplicationクラスはContextWrapperの子供、つまりActivityのおじさんにあたるのがApplicationで、どっちもmBaseを持ちます。）

しかし、やっとたどりつきました。
ここまで長い道のりでした。

ということは先ほどのperformLaunchActivity()メソッド内のコード

```java
                Context appContext = createBaseContextForActivity(r, activity);
```
ここでくつられたContextがgetApplicationContext()で渡されるものの正体を握っていることになります。



# ここまでのまとめ2

またここでいったん頭の中を整理しましょう。

・ActivityのgetApplicationContextはContextWrapperのmBase.getApplicationContext()メソッドが返すインスタンスでした。

・そしてそのContextはContextImplインスタンスで、ActivityThreadのperformLaunchActivity()の流れ中で生成されていました。

・そして実はApplicationクラスが保持するContextもContextImpleのインスタンスで、二つのContextImplは別ものです。二つのContextImplのインスタンスがこの流れで生成されることがわかりました。（Applicationインスタンスが既に生成されていればActivityが保持するインスタンスのみになります。）

いよいよ佳境です。


# getApplicationContext()で渡される参照の正体

ではここまでの流れだとContextImplがgetApplicationContext()の実装担当者です。
早速見てみましょう！

```java
    @Override
    public Context getApplicationContext() {
        return (mPackageInfo != null) ?
                mPackageInfo.getApplication() : mMainThread.getApplication();
    }

```

読み通りでした。しかしmPackageInfoがnullだった場合とそうでない場合で返すものが違います。
ここで二つのContextImplインスタンスの生成を見直します。

まずApplicationが保持する方のContextImpl

```java
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
```

次にActivityが保持する方のContextImpl

```java
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, displayId, r.overrideConfig);
```

そうです。
どっちもpackageInfoインスタンスは渡してますから、きっとmPakageInfo.getApplication()です！
そして、mPakageInfoはどこからきたでしょう？
それはperformLaunchActivity()メソッドの引数の一つActivityClientRecodeのメンバです。
つまり、二つのContextImplインスタンスが保持しているmPackageInfo（LoadedApkクラスのインスタンス）は全く同じものです！

そうこれが！ApplicationContextです。
一つのアプリに一つしか持てないLoadedApkのmApplicationインスタンスがgetApplicationContext()で返されるインスタンスの正体だったんです！
わかってよかったです！




ということは。

```java
    Context appContext = MainActivity.this.getApplication().getApplicationContext();
```

と

```java
    Context appContext = MainActivity.this.getApplicationContext();
```

は全く同じインスタンスが返ります。

ですが。

```java
    Context baseContext = MainActivity.this.getApplication().getBaseContext();
```

と

```java
    Context baseContext = MainActivity.this.getBaseContext();
```

そしてAcitivity生成時ContextImplは毎回生成されるのだから。

```java
    Context baseContext = SecondActivity.this.getBaseContext();
```

は別ものです！


そしてActivityにAddされているFragmentのgetContext()は、そのActivity自身です。（なぜそうなるか気になる方はFragmentHostCallbackクラスの実装をみてください！）

# いつどこでつくられているか？

え？結局どこでつくられているのかって？わすれてました。はい。
LoadedApkクラスのmakeApplication()メソッドの中です。
返すmApplicationインスタンスがnullであれば

```java
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
```

によって作られています。Instrumentationでは

```java
        Application app = (Application)clazz.newInstance();
        app.attach(context);
```
とnewされず、引数で呼ばれたClass<?>がnewInstance()しているだけでした。

# さいごに
長文駄文で申し訳ないです。
ここまで読んでくださった方、ありがとうございました！

SDKのソースコードはどこでみつければいいのかなどは、別の誰かさんが書いてくれていると思いますので省いてます。
それでもわからないときは、AndroidSDKの中にあるsourceフォルダの中をのぞいてみて、それをAndroidStudioにD&Dすれば見れるはずです。

今回のコードはAndroid/sdk/sources/android-23/以下を参照してます。


次回はHandlerのマメ知識を書こうと思います。