컨텍스트로 getApplication ()을 사용하여 "창을 추가 할 수 없음-토큰 널이 애플리케이션에 해당되지 않음"을 표시하는 대화 상자


665

내 활동은 컨텍스트를 매개 변수로 필요로하는 AlertDialog를 만들려고합니다. 내가 사용하면 예상대로 작동합니다.

AlertDialog.Builder builder = new AlertDialog.Builder(this);

그러나 화면 회전과 같은 간단한 작업 중에도 액티비티가 파괴되고 다시 생성 될 때 메모리 누수 가능성으로 인해 "this"를 컨텍스트로 사용하는 것은 부적절합니다. A로부터 안드로이드 개발자 블로그에 관련 포스트 :

컨텍스트 관련 메모리 누수를 피하는 두 가지 쉬운 방법이 있습니다. 가장 분명한 것은 컨텍스트가 자신의 범위를 벗어나는 것을 피하는 것입니다. 위의 예는 정적 참조의 경우를 보여 주지만 내부 클래스와 외부 클래스에 대한 암시 적 참조는 똑같이 위험 할 수 있습니다. 두 번째 해결책은 응용 프로그램 컨텍스트를 사용하는 것입니다. 이 컨텍스트는 애플리케이션이 활성 상태이고 활동 수명주기에 의존하지 않는 한 지속됩니다. 컨텍스트가 필요한 오래 지속되는 오브젝트를 유지하려는 경우 애플리케이션 오브젝트를 기억하십시오. Context.getApplicationContext () 또는 Activity.getApplication ()을 호출하여 쉽게 얻을 수 있습니다.

그러나 예외를 던지기 때문에 컨텍스트로 허용 AlertDialog()되지 않는 getApplicationContext()또는 getApplication()허용되는 경우 :

"창을 추가 할 수 없음-토큰 널이 응용 프로그램 용이 아닙니다"

참조 당 : 1 , 2 , 3 등

따라서 공식적으로 사용하는 것이 좋지만 Activity.getApplication()광고 된대로 작동하지 않기 때문에 이것이 실제로 "버그"로 간주되어야 합니까?

짐


R.Guy가 getApplication을 사용하여 조언하는 첫 번째 항목에 대한 참조 : android-developers.blogspot.com/2009/01/…
— gymshoe




답변:


1354

대신을 getApplicationContext()사용하십시오 ActivityName.this.


67
큰! 그것에 대해 언급하기 만하면됩니다. 예를 들어 자체적으로 'this'를 가진 리스너의 구현 된 메소드 내에서 액세스하기 위해 "this"를 전역 적으로 저장해야 할 수도 있습니다. 이 경우 전역 적으로 "컨텍스트 컨텍스트"를 정의한 다음 onCreate에서 "context = this"를 설정 한 다음 "context"를 참조하십시오. 그것이 도움이되기를 바랍니다.
— Steven L

8
실제로, Listener수업은 종종 익명 으로 진행되기 때문에, 나는 그냥하는 경향이 final Context ctx = this;있고 멀리 있습니다;)
— Alex

28
@StevenL 무슨 말을하기 위해서는 ExternalClassName.this를 사용하여 외부 클래스의 "this"를 명시 적으로 참조해야합니다.
— Artem Russakovskii

11
대화 상자가 콜백에 사용되고 콜백이 호출되기 전에 활동을 종료하면 "this"를 사용하지 않습니까? 적어도 그것이 안드로이드가 logcat에서 불평하는 것 같습니다.
— Artem Russakovskii

6
onDestroy-Artem의 정적 참조를 지우는 것을 기억하지 않으면 해당 활동의 메모리를 쉽게 누출 할 수 있으므로 @StevenLs 접근 방식을 권장하지 않습니다. StevenL의 접근 방식은 Java 작동 방식에 대한 이해 부족으로 인해 발생합니다
— Dori

192

사용 this은 나에게 효과가 없었지만 MyActivityName.this그랬습니다. 이것이 this일 을 할 수없는 사람에게 도움이되기를 바랍니다 .


63
그것은 당신 this이 내부 클래스 내부에서 사용할 때 일어나는 일 입니다. 외부 클래스의 인스턴스를 참조하려면와 같이 지정해야합니다 OuterClass.this. 그냥 사용하면 this항상 가장 내부 클래스의 인스턴스를 참조합니다.
— kaka

