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

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시작 목적으로 만의 정적 인스턴스를 만드는 것은 의미가 없습니다 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);
    }
}

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


답변

멤버 필드에 저장하기로 결정한 경우 메서드 / 생성자를 통해 전달 된 컨텍스트에서 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.


답변

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


답변

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

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


답변

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

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

public static Context ctx;

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

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

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

public static Example example;

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

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

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

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


답변

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

@SuppressLint("StaticFieldLeak")


답변

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