Как объявить глобальные переменные в Android?

Я создаю приложение, которое требует входа. Я создал основную и регистрационную активность.

В основном методе onCreate я добавил следующее условие:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    ...

    loadSettings();
    if(strSessionString == null)
    {
        login();
    }
    ...
}

Метод onActivityResult, который выполняется при завершении формы входа в систему, выглядит следующим образом:

@Override
public void onActivityResult(int requestCode,
                             int resultCode,
                             Intent data)
{
    super.onActivityResult(requestCode, resultCode, data);
    switch(requestCode)
    {
        case(SHOW_SUBACTICITY_LOGIN):
        {
            if(resultCode == Activity.RESULT_OK)
            {

                strSessionString = data.getStringExtra(Login.SESSIONSTRING);
                connectionAvailable = true;
                strUsername = data.getStringExtra(Login.USERNAME);
            }
        }
    }

Проблема заключается в том, что форма входа иногда появляется дважды (метод login() вызывается дважды), а также когда клавиатура телефона скользит, форма входа появляется снова, и я думаю, что проблема заключается в переменной strSessionString.

Кто-нибудь знает, как установить переменную global, чтобы избежать появления формы входа в систему после того, как пользователь уже успешно прошел проверку подлинности?

Ответ 1

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

ОРИГИНАЛЬНЫЙ ОТВЕТ:

Более общая проблема, с которой вы сталкиваетесь, заключается в том, как сохранить состояние в нескольких действиях и во всех частях приложения. Статическая переменная (например, singleton) является обычным способом реализации Java. Однако я обнаружил, что более элегантный способ в Android - связать ваше состояние с контекстом приложения.

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

Способ сделать это - создать собственный подкласс android.app.Application, а затем указать этот класс в теге приложения в ваш манифест. Теперь Android автоматически создаст экземпляр этого класса и сделает его доступным для всего вашего приложения. Вы можете получить к нему доступ из любого context с помощью метода Context.getApplicationContext() (Activity также предоставляет метод getApplication(), который имеет тот же эффект). Ниже приведен чрезвычайно упрощенный пример с последующими оговорками:

class MyApp extends Application {

  private String myState;

  public String getState(){
    return myState;
  }
  public void setState(String s){
    myState = s;
  }
}

class Blah extends Activity {

  @Override
  public void onCreate(Bundle b){
    ...
    MyApp appState = ((MyApp)getApplicationContext());
    String state = appState.getState();
    ...
  }
}

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

Что-то примечание из приведенного выше примера; предположим, что мы сделали что-то вроде:

class MyApp extends Application {

  private String myState = /* complicated and slow initialization */;

  public String getState(){
    return myState;
  }
}

Теперь эта медленная инициализация (например, попадание диска, попадание в сеть, блокирование и т.д.) будет выполняться каждый раз при создании приложения! Вы можете подумать, что это только один раз для процесса, и мне придется платить все равно, так? Например, как упоминает ниже Диана Хакборн, вполне возможно, чтобы ваш процесс был создан, - просто - для обработки события широковещательной передачи. Если ваша трансляция не нуждается в этом состоянии, вы потенциально просто сделали целую серию сложных и медленных операций для ничего. Здесь можно назвать ленивое инстанцирование. Ниже приводится несколько более сложный способ использования приложения, который имеет больше смысла ни для чего, кроме простейшего использования:

class MyApp extends Application {

  private MyStateManager myStateManager = new MyStateManager();

  public MyStateManager getStateManager(){
    return myStateManager ;
  }
}

class MyStateManager {

  MyStateManager() {
    /* this should be fast */
  }

  String getState() {
    /* if necessary, perform blocking calls here */
    /* make sure to deal with any multithreading/synchronicity issues */

    ...

    return state;
  }
}

class Blah extends Activity {

  @Override
  public void onCreate(Bundle b){
    ...
    MyStateManager stateManager = ((MyApp)getApplicationContext()).getStateManager();
    String state = stateManager.getState();
    ...
  }
}

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

ПРИМЕЧАНИЕ 1: Также, как указано в антифашировании, для правильной привязки вашего приложения к вашему приложению в файле манифеста необходим тег. Опять же, см. Документы Android для получения дополнительной информации. Пример:

<application
     android:name="my.application.MyApp" 
     android:icon="..."
     android:label="...">
</application>

ПРИМЕЧАНИЕ 2: user608578 спрашивает ниже, как это работает с управлением жизненными циклами собственных объектов. Я не в курсе, как использовать собственный код с Android в малейшей степени, и я не могу ответить, как это будет взаимодействовать с моим решением. Если у кого-то есть ответ на этот вопрос, я готов отблагодарить их и поместить информацию в этот пост для максимальной видимости.

ДОПОЛНЕНИЕ:

Как отметили некоторые люди, это не решение для состояния постоянного, что, возможно, должно было бы больше подчеркнуть в исходном ответе. То есть это не предназначено для решения проблемы сохранения пользовательской или другой информации, которая должна сохраняться в течение жизни приложений. Таким образом, я считаю, что большая часть критики ниже связана с тем, что Приложения были убиты в любое время и т.д., Но они не были сохранены на подклассе приложения, поскольку все, что нужно было сохранить на диске, не должно храниться на диске. Он предназначен для хранения временного, легко восстанавливаемого состояния приложения (например, если пользователь зарегистрирован, например) и компонентов, которые представляют собой один экземпляр (например, сетевой диспетчер приложений) ( НЕ singleton!) в природе.

Dayerman был достаточно любезен, чтобы указать на интересную беседу с Рето Мейером и Дианой Хакборн, в которых использование подклассов приложений не рекомендуется в пользу Синглтон. Соматик также отметил нечто подобное ранее, хотя я не видел его в то время. Из-за ролей Reto и Dianne в поддержании платформы Android я не могу добросовестно рекомендовать игнорировать их советы. Что они говорят, идет. Я хочу не согласиться с мнением, выраженным в отношении предпочтения Singleton над подклассами приложений. В моем несогласии я буду использовать понятия, которые лучше всего объясняются в этом объяснении StackExchange шаблона проектирования Singleton, так что мне не нужно определять термины в этом ответе. Я настоятельно рекомендую скрыть ссылку, прежде чем продолжить. Точка за точкой:

Dianne утверждает: "Нет оснований для подкласса из приложения. Это ничем не отличается от создания одноэлементного..." Это первое утверждение неверно. Для этого есть две основные причины. 1) Класс Application обеспечивает лучшую пожизненную гарантию для разработчика приложений; гарантируется срок службы приложения. Синглтон не EXPLICITLY привязан к времени жизни приложения (хотя это эффективно). Это может быть не проблема для вашего среднего разработчика приложений, но я бы сказал, что это именно тот тип контракта, который должен предлагать Android API, и он обеспечивает гораздо большую гибкость для системы Android, минимизируя время жизни связанных данные. 2) Класс Application предоставляет разработчику приложения один держатель экземпляра для состояния, который сильно отличается от держателя статуса Singleton. Список различий см. В пояснительной ссылке Singleton выше.

