Вопрос

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

В основной деятельности 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.

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

Это было полезно?

Решение

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

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

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

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

Способ сделать это — создать свой собственный подкласс android.app.Приложение, а затем укажите этот класс в теге приложения в манифесте.Теперь 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();
    ...
  }
}

По сути, это имеет тот же эффект, что и использование статической переменной или синглтона, но довольно хорошо интегрируется в существующую платформу 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();
    ...
  }
}

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

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

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

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

ДОПОЛНЕНИЕ:

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

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

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

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

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

Дайанн заканчивает словами: «В самой платформе есть масса синглтонов для всех небольших общих данных, которые она поддерживает для приложения, таких как кеши загруженных ресурсов, пулы объектов и т. д.Это прекрасно работает.» Я не утверждаю, что использование синглтонов не может работать нормально или не является законной альтернативой.Я утверждаю, что синглтоны не обеспечивают столь же прочного контракта с системой Android, как использование подкласса приложения, и, кроме того, использование синглтонов обычно указывает на негибкий дизайн, который нелегко модифицировать и который приводит ко многим проблемам в будущем.ИМХО, прочный контракт, который Android API предлагает разработчикам приложений, является одним из наиболее привлекательных и приятных аспектов программирования под Android и помог привести к раннему внедрению разработчиков, что привело платформу Android к успеху, которого она имеет сегодня.Предложение использовать Singletons неявно отходит от строгого контракта API и, на мой взгляд, ослабляет платформу Android.

Дайанна также прокомментировала ниже, упомянув дополнительный недостаток использования подклассов приложений: они могут поощрять или облегчать написание кода с меньшей производительностью.Это очень верно, и я отредактировал этот ответ, чтобы подчеркнуть важность рассмотрения здесь производительности и правильного подхода, если вы используете подклассы приложений.Как утверждает Дайанна, важно помнить, что экземпляр вашего класса приложения будет создаваться каждый раз, когда загружается ваш процесс (может быть несколько раз одновременно, если ваше приложение выполняется в нескольких процессах!), даже если процесс загружается только для фоновой трансляции. событие.Поэтому важно использовать класс 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">

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

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

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

Просто замечание..

добавлять:

android:name=".Globals"

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

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

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

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

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

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

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

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

Как обсуждалось выше, ОС может закрыть ПРИЛОЖЕНИЕ без какого-либо уведомления (событие onDestroy отсутствует), поэтому нет возможности сохранить эти глобальные переменные.

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

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

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

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

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

НЕ Используйте другой <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"
        >

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

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

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

Этот метод подойдет для временного хранения.Вы действительно понятия не имеете, когда операционная система закроет приложение из-за нехватки памяти.Когда ваше приложение находится в фоновом режиме, а пользователь перемещается по другому приложению, для запуска которого требуется больше памяти, ваше приложение будет закрыто, поскольку операционная система отдает больший приоритет приоритетным процессам, чем фоновым.Следовательно, ваш объект приложения будет иметь значение 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");
    

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

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

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

    synchronized public String getMyString {
        return str;
    }
}

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

MyApplication application = (MyApplication) getApplication();
String myVar = application.getMyString();
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top