Освой программирование играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Context

Context – это объект, который предоставляет доступ к базовым функциям приложения: доступ к ресурсам, к файловой системе, вызов активности и т.д. Activity является подклассом Context, поэтому в коде мы можем использовать её как ИмяАктивности.this (напр. MainActivity.this), или укороченную запись this. Классы Service, Application и др. также работают с контекстом.

Доступ к контексту можно получить разными способами. Существуют такие методы как getApplicationContext(), getContext(), getBaseContext() или this, который упоминался выше, если используется в активности.

На первых порах не обязательно понимать, зачем он нужен. Достаточно помнить о методах, которые позволяют получить контекст и использовать их в случае необходимости, когда какой-нибудь метод или конструктор будет требовать объект Context в своих параметрах.

В свою очередь Context имеет свои методы, позволяющие получать доступ к ресурсам и другим объектам.

  • getAssets()
  • getResources()
  • getPackageManager()
  • getString()
  • getSharedPrefsFile()

Возьмём к примеру метод getAssets(). Ваше приложение может иметь ресурсы в папке assets вашего проекта. Чтобы получить доступ к данным ресурсам, приложение использует механизм контекста, который и отвечает доступность ресурсов для тех, кто запрашивает доступ - активность, служба и т.д. Аналогично происходит с методом getResources. Например, чтобы получить доступ к ресурсу цвета используется конструкция getResources().getColor(), которая может получить доступ к данным из файла res/colors.xml.

Таким образом, создавая, например, вторую активность, мы можем сразу обеспечить ей доступ к своим ресурсам, так как активность относится к контексту. При создании собственных компонентов View также используется контекст в конструкторах, так как компонент тоже может использовать ваши ресурсы. При создании собственных классов, если вам нужно будет обращаться к контексту, то необходимо создать конструктор:


// В классе сделайте конструктор, куда будет передаваться контекст:
public MyClass(Context context) {
    this.context = context;
}

// в активности
MyClass cat = new MyClass(this); 

Через контекст можно узнать практически всю информацию о вашем приложении - имя пакета, класса и т.п.


// имя вашего пакета
String info = this.getApplicationInfo().packageName;

Тем не менее, следует различать контекст в разных ситуациях. Допустим, у вас есть приложение с несколькими активностями. В манифесте можно прописать используемую тему как для всего приложения, так и для каждой активности в отдельности. Соответственно, выбор контекста повлияет на результат. Как правило, при использовании собственной темы предпочтительнее использовать контекст активности, а не приложения.

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


Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Toast.makeText(MainActivity.this, "Мяу", Toast.LENGTH_SHORT).show();
    }
});

При создании адаптеров для списков также обращаются к контексту.


if (convertView == null) {
    convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_user, parent, false);
}

Или ещё пример для адаптера в фрагменте ListFragment:


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    String[] names; // имена котов
    // бла-бла-бла, т.е. мяу-мяу-мяу
    ArrayAdapter<String> adapter = new ArrayAdapter<>(
            inflater.getContext(), android.R.layout.simple_list_item_1,
            names);
    setListAdapter(adapter);

    return super.onCreateView(inflater, container, savedInstanceState);
}

Здесь тоже следует быть внимательным, если используется своя тема для списка.

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

ContextCompat

В библиотеки совместимости появился свой класс для контекста ContextCompat. Он может вам пригодиться, когда студия вдруг подчеркнёт метод в старом проекте и объявит его устаревшим.


context.getResources().getColor(R.color.some_color_resource_id);

Допустим, мы хотим поменять цвет текста на кнопки.


public void onClick(View view) {
    int color = getResources().getColor(R.color.colorPrimary); // ругается
    Button button = (Button) findViewById(R.id.button);
    button.setTextColor(color);
}

Студия ругается, что нужно использовать новый вариант getColor(int, Theme). Заменим строчку.


// Теперь не ругается
int color = ContextCompat.getColor(this, R.color.colorPrimary);

Если посмотреть на исходники этого варианта, то увидим, что там тоже идёт вызов нового метода. Поэтому можно сразу использовать правильный вариант, если вы пишете под Marshmallow и выше.


// Только для API 23 и выше
int color = getResources().getColor(R.color.colorPrimary, getTheme());

Дополнительное чтение

Используем правильный Context для ActionBar

Реклама