Programing

Android에서 전역 변수를 선언하는 방법은 무엇입니까?

lottogame 2020. 10. 3. 09:48
반응형

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.

사용자가 이미 성공적으로 인증 한 후에 로그인 양식이 나타나지 않도록 변수 전역을 설정하는 방법을 아는 사람이 있습니까?


나는 안드로이드가 비교적 새롭고 안드로이드 개발에서 잘 확립되지 않은 영역이 많았던 '09에이 답변을 썼습니다. 이 게시물의 하단에 긴 부록을 추가하여 일부 비판을 다루고 Application을 서브 클래 싱하는 대신 싱글 톤 사용에 대한 철학적 불일치를 자세히 설명했습니다. 자신의 책임하에 읽으십시오.

원래 답변 :

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

여기서는 더 우아한 솔루션으로 싱글 톤을 사용하는 것보다 Application 서브 클래 싱을 선호하지만, 실제로 필요하다면 개발자는 상태를 Application 서브 클래스와 연관시키는 성능 및 멀티 스레딩 의미를 전혀 생각하지 않는 것보다 싱글 톤을 사용하는 것이 좋습니다.

참고 1 : 또한 anticafe가 언급했듯이 애플리케이션 재정의를 애플리케이션에 올바르게 연결하려면 매니페스트 파일에 태그가 필요합니다. 다시 한 번 더 자세한 정보는 Android 문서를 참조하세요. 예 :

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

참고 2 : user608578은 기본 개체 수명주기 관리와 함께 작동하는 방법을 아래에 묻습니다. 나는 안드로이드에서 네이티브 코드를 사용하는 데 조금이라도 빠르지 않으며 그것이 내 솔루션과 어떻게 상호 작용하는지 대답 할 자격이 없습니다. 누군가 이것에 대한 답을 가지고 있다면, 나는 그들을 기꺼이 인정하고 최대한의 가시성을 위해이 게시물에 정보를 넣을 것입니다.

추가:

일부 사람들이 지적했듯이 이것은 지속적인 상태에 대한 해결책 이 아니며 아마도 원래 답변에서 더 강조 했어야 할 것입니다. 즉, 이것은 사용자 또는 응용 프로그램 수명 동안 유지되는 기타 정보를 저장하기위한 솔루션이 아닙니다. 따라서 디스크에 유지되어야하는 모든 항목이 Application 하위 클래스를 통해 저장되어서는 안되므로 언제든지 종료되는 응용 프로그램 등과 관련된 아래의 대부분의 비판을 고려합니다. 이는 일시적이고 쉽게 다시 생성 할 수있는 응용 프로그램 상태 (예 : 사용자가 로그인했는지 여부) 및 본질적 으로 단일 인스턴스 (예 : 응용 프로그램 네트워크 관리자) ( 단일 항목이 아님 ) 인 구성 요소를 저장하기위한 솔루션입니다 .

Dayerman은 Reto Meier 및 Dianne Hackborn과 의 흥미로운 대화 를 지적 할만큼 친절했습니다. Application 하위 클래스의 사용은 Singleton 패턴을 선호하는 것을 권장하지 않습니다. Somatik은 또한 초기에 이러한 성격을 지적했지만 당시에는 보지 못했습니다. Android 플랫폼을 유지하는 Reto와 Dianne의 역할 때문에 나는 선의로 그들의 조언을 무시할 것을 권장 할 수 없습니다. 그들이 말하는 것은 간다. Application 하위 클래스보다 Singleton을 선호하는 것과 관련하여 표현 된 의견에 동의하지 않습니다. 내 의견 불일치에서 나는 Singleton 디자인 패턴에 대한 StackExchange 설명 에서 가장 잘 설명 된 개념을 사용할 것 입니다., 그래서이 답변에서 용어를 정의 할 필요가 없습니다. 계속하기 전에 링크를 훑어 보는 것이 좋습니다. 포인트 별 :

