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

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

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

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

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

Ответы

Ответ 1

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

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

В обычном приложении для Android вы обычно имеют два вида Контекста, Деятельность и применение.

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

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

Не используйте getBaseContext(), просто используйте Контекст, который у вас есть.

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

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

Ответ 2

Вот что я нашел относительно использования 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) используйте подстановку Context, например:

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.

UPDATE: Добавьте примеры использования context.

Ответ 3

Я прочитал эту тему несколько дней назад, задавая себе тот же вопрос. Мое решение после прочтения было простым: всегда используйте 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, тогда, когда вы нажмете на spinner в своей деятельности, у вас будет неперехваченное исключение, исходящее из виртуальной машины dalvik (а не из вашего кода, поэтому у меня есть потратил много времени, чтобы найти, где была моя ошибка...).

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

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

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

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

Ответ 4

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

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

То, что я наблюдал. Возможно, есть случаи, когда вы должны их отличать.

Ответ 5

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

((YourActivity) context).yourCallbackMethod(yourResultFromThread, ...);

Ответ 6

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

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

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

Ответ 7

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

Ответ 8

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

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

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

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

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

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

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

ContextWrapper
Application
Activity
Service
IntentService

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

контекст

| - ContextWrapper

| - - Применение

| - - ContextThemeWrapper

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

| - - Обслуживание

| - - - IntentService

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