В чем разница между различными методами получения контекста?

В различных битах кода Android я видел:

 public class MyActivity extends Activity {
    public void method() {
       mContext = this;    // since Activity extends Context
       mContext = getApplicationContext();
       mContext = getBaseContext();
    }
 }

Однако я не могу найти достойного объяснения того, что предпочтительнее и при каких обстоятельствах следует использовать.

Указатели на документацию по этому вопросу и рекомендации о том, что может сломаться, если будет выбран неправильный вариант, будут очень признательны.


person Alnitak    schedule 22.06.2009    source источник
comment
Эта ссылка может вам помочь. Просмотрите это ..   -  person Aju    schedule 28.05.2012


Ответы (8)


Я согласен с тем, что документация по контекстам в Android скудна, но вы можете собрать воедино несколько фактов из разных источников.

Это сообщение в официальном блоге разработчиков Google Android было написано в основном для устранения утечек памяти, но также предоставляет некоторую полезную информацию о контекстах:

В обычном приложении для Android обычно есть два типа контекста: действие и приложение.

Прочитав статью немного дальше, вы узнаете о разнице между ними и о том, когда вы можете рассмотреть возможность использования контекста приложения (Activity.getApplicationContext()) вместо использования контекста действия this). В основном контекст приложения связан с приложением и всегда будет одним и тем же на протяжении всего жизненного цикла вашего приложения, в то время как контекст действия связан с действием и может быть уничтожен много раз, поскольку действие уничтожается во время изменения ориентации экрана и такой.

Я не смог найти ничего о том, когда использовать getBaseContext (), кроме сообщения Дайанн Хакборн, одного из инженеров Google, работающих над Android SDK:

Не используйте getBaseContext (), просто используйте имеющийся у вас контекст.

Это было из сообщения в группа новостей android-developers, возможно, вы захотите задать свой вопрос и там, потому что некоторые люди, работающие над Android, следят за этой группой новостей и отвечают на вопросы.

Поэтому в целом кажется предпочтительным использовать глобальный контекст приложения, когда это возможно.

person snctln    schedule 22.06.2009
comment
Когда у меня есть действие A, которое может запустить действие B, которое, в свою очередь, может перезапустить A с флагом CLEAR_TOP (и, возможно, повторить этот цикл много раз) - какой контекст я должен использовать в этом случае, чтобы избежать создания огромного следа ссылочные контексты? Диана говорит, что использует this, а не getBaseContext, но тогда ... в большинстве случаев A будет повторно использоваться, но бывают ситуации, когда новый объект для A будет создан, а затем старый A просочится. Таким образом, кажется, что getBaseContext является наиболее подходящим выбором для большинства случаев. Тогда непонятно почему Don't use getBaseContext(). Может кто-нибудь прояснить это? - person JBM; 30.03.2011
comment
как получить доступ к объекту контекста внутри класса, который не расширяет Activity? - person Cole; 20.04.2011
comment
@Cole, вы можете создать класс, который мы назовем здесь ExampleClass, конструктор которого принимает объект Context и создает экземпляр переменной экземпляра класса appContext. Затем ваш класс Activity (или любой другой класс в этом отношении) может вызвать метод ExampleClass, который использует переменную экземпляра appContext ExampleClass. - person Archie1986; 24.06.2011

Вот что я обнаружил относительно использования context:

1). Внутри самого Activity используйте this для расширения макетов и меню, регистрации контекстных меню, создания экземпляров виджетов, запуска других действий, создания нового Intent в Activity, создания экземпляров предпочтений или других методов, доступных в Activity.

Раздувание макета:

View mView = this.getLayoutInflater().inflate(R.layout.myLayout, myViewGroup);

Меню надувания:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    this.getMenuInflater().inflate(R.menu.mymenu, menu);
    return true;
}

Контекстное меню регистрации:

this.registerForContextMenu(myView);

Создание экземпляра виджета.

TextView myTextView = (TextView) this.findViewById(R.id.myTextView);

Начать Activity:

Intent mIntent = new Intent(this, MyActivity.class);
this.startActivity(mIntent);

