"Android.view.WindowManager $ BadTokenException: buider.show () पर विंडो जोड़ने में असमर्थ"


118

मेरे मुख्य से activity, मुझे एक आंतरिक वर्ग को कॉल करने की आवश्यकता है और कक्षा के भीतर एक विधि में, मुझे दिखाने की आवश्यकता है AlertDialog। इसे खारिज करने के बाद, जब ओके बटन दबाया जाता है, तो आगे खरीद के लिए Google Play पर जाएं।

चीजें ज्यादातर समय के लिए पूरी तरह से काम करती हैं, लेकिन कुछ उपयोगकर्ताओं के लिए यह दुर्घटनाग्रस्त builder.show()हो रहा है और मैं "android.view.WindowManager$BadTokenException:क्रैश लॉग से विंडो को जोड़ने में असमर्थ देख सकता हूं । कृपया सुझाव दें।

मेरा कोड इस तरह बहुत अधिक है:

public class classname1 extends Activity{

  public void onCreate(Bundle savedInstanceState) {
    this.requestWindowFeature(Window.FEATURE_NO_TITLE);
    super.onCreate(savedInstanceState);
    setContentView(R.layout.<view>); 

    //call the <className1> class to execute
  }

  private class classNamename2 extends AsyncTask<String, Void, String>{

    protected String doInBackground(String... params) {}

    protected void onPostExecute(String result){
      if(page.contains("error")) 
      {
        AlertDialog.Builder builder = new AlertDialog.Builder(classname1.this);
        builder.setCancelable(true);
        builder.setMessage("");
        builder.setInverseBackgroundForced(true);
        builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
          public void onClick(DialogInterface dialog, int whichButton){
            dialog.dismiss();
            if(!<condition>)
            {
              try
              {
                String pl = ""; 

                mHelper.<flow>(<class>.this, SKU, RC_REQUEST, 
                  <listener>, pl);
              }

              catch(Exception e)
              {
                e.printStackTrace();
              }
            }  
          }
        });

        builder.show();
      }
    }
  }
}

मैंने एक अन्य अलर्ट में भी त्रुटि देखी है जहां मैं किसी अन्य को अग्रेषित नहीं कर रहा हूं activity। यह इस तरह सरल है:

AlertDialog.Builder builder = new AlertDialog.Builder(classname1.this);
    builder.setCancelable(true);

    //if successful
    builder.setMessage(" ");
    builder.setInverseBackgroundForced(true);
    builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int whichButton){
            // dialog.dismiss();
                   }
    });
    builder.show();
}

2
यदि यह आपका पूरा कोड है, तो क्या आपको वास्तव में AsyncTask की आवश्यकता है?
शोभित पुरी

यह पूर्ण कोड नहीं है, यह काफी बड़ा कोड है इसलिए मैंने केवल उस हिस्से को यहां जोड़ा है जहां मैं क्रैश रिपोर्ट से समस्या देख रहा हूं
MSIslam

ठीक है अच्छा। आमतौर पर आप केवल फ़ंक्शन नाम पोस्ट कर सकते हैं और टिप्पणी कर सकते हैं कि आप वहां सामान का गुच्छा बना रहे हैं (जैसा कि आपने अभी किया था)। इसे समझना आसान है। :)।
शोभित पुरी

क्या आप कहीं और इस गतिविधि से किसी अन्य गतिविधि के लिए नेविगेट कर रहे हैं?
शोभित पुरी

1
आपने लिखा टिप्पणी है //send to some other activity। मुझे लगता है कि यदि आप उस हिस्से पर टिप्पणी करेंगे जहां आप एक नई गतिविधि पर जा रहे हैं, तो यह त्रुटि दूर हो जाएगी। त्रुटि इसलिए होती है क्योंकि आपका संवाद पूरी तरह से खारिज हो जाता है, आपकी नई गतिविधि शुरू हो जाती है। में onPostExecute(), आपके पास चेतावनी संवाद है और आप loginगतिविधि का संदर्भ दे रहे हैं । लेकिन आप अन्य गतिविधि के लिए नेविगेट कर रहे हैं, इसलिए संदर्भ गलत हो जाता है। इसलिए आपको यह त्रुटि मिल रही है! Stackoverflow.com/questions/15104677/… समान प्रश्न देखें ।
शोभित पुरी

