문제

로그인이 필요한 응용 프로그램을 작성하고 있습니다. 메인과 로그인 활동을 만들었습니다.

주요 활동에서 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.

사용자가 이미 성공적으로 인증 한 후 로그인 양식을 피하기 위해 가변적 인 글로벌을 설정하는 방법을 아는 사람이 있습니까?

도움이 되었습니까?

해결책

나는 Android가 비교적 새롭고 Android 개발에 잘 확립되지 않은 많은 영역이 있었던 09 년 에이 답을 썼습니다. 나는이 게시물의 맨 아래에 긴 부록을 추가하여 일부 비판을 다루고, 서브 클래싱 응용 프로그램보다는 싱글 톤을 사용하는 것과 관련된 철학적 불일치를 자세히 설명했습니다. 자신의 위험으로 읽으십시오.

원래 답변 :

더 일반적인 문제는 여러 활동과 응용 프로그램의 모든 부분에서 상태를 저장하는 방법입니다. 정적 변수 (예 : 싱글 톤)는이를 달성하는 일반적인 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();
    ...
  }
}

이것은 본질적으로 정적 변수 또는 싱글 톤을 사용하는 것과 동일한 효과를 가지지 만 기존 Android 프레임 워크에 상당히 잘 통합됩니다. 이것은 프로세스에서 작동하지 않습니다 (앱이 여러 프로세스가있는 드문 드문 중 하나라면).

위의 예에서 주목해야 할 것; 대신 우리가 다음과 같은 일을했다고 가정 해 봅시다.

class MyApp extends Application {

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

  public String getState(){
    return myState;
  }
}

이제이 느린 초기화 (예를 들어 디스크 타격, 네트워크, 차단 등)와 같은 응용 프로그램이 인스턴스화 될 때마다 수행됩니다! 글쎄요 예를 들어, Dianne Hackborn이 아래에서 언급했듯이, 배경 방송 이벤트를 처리하기 위해 프로세스를 인스턴스화 할 수 있습니다. 방송 처리 에이 상태가 필요하지 않은 경우 잠재적으로 일련의 복잡하고 느린 작업을 수행하지 못했습니다. 게으른 인스턴스화는 여기 게임의 이름입니다. 다음은 애플리케이션을 사용하는 약간 더 복잡한 방법으로, 가장 간단한 용도를 제외하고는 더 의미가 있습니다.

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

여기에서 싱글 톤을보다 우아한 솔루션으로 사용하는 것보다 응용 프로그램 서브 클래싱을 선호하지만, 개발자는 국가의 성능 및 멀티 스레딩 의미를 응용 프로그램 서브 클래스와 전혀 생각하지 않는 것보다 실제로 필요한 경우 싱글 톤을 사용하는 것입니다.

NOTE 1: 또한 Anticafe가 언급 한대로 응용 프로그램에 응용 프로그램을 올바르게 묶으려면 매니페스트 파일에 태그가 필요합니다. 자세한 내용은 Android 문서를 참조하십시오. An example:

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

노트 2: user608578은 이것이 네이티브 객체 라이프 사이클 관리와 어떻게 작동하는지 아래를 묻습니다. 나는 Android와 함께 Native Code를 조금씩 사용하는 데 속도가 빠르지 않으며, 그것이 내 솔루션과 어떻게 상호 작용하는지 대답 할 자격이 없습니다. 누군가 이에 대한 답이 있다면, 나는 그것들을 신용 하고이 게시물에 정보를 최대한 활용할 수 있도록 기꺼이 정보를 넣을 것입니다.

부록:

어떤 사람들이 지적했듯이, 이것은입니다 ~ 아니다 솔루션 지속성 있는 상태, 원래의 대답에서 더 강조했을 것입니다. 즉, 이는 응용 프로그램 수명에 걸쳐 지속되는 사용자 또는 기타 정보를 저장하기위한 솔루션이 아닙니다. 따라서, 나는 디스크에 지속되어야하는 모든 것이 애플리케이션 서브 클래스를 통해 저장되어서는 안되기 때문에 언제든지 사망하는 응용 프로그램과 관련하여 아래의 대부분의 비판을 고려합니다. 임시적이고 쉽게 다시 만들 수있는 응용 프로그램 상태 (예 : 사용자가 로그인되는지) 및 단일 인스턴스 (예 : 응용 프로그램 네트워크 관리자) 인 구성 요소를 저장하기위한 솔루션입니다.아니다 싱글 톤!) 본질적으로.

Dayerman은 흥미로운 것을 지적하기에 충분히 친절했습니다. Reto Meier 및 Dianne Hackborn과의 대화 적용 서브 클래스의 사용은 싱글 톤 패턴에 유리하게 권장되지 않습니다. Somatik은 또한 당시에는 보지 못했지만이 성격의 일찍 무언가를 지적했습니다. 안드로이드 플랫폼을 유지하는 데있어서 Reto와 Dianne의 역할 때문에, 나는 그들의 조언을 무시하는 것을 권장 할 수는 없습니다. 그들이 말하는 것은 간다. 나는 응용 프로그램 서브 클래스보다 싱글 톤을 선호하는 것과 관련하여 표현 된 의견에 동의하지 않기를 원한다. 내 의견 불일치로 나는 가장 잘 설명 된 개념을 활용할 것입니다. 싱글 톤 디자인 패턴에 대한이 stackexchange 설명, 이 답변에서 용어를 정의 할 필요가 없습니다. 계속하기 전에 링크를 훑어 보는 것이 좋습니다. 포인트 :

Dianne은 "응용 프로그램에서 서브 클래스 할 이유가 없다. 싱글 톤을 만드는 것과 다르지 않다"고 말합니다.이 첫 번째 주장은 잘못되었습니다. 이에 대한 두 가지 주요 이유가 있습니다. 1) 응용 프로그램 클래스는 응용 프로그램 개발자에게 더 나은 수명 보증을 제공합니다. 응용 프로그램의 수명이 보장됩니다. 싱글 톤은 응용 프로그램의 수명에 명시 적으로 연결되어 있지 않습니다 (효과적이지만). 이것은 평균 애플리케이션 개발자에게는 문제가되지 않을 수 있지만, 이것이 바로 Android API가 제공 해야하는 계약 유형이라고 주장하며, 관련 수명을 최소화함으로써 Android 시스템에 훨씬 더 많은 유연성을 제공합니다. 데이터. 2) 애플리케이션 클래스는 응용 프로그램 개발자에게 단일 인스턴스 보유자를 제공합니다. 차이점 목록은 위의 싱글 톤 설명 링크를 참조하십시오.

Dianne은 계속해서 "... 당신의 애플리케이션 객체가 독립적 인 응용 프로그램 논리가되어야하는 것에 대한이 큰 엉킨 혼란이되는 것을 발견함에 따라 앞으로 후회할 것입니다." 이것은 확실히 틀리지 않지만 응용 프로그램 서브 클래스보다 싱글 톤을 선택하는 이유는 아닙니다. Diane의 주장 중 어느 것도 싱글 톤을 사용하는 것이 응용 프로그램 서브 클래스보다 낫다는 이유는 없으며, 그녀가 설정하려는 모든 것은 싱글 톤을 사용하는 것이 응용 프로그램 서브 클래스보다 나쁘지 않기 때문에 내가 거짓이라고 생각하는 것입니다.

그녀는 계속해서 "이것은 당신이 이러한 것들을 어떻게 관리 해야하는지에 더 자연스럽게 이어집니다. 이것은 응용 프로그램 서브 클래스를 사용하여 주문형을 초기화 할 수없는 이유가 없다는 사실을 무시합니다. 다시 한 번 차이가 없습니다.

Dianne은 "프레임 워크 자체는로드 된 자원의 캐시, 객체 풀 등과 같이 앱에 대해 유지 관리하는 모든 작은 공유 데이터에 대해 톤과 톤의 싱글 톤을 가지고 있습니다." 나는 싱글 톤을 사용하는 것이 잘 작동 할 수 없거나 합법적 인 대안이 아니라고 주장하지 않습니다. 나는 싱글 톤이 애플리케이션 서브 클래스를 사용하는 것만 큼 안드로이드 시스템과의 계약을 강력하게 제공하지 않으며, 싱글 톤을 사용하는 것은 일반적으로 쉽게 수정되지 않는 융통성없는 디자인을 가리키며 많은 문제로 이어진다 고 주장하고 있습니다. IMHO, Android API가 개발자 애플리케이션에 제공하는 강력한 계약 인 IMHO는 Android와 함께 프로그래밍의 가장 매력적이고 즐거운 측면 중 하나이며, 초기 개발자 채택으로 인해 Android 플랫폼을 오늘날의 성공으로 이끌었습니다. 싱글 톤을 사용하는 것은 강력한 API 계약에서 암시 적으로 멀어지고 있으며, 제 생각에는 안드로이드 프레임 워크가 약화됩니다.

Dianne은 아래에 언급했으며, 응용 프로그램 하위 클래스 사용에 대한 추가 단점을 언급하면서 성능 코드를 덜 작성하는 것을 더 쉽게 작성하거나 쉽게 작성할 수 있습니다. 이것은 매우 사실이며, 여기에서 Perf를 고려하는 것의 중요성을 강조하고 응용 프로그램 서브 클래스를 사용하는 경우 올바른 접근 방식을 취하는이 답변을 편집했습니다. Dianne이 말하면, 프로세스가 배경 방송에 대해서만로드 되더라도 프로세스가로드 될 때마다 응용 프로그램 클래스가 인스턴스화 될 때마다 (응용 프로그램이 여러 프로세스에서 한 번에 여러 번있을 수 있음) 기억하는 것이 중요합니다. 이벤트. 따라서 애플리케이션 클래스를 더 많이 사용하는 것이 중요합니다.

초기 StackexChange 링크에서 도난당한 다음과 같은 단일 단점 목록을 남겨 둡니다.

  • 추상 또는 인터페이스 클래스를 사용할 수 없음;
  • 서브 클래스의 무능력;
  • 응용 프로그램 전체의 높은 커플 링 (수정하기 어렵다);
  • 테스트하기 어렵습니다 (단위 테스트에서 가짜/조롱 할 수 없습니다);
  • 변이 가능한 상태의 경우 병렬화하기 어렵다 (광범위한 잠금이 필요하다).

내 자신을 추가하십시오.

  • 안드로이드 (또는 대부분의 다른) 개발에 적합하지 않은 불분명하고 관리 할 수없는 평생 계약;

다른 팁

이 서브 클래스를 만듭니다

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에 의해 제안 된 것은 양호하지만 OS가 전체 애플리케이션 프로세스를 죽이는 경우가 있습니다. 다음은 이것에 대한 문서입니다. 프로세스 및 라이프 사이클.

사례를 고려하십시오 - 누군가가 당신을 부르기 때문에 앱이 배경으로 들어갑니다 (전화 앱은 지금 전경에 있습니다). 이 경우 && 다른 조건에서 (위의 링크를 확인할 수 있는지 확인) OS는 Application 서브 클래스 인스턴스. 결과적으로 상태는 손실됩니다. 나중에 응용 프로그램으로 돌아 오면 OS는 활동 스택을 복원하고 Application 그러나 서브 클래스 인스턴스 myState 필드가 될 것입니다 null.

AFAIK, 상태 안전을 보장하는 유일한 방법은 상태를 지속하는 모든 종류의 상태를 사용하는 것, 예를 들어 응용 프로그램 파일 또는 SharedPrefernces (결국 내부 파일 시스템의 응용 프로그램 파일에 개인을 사용합니다).

그냥 메모 ..

추가하다:

android:name=".Globals"

또는 당신이 당신의 서브 클래스를 기존의 <application> 꼬리표. 나는 계속 다른 것을 추가하려고 노력했다 <application> 매니페스트에 태그를 지정하고 예외를 얻을 수 있습니다.

응용 프로그램 태그를 지정하는 방법을 찾을 수 없었지만 많은 인터넷 검색 후 매니페스트 파일 문서에서 분명해졌습니다.

Android : 응용 프로그램에 구현 된 응용 프로그램 서브 클래스의 자격을 갖춘 이름을 지정하십시오. 신청 프로세스가 시작되면이 클래스는 응용 프로그램의 구성 요소 전에 인스턴스화됩니다.

서브 클래스는 선택 사항입니다. 대부분의 응용 프로그램은 필요하지 않습니다. 서브 클래스가없는 경우 Android는 기본 응용 프로그램 클래스의 인스턴스를 사용합니다.

이러한 글로벌 구조로 기본 메모리를 수집하는 것은 어떻습니까?

활동에는 있습니다 onPause/onDestroy() 파괴시 요청되었지만 응용 프로그램 클래스에는 동등한 방법이 없습니다. 애플리케이션이 사망하거나 작업 스택이 백그라운드에 넣을 때 글로벌 구조 (특히 기본 메모리에 대한 참조를 포함하는 구조물)가 쓰레기를 적절하게 수집 할 수 있도록 권장됩니까?

아래에서와 같이 응용 프로그램 이름을 정의하면됩니다.

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

위에서 논의 된 것처럼 OS는 알림없이 응용 프로그램을 죽일 수 있으므로 (OnDestroy 이벤트가 없음) 이러한 글로벌 변수를 저장할 방법이 없습니다.

복잡한 구조화 된 변수가있는 것을 제외하고는 공유 차가가 솔루션이 될 수 있습니다 (제 경우에는 사용자가 이미 처리 한 ID를 저장할 정수 배열이있었습니다). 공유 예약의 문제점은 필요한 값이 필요할 때마다 이러한 구조를 저장하고 검색하기가 어렵다는 것입니다.

제 경우에는 배경 서비스가 있었기 때문에이 변수를 거기로 옮길 수 있었고 서비스에 OnDestroy 이벤트가 있기 때문에 해당 값을 쉽게 저장할 수 있습니다.

일부 변수가 SQLite에 저장되고 앱의 대부분의 활동에서 사용해야하는 경우. 그런 다음 응용 프로그램이이를 달성하는 가장 좋은 방법 일 수 있습니다. 응용 프로그램이 시작될 때 데이터베이스에서 변수를 쿼리하고 필드에 저장하십시오. 그런 다음 활동에서 이러한 변수를 사용할 수 있습니다.

따라서 올바른 방법을 찾으십시오. 최선의 방법은 없습니다.

이런 종류의 상태를 저장할 정적 필드가있을 수 있습니다. 또는 리소스 번들에 넣고 OnCreate (Bundle SavedInstancestate)에서 거기에서 복원하십시오. Android App Managed Lifecycle (예 : Login ()가 키보드 방향 변경에서 호출되는 이유를 완전히 이해하십시오).

하지 않다 다른 것을 사용하십시오 <application> Manifest 파일의 태그. 기존에서 한 가지 변경 사항을 수행하십시오 <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"
        >

의도, SQLITE 또는 공유 환경 설정을 사용할 수 있습니다. 문서, 사진 및 비디오와 같이 미디어 저장소와 관련하여 대신 새 파일을 만들 수 있습니다.

두 가지 접근 방식을 사용 하여이 작업을 수행 할 수 있습니다.

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

활동 결과는 이력서 전에 호출됩니다. 따라서 로그인 수표를 이력서로 이동하면 SECOMD 활동이 긍정적 인 결과를 반환하면 두 번째 로그인을 차단할 수 있습니다. 이력서는 매번 호출되므로 처음으로 불리는 걱정이 없습니다.

서브 클래싱의 접근 방식은 Baracus 프레임 워크에서도 사용되었습니다. 내 관점에서 서브 클래싱 애플리케이션은 Android의 수명 인과 함께 작동하기위한 것입니다. 이것이 무엇입니다 어느 응용 프로그램 컨테이너. 그런 다음 글로벌을 갖는 대신, 나는이 맥락에 콩을 등록하여 상황에 따라 관리 할 수있는 모든 클래스에 꿀벌을 주입하게합니다. 모든 주입 된 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();
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top