경고 : 정적 필드에 Android 컨텍스트 클래스를 배치하지 마십시오. 이것은 메모리 누수입니다 (또한 Instant Run을 중단 함).


84

Android 스튜디오 :

정적 필드에 Android 컨텍스트 클래스를 배치하지 마십시오. 이것은 메모리 누수입니다 (또한 Instant Run을 중단 함).

그래서 두 가지 질문 :

# 1 startService컨텍스트에 대한 정적 변수없이 정적 메서드에서를 어떻게 호출 합니까?
# 2 정적 메서드 (동일)에서 localBroadcast를 어떻게 보내나요?

예 :

public static void log(int iLogLevel, String sRequest, String sData) {
    if(iLogLevel > 0) {

        Intent intent = new Intent(mContext, LogService.class);
        intent.putExtra("UPDATE_MAIN_ACTIVITY_VIEW", "UPDATE_MAIN_ACTIVITY_VIEW");
        mContext.startService(intent);
    }
}

또는

        Intent intent = new Intent(MAIN_ACTIVITY_RECEIVER_INTENT);
        intent.putExtra(MAIN_ACTIVITY_REQUEST_FOR_UPDATE, sRequest));
        intent.putExtra(MAIN_ACTIVITY_DATA_FOR_VIEW, sData);
        intent.putExtra(MAIN_ACTIVITY_LOG_LEVEL, iLogLevel);
        LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);

사용하지 않고 이것을 수행하는 올바른 방법은 무엇입니까 mContext?

참고 : 내 주요 질문은 호출 메서드가있는 클래스에 컨텍스트를 전달하는 방법이라고 생각합니다.


메소드에서 Context를 매개 변수로 전달할 수 없습니까?
Juan Cruz Soler 2016 년

컨텍스트가없는 곳에서이 루틴을 호출 할 것입니다.
존 스미스

# 1은 매개 변수 # 2로 동일하게 전달합니다.
njzk2

그런 다음 컨텍스트를 호출자 메서드에도 전달해야합니다. 당신은 모든 뷰와 활동 누설 수 있도록 문제는 정적 필드는 쓰레기 수집되지 않습니다이다
후안 크루즈 솔러

1
@JohnSmith 시작 활동 (생성자 매개 변수 또는 메서드 매개 변수를 통해)에서 필요한 지점까지 계단식으로 배열합니다.
AndroidMechanic-Viral Patel

답변:


56

메소드에 매개 변수로 전달하기 만하면됩니다. Context시작 목적으로 만의 정적 인스턴스를 만드는 것은 의미가 없습니다 Intent.

방법은 다음과 같습니다.

public static void log(int iLogLevel, String sRequest, String sData, Context ctx) {
    if(iLogLevel > 0) {

        Intent intent = new Intent(ctx, LogService.class);
        intent1.putExtra("UPDATE_MAIN_ACTIVITY_VIEW", "UPDATE_MAIN_ACTIVITY_VIEW");
        ctx.startService(intent);
    }
}

질문에 대한 주석에서 업데이트 : 시작 활동의 컨텍스트 (생성자 매개 변수 또는 메서드 매개 변수를 통해)를 필요한 지점까지 계단식으로 배열합니다.


생성자 예제를 제공 할 수 있습니까?
존 스미스

클래스 이름 인 경우 MyClass그냥하는 방법처럼 public 생성자를 추가 public MyClass(Context ctx) { // put this ctx somewhere to use later }(이것은 당신의 생성자입니다) 지금의 새 인스턴스를 만들 MyClass이 생성자 예를 들어, 사용MyClass mc = new MyClass(ctx);
바이러스 성 파텔 - AndroidMechanic을

주문형 전달이 그렇게 간단하다고 생각하지 않습니다. 오래된 컨텍스트 나 여기와 같은 정적 인 컨텍스트에 대해 걱정할 필요가없는 것과 같은 명백한 이점이 있지만. 비동기 적으로 호출되는 응답 콜백에 컨텍스트가 필요하다고 가정 해 보겠습니다. 따라서 때때로 구성원 필드에 입력해야합니다. 이제 정적으로 만들지 않는 방법을 생각해야합니다. stackoverflow.com/a/40235834/2695276 이 작동하는 것 같습니다.
Rajat Sharma

1
ApplicationContext를 정적 필드로 사용해도 괜찮습니까? 활동과 달리 응용 프로그램 객체는 파괴되지 않습니다.
NeoWang 19

50

멤버 필드에 저장하기로 결정한 경우 메서드 / 생성자를 통해 전달 된 컨텍스트에서 context.getApplicationContext ()를 전달하거나 getApplicationContext ()를 호출했는지 확인하십시오.

바보 증명 예제 (누군가 활동을 전달하더라도 앱 컨텍스트를 가져 와서 싱글 톤을 인스턴스화하는 데 사용합니다) :

public static synchronized RestClient getInstance(Context context) {
    if (mInstance == null) {
        mInstance = new RestClient(context.getApplicationContext());
    }
    return mInstance;
}

문서에 따르면 getApplicationContext () : "현재 프로세스의 단일 글로벌 애플리케이션 객체의 컨텍스트를 반환합니다."

이는 "getApplicationContext ()"에 의해 반환 된 컨텍스트가 전체 프로세스를 통해 유지 될 것임을 의미하며, 따라서 앱의 런타임 동안 항상 거기에있을 것이기 때문에 어디에나 정적 참조를 저장하더라도 상관 없습니다. / singletons 인스턴스화 됨).