Создание экземпляров предпочтений:

SharedPreferences mSharedPreferences = this.getPreferenceManager().getSharedPreferences();

2). Для класса всего приложения используйте getApplicationContext(), поскольку этот контекст существует на протяжении всего срока службы приложения.

Получить имя текущего пакета Android:

public class MyApplication extends Application {    
    public static String getPackageName() {
        String packageName = null;
        try {
            PackageInfo mPackageInfo = getApplicationContext().getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), 0);
            packageName = mPackageInfo.packageName;
        } catch (NameNotFoundException e) {
            // Log error here.
        }
        return packageName;
    }
}

Связывание класса всего приложения:

Intent mIntent = new Intent(this, MyPersistent.class);
MyServiceConnection mServiceConnection = new MyServiceConnection();
if (mServiceConnection != null) {
    getApplicationContext().bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
}

3). Для слушателей и других типов классов Android (например, ContentObserver) используйте подстановку контекста, например:

mContext = this;    // Example 1
mContext = context; // Example 2

где this или context - это контекст класса (Activity и т. д.).

Activity подстановка контекста:

public class MyActivity extends Activity {
    private Context mContext;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);        
        mContext = this;
    }
}

Подстановка контекста слушателя:

public class MyLocationListener implements LocationListener {
    private Context mContext;
    public MyLocationListener(Context context) {
        mContext = context;
    }
}

ContentObserver подстановка контекста:

public class MyContentObserver extends ContentObserver {
    private Context mContext;
    public MyContentObserver(Handler handler, Context context) {
        super(handler);
        mContext = context;
    }
}

4). Для BroadcastReceiver (включая встроенный / встроенный получатель) используйте собственный контекст получателя.

Внешний BroadcastReceiver:

public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        if (action.equals(Intent.ACTION_SCREEN_OFF)) {
            sendReceiverAction(context, true);
        }
        private static void sendReceiverAction(Context context, boolean state) {
            Intent mIntent = new Intent(context.getClass().getName() + "." + context.getString(R.string.receiver_action));
            mIntent.putExtra("extra", state);
            context.sendBroadcast(mIntent, null);
        }
    }
}

Встроенный / встроенный BroadcastReceiver:

public class MyActivity extends Activity {
    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final boolean connected = intent.getBooleanExtra(context.getString(R.string.connected), false);
            if (connected) {
                // Do something.
            }
        }
    };
}

5). Для Служб используйте собственный контекст службы.

public class MyService extends Service {
    private BroadcastReceiver mBroadcastReceiver;
    @Override
    public void onCreate() {
        super.onCreate();
        registerReceiver();
    }
    private void registerReceiver() {
        IntentFilter mIntentFilter = new IntentFilter();
        mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
        this.mBroadcastReceiver = new MyBroadcastReceiver();
        this.registerReceiver(this.mBroadcastReceiver, mIntentFilter);
    } 
}

6). Для тостов обычно используйте getApplicationContext(), но по возможности используйте контекст, переданный из Activity, Service и т. д.

Используйте контекст приложения:

Toast mToast = Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG);
mToast.show();

Использовать контекст, переданный из источника:

public static void showLongToast(Context context, String message) {
    if (context != null && message != null) {
        Toast mToast = Toast.makeText(context, message, Toast.LENGTH_LONG);
        mToast.show();
    }
}

И наконец, не используйте getBaseContext(), как советуют разработчики фреймворка Android.

ОБНОВЛЕНИЕ: добавьте примеры Context использования.

person ChuongPham    schedule 19.12.2012
comment
Вместо mContext можно использовать OuterClass.this; см. комментарии в stackoverflow .com / questions / 9605459 / - person Paul Verest; 15.01.2015
comment
+1 за такой полезный ответ! Я согласен с тем, что принятый ответ хорош как принятый ответ, но, черт возьми, этот ответ был супер информативным! Спасибо за все эти примеры, они помогли мне лучше понять использование контекста в целом. Я даже скопировал ваш ответ в текстовый файл на своей машине для справки. - person Ryan; 12.09.2016