Dianne продолжает: "... скорее всего, вы будете сожалеть об этом в будущем, так как вы обнаружите, что ваш объект приложения становится таким большим запутанным беспорядком того, что должно быть независимой логикой приложения". Это, безусловно, неверно, но это не является основанием для выбора Singleton над подклассом Application. Ни один из аргументов Diane не дает основания полагать, что использование Singleton лучше, чем подкласс приложения, все, что она пытается установить, заключается в том, что использование Singleton не хуже подкласса Application, который, как я считаю, является ложным.

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

Dianne заканчивается на "В самой структуре есть тонны и тонны синглтонов для всех небольших общих данных, которые он поддерживает для приложения, таких как кеши загруженных ресурсов, пулы объектов и т.д. Он отлично работает". Я не утверждаю, что использование Singletons не может работать нормально или не является законной альтернативой. Я утверждаю, что Singletons не обеспечивают столь сильный контракт с системой Android, как использование подкласса Application, и далее, что использование Singletons обычно указывает на негибкий дизайн, который не так легко модифицировать и приводит к множеству проблем в будущем. IMHO, сильный контракт, предлагаемый Android API для приложений для разработчиков, является одним из самых привлекательных и приятных аспектов программирования на Android, и помог ему в раннем принятии разработчиков, что привело платформу Android к успеху, который у нее есть сегодня. Предложение использования Singletons неявно отходит от сильного контракта API и, на мой взгляд, ослабляет рамки Android.

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