60

을 계속 사용할 수 getApplicationContext()있지만 사용하기 전에이 플래그를 추가해야합니다 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT). 오류가 표시되지 않습니다.

매니페스트에 다음 권한을 추가하십시오.

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

1
창 android.view.ViewRootImpl$W@426ce670을 추가 할 수 없습니다.이 창 유형에 대한 권한이 거부되었습니다.
— Ram G.

권한 추가 : <uses-permission android : name = "android.permission.SYSTEM_ALERT_WINDOW"/>
— codezjx


1
API 23 이상에서 사용할 수 있지만 startActivityForResult (new Intent (Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse ( "package :"+ getPackageName ()))), OVERLAY_PERMISSION_REQ_CODE); 그러나, 당신은 여부를 해야 사용이 또 다른 문제는 ...이다
— 벤 닐

2
진행중인 대화 내부 서비스를 보여주는 때 유용합니다
— 아난드 Savjani

37

"... AlertDialog ()에 대해 getApplicationContext () 또는 getApplication ()을 컨텍스트로 사용할 수 없다고 말했을 때 문제를 올바르게 식별했습니다. '창을 추가 할 수 없습니다.-토큰 null은 지원서'"

대화 상자를 작성하려면 애플리케이션 컨텍스트가 아닌 활동 컨텍스트 또는 서비스 컨텍스트 가 필요합니다 (getApplicationContext () 및 getApplication () 모두 애플리케이션 컨텍스트를 리턴 함).

활동 컨텍스트 를 얻는 방법은 다음과 같습니다 .

(1) 활동 또는 서비스에서 :

AlertDialog.Builder builder = new AlertDialog.Builder(this);

(2) 조각에서 : AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

메모리 누수는 "this"참조에 내재 된 문제가 아니며, 이는 객체 자체에 대한 참조입니다 (즉, 객체 데이터를 저장하기 위해 실제로 할당 된 메모리에 대한 참조). 그것은 일어나는 모든 가비지 콜렉터 (GC)가 할당 된 메모리가 유용한 수명을 지났다 후 확보 할 수없는있는 할당 된 메모리.

대부분의 경우 변수가 범위를 벗어나면 GC가 메모리를 회수합니다. 그러나 변수가 보유한 개체 (예 : "x")에 대한 참조가 개체의 유효 수명보다 오래 지속 된 후에도 메모리 누수가 발생할 수 있습니다. 따라서 "x"가 참조를 보유하는 한 할당 된 메모리는 손실됩니다. GC 는 해당 메모리가 여전히 참조되는 동안 메모리를 해제 하지 않기 때문 입니다. 할당 된 메모리 에 대한 참조 체인으로 인해 메모리 누수가 명확하지 않은 경우가 있습니다. 이 경우 GC는 해당 메모리에 대한 모든 참조가 제거 될 때까지 메모리를 비우지 않습니다.

메모리 누수를 방지하려면 할당 된 메모리가 "this"(또는 다른 참조)에 의해 무한정 참조되도록하는 논리적 오류가 있는지 코드를 확인하십시오. 체인 참조도 확인하십시오. 다음은 메모리 사용을 분석하고 성가신 메모리 누수를 찾는 데 도움이되는 몇 가지 도구입니다.


액티비티를 들어 당신은 또한 사용할 수 있습니다 ActivityName.this ActivityName는 (분명히) (예를 들어, MainActivity에 대한) 활동의 이름입니다
— 루이스 카브레라 베니토

34

대화 상자는 "컨텍스트가 필요한 오래 지속되는 개체"가되어서는 안됩니다. 문서가 혼란 스럽다. 기본적으로 다음과 같은 작업을 수행하는 경우 :

static Dialog sDialog;

( 정적 참고 )

그런 다음 어딘가 활동에서

 sDialog = new Dialog(this);

회전 또는 이와 유사한 작업을 수행하는 동안 원래 활동이 유출되어 활동이 중단 될 수 있습니다. (onDestroy에서 정리하지 않는 한 Dialog 객체를 정적으로 만들지 않을 것입니다)

일부 데이터 구조의 경우 정적 및 애플리케이션의 컨텍스트를 기반으로하는 것이 바람직하지만 일반적으로 대화 상자와 같은 UI 관련 항목에는 적합하지 않습니다. 그래서 이런 식으로 :

Dialog mDialog;

...

mDialog = new Dialog(this);

mDialog가 정적이 아니기 때문에 활동과 함께 해제되므로 활동이 누출되지 않아야합니다.


나는 asynctask에서 그것을 부르고있다, 이것은 나를 위해 일했다 thx 메이트
— MemLeak

정적 선언을 제거한 후에는 대화가 정적이었습니다.
— Ceddy Muhoza

25

프래그먼트에 표시된 사용자 정의 어댑터의 생성자를 통해 컨텍스트를 보내야했으며 getApplicationContext ()와 관련 하여이 문제가 발생했습니다. 나는 그것을 해결했다 :

this.getActivity().getWindow().getContext()조각의 onCreate콜백에서.


4
이것은 나에게도 효과가 있었고, 사용중인 외부 AsyncTask의 생성자에게 전달했습니다 (진행 대화 상자를 보여줍니다).
— Rohan Kandwal

3
이것은 더 복잡한 작업에 대한 실제 답변입니다 :)
— teejay December

1
@teejay에 동의합니다
— Erdi İzgi


20

에서 Activity의 대화 상자를 표시 버튼의 클릭

Dialog dialog = new Dialog(MyActivity.this);

나를 위해 일했다.


19

***** 코 틀린 버전 *****

또는 this@YourActivity대신에 전달해야합니다applicationContextbaseContext


18

작은 해킹 : GC에 의해 활동이 중단되는 것을 막을 수 있습니다 (하지 말아야하지만 상황에 따라 도움이 될 수 있습니다. 더 이상 필요하지 않은 경우 로 설정 contextForDialog하는 것을 잊지 마십시오 null).

public class PostActivity extends Activity  {
    ...
    private Context contextForDialog = null;
    ...
    public void onCreate(Bundle savedInstanceState) {
        ...
        contextForDialog = this;
    }
    ...
    private void showAnimatedDialog() {
        mSpinner = new Dialog(contextForDialog);
        mSpinner.setContentView(new MySpinner(contextForDialog));
        mSpinner.show();
    }
    ...
}

@MurtuzaKabul이 기능은 == PostActivity에서 상속되는 Activity-> Context에서 상속하므로 대화를 전달하면 컨텍스트를 실제로 전달하는 것입니다
— Elad Gelman

13

프래그먼트를 사용하고 AlertDialog / Toast 메시지를 사용하는 경우 컨텍스트 매개 변수에 getActivity ()를 사용하십시오.

이처럼

ProgressDialog pdialog;
pdialog = new ProgressDialog(getActivity());
pdialog.setCancelable(true);
pdialog.setMessage("Loading ....");
pdialog.show();

12

다음을 사용하십시오.

자바 사용자

활동을 사용하는 경우-> AlertDialog.Builder builder = new AlertDialog.Builder(this);

또는

AlertDialog.Builder builder = new AlertDialog.Builder(your_activity.this);

조각을 사용하는 경우-> AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

코 틀린 사용자를위한

활동을 사용하는 경우-> val builder = AlertDialog.Builder(this)

또는

val builder = AlertDialog.Builder(this@your_activity.this)

조각을 사용하는 경우-> val builder = AlertDialog.Builder(activity!!)


9

첨가

dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

과

"android.permission.SYSTEM_ALERT_WINDOW"/> 명백하게

그것은 지금 나를 위해 작동합니다. 응용 프로그램을 닫고 연 후에도 오류가 발생했습니다.


9

ProgressDialog조각에서 사용 getActivity().getApplicationContext()하고 생성자 매개 변수로 전달하면이 오류가 발생했습니다 . 로 변경해도 getActivity().getBaseContext()작동하지 않았습니다.

나를 위해 일한 해결책은 통과하는 것이 었습니다 getActivity(). 즉

progressDialog = new ProgressDialog(getActivity());


6

사용하다 MyDialog md = new MyDialog(MyActivity.this.getParent());


6

활동 외부에있는 경우 "NameOfMyActivity.this"함수에서 활동 활동으로 사용해야합니다 (예 :

public static void showDialog(Activity activity) {
        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setMessage("Your Message")
        .setPositiveButton("Yes", dialogClickListener)
        .setNegativeButton("No", dialogClickListener).show();
}


//Outside your Activity
showDialog(NameOfMyActivity.this);

5

조각을 사용하고 AlertDialog / Toast메시지를 사용 getActivity()하는 경우 context 매개 변수에서 사용 하십시오.

나를 위해 일했다.

건배!


5

대화 상자 아래에있는 활동의 컨텍스트를 사용하십시오. 그러나 "this"키워드를 사용할 때는 항상 작동하지 않으므로주의하십시오.

예를 들어, 두 개의 탭이있는 호스트로 TabActivity가 있고 각 탭이 다른 활동이고 탭 (활동) 중 하나에서 대화 상자를 작성하려고 시도하고 "this"를 사용하면 예외가 발생합니다. 사례 대화 상자는 모든 것을 호스트하고 볼 수있는 호스트 활동에 연결되어야합니다. (가장 눈에 띄는 부모 활동의 맥락을 말할 수 있습니다)

나는 어떤 문서 에서도이 정보를 찾지 못했지만 시도했습니다. 이것은 더 강력한 지식이없는 나의 해결책입니다.


4

미래의 독자들에게는 다음과 같은 도움이 될 것입니다.

public void show() {
    if(mContext instanceof Activity) {
        Activity activity = (Activity) mContext;
        if (!activity.isFinishing() && !activity.isDestroyed()) {
            dialog.show();
        }
    }
}

2

내 경우에는 일하십시오 :

this.getContext();

2

또는 다음과 같이 대화 상자를 만들 수도 있습니다.

final Dialog dialog = new Dialog(new ContextThemeWrapper(
            this, R.style.MyThemeDialog));

2

기본 UI 스레드가 아닌 스레드에서 대화 상자를 표시하려고하면이 문제가 발생할 수 있다고 생각합니다.

runOnUiThread()이 경우에 사용하십시오 .


2

getParent()새로운 AlertDialog.Builder(getParent());희망 과 같은 맥락의 논쟁 장소에서 시도해보십시오 .


1

API를 살펴본 후 조각에있는 경우 대화 상자에 활동 또는 getActivity를 전달한 다음 리턴 메소드에서 dialog.dismiss ()를 사용하여 강제로 정리하여 누출을 방지 할 수 있습니다.

내가 아는 곳에서는 명시 적으로 언급되어 있지 않지만 OnClickHandlers의 대화 상자가 다시 전달되는 것처럼 보입니다.


0

대화 상자가 어댑터에서 작성중인 경우 :

활동을 어댑터 생성자에 전달하십시오.

adapter = new MyAdapter(getActivity(),data);

어댑터 수신 :

 public MyAdapter(Activity activity, List<Data> dataList){
       this.activity = activity;
    }

이제 빌더에서 사용할 수 있습니다

            AlertDialog.Builder alert = new AlertDialog.Builder(activity);

-1

내 응용 프로그램에 대해 동일한 오류를 해결하는 방법
은 다음과 같습니다. 대화 상자를 만든 후 다음 줄을 추가하십시오.

dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);  

컨텍스트를 얻을 필요는 없습니다. 이것은 현재 팝업 대화 상자 위에 다른 대화 상자를 팝업하는 경우 특히 유용합니다. 또는 상황에 맞는 것이 불편할 때.

이것이 앱 개발에 도움이되기를 바랍니다.

데이비드


-1
android.support.v7.app.AlertDialog.Builder builder = new android.support.v7.app.AlertDialog.Builder(getWindow().getDecorView().getRootView().getContext());

builder.setTitle("Confirm");
builder.setMessage("Are you sure you want delete your old account?");

builder.setPositiveButton("YES", new DialogInterface.OnClickListener() {

    public void onClick(DialogInterface dialog, int which) {
        //Do nothing but close the dialog



        dialog.dismiss();

    }
});

builder.setNegativeButton("NO", new DialogInterface.OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {

        //Do nothing
        dialog.dismiss();
    }
});

android.support.v7.app.AlertDialog alert = builder.create();
alert.show();
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.