Dianne은 "Application에서 서브 클래스 할 이유가 없습니다. 싱글 톤을 만드는 것과 다르지 않습니다 ..."이 첫 번째 주장은 올바르지 않습니다. 여기에는 두 가지 주요 이유가 있습니다. 1) Application 클래스는 응용 프로그램 개발자에게 더 나은 수명 보장을 제공합니다. 응용 프로그램의 수명이 보장됩니다. 싱글 톤은 애플리케이션의 수명과 명시 적으로 연결되지 않습니다 (효과적 임에도 불구하고). 이것은 일반 애플리케이션 개발자에게는 문제가되지 않을 수 있지만 이것이 Android API가 제공해야하는 계약 유형과 정확히 일치하며 관련 수명을 최소화하여 Android 시스템에 훨씬 더 많은 유연성을 제공한다고 주장합니다. 데이터. 2) Application 클래스는 애플리케이션 개발자에게 상태에 대한 단일 인스턴스 홀더를 제공합니다. 이것은 Singleton 국가 보유자와는 매우 다릅니다. 차이점 목록은 위의 싱글 톤 설명 링크를 참조하십시오.

Dianne은 계속해서 "... 당신의 Application 객체가 독립적 인 애플리케이션 로직이되어야하는 것들이 이렇게 엉망진창이되는 것을 발견 할 때 후회할 것 같습니다." 이것은 확실히 틀린 것은 아니지만 이것은 응용 프로그램 하위 클래스보다 싱글 톤을 선택하는 이유가 아닙니다. Diane의 주장 중 어느 것도 Singleton을 사용하는 것이 Application 하위 클래스보다 낫다는 이유를 제공하지 않습니다. 그녀가 설정하려는 모든 시도는 Singleton을 사용하는 것이 내가 거짓이라고 생각하는 Application 하위 클래스보다 나쁘지 않다는 것입니다.

그녀는 계속해서 "그리고 이것은 이러한 것들을 어떻게 관리해야하는지, 즉 주문형으로 초기화하는 방법으로 더욱 자연스럽게 이어집니다." 이는 Application 서브 클래스를 사용하여 요청시 초기화 할 수없는 이유가 없다는 사실을 무시합니다. 다시 한 번 차이가 없습니다.

Dianne은 "프레임 워크 자체에는로드 된 리소스 캐시, 개체 풀 등과 같이 앱에 대해 유지 관리하는 모든 작은 공유 데이터에 대해 수많은 싱글 톤이 있습니다. 훌륭하게 작동합니다." 나는 Singletons를 사용하는 것이 잘 작동하지 않거나 합법적 인 대안이 아니라고 주장하는 것이 아닙니다. 저는 Singletons가 Application 하위 클래스를 사용하는 것만 큼 Android 시스템과 강력한 계약을 제공하지 않으며, 또한 Singletons를 사용하는 것은 일반적으로 쉽게 수정할 수없는 유연하지 않은 디자인을 가리키며 앞으로 많은 문제를 야기한다고 주장합니다. Android API가 개발자 애플리케이션에 제공하는 강력한 계약 인 IMHO는 Android 프로그래밍의 가장 매력적이고 즐거운 측면 중 하나이며, Android 플랫폼을 오늘날 성공으로 이끄는 초기 개발자 채택으로 이어졌습니다.

Dianne은 또한 Application 서브 클래스 사용에 대한 추가적인 단점을 언급하면서 아래에서 언급했습니다. 그들은 성능이 떨어지는 코드를 작성하는 것을 권장하거나 쉽게 만들 수 있습니다. 이것은 매우 사실이며 여기에서 perf를 고려하고 Application 하위 클래스를 사용하는 경우 올바른 접근 방식을 취하는 중요성을 강조하기 위해이 답변을 편집했습니다. Dianne이 말했듯이, 프로세스가 백그라운드 브로드 캐스트 용으로 만로드 되더라도 프로세스가로드 될 때마다 (애플리케이션이 여러 프로세스에서 실행되는 경우 한 번에 여러 번) Application 클래스가 인스턴스화된다는 점을 기억하는 것이 중요합니다. 행사. 따라서 응용 프로그램의 공유 구성 요소에 대한 포인터의 저장소로 응용 프로그램 클래스를 처리를 수행하는 장소로 사용하는 것이 더 중요합니다!

이전 StackExchange 링크에서 훔친 Singletons의 단점 목록은 다음과 같습니다.

  • 추상 또는 인터페이스 클래스를 사용할 수 없습니다.
  • 하위 클래스에 대한 불능;
  • 애플리케이션 전반에 걸친 높은 결합 (수정하기 어려움)
  • 테스트하기 어려움 (단위 테스트에서 위조 / 모의 할 수 없음)
  • 변경 가능한 상태의 경우 병렬화하기 어려움 (광범위한 잠금이 필요함)

