В этом уроке вы познакомитесь с концепцией отражения Java: способностью класса или объекта программно исследовать детали своей собственной реализации.
Приложения Android написаны на Java, языке программирования, который поддерживает рефлексию — способность объекта исследовать себя. В этом руководстве вы познакомитесь с основами отражения Java, в том числе с тем, как проверять методы и поля данного класса, проверять наличие определенных методов и другими практическими задачами, которые вам могут понадобиться при разработке для разных версий. Android SDK.
Что вам нужно
Технически, вам не нужны никакие инструменты для завершения этого урока, но вам, безусловно, они понадобятся для разработки приложений для Android.
Для разработки приложений Android (или любых других приложений Java) вам необходима среда разработки для написания и создания приложений. Eclipse — это очень популярная среда разработки (IDE) для Java и предпочтительная IDE для разработки под Android. Он свободно доступен для операционных систем Windows, Mac и Linux.
Полные инструкции по установке Eclipse (включая поддерживаемые версии) и Android SDK см. На веб-сайте разработчиков Android .
Зачем использовать отражение?
Reflection дает разработчикам возможность проверять и определять характеристики API во время выполнения, а не во время компиляции. В рамках ограничений безопасности, наложенных Java (например, использование public, protected, private), вы можете затем создавать объекты, обращаться к полям и динамически вызывать методы. Java Reflection API доступны как часть пакета java.lang.reflect, который включен в Android SDK для использования разработчиками.
Так какое это имеет отношение к разработке Android? Итак, с каждой новой версией Android SDK классы, интерфейсы, методы и т. Д. Добавляются, обновляются и (реже) удаляются. Тем не менее, разработчики Android часто хотят ориентировать устройства под управлением разных версий Android с помощью простого пакета приложений. Для этого разработчики Android могут использовать методы отражения, чтобы во время выполнения определить, доступен ли определенный класс или метод, прежде чем пытаться его использовать. Это позволяет разработчику использовать новые API там, где они доступны, при этом поддерживая старые устройства — все в одном приложении.
Проверка классов
Классы Java представлены во время выполнения с использованием класса Class (java.lang.Class). Этот класс обеспечивает отправную точку для всех API отражений. В этом классе вы найдете много методов для проверки различных аспектов класса, таких как его поля, конструкторы, методы, разрешения и многое другое. Вы также можете использовать метод Class, называемый forName (), чтобы загружать не примитивный класс (например, не int, а Integer) по имени динамически во время выполнения, а не во время компиляции:
String sClassName = "android.app.NotificationManager"; пытаться { Class classToInvestigate = Class.forName (sClassName); // Динамически делать вещи с этим классом // Список конструкторов, полей, методов и т. Д. } catch (ClassNotFoundException e) { // Класс не найден! } catch (исключение e) { // Неизвестное исключение }
Классу (в данном случае NotificationManager) не обязательно иметь соответствующий оператор импорта в вашем коде; вы не компилируете этот класс в свое приложение. Вместо этого загрузчик классов будет динамически загружать класс во время выполнения, если это возможно. Затем вы можете осмотреть этот объект класса и использовать методы отражения, описанные в оставшейся части этого урока.
Проверка конструкторов, доступных в классе
Вы можете проверить конструкторы, доступные в данном классе. Чтобы получить только общедоступные конструкторы, используйте getConstructors (). Однако, если вы хотите проверить эти методы, специально объявленные в классе, независимо от того, являются ли они открытыми или нет, используйте взамен getDeclaredConstructors (). Оба метода возвращают массив объектов Constructor (java.lang.reflect.Constructor).
Например, следующий код перебирает объявленные конструкторы класса:
Constructor [] aClassConstructors = classToInvestigate.getDeclaredConstructors (); for (Конструктор c: aClassConstructors) { // Нашел конструктор c }
Получив действительный объект Constructor, вы можете проверить его параметры и даже объявить новый экземпляр класса, используя этот конструктор с методом newInstance ().
Проверка полей, доступных в классе
Вы можете проверить поля (или атрибуты), доступные в данном классе. Чтобы получить только общедоступные методы, включая унаследованные поля, используйте getFields (). Однако, если вы хотите проверить эти поля, специально объявленные в классе (а не унаследованные), независимо от того, являются ли они открытыми или нет, используйте взамен getDeclaredFields (). Оба метода возвращают массив объектов Field (java.lang.reflect.Field).
Например, следующий код перебирает объявленные поля класса:
Field [] aClassFields = classToInvestigate.getDeclaredFields (); for (Поле f: aClassFields) { // Нашли поле f }
Вы также можете проверить определенное открытое поле по имени, используя метод getField (). Например, чтобы проверить поле EXTRA_CHANGED_PACKAGE_LIST класса Intent (которое было добавлено в API Level 8 или Android 2.2), вы можете использовать:
String sClassName = "android.content.Intent"; пытаться { Class classToInvestigate = Class.forName (sClassName); String strNewFieldName = "EXTRA_CHANGED_PACKAGE_LIST"; Поле newIn22 = classToInvestigate.getField (strNewFieldName); } catch (ClassNotFoundException e) { // Класс не найден } catch (NoSuchFieldException e) { // Поле не существует, вероятно, мы на Android 2.1 или старше // предоставляем альтернативную функциональность для поддержки старых устройств } catch (SecurityException e) { // Доступ запрещен! } catch (исключение e) { // Неизвестное исключение }
Получив действительный объект Field, вы можете получить его имя с помощью метода toGenericString (). Если у вас есть соответствующие разрешения, вы также можете получить доступ к значению этого поля класса, используя соответствующие методы get () и set ().
Проверка методов, доступных в классе
Вы можете проверить методы, доступные в данном классе. Чтобы получить только общедоступные методы, в том числе унаследованные, используйте getMethods (). Однако, если вы хотите проверить эти методы, специально объявленные в классе (без унаследованных), независимо от того, являются ли они открытыми или нет, используйте взамен getDeclaredMethods (). Оба метода возвращают массив объектов метода (java.lang.reflect.Method).
Например, следующий код перебирает объявленные методы класса:
Method [] aClassMethods = classToInvestigate.getDeclaredMethods (); для (Метод m: aClassMethods) { // Нашел метод m }
Получив действительный объект Method, вы можете получить его имя с помощью метода toGenericString (). Вы также можете проверить параметры, используемые методом, и исключения, которые он может генерировать. Наконец, если у вас есть соответствующие разрешения, вы также можете вызвать метод с помощью метода invoke ().
Осматривать внутренние классы
Вы можете проверить внутренние классы, определенные в Class, используя метод getDeclaredClasses (). Этот метод возвращает массив объектов Class (java.lang.class), объявленных в родительском классе. Эти классы могут быть проверены, как и любой другой.
Проверка модификаторов членов
Вы также можете проверить флаги и параметры безопасности, называемые модификаторами, связанные с данным классом, полем или методом, используя метод getModifiers (). Интересные модификаторы включают в себя, является ли компонент общедоступным, закрытым, защищенным, абстрактным, окончательным или статическим (среди прочих).
Например, следующий код проверяет модификаторы безопасности класса:
int permissions = classToInvestigate.getModifiers (); if (Modifier.isPublic (permissions)) { // Класс публичный } if (Modifier.isProtected (permissions)) { // Класс защищен } if (Modifier.isPrivate (permissions)) { // Класс закрытый }
Имейте в виду, что вы не можете динамически обращаться к любому классу, методу или полю, используя отражение, к которому вы обычно не сможете получить доступ во время компиляции. Другими словами, обычная защита класса все еще применяется во время выполнения.
Проверка метаданных класса
Вы также можете проверить метаданные, называемые аннотациями, связанные с данным классом, полем или методом, используя метод getAnnotations (). Интересные метаданные, связанные с классом, могут включать, помимо прочего, информацию об устаревании, предупреждениях и переопределениях.
Например, следующий код проверяет метаданные, доступные для класса AbsoluteLayout. Поскольку этот класс устарел в Android 1.5, одна из возвращаемых аннотаций — @ java.lang.Deprecated (), когда этот код выполняется на Android 2.2:
String sClassName = "android.widget.AbsoluteLayout"; пытаться { Class classToInvestigate = Class.forName (sClassName); Annotation [] aAnnotations = classToInvestigate.getDeclaredAnnotations (); для (Аннотации А: Аннотации) { // Находим аннотацию, используем a.toString () для ее распечатки } } catch (ClassNotFoundException e) { // Класс не найден! } catch (исключение e) { // Обработка неизвестного исключения! }
Аналогично, вы можете просто проверить наличие конкретной аннотации, например, устаревшей, по типу:
if (classToInvestigate.isAnnotationPresent (java.lang.Deprecated.class) == true) { // Класс устарел! }
Отражение: удобно для отладки
Вы также можете использовать отражение, чтобы помочь с отладкой. Например, вы можете использовать ключевое слово class для доступа к данным базового класса для данного типа:
импорт android.app.Activity; ... String strClassName = Activity.class.getName (); // android.app.Activity
Вы также можете получить информацию о классе из экземпляра переменной, используя метод getClass () класса Object (который, следовательно, наследуется всеми классами в Java):
String глупо = "Глупая строка!"; Class someKindOfClass = silly.getClass (); String strSillyClassName = someKindOfClass.getName (); // java.lang.String
Если вы хотите проверить класс переменной, лучше использовать instanceof. Смотрите предыдущий урок на instanceof для более подробной информации.
Точно так же вы можете использовать метод getClass () с ключевым словом this, чтобы проверить имя класса, в котором вы находитесь в данный момент, и включить эту информацию как часть вашего журнала отладки в LogCat:
String strCurrentClass = this.getClass (). GetName (); // например текущая активность Log.v (strCurrentClass, «Тег отладки является текущим классом.»);
Почему бы не использовать отражение
Как вы уже видели, рефлексия может быть очень полезна, особенно если вы не уверены, доступен ли определенный класс или метод во время компиляции. Однако у Reflection есть некоторые недостатки, в том числе снижение производительности и потеря строгой типизации и безопасных методов кодирования, применяемых во время компиляции. Лучше использовать рефлексию экономно, но используйте ее, когда это необходимо.
Завершение
Reflection — это мощный инструмент, который разработчики Java могут использовать для программного анализа пакетов и API во время выполнения. Хотя операции по отражению обходятся дорого, они дают разработчику гибкость, которая иногда необходима для выполнения работы. Разработчики Android часто используют эти простые методы отражения для проверки доступности определенных классов, интерфейсов, методов и полей во время выполнения, что позволяет им поддерживать различные версии.
Об авторах
Разработчики мобильных приложений Лорен Дарси и Шейн Кондер являются соавторами нескольких книг по разработке Android: углубленная книга по программированию под названием « Разработка беспроводных приложений для Android» и « Разработка Android-приложений Sams TeachYourself за 24 часа» . Когда они не пишут, они тратят свое время на разработку мобильного программного обеспечения в своей компании и оказание консультационных услуг. С ними можно связаться по электронной почте [email protected] , через их блог на androidbook.blogspot.com и в Twitter @androidwireless .