많은 양의 데이터를 보유한 뷰 / 액티비티 내부의 컨텍스트와 비교해보십시오. 활동이 보유한 컨텍스트를 유출하면 시스템이 분명히 좋지 않은 리소스를 해제 할 수 없습니다.

컨텍스트 별 활동에 대한 참조는 활동 자체와 동일한 수명주기를 유지해야합니다. 그렇지 않으면 컨텍스트를 인질로 잡아서 메모리 누수를 유발합니다 (이는 Lint 경고의 원인입니다).

편집 : 위의 문서에서 예제를 강타하는 사람에게는 방금 작성한 코드에 대한 주석 섹션도 있습니다.

    // getApplicationContext() is key, it keeps you from leaking the
    // Activity or BroadcastReceiver if someone passes one in.

8
위의 예를 강타한 사람을 강타하는 사람에게 :이 스레드의 요점은 Google에서 권장하는 싱글 톤 생성 패턴과 충돌하는 Lint 경고입니다.
Raphael C

7
읽어보기 : "정적 필드에 Android 컨텍스트 클래스를 배치하지 마십시오. 이것은 메모리 누수이며 Instant Run도 중단됩니다."컨텍스트 클래스가 무엇인지 알고 있습니까? 활동은 그중 하나이며 자신이 설명했듯이 Activity를 정적 필드로 저장해서는 안됩니다 (그렇지 않으면 메모리가 누수됩니다). 그러나 Context (애플리케이션 컨텍스트 인 한)는 모든 것보다 오래 살기 때문에 정적 필드로 저장할 수 있습니다. (따라서 경고를 무시하십시오). 저는 우리가이 간단한 사실에 동의 할 수 있다고 확신합니다.
Marcus Gruneau 2016

iOS 수의사로서 Android 첫 주에 ... 이와 같은 설명은이 상황을 이해하는 데 도움이됩니다. .
eric

@Marcus 자식 클래스가 누가 어떤 컨텍스트로 인스턴스화하는지 알지 못하는 경우 정적 멤버로 저장하는 것은 나쁜 습관입니다. 또한 응용 프로그램 컨텍스트는 응용 프로그램의 응용 프로그램 개체의 일부로 존재하며 응용 프로그램 개체는 메모리에 영원히 유지되지 않고 종료됩니다. 일반적인 믿음과는 달리 앱이 처음부터 다시 시작되지 않습니다. Android는 새로운 Application 개체를 만들고 사용자가 이전에 있었던 활동을 시작하여 애플리케이션이 애초에 죽지 않았다는 환상을줍니다.
Raphael C

@RaphaelC 그러한 문서가 있습니까? Android는 각 프로세스의 실행 당 하나의 애플리케이션 컨텍스트 만 보장하기 때문에 완전히 잘못된 것 같습니다.
HaydenKai

6

경고 일뿐입니다. 걱정하지 마세요. 애플리케이션 컨텍스트를 사용하려면 프로젝트의 모든 싱글 톤 클래스를 저장하는 데 사용되는 "singleton"클래스에 저장할 수 있습니다.


2

귀하의 경우에는 정적 필드로 사용하는 것이별로 의미가 없지만 모든 경우에 나쁘다고 생각하지 않습니다. 지금 당신이 무엇을하고 있다면 컨텍스트가 있고 나중에 null을 갖는 정적 필드를 가질 수 있습니다. 컨텍스트가 내부에 있고 응용 프로그램 컨텍스트가 활동 컨텍스트가 아닌 기본 모델 클래스에 대한 정적 인스턴스를 만들고 있으며 파괴시 null 인 활동을 포함하는 클래스의 정적 인스턴스 필드가 있습니다. 나는 메모리 누수가 있다는 것을 알지 못합니다. 그래서 어떤 영리한 사람이 내가 틀렸다고 생각하면 자유롭게 의견을 말하십시오 ...