내 자신을 추가하십시오.

  • 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이 제안한 애플리케이션 상태 유지 방법은 좋지만 한 가지 단점이 있습니다. OS가 전체 애플리케이션 프로세스를 죽이는 경우가 있습니다. 다음은 이에 대한 문서입니다 프로세스 및 라이프 사이클 .

누군가가 전화를 걸었 기 때문에 앱이 백그라운드로 전환됩니다 (이제 전화 앱이 포 그라운드에 있음). 이 경우 && 다른 조건 (위의 링크에서 확인 가능)에서 OS가 Application하위 클래스 인스턴스를 포함한 애플리케이션 프로세스를 종료 할 수 있습니다 결과적으로 상태가 손실됩니다. 나중에 애플리케이션으로 돌아 오면 OS는 활동 스택과 Application하위 클래스 인스턴스를 복원 하지만 myState필드는 null.

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


그냥 메모 ..

더하다:

android:name=".Globals"

또는 기존 <application> 태그에 하위 클래스의 이름을 지정했습니다. <application>매니페스트 에 다른 태그를 계속 추가하려고했는데 예외가 발생했습니다.


애플리케이션 태그를 지정하는 방법도 찾을 수 없었지만, 많은 인터넷 검색 후에 매니페스트 파일 문서에서 분명해졌습니다. 애플리케이션 스탠자의 기본 아이콘과 레이블 외에도 android : name을 사용하세요.

android : name 애플리케이션을 위해 구현 된 Application 서브 클래스의 완전한 이름입니다. 응용 프로그램 프로세스가 시작되면이 클래스는 응용 프로그램의 구성 요소보다 먼저 인스턴스화됩니다.

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


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

활동에는 onPause/onDestroy()소멸시 호출 되는 메서드가 있지만 Application 클래스에는 해당 하는 메서드가 없습니다. 응용 프로그램이 종료되거나 작업 스택이 백그라운드에 배치 될 때 전역 구조 (특히 네이티브 메모리에 대한 참조를 포함하는 구조)가 적절하게 가비지 수집되도록하려면 어떤 메커니즘이 권장됩니까?


다음과 같이 작동 할 애플리케이션 이름을 정의하기 만하면됩니다.

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

위에서 설명한 것처럼 OS는 알림없이 (onDestroy 이벤트가 없음) APPLICATION을 종료 할 수 있으므로 이러한 전역 변수를 저장할 방법이 없습니다.

SharedPreferences는 COMPLEX STRUCTURED 변수가있는 경우를 제외하고 솔루션이 될 수 있습니다 (제 경우에는 사용자가 이미 처리 한 ID를 저장할 정수 배열이 있음). SharedPreferences의 문제점은 값이 필요할 때마다 이러한 구조를 저장하고 검색하기가 어렵다는 것입니다.

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


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

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


이러한 종류의 상태를 저장하는 정적 필드를 가질 수 있습니다. 또는 리소스 번들에 넣고 onCreate (Bundle savedInstanceState)에서 복원합니다. 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");
    

활동 결과는 재개 전에 호출됩니다. 따라서 다시 시작할 때 로그인 확인으로 이동하면 secomd 활동이 긍정적 인 결과를 반환하면 두 번째 로그인이 차단 될 수 있습니다. On 이력서는 매번 호출되므로 처음 호출되지 않을까 걱정하지 않습니다.


서브 클래 싱 방식은 BARACUS 프레임 워크에서도 사용되었습니다. 내 관점에서 애플리케이션 하위 분류 는 Android의 수명주기와 함께 작동하도록 의도되었습니다. 이것이 모든 애플리케이션 컨테이너가하는 일입니다. 그런 다음 전역을 사용하는 대신이 컨텍스트에 빈을 등록하여 컨텍스트에서 관리 할 수있는 모든 클래스에 주입되도록합니다. 주입 된 모든 빈 인스턴스는 실제로 싱글 톤입니다.

자세한 내용은이 예를 참조하십시오.

더 많은 것을 가질 수 있는데 왜 수동 작업을합니까?


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

참고URL : https://stackoverflow.com/questions/708012/how-to-declare-global-variables-in-android

반응형