जवाबों:


264
android.view.WindowManager$BadTokenException: Unable to add window"

संकट :

यह अपवाद तब होता है जब ऐप एक डायलॉग खोलकर उपयोगकर्ता को बैकग्राउंड थ्रेड (AsyncTask) से सूचित करने का प्रयास कर रहा है।

यदि आप पृष्ठभूमि थ्रेड (आमतौर पर AsyncTask के onPostExecute () से UI को संशोधित करने की कोशिश कर रहे हैं और यदि गतिविधि फ़िनिशिंग फ़ेज़ यानी) में प्रवेश कर रही है, तो स्पष्ट रूप से कॉल खत्म हो रही है (), उपयोगकर्ता दबाने वाला घर या बैक बटन या ऐंड्रॉयड पर क्लीन की गई गतिविधि यह त्रुटि प्राप्त करें।

कारण :

इस अपवाद का कारण यह है, जैसा कि अपवाद संदेश कहता है, गतिविधि समाप्त हो गई है लेकिन आप समाप्त गतिविधि के संदर्भ के साथ एक संवाद प्रदर्शित करने का प्रयास कर रहे हैं। चूंकि एंड्रॉइड रनटाइम को प्रदर्शित करने के लिए संवाद के लिए कोई विंडो नहीं है इसलिए यह अपवाद फेंकता है।

उपाय:

इस isFinishing()विधि को समाप्त करने की प्रक्रिया में है या नहीं यह जाँचने के लिए एंड्रॉइड द्वारा उपयोग की जाने वाली विधि का उपयोग करें: क्या यह एंड्रॉइड द्वारा बनाई गई स्पष्ट () कॉल या गतिविधि है। इस विधि का उपयोग करके गतिविधि समाप्त होने पर पृष्ठभूमि थ्रेड से संवाद खोलने से बचना बहुत आसान है।

weak referenceगतिविधि के लिए भी बनाए रखें (और एक मजबूत संदर्भ नहीं है ताकि गतिविधि को एक बार ज़रूरत न होने पर नष्ट किया जा सके) और जांचें कि क्या इस गतिविधि संदर्भ (यानी एक संवाद दिखाते हुए) का उपयोग करके कोई भी यूआई प्रदर्शन करने से पहले गतिविधि समाप्त नहीं हो रही है।

उदा

private class chkSubscription extends AsyncTask<String, Void, String>{

  private final WeakReference<login> loginActivityWeakRef;

  public chkSubscription (login loginActivity) {
    super();
    this.loginActivityWeakRef= new WeakReference<login >(loginActivity)
  }

  protected String doInBackground(String... params) {
    //web service call
  }

  protected void onPostExecute(String result) {
    if(page.contains("error")) //when not subscribed
    {
      if (loginActivityWeakRef.get() != null && !loginActivityWeakRef.get().isFinishing()) {
        AlertDialog.Builder builder = new AlertDialog.Builder(login.this);
        builder.setCancelable(true);
        builder.setMessage(sucObject);
        builder.setInverseBackgroundForced(true);

        builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
          public void onClick(DialogInterface dialog, int whichButton){
            dialog.dismiss();
          }
        });

        builder.show();
      }
    }
  }
}

अपडेट करें :

विंडो कैम:

जैसा कि इसके नाम का तात्पर्य है, एक विंडो टोकन एक विशेष प्रकार का बाइंडर टोकन है जो सिस्टम में एक विंडो को विशिष्ट रूप से पहचानने के लिए विंडो मैनेजर का उपयोग करता है। सुरक्षा के लिए विंडो टोकन महत्वपूर्ण हैं क्योंकि वे दुर्भावनापूर्ण अनुप्रयोगों के लिए अन्य अनुप्रयोगों की खिड़कियों के शीर्ष पर आकर्षित करना असंभव बनाते हैं। विंडो मैनेजर, विंडो को जोड़ने या हटाने के लिए प्रत्येक अनुरोध के हिस्से के रूप में अपने आवेदन के विंडो टोकन को पारित करने के लिए एप्लिकेशन की आवश्यकता के द्वारा इसके खिलाफ सुरक्षा करता है। यदि टोकन मेल नहीं खाते हैं, तो विंडो प्रबंधक अनुरोध को अस्वीकार करता है और एक BadTokenException को फेंकता है । विंडो टोकन के बिना, यह आवश्यक पहचान कदम संभव नहीं होगा और विंडो मैनेजर खुद को दुर्भावनापूर्ण अनुप्रयोगों से बचाने में सक्षम नहीं होगा।

 एक वास्तविक दुनिया परिदृश्य:

जब कोई एप्लिकेशन पहली बार शुरू होता है, तो  ActivManagerService  एक विशेष प्रकार की विंडो टोकन बनाता है जिसे एप्लिकेशन विंडो टोकन कहा जाता है, जो विशिष्ट रूप से एप्लिकेशन के शीर्ष-स्तरीय कंटेनर विंडो की पहचान करता है। गतिविधि प्रबंधक इस एप्लिकेशन और विंडो प्रबंधक दोनों को टोकन देता है, और एप्लिकेशन हर बार विंडो प्रबंधक को टोकन भेजता है, जो स्क्रीन पर एक नई विंडो जोड़ना चाहता है। यह एप्लिकेशन और विंडो मैनेजर के बीच सुरक्षित संपर्क सुनिश्चित करता है (अन्य अनुप्रयोगों के शीर्ष पर विंडोज़ को जोड़ना असंभव बनाकर), और यह भी गतिविधि प्रबंधक के लिए विंडो मैनेजर से सीधे अनुरोध करना आसान बनाता है।


यह बहुत समझ में आता है! इसके अलावा आपका समाधान मुझे बहुत अच्छा लगता है। (y)
MSIslam

'रिक्त अंतिम फ़ील्ड लॉगिनActivityWeakRef शायद आरंभिक संदेश' नहीं हो सकता है और इस तरह आज़माया गया है: निजी अंतिम WeakReference <login> loginActivityWeakRef = नया WeakReference <login> (लॉगिन)। यकीन नहीं होता है कि यह सही काम है
MSIslam

इसके अलावा मैंने WeakReference <login> loginActivityWeakRef से पहले फाइनल को हटा दिया क्योंकि यह कंस्ट्रक्टर में त्रुटि दिखा रहा था।
MSIslam

1
नए chkCubscription (यह) .execute ("") का उपयोग करने का प्रयास करें; नए chkCubscription.execute ("") के बजाय; जैसा कि आपने ऊपर पोस्ट किया है।
रितेश गुने

2
भयानक त्रुटि !! मैं एक ट्यूटोरियल का अनुसरण कर रहा हूं और @PhilRoggenbuck के रूप में, मेरा मुद्दा StartActivity (...) को कॉल करने से ठीक पहले Toast..Show () कॉल करने के कारण हुआ था। इसे ठीक करने के लिए, मैंने इसके बजाय नई तथाकथित गतिविधि में टोस्ट को स्थानांतरित किया!
थियरी

26

मेरे पास डायलॉग दिखाने का फंक्शन था:

void showDialog(){
    new AlertDialog.Builder(MyActivity.this)
    ...
    .show();
}

मुझे यह त्रुटि मिल रही थी और मुझे isFinishing()इस संवाद को दिखाने वाले कार्य को कॉल करने से पहले जांचना था ।

if(!isFinishing())
    showDialog();

1
क्या हमें लिखना नहीं चाहिए if(!MyActivity.this.isFinishing())? यदि यह MyActivity में सही नहीं है
बिबासवन बंद्योपाध्याय

2
अगर यह पहले से ही खत्म हो रहा है, तो एंड्रॉइड किसी भी कोड को क्यों चलाएगा? यदि हम इस समाधान का पालन करते हैं, तो कल्पना करें कि हमें कितने समय का उपयोग करना चाहिए ताकि अनजाने मुद्दों से बचने के लिए बेहतर हो।
डेविड

@ मुझे लगता है कि यह कुछ विवरणों को याद कर रहा है, जैसे कि संवाद को पृष्ठभूमि में बुलाया जा रहा है, लेकिन मैं आपकी बात से पूरी तरह सहमत हूं क्योंकि यह अब है।
कार्ट

महान बिंदु, क्यों बिल्ली के लिए मैं जांच करने की आवश्यकता होगी!
चिबुजे ओपटा

9

संभावित कारण सतर्क संवाद का संदर्भ है। आप उस गतिविधि को समाप्त कर सकते हैं, इसलिए उस संदर्भ में खोलने की कोशिश कर रहे हैं लेकिन जो पहले से ही बंद है। आप उस गतिविधि के संदर्भ को पहली गतिविधि में बदलने का प्रयास करें क्योंकि यह अंत तक समाप्त नहीं होगा।

जैसे

इसके बजाय।

AlertDialog alertDialog = new AlertDialog.Builder(this).create();

उपयोग करने का प्रयास करें

AlertDialog alertDialog = new AlertDialog.Builder(FirstActivity.getInstance()).create();

3
  • पहले आप ओवरराइड doInBackground के बिना AsyncTask का विस्तार नहीं कर सकते
  • दूसरा बिल्डर से AlterDailog बनाने की कोशिश करें, फिर कॉल शो () करें।

    private boolean visible = false;
    class chkSubscription extends AsyncTask<String, Void, String>
    {
    
        protected void onPostExecute(String result)
        {
            AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
            builder.setCancelable(true);
            builder.setMessage(sucObject);
            builder.setInverseBackgroundForced(true);
            builder.setNeutralButton("Ok", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton)
                {
                    dialog.dismiss();
                }
            });
    
            AlertDialog myAlertDialog = builder.create();
            if(visible) myAlertDialog.show();
        }
    
        @Override
        protected String doInBackground(String... arg0)
        {
            // TODO Auto-generated method stub
            return null;
        }
    }
    
    
    @Override
    protected void onResume()
    {
        // TODO Auto-generated method stub
        super.onResume();
        visible = true;
    }
    
    @Override
    protected void onStop()
    {
        visible = false; 
        super.onStop();
    }

1
जवाब के लिए धन्यवाद। मैंने वास्तव में doInBackground पद्धति का उपयोग किया, बस इसका उल्लेख यहां नहीं किया क्योंकि यह अलर्ट से संबंधित नहीं है। बिल्डर को जोड़ने के संबंध में। (), यह ठीक काम कर रहा है, लेकिन यह नहीं जानता कि क्या यह सभी के लिए ठीक काम करेगा। जैसा कि II ने पहले कहा था, मेरा वर्तमान कोड भी ठीक काम करता है, लेकिन कुछ उपयोगकर्ताओं के लिए केवल कुछ ही बार यह विंडो समस्या को जोड़ने में असमर्थ दिखाता है। क्या आप मुझे सुझाव दे सकते हैं कि मेरे कोडिंग में वास्तविक समस्या क्या हो सकती है?
MSIslam

इस मामले में उपयोगकर्ता onPostExecute को कॉल करने से पहले आपकी गतिविधि से बाहर निकल जाता है, इसलिए संवाद रखने के लिए कोई विंडो नहीं है, और इससे आपका एप्लिकेशन क्रैश हो जाता है। यह जानने के लिए onStop में ध्वज जोड़ें कि यदि आपकी गतिविधि अधिक दृश्यमान नहीं है तो संवाद न दिखाएं।
moh.sukhni

onPostExecute वास्तव में बुलाया जाता है, जैसा कि बिल्डर .show () एक ऐसी स्थिति में होता है जब मैं जाँच रहा होता हूं कि क्या उपयोगकर्ता doInBackground () से वेब सेवा कॉल के परिणाम के आधार पर सदस्यता नहीं ली गई है। इसलिए अगर onPostExecute को नहीं बुलाया गया था तो यह बिल्डर.शो () लाइन तक नहीं आएगा।
MSIslam

onPostExecute doInBackground के बाद डिफ़ॉल्ट रूप से कहा जाता है, आप इसे कॉल नहीं कर सकते हैं, और कभी भी वहाँ निष्पादित किया जाएगा।
moh.sukhni 20

1
उपयोगकर्ता द्वारा आपकी गतिविधि से नेविगेट करने के बाद आपका async कार्य काम करना जारी रखेगा और इससे बिल्डर.शो () आपके एप्लिकेशन को क्रैच कर देगा क्योंकि आपके लिए UI को संभालने के लिए कोई गतिविधि नहीं है। इसलिए आपका ऐप वेब से डेटा खींच रहा है, लेकिन डेटा प्राप्त करने से पहले आपकी गतिविधि नष्ट हो गई है।
moh.sukhni

1