또한 Instant Run은 여기서 잘 작동합니다 ...


나는 당신이 원칙에 대해 틀렸다고 생각하지 않지만, 당신이 말하는 활동이 정적 필드를 사용하기 전에 주어진 시간에 최대 하나의 단일 인스턴스만을 가지고 있다는 점에 특히주의해야합니다. 앱이 다른 위치 (알림, 딥 링크 등)에서 시작될 수 있기 때문에 둘 이상의 백 스택이있는 경우 매니페스트에서 singleInstance와 같은 일부 플래그를 사용하지 않으면 문제가 발생합니다. 따라서 활동의 정적 필드를 피하는 것이 항상 더 쉽습니다.
BladeCoder

android : launchMode = "singleTask"로 충분해야합니다. 그래서 저는 그것으로 전환하고 있습니다. 저는 singleTop을 사용했지만 그것이 제 앱이 디자인 된 방식 인 주 활동의 단일 인스턴스를 항상 원하기 때문에 충분하지 않았습니다.
Renetik

2
"singleTask"는 작업 당 하나의 인스턴스 만 보장합니다. 앱에 딥 링크와 같은 여러 진입 점이 있거나 알림에서 시작하는 경우 여러 작업이 발생할 수 있습니다.
BladeCoder jul.

1

일반적으로 컨텍스트 필드를 정적으로 정의하지 마십시오. 경고 자체가 이유를 설명합니다. 메모리 누수입니다. 즉석 달리기를 깨는 것은 지구상에서 가장 큰 문제 아닐 수도 있습니다.

이제이 경고가 표시되는 두 가지 시나리오가 있습니다. 인스턴스 (가장 분명한 것)의 경우 :

public static Context ctx;

그리고 컨텍스트가 클래스로 래핑되는 조금 더 까다로운 것이 있습니다.

public class Example{
    public Context ctx;
    //Constructor omitted for brievety 
}

그리고 그 클래스는 어딘가에서 정적으로 정의됩니다.

public static Example example;

그리고 당신은 경고를 받게 될 것입니다.

솔루션 자체는 매우 간단합니다. 클래스 래핑이든 직접 정적으로 선언하든 컨텍스트 필드를 정적 인스턴스에 배치하지 마십시오 .

경고에 대한 해결책은 간단합니다. 필드를 정적으로 배치하지 마십시오. 귀하의 경우 컨텍스트를 인스턴스로 메서드에 전달하십시오. 여러 Context 호출이 수행되는 클래스의 경우 생성자를 사용하여 컨텍스트 (또는 해당 문제에 대한 활동)를 클래스에 전달합니다.

오류가 아니라 경고입니다. 어떤 이유로 든 정적 컨텍스트 가 필요한 경우이를 수행 할 수 있습니다. 그렇게 할 때 메모리 누수가 발생하지만.


메모리 누수없이 어떻게 할 수 있습니까?
isJulian00

1
당신은 할 수 없습니다. 당신이 절대적으로 주위 상황을 통과해야하는 경우 이벤트 버스에 볼 수 있었다
조이

좋아, 이건 내가 가지고 있던 문제 였는데, 다른 방법이 있을지도 모르지만 C ++ 코드에서 호출하기 때문에 메서드가 정적이어야합니다. stackoverflow.com/questions/54683863/…
isJulian00

0

애플리케이션 컨텍스트인지 확인하는 경우. 상관 없습니다. 이거 추가 해봐

@SuppressLint("StaticFieldLeak")

1
어쨌든 이것을 권장하지 않습니다. 컨텍스트가 필요한 경우 AndroidX libs를 사용하는 경우 requireContext () 메서드를 사용할 수 있습니다. 또는 컨텍스트를 필요한 메서드에 직접 전달할 수 있습니다. 또는 앱의 클래스 참조를 얻을 수도 있지만 이러한 SuppressLint 제안을 사용하지 않는 것이 좋습니다.
Oleksandr Nos

0

WeakReferenceSingleton 클래스에 컨텍스트를 저장하는 데 사용 하면 경고가 사라집니다.

private WeakReference<Context> context;

//Private contructor
private WidgetManager(Context context) {
    this.context = new WeakReference<>(context);
}

//Singleton
public static WidgetManager getInstance(Context context) {
    if (null == widgetManager) {
        widgetManager = new WidgetManager(context);
    }
    return widgetManager;
}

이제 다음과 같은 컨텍스트에 액세스 할 수 있습니다.

  if (context.get() instanceof MainActivity) {
            ((MainActivity) context.get()).startActivityForResult(pickIntent, CODE_REQUEST_PICK_APPWIDGET);
        }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.