Я оставляю вас с нижеследующим списком минусов для синглтонов, которые украдены из предыдущей ссылки StackExchange:

  • Неспособность использовать абстрактные или интерфейсные классы;
  • Неспособность к подклассу;
  • Высокая связь между приложениями (трудно модифицировать);
  • Трудно проверить (не может подделывать/издеваться над модульными тестами);
  • Трудно распараллеливаться в случае изменчивого состояния (требуется обширная блокировка);

и добавьте свой собственный:

  • Нечеткий и неуправляемый контракт на жизнь, не подходящий для разработки Android (или большинства других);

Ответ 2

Создайте этот подкласс

public class MyApp extends Application {
  String foo;
}

В AndroidManifest.xml добавьте android: name

Пример

<application android:name=".MyApp" 
       android:icon="@drawable/icon" 
       android:label="@string/app_name">

Ответ 3

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

Рассмотрим случай - ваше приложение переходит в фоновый режим, потому что кто-то звонит вам (сейчас приложение на телефоне находится на переднем плане). В этом случае && в некоторых других условиях (проверьте приведенную выше ссылку на то, что они могут быть) ОС может убить ваш процесс приложения, включая экземпляр подкласса Application. В результате государство теряется. Когда вы позже вернетесь в приложение, ОС восстановит свой стек активности и Application экземпляр подкласса, однако поле myState будет null.

AFAIK, единственным способом гарантировать безопасность государства является использование любого вида состояния, например, используя приватный файл приложения или SharedPrefernces (в конечном итоге он использует закрытый для файла приложения во внутренней файловой системе).

Ответ 4

Просто заметьте.

добавить:

android:name=".Globals"

или как вы назвали ваш подкласс тегу существующий <application>. Я продолжал пытаться добавить еще один тег <application> в манифест и получить исключение.

Ответ 5

Я также не мог найти, как указать тег приложения, но после большого количества Googling это стало очевидным из документов файла манифеста: используйте android: name, в дополнение к значку по умолчанию и метке в строфе приложения.

Android: имя Полноценное имя подкласса Application, реализованное для приложения. Когда процесс приложения запускается, этот класс создается до любого из компонентов приложения.

Подкласс не является обязательным; большинству приложений это не понадобится. В отсутствие подкласса Android использует экземпляр базового класса Application.

Ответ 6

Как обеспечить сбор встроенной памяти с такими глобальными структурами?

Действия имеют метод onPause/onDestroy(), который требует уничтожения, но класс Application не имеет эквивалентов. Какой механизм рекомендуется для обеспечения того, чтобы глобальные структуры (особенно те, которые содержат ссылки на встроенную память), собирали мусор соответствующим образом, когда приложение либо убито, либо стек задачи помещен в фоновом режиме?

Ответ 7

Просто вам нужно определить имя приложения, как показано ниже:

<application
  android:name="ApplicationName" android:icon="@drawable/icon">
</application>

Ответ 8

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

Итак, найдите правильный путь, и нет лучшего способа.

Ответ 9

Как было сказано выше, ОС может убить APPLICATION без уведомления (нет события onDestroy), поэтому нет возможности сохранить эти глобальные переменные.

SharedPreferences может быть решением EXCEPT, у вас есть КОМПЛЕКСНЫЕ СТРУКТУРНЫЕ переменные (в моем случае у меня был целочисленный массив для хранения идентификаторов, которые пользователь уже обработал). Проблема с SharedPreferences заключается в том, что хранить и извлекать эти структуры сложно каждый раз при необходимости.

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

Ответ 10

У вас может быть статическое поле для хранения такого состояния. Или поместите его в Resource Bundle и восстановите его на onCreate (Bundle savedInstanceState). Просто убедитесь, что вы полностью понимаете жизненный цикл, управляемый приложениями Android (например, почему login() вызывается при изменении ориентации клавиатуры).

Ответ 11

НЕ используйте Используйте другой тег <application> в файле манифеста. Просто сделайте одно изменение в существующем теге <application>, добавьте эту строку android:name=".ApplicationName", где ApplicationName будет именем ваш подкласс (используйте для хранения глобального), который вы собираетесь создать.

так что, наконец, тэг ОДИН И ТОЛЬКО <application> в файле манифеста должен выглядеть так: -

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.AppCompat.NoActionBar"
        android:name=".ApplicationName"
        >

Ответ 12

Вы можете сделать это, используя два подхода:

  • Использование класса приложения
  • Использование общих настроек

  • Использование класса приложения

Пример:

class SessionManager extends Application{

  String sessionKey;

  setSessionKey(String key){
    this.sessionKey=key;
  }

  String getSessisonKey(){
    return this.sessionKey;
  }
}

Вы можете использовать вышеуказанный класс для реализации входа в свой MainActivity, как показано ниже. Код будет выглядеть примерно так:

@override 
public void onCreate (Bundle savedInstanceState){
  // you will this key when first time login is successful.
  SessionManager session= (SessionManager)getApplicationContext();
  String key=getSessisonKey.getKey();
  //Use this key to identify whether session is alive or not.
}

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

  1. Использование общих настроек.

    String MYPREF="com.your.application.session"
    
    SharedPreferences pref= context.getSharedPreferences(MyPREF,MODE_PRIVATE);
    
    //Insert key as below:
    
    Editot editor= pref.edit();
    
    editor.putString("key","value");
    
    editor.commit();
    
    //Get key as below.
    
    SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
    
    String key= getResources().getString("key");
    

Ответ 13

вы можете использовать Intents, Sqlite или Shared Preferences. Когда речь заходит о медиа-хранилище, например о документах, фотографиях и видео, вы можете создавать новые файлы.

Ответ 14

class GlobaleVariableDemo extends Application {

    private String myGlobalState;

    public String getGlobalState(){
     return myGlobalState;
    }
    public void setGlobalState(String s){
     myGlobalState = s;
    }
}

class Demo extends Activity {

@Override
public void onCreate(Bundle b){
    ...
    GlobaleVariableDemo appState = ((GlobaleVariableDemo)getApplicationContext());
    String state = appState.getGlobalState();
    ...
    }
}

Ответ 15

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

Ответ 16

Подход к подклассу также используется базой BARACUS. С моей точки зрения подклассы Приложение предназначалось для работы с жизненными циклами Android; это то, что делает любой контейнер приложений. Вместо того, чтобы иметь глобальные значения, я регистрирую beans в этом контексте, позволяя им вводиться в любой класс, управляемый контекстом. Каждый инъецируемый экземпляр bean на самом деле является одноэлементным.

Подробнее см. в этом примере

Почему ручная работа, если вы можете иметь гораздо больше?

Ответ 17

Вы можете создать класс, который расширяет класс Application, а затем объявит вашу переменную как поле этого класса и предоставит метод getter для нее.

public class MyApplication extends Application {
    private String str = "My String";

    synchronized public String getMyString {
        return str;
    }
}

И затем, чтобы получить доступ к этой переменной в вашей Деятельности, используйте это:

MyApplication application = (MyApplication) getApplication();
String myVar = application.getMyString();