Где код, который вызывается при вызове getApplicationContext()?

Я смотрел в Android источники, просто из интереса. Я обнаружил, что Context является абстрактным классом с абстрактным методом:

public abstract Context getApplicationContext();

ContextWrapper.java расширяет Context.java, что привело к реализации метода getApplicationContext():

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

Но mBase ссылается на объект типа Context, который инициализируется в конструкторе ContextWrapper:

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

Итак, эта ссылка mBase относится к классу abstract? Ну, я просто не понимаю, где код, который выполняется, когда вы вызываете getApplicationContext() из вашего Activity.

Ответ 1

Это интересный пример полиморфизма.

Пока ваш base extends Context, он должен обеспечить реализацию getApplicationContext(), которая в случае ContextWrapper - это код, указанный здесь. Там эта реализация и одна в ContextImpl.

Важно отметить пару вещей при чтении кода, который вы указали: ContextWrapper сам расширяет Context, но он также принимает Context как вход (который может быть ContextWrapper, или Service, или Application, или Activity). ContextWrapper не волнует, какой он; он просто знает, что у них есть метод getApplicationContext, и он хочет вызвать этот метод, когда его спросят. (Например, ему может быть передан другой ContextWrapper, но поскольку упомянутый ContextWrapper также потребует Context в его конструкторе, который просто добавит еще один уровень вложенности.)

Класс Application extends ContextWrapper вызывает super(null), что означало бы, что getApplicationContext() будет бросать NullPointerException, если он оставлен таким образом, однако в ContextWrapper он также настраивается на attachBaseContext(Context), и это где он становится интересным.

Оба Activity и Application имеют методы attach(Context [...other stuff]). Каждый из них вызывает attachBaseContext() с переданным Context.

  • В классе Instrumentation вы найдете android.app.Instrumentation.newApplication(), где создается ContextImpl и передается в Application.
  • В классе ActivityThread вы найдете handleBindApplication, который создает ContextImpl, который передается в Activity как его корень Context.
  • В классе LoadedApk вы найдете makeApplication, который создает ContextImpl, который передается в Application. Здесь указаны те места, которые он назвал.

Итак, в конце дня mBase обычно заканчивается как ContextImpl.

Потенциально полезные ссылки, на которые я смотрел, находя все это: