안드로이드의 싱글 톤과 애플리케이션 컨텍스트?


362

싱글 톤을 사용하고 싱글 톤 패턴을 사용하는 Android 애플리케이션의 몇 가지 예를 보았을 때이 게시물을 기억하면서 글로벌 애플리케이션 상태를 통해 공유되는 단일 인스턴스 대신 싱글 톤을 사용하는 것이 좋은지 궁금합니다. context.getApplication ()을 통해)

두 메커니즘이 갖는 장점 / 단점은 무엇입니까?

솔직히 말해서,이 게시물의 웹 응용 프로그램 이있는 싱글 톤 패턴 에서 같은 대답을 기대합니다 . 좋은 생각이 아닙니다! 안드로이드에 적용됩니다. 제가 맞습니까? DalvikVM과 다른 점은 무엇입니까?

편집 : 관련된 여러 측면에 대한 의견을 갖고 싶습니다.

  • 동기화
  • 재사용 성
  • 테스팅

답변:


295

Dianne Hackborn의 답변에 동의하지 않습니다. 우리는 프로젝트에서 모든 싱글 톤을 조금씩 제거하여 가볍고 작업 범위가 지정된 객체를 선호하므로 실제로 필요할 때 쉽게 다시 만들 수 있습니다.

싱글 톤은 테스트하기에 악몽이며, 게으르게 초기화 된 경우 미묘한 부작용 ( "getInstance() 한 범위에서 다른 범위 로 호출을 이동할 때 갑자기 표면화 될 수 있음 ")을 갖는 "상태 결정론" 을 도입 할 것 입니다. 가시성은 또 다른 문제로 언급되었으며 싱글 톤 은 공유 상태에 대한 "전역"(= 랜덤) 액세스를 의미하기 때문에 동시 애플리케이션에서 올바르게 동기화되지 않으면 미묘한 버그가 발생할 수 있습니다.

나는 그것을 반 패턴으로 간주합니다. 그것은 본질적으로 전역 상태를 유지하는 데 나쁜 객체 지향 스타일입니다.

질문으로 돌아가려면 :

앱 컨텍스트는 싱글 톤 자체로 간주 될 수 있지만 프레임 워크 관리 방식이며 수명주기 , 범위 및 액세스 경로 가 잘 정의 되어 있습니다. 따라서 앱 전역 상태를 관리 해야하는 경우 다른 곳으로 가야한다고 생각합니다. 다른 무엇이든, 싱글 톤 객체가 정말로 필요하거나 싱글 톤 클래스를 다시 작성하여 현재 작업을 수행하는 작고 수명이 짧은 객체를 인스턴스화 할 수 있는지 다시 생각하십시오.


131
응용 프로그램을 권장하는 경우 싱글 톤 사용을 권장합니다. 솔직히 말해서, 주변에 방법이 없습니다. 응용 프로그램 긁는 의미 있는 싱글 톤입니다. 나는 당신이 절대 사용해서는 안되는 싱글 톤에 대한 종교적 논쟁에 빠지지 않을 것입니다. 나는 실용적 인 것을 선호합니다. 프로세스 당 상태를 유지하기에 좋은 선택이며 그렇게함으로써 일을 단순화 할 수있는 장소가 있으며, 잘못된 상황에서도 사용하고 발로 쏠 수 있습니다.
hackbod

18
사실 "앱 컨텍스트는 싱글 톤 자체로 간주 될 수 있습니다"라고 언급했습니다. 차이점은 앱 인스턴스의 경우 수명주기가 프레임 워크에 의해 처리되므로 발로 자신을 촬영하는 것이 훨씬 어렵다는 것입니다. Guice, Hivemind 또는 Spring과 같은 DI 프레임 워크도 싱글 톤을 사용하지만 개발자가 신경 쓰지 않아야 할 구현 세부 사항입니다. 필자는 일반적으로 자신의 코드가 아닌 프레임 워크 의미가 올바르게 구현되는 것이 더 안전하다고 생각합니다. 네, 인정합니다! :-)
Matthias

93
솔직히 말해서 싱글 톤보다 발에서 스스로를 쏘는 것을 막지는 못합니다. 약간 혼란 스럽지만 응용 프로그램의 수명주기 는 없습니다 . 앱이 시작될 때 (구성 요소가 인스턴스화되기 전에) 해당 시점에서 onCreate ()가 호출되면 생성됩니다. 그것은 프로세스가 죽을 때까지 거기에 앉아서 영원히 살 것입니다. 싱글 톤처럼. :)
hackbod

30
오해하는 것이 한 가지 있습니다 .Android는 프로세스에서 앱을 실행하고 해당 프로세스의 수명주기를 관리하도록 설계되었습니다. 따라서 Android 싱글 톤은 프로세스 관리를 활용할 수있는 매우 자연스러운 방법입니다. 플랫폼에서 프로세스 메모리를 다른 용도로 회수해야 할 때까지 프로세스에서 무언가를 캐시하려는 경우 해당 상태를 싱글 톤으로 설정하면됩니다.
hackbod

7
알았어. 우리가 자체 관리 싱글 톤에서 한 걸음 뒤로 물러서지 않았다고 말할 수 있습니다. 우리는 이제 하나의 팩토리 싱글 톤 (RootFactory)을 유지하는 경량 DI 스타일 솔루션을 선택하고 있습니다. 이 싱글 톤은 모든 앱 구성 요소가 의존하는 공통 종속성을 관리하지만 인스턴스화는 단일 위치 (앱 클래스)에서 관리됩니다. 이 접근 방식을 사용하면 하나의 싱글 톤이 남아 있지만 Application 클래스에 국한되므로 다른 코드 모듈은 그 "세부 사항"에 대해 알지 못합니다.
마티아스

231

나는 싱글 톤을 매우 권장합니다. 문맥이 필요한 싱글 톤이 있다면,

MySingleton.getInstance(Context c) {
    //
    // ... needing to create ...
    sInstance = new MySingleton(c.getApplicationContext());
}

애플리케이션보다 단일 구성 요소를 선호합니다. 앱 전체의 모든 전역 상태를 유지 관리해야하는 한 장소를 갖지 않고 각 개별 요소가 자체를 처리 할 수있는 한 곳이 아니라 훨씬 체계적이고 모듈화 된 앱을 유지하는 데 도움이되므로 애플리케이션보다 싱글 톤을 선호합니다. 또한 Singletons가 Application.onCreate ()에서 모든 초기화를 수행하는 경로를 낮추는 대신 (요청시) 게으르게 초기화한다는 사실이 좋습니다.

싱글 톤을 사용하는 데 본질적으로 잘못된 것은 없습니다. 말이 올 바르면 올바르게 사용하십시오. Android 프레임 워크에는 실제로로드 된 리소스 및 기타 항목의 프로세스 별 캐시를 유지 관리하기 위해 많은 기능이 있습니다.

또한 간단한 응용 프로그램의 경우 멀티 스레딩은 싱글 톤에서 문제가되지 않습니다. 설계에 따라 응용 프로그램에 대한 모든 표준 콜백이 프로세스의 기본 스레드에서 전달되므로 스레드를 통해 명시 적으로 도입하지 않으면 멀티 스레딩이 발생하지 않기 때문입니다. 컨텐츠 제공자 또는 서비스 IBinder를 다른 프로세스에 공개함으로써 암시 적으로.

하고있는 일에 대해 신중하게 생각하십시오. :)


1
나중에 외부 이벤트를 듣고 싶거나 IBinder에서 공유하고 싶다면 (단순한 앱이 아닐 것 같습니다) 이중 잠금, 동기화, 일시적, 휘발성, 오른쪽을 추가해야합니까? 답변 주셔서 감사합니다 :)
mschonaker

2
외부 이벤트가 아닌 경우-BroadcastReceiver.onReceive ()도 메인 스레드에서 호출됩니다.
hackbod

2
괜찮아. 메인 스레드 디스패치 메커니즘을 볼 수있는 읽기 자료 (코드를 선호 함)를 알려주시겠습니까? 나는 그것이 몇 가지 개념을 한 번에 명확히 할 것이라고 생각합니다. 미리 감사드립니다.
mschonaker

2
이것은 주요 앱 측 디스패치 코드입니다. android.git.kernel.org/?p=platform/frameworks/…
hackbod

8
싱글 톤을 사용하는 데 본질적으로 잘못된 것은 없습니다. 말이 올 바르면 올바르게 사용하십시오. .. 확실히, 잘 말했다. Android 프레임 워크에는 실제로로드 된 리소스 및 기타 항목의 프로세스 별 캐시를 유지 관리하기 위해 많은 기능이 있습니다. 당신이 말한대로. iOS 세계의 친구들로부터 iOS의 "모든 것이 싱글 톤"입니다. GPS, 시계, 자이로 등 싱글 톤 개념보다 물리적 장치에서 더 자연스러운 것은 없습니다. 싱글 톤으로? 그래
Fattie

22

보낸 사람 : 개발자> 참조-응용 프로그램

일반적으로 Application을 서브 클래 싱 할 필요가 없습니다. 대부분의 상황에서 정적 싱글 톤은보다 기능적인 방식으로 동일한 기능을 제공 할 수 있습니다. 싱글 톤에 글로벌 컨텍스트가 필요한 경우 (예 : 브로드 캐스트 수신기 등록) 싱글 톤을 처음 생성 할 때 내부적으로 Context.getApplicationContext ()를 사용하는 컨텍스트를 검색하는 함수를 검색 할 수 있습니다.


1
단일 인스턴스에 대한 인터페이스를 작성하고 getInstance를 정적이 아닌 상태로 유지하는 경우 단일 클래스를 사용하는 클래스의 기본 생성자가 기본이 아닌 생성자를 통해 프로덕션 단일 톤을 주입하도록 할 수도 있습니다. 단위 테스트에서 싱글 톤 사용 클래스.
android.weasel

11

응용 프로그램이 싱글 톤과 동일하지 않은 이유는 다음과 같습니다.

  1. ui 스레드에서 애플리케이션의 메소드 (예 : onCreate)가 호출됩니다.
  2. 싱글 톤의 메소드는 모든 스레드에서 호출 될 수 있습니다.
  3. Application의 "onCreate"메소드에서 핸들러를 인스턴스화 할 수 있습니다.
  4. 싱글 톤이 none-ui 스레드에서 실행되면 핸들러를 인스턴스화 할 수 없습니다.
  5. 응용 프로그램은 응용 프로그램에서 활동의 수명주기를 관리하는 기능이 있습니다. "registerActivityLifecycleCallbacks"메서드가 있지만 싱글 톤에는 기능이 없습니다.

1
참고 : 모든 스레드에서 핸들러를 인스턴스화 할 수 있습니다. from doc : "새로운 핸들러를 생성 할 때,이를 생성하는 스레드의 스레드 / 메시지 큐에 바인딩됩니다"
Christ

1
@Christ 감사합니다! 지금은 "루퍼의 메커니즘"을 배웠습니다. 코드 'Looper.prepare ()'없이 none-ui 스레드에서 핸들러를 인스턴스화하면 "java.lang.RuntimeException : Can 오류가보고됩니다. Looper.prepare () "를 호출하지 않은 스레드 내에 핸들러를 작성하지 않습니다.
sunhang 2016 년

11

나는 같은 문제가 있었다 : Singleton 또는 하위 클래스 android.os.Application?

먼저 Singleton으로 시도했지만 어느 시점에서 내 앱이 브라우저를 호출합니다.

Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"));

문제는 핸드셋에 메모리가 충분하지 않으면 대부분의 클래스 (싱글 톤조차)가 메모리를 확보하기 위해 청소되므로 브라우저에서 내 앱으로 돌아올 때마다 매번 충돌한다는 것입니다.

솔루션 : 필요한 데이터를 Application 클래스의 서브 클래스에 넣습니다.


1
나는 종종 사람들이 이런 일이 일어날 수 있다고 언급 한 게시물을 발견했습니다. 따라서 단순히 수명이 문서화되고 알려져 있는지 확인하기 위해 게으른 로딩 등을 사용하여 싱글 톤과 같은 응용 프로그램에 객체를 간단히 연결합니다. 앱이 백그라운드에 있고 다른 활동을 위해 메모리를 확보하기 위해 모든 활동이 파괴되면 메모리에서 지워지지 않는다는 것을 이해하므로 수백 개의 이미지를 애플리케이션 객체에 저장하지 마십시오.
Janusz

응용 프로그램을 다시 시작한 후 Singleton 게으른 로딩이 GC에 의해 객체를 스윕 할 수있는 올바른 방법이 아닙니다. 약한 참조가 맞습니까?
mschonaker

15
정말? Dalvik은 클래스를 언로드하고 프로그램 상태를 잃습니까? 처음에는 싱글 톤에 넣지 말아야 할 제한된 수명주기 활동 관련 객체를 가비지 수집하지 않는 것이 확실하지 않습니까? 그러한 특별한 주장에 대해 별다른 예를 제시해야합니다!
android.weasel

1
내가 모르는 변경 사항이 없으면 Dalvik은 클래스를 언로드 하지 않습니다 . 이제까지. 그들이보고있는 행동은 브라우저를위한 공간을 만들기 위해 백그라운드에서 프로세스가 종료되는 것입니다. 아마도 "메인"활동에서 변수를 초기화하고 있었는데, 이는 브라우저에서 돌아올 때 새 프로세스에서 작성되지 않았을 수 있습니다.
Groxx

5

동시에 둘 다 고려하십시오.

  • 클래스 내에서 싱글 톤 객체를 정적 인스턴스로 사용합니다.
  • 응용 프로그램의 모든 단일 객체에 대한 단일 인스턴스를 반환하는 공통 클래스 (Context)가 있으므로 Context의 메소드 이름이 의미가 있다는 이점이 있습니다 (예 : User.getInstance () 대신 context.getLoggedinUser ()).

또한 싱글 톤 객체에 대한 액세스뿐만 아니라 context.logOffUser (), context.readSavedData () 등과 같이 전역 적으로 액세스 해야하는 일부 기능을 포함하도록 컨텍스트를 확장하는 것이 좋습니다. 그러면 외관이 의미가있을 것입니다.


4

그들은 실제로 동일합니다. 내가 볼 수있는 한 가지 차이점이 있습니다. Application 클래스를 사용하면 Application.onCreate ()에서 변수를 초기화하고 Application.onTerminate ()에서 변수를 삭제할 수 있습니다. 싱글 톤을 사용하면 VM 초기화 및 정적 제거에 의존해야합니다.


16
onTerminate 문서는 에뮬레이터에서만 호출한다고 말합니다. 장치에서는 해당 메소드가 호출되지 않을 것입니다. developer.android.com/reference/android/app/…
danb

3

내 2 센트 :

내 활동이 파괴 될 때 일부 싱글 톤 / 정적 필드가 재설정되었음을 알았습니다. 나는 저급 2.3 장치에서 이것을 발견했다.

내 경우는 매우 간단했습니다. 개인 파일 "init_done"과 static. "init"메소드를 activity.onCreate ()에서 호출했습니다. 나는 init 메소드가 액티비티의 일부 재생성에서 스스로 다시 실행되고 있음을 알았습니다.

확인을 증명할 수는 없지만 싱글턴 / 클래스가 처음 생성 / 사용 된 시점과 관련이있을 수 있습니다. 활동이 소멸 / 재활용 될 때이 활동 만 참조하는 모든 클래스도 재활용되는 것 같습니다.

싱글 톤 인스턴스를 Application의 하위 클래스로 옮겼습니다. 응용 프로그램 인스턴스에서 액세스합니다. 그 이후로 문제를 다시는 눈치 채지 못했습니다.

이것이 누군가를 도울 수 있기를 바랍니다.


3

속담의 입에서 ...

앱을 개발할 때 앱에서 전 세계적으로 데이터, 컨텍스트 또는 서비스를 공유해야 할 수도 있습니다. 예를 들어 앱에 현재 로그인 한 사용자와 같은 세션 데이터가있는 경우이 정보를 노출 할 수 있습니다. Android에서이 문제를 해결하기위한 패턴은 android.app.Application 인스턴스가 모든 전역 데이터를 소유 한 다음 Application 인스턴스를 다양한 데이터 및 서비스에 대한 정적 접근자가있는 단일 항목으로 처리하는 것입니다.

Android 앱을 작성할 때는 android.app.Application 클래스의 인스턴스가 하나만 보장되므로 싱글 톤으로 취급하는 것이 안전합니다 (Google Android 팀에서 권장). 즉, 정적 getInstance () 메소드를 애플리케이션 구현에 안전하게 추가 할 수 있습니다. 이렇게 :

public class AndroidApplication extends Application {

    private static AndroidApplication sInstance;

    public static AndroidApplication getInstance(){
        return sInstance;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        sInstance = this;
    }
}

2

내 활동은 finish ()를 호출하고 (즉시 완료하지는 않지만 결국에는 완료 됨) Google Street Viewer를 호출합니다. Eclipse에서 디버깅 할 때 Street Viewer가 호출되면 앱에 대한 연결이 끊어지며 (전체) 응용 프로그램이 닫히고 메모리를 확보해야한다고 생각합니다 (단일 활동이 완료되면이 동작이 발생하지 않아야 함) . 그럼에도 불구하고 onSaveInstanceState ()를 통해 번들에 상태를 저장하고 스택에서 다음 활동의 onCreate () 메소드로 복원 할 수 있습니다. 정적 싱글 톤 또는 서브 클래스 링 응용 프로그램을 사용하여 응용 프로그램을 닫고 상태를 잃을 수 있습니다 (번들에 저장하지 않는 한). 내 경험에 의하면 그들은 국가 보존과 관련하여 동일합니다. Android 4.1.2 및 4.2.2에서는 연결이 끊어졌지만 4.0.7 또는 3.2.4에서는 연결이 끊어졌습니다.


"Android 4.1.2 및 4.2.2에서는 연결이 끊어졌지만 4.0.7 또는 3.2.4에서는 연결이 끊어 졌음을 알았습니다. 이는 어느 시점에서 메모리 복구 메커니즘이 변경되었음을 나타냅니다." ..... 기기에 사용 가능한 메모리 용량이 같지 않거나 동일한 앱이 설치되어 있지 않다고 생각합니다. 그러므로 당신의 결론은 틀릴 것입니다
Christ

@Christ : 그렇습니다. 메모리 복구 메커니즘이 버전간에 바뀌면 이상 할 것입니다. 다른 메모리 사용으로 인해 고유 한 동작이 발생했을 수 있습니다.
Piovezan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.