Я прочитал эту ветку несколько дней назад, задав себе тот же вопрос. После прочтения я принял простое решение: всегда использовать applicationContext.

Однако у меня возникла проблема с этим, я потратил несколько часов, чтобы найти ее, и несколько секунд, чтобы решить ее ... (изменив одно слово ...)

Я использую LayoutInflater, чтобы раздувать представление, содержащее Spinner.

Итак, вот две возможности:

1)

    LayoutInflater layoutInflater = LayoutInflater.from(this.getApplicationContext());

2)

    LayoutInflater layoutInflater = LayoutInflater.from(this.getBaseContext());

Затем я делаю что-то вроде этого:

    // managing views part
    View view = ContactViewer.mLayoutInflater.inflate(R.layout.aViewContainingASpinner, theParentView, false);
    Spinner spinner = (Spinner) view.findViewById(R.id.theSpinnerId);
    String[] myStringArray = new String[] {"sweet","love"};

    // managing adapter part
    // The context used here don't have any importance -- both work.
    ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this.getApplicationContext(), myStringArray, android.R.layout.simple_spinner_item);
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spinner.setAdapter(adapter);

    theParentView.addView(view);

Что я заметил: если вы создали экземпляр своего linearLayout с помощью applicationContext, то, когда вы нажмете на счетчик в своей деятельности, у вас будет неперехваченное исключение, исходящее от виртуальной машины dalvik (не из вашего кода, поэтому я потратил много времени, чтобы найти, в чем была моя ошибка ...).

Если вы используете baseContext, тогда все в порядке, откроется контекстное меню, и вы сможете выбрать один из вариантов.

Итак, вот мой вывод: я полагаю (я не тестировал его дальше), что baseContext требуется при работе с contextMenu в вашей Activity ...

Тест был выполнен с использованием API 8 и протестирован на HTC Desire с Android 2.3.3.

Надеюсь, мой комментарий пока что вам не наскучил, и желаю вам всего наилучшего. Удачного кодирования ;-)

person Mav3656    schedule 06.01.2012
comment
Я всегда использовал это при создании представлений в действии. Исходя из того, что если действие перезапускается, представления переделываются, и, возможно, существует новый контекст, который можно использовать для повторного создания представлений. Недостаток, опубликованный в блоге разработчиков, заключается в том, что пока ImageView уничтожен, используемые растровые изображения могут зависеть от этого контекста. Тем не менее, это то, чем я занимаюсь сейчас. Что касается кода в другом месте приложения (обычные классы), я просто использую контекст приложения, поскольку он не привязан к какой-либо активности или элементам пользовательского интерфейса. - person JonWillis; 14.09.2012

Во-первых, я согласен с тем, что мы должны по возможности использовать appcontext. затем «это» в действии. у меня никогда не было потребности в базовом контексте.

В моих тестах в большинстве случаев их можно поменять местами. В большинстве случаев причина, по которой вы хотите получить контекст, - это доступ к файлам, настройкам, базе данных и т. Д. Эти данные в конечном итоге отражаются в виде файлов в папке личных данных вашего приложения (/ data / data /). Независимо от того, какой контекст вы используете, они будут сопоставлены с одной и той же папкой / файлами, так что все в порядке.

Вот что я заметил. Может быть, есть случаи, когда стоит их различать.

person samsonsu    schedule 20.03.2010
comment
Мне нужен базовый контекст, чтобы глобально установить язык приложения при запуске (когда он не соответствует языку телефона по умолчанию). - person Tina; 06.02.2020

В некоторых случаях вы можете использовать контекст действия вместо контекста приложения при запуске чего-либо в потоке. Когда поток завершает выполнение, и вам нужно вернуть результат обратно вызывающей стороне, вам понадобится этот контекст с обработчиком.

((YourActivity) context).yourCallbackMethod(yourResultFromThread, ...);
person Paul    schedule 24.09.2010

Простыми словами

getApplicationContext(), как следует из названия метода, сообщит вашему приложению обо всех подробностях приложения, к которым вы можете получить доступ из любого места в приложении. Таким образом, вы можете использовать это в привязке службы, регистрации широковещательной передачи и т. Д. Application context будет активен до выхода из приложения.

