В чем разница между различными методами для получения контекста? - PullRequest
381 голосов
/ 22 июня 2009

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

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

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

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

Ответы [ 7 ]

301 голосов
/ 22 июня 2009

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

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

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

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

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

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

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

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

50 голосов
/ 19 декабря 2012

Вот что я нашел относительно использования 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.

12 голосов
/ 06 января 2012

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

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

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

6 голосов
/ 20 марта 2010

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

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

Вот что я заметил. Возможно, есть случаи, которые вы должны различать.

3 голосов
/ 25 сентября 2010

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

((YourActivity) context).yourCallbackMethod(yourResultFromThread, ...);
2 голосов
/ 09 февраля 2017

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

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

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

0 голосов
/ 12 декабря 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...