Intereting Posts

Как объявить глобальные переменные в 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, чтобы избежать появления формы входа в систему после того, как пользователь уже успешно аутентифицирует?

Solutions Collecting From Web of "Как объявить глобальные переменные в Android?"

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

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

Более общая проблема, с которой вы сталкиваетесь, заключается в том, как сохранить состояние в нескольких действиях и во всех частях вашего приложения. Статическая переменная (например, 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 в малейшей степени, и я не могу ответить, как это будет взаимодействовать с моим решением. Если у кого-то есть ответ на этот вопрос, я готов кредитовать их и помещать информацию в этот пост для максимальной видимости.

ДОПОЛНЕНИЕ:

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

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

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

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

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

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

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

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

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

И добавьте свое:

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

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

 public class MyApp extends Application { String foo; } 

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

пример

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

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

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

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

Просто записка ..

Добавить:

 android:name=".Globals" 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Поэтому, наконец, ваш тег ONE AND ONLY <application> в файле манифеста должен выглядеть следующим образом:

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

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

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

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

Пример:

 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. } 

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

  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"); 

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

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

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

См. Этот пример для подробностей

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

 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(); ... } } 

Вы можете создать класс, который расширяет класс 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();