getActivity() или this сообщит вашему приложению о текущем экране, который также будет виден на уровне приложения, предоставленном application context. Итак, все, что вы хотите знать о текущем экране, например Window ActionBar Fragementmanger, доступно в этом контексте. В основном и Activity расширяют Context. Этот контекст будет жить до тех пор, пока не будет активен текущий компонент (действие).

person arjun    schedule 09.02.2017

Путаница проистекает из того факта, что существует множество способов доступа к контексту, при этом (на поверхности) нет заметных различий. Ниже приведены четыре наиболее распространенных способа доступа к Context в Activity.

getContext()
getBaseContext()
getApplicationContext()
getActionBar().getThemedContext() //new

Что такое контекст? Мне лично нравится думать о контексте как о состоянии вашего приложения в любой момент времени. Контекст приложения представляет собой глобальную или базовую конфигурацию вашего приложения, и действие или служба могут основываться на ней и представлять экземпляр конфигурации вашего приложения или переходное состояние для него.

Если вы посмотрите на источник android.content.Context, вы увидите, что Context - это абстрактный класс, и комментарии к этому классу следующие:

Интерфейс к глобальной информации о среде приложения. Это абстрактный класс, реализация которого обеспечивается системой Android. Он позволяет получить доступ к application-specific ресурсам и классам, а также выполнять вызовы для application-level операций, таких как запуск действий, широковещательная передача и получение намерений и т. Д. Что я извлекаю из этого, так это то, что Context предоставляет общую реализацию для доступа к уровню приложения, а также ресурсы системного уровня. Ресурсы уровня приложения могут иметь доступ к таким вещам, как строковые ресурсы [getResources()] или активы [getAssets()], а ресурс системного уровня - это все, к чему вы обращаетесь с помощью Context.getSystemService().

На самом деле, взгляните на комментарии к методам, и они, кажется, усиливают это понятие:

getSystemService(): вернуть дескриптор службы system-level по имени. Класс возвращаемого объекта зависит от запрошенного имени. getResources(): вернуть экземпляр ресурсов для пакета вашего приложения. getAssets(): вернуть экземпляр ресурсов для пакета вашего приложения. Возможно, стоит отметить, что в абстрактном классе Context все вышеперечисленные методы являются абстрактными! Только один экземпляр getSystemService (Class) имеет реализацию и вызывает абстрактный метод. Это означает, что их реализация должна предоставляться в основном реализующими классами, которые включают:

ContextWrapper
Application
Activity
Service
IntentService

Глядя на документацию по API, иерархия классов выглядит так:

Контекст

| - ContextWrapper

| - - Приложение

| - - ContextThemeWrapper

| - - - - Деятельность

| - - Услуга

| - - - IntentService

Поскольку мы знаем, что Context сам по себе не дает никакой информации, мы спускаемся вниз по дереву, смотрим на ContextWrapper и понимаем, что там тоже немного. Поскольку приложение расширяет ContextWrapper, там тоже особо не на что смотреть, поскольку оно не отменяет реализацию, предоставленную ContextWrapper. Это означает, что реализация Context предоставляется ОС и скрыта от API. Вы можете взглянуть на конкретную реализацию Context, посмотрев на источник класса ContextImpl.

person Chanaka Weerasinghe    schedule 02.07.2019

Я использовал это и getBaseContext только при тостах из onClick (очень зеленого нуба как для Java, так и для Android). Я использую это, когда мой кликер находится непосредственно в действии и должен использовать getBaseContext в анонимном внутреннем кликере. Я предполагаю, что это в значительной степени трюк с getBaseContext, он, возможно, возвращает контекст действия, в котором скрывается внутренний класс.

person Tony    schedule 12.12.2011
comment
Это неправильно, он возвращает базовый контекст самого действия. Чтобы получить активность (ту, которую вы хотите использовать в качестве контекста) из анонимного внутреннего класса, используйте что-то вроде MyActivity.this. Использование базового контекста, как вы описываете, вероятно, не вызовет проблем, но это неправильно. - person nickmartens1980; 17.12.2013