मैं onCreateइसमें showऔर इसके साथ डायलॉग बना रहा हूं और इसका उपयोग कर रहा हूं hide। मेरे लिए मूल कारण खारिज नहीं हो रहा था onBackPressed, जो Homeगतिविधि को खत्म कर रहा था ।

@Override
public void onBackPressed() {
new AlertDialog.Builder(this)
                .setTitle("Really Exit?")
                .setMessage("Are you sure you want to exit?")
                .setNegativeButton(android.R.string.no, null)
                .setPositiveButton(android.R.string.yes,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog,
                                    int which) {
                                Home.this.finish();
                                return;
                            }
                        }).create().show();

मैं onBackPressedअपने संवादों को बंद / खारिज किए बिना होम गतिविधि समाप्त कर रहा था ।

जब मैंने अपने संवादों को खारिज कर दिया तो दुर्घटना गायब हो गई।

new AlertDialog.Builder(this)
                .setTitle("Really Exit?")
                .setMessage("Are you sure you want to exit?")
                .setNegativeButton(android.R.string.no, null)
                .setPositiveButton(android.R.string.yes,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog,
                                    int which) {
                                networkErrorDialog.dismiss() ;
                                homeLocationErrorDialog.dismiss() ;
                                currentLocationErrorDialog.dismiss() ;
                                Home.this.finish();
                                return;
                            }
                        }).create().show();

0

मैं इसे हल करने की कोशिश करता हूं।

 AlertDialog.Builder builder = new AlertDialog.Builder(
                   this);
            builder.setCancelable(true);
            builder.setTitle("Opss!!");

            builder.setMessage("You Don't have anough coins to withdraw. ");
            builder.setMessage("Please read the Withdraw rules.");
            builder.setInverseBackgroundForced(true);
            builder.setPositiveButton("OK",
                    (dialog, which) -> dialog.dismiss());
            builder.create().show();

-1

इसे इस्तेमाल करे :

    public class <class> extends Activity{

    private AlertDialog.Builder builder;

    public void onCreate(Bundle savedInstanceState) {
                    this.requestWindowFeature(Window.FEATURE_NO_TITLE);
                    super.onCreate(savedInstanceState);

                setContentView(R.layout.<view>); 

                builder = new AlertDialog.Builder(<class>.this);
                builder.setCancelable(true);
                builder.setMessage(<message>);
                builder.setInverseBackgroundForced(true);

        //call the <className> class to execute
}

    private class <className> extends AsyncTask<String, Void, String>{

    protected String doInBackground(String... params) {

    }
    protected void onPostExecute(String result){
        if(page.contains("error")) //when not subscribed
        {   
           if(builder!=null){
                builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton){
                    dialog.dismiss();
                        if(!<condition>)
                        {
                        try
                        {
                        String pl = ""; 

                        mHelper.<flow>(<class>.this, SKU, RC_REQUEST, 
                        <listener>, pl);
                        }

                        catch(Exception e)
                        {
                        e.printStackTrace();
                        }
                    }  
                }
            });

            builder.show();
        }
    }

}
}

-4

इस ग्लोबल्स चर विचार के साथ, मैंने ऑनक्रीट () में MainActivity इंस्टेंस को बचाया; Android वैश्विक चर

public class ApplicationController extends Application {

    public static MainActivity this_MainActivity;
}

और इस तरह के संवाद खोलें। इसने काम कर दिया।

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Global Var
    globals = (ApplicationController) this.getApplication();
    globals.this_MainActivity = this;
}

और एक सूत्र में, मैं इस तरह से संवाद खोलता हूं।

AlertDialog.Builder alert = new AlertDialog.Builder(globals.this_MainActivity);
  1. ओपन मेनएक्टिविटी
  2. एक धागा शुरू करो।
  3. धागे से खुला संवाद -> काम।
  4. "बैक बटन" पर क्लिक करें (ऑनक्रीट को कॉल किया जाएगा और पहले मेनऐक्टिविटी हटा दें)
  5. नई मेनएक्टिविटी शुरू होगी। (और इसे ग्लोबल्स को सहेजना है)
  6. पहले धागे से खुला संवाद -> यह खुलेगा और काम करेगा।

:)


4
किसी गतिविधि का स्थिर संदर्भ कभी न रखें। यह स्मृति रिसाव का कारण होगा
LeanDroid
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.