Android: ProgressDialog.show () ขัดข้องด้วย getApplicationContext


109

ฉันไม่สามารถเข้าใจได้ว่าทำไมสิ่งนี้จึงเกิดขึ้น รหัสนี้:

mProgressDialog = ProgressDialog.show(this, "", getString(R.string.loading), true);

ใช้งานได้ดี อย่างไรก็ตามรหัสนี้:

mProgressDialog = ProgressDialog.show(getApplicationContext(), "", getString(R.string.loading), true);

แสดงข้อยกเว้นต่อไปนี้:

W/WindowManager(  569): Attempted to add window with non-application token WindowToken{438bee58 token=null}.  Aborting.
D/AndroidRuntime( 2049): Shutting down VM
W/dalvikvm( 2049): threadid=3: thread exiting with uncaught exception (group=0x4001aa28)
E/AndroidRuntime( 2049): Uncaught handler: thread main exiting due to uncaught exception
E/AndroidRuntime( 2049): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.tastekid.TasteKid/com.tastekid.TasteKid.YouTube}: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
E/AndroidRuntime( 2049):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2401)
E/AndroidRuntime( 2049):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2417)
E/AndroidRuntime( 2049):    at android.app.ActivityThread.access$2100(ActivityThread.java:116)
E/AndroidRuntime( 2049):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1794)
E/AndroidRuntime( 2049):    at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime( 2049):    at android.os.Looper.loop(Looper.java:123)
E/AndroidRuntime( 2049):    at android.app.ActivityThread.main(ActivityThread.java:4203)
E/AndroidRuntime( 2049):    at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 2049):    at java.lang.reflect.Method.invoke(Method.java:521)
E/AndroidRuntime( 2049):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
E/AndroidRuntime( 2049):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:549)
E/AndroidRuntime( 2049):    at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime( 2049): Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
E/AndroidRuntime( 2049):    at android.view.ViewRoot.setView(ViewRoot.java:460)
E/AndroidRuntime( 2049):    at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:177)
E/AndroidRuntime( 2049):    at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
E/AndroidRuntime( 2049):    at android.app.Dialog.show(Dialog.java:238)
E/AndroidRuntime( 2049):    at android.app.ProgressDialog.show(ProgressDialog.java:107)
E/AndroidRuntime( 2049):    at android.app.ProgressDialog.show(ProgressDialog.java:90)
E/AndroidRuntime( 2049):    at com.tastekid.TasteKid.YouTube.onCreate(YouTube.java:45)
E/AndroidRuntime( 2049):    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123)
E/AndroidRuntime( 2049):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2364)
E/AndroidRuntime( 2049):    ... 11 more

มีความคิดว่าเหตุใดจึงเกิดขึ้น ฉันเรียกสิ่งนี้จากonCreateวิธีการ


หากคุณกำลังใช้Fragment, stackoverflow.com/questions/24825114/...
Yeo

คำตอบ:


42

คุณใช้ API เวอร์ชันใด หากฉันพูดถูกว่าปัญหาคืออะไรสิ่งนี้ได้รับการแก้ไขแล้วใน Android 1.6 (API เวอร์ชัน 4)

ดูเหมือนว่าการอ้างอิงวัตถุที่getApplicationContext()ส่งคืนเพียงแค่ชี้ไปที่ว่าง ฉันคิดว่าคุณกำลังมีปัญหาคล้ายกับที่ฉันมีเนื่องจากโค้ดบางส่วนในonCreate()กำลังทำงานก่อนที่จะสร้างหน้าต่างเสร็จ นี่จะเป็นการแฮ็ก แต่ลองเปิดเธรดใหม่ในเวลาไม่กี่ร้อยมิลลิวินาที (IIRC: 300-400 ดูเหมือนจะใช้ได้สำหรับฉัน แต่คุณจะต้องใช้คนจรจัด) ที่เปิด ProgressDialog ของคุณและเริ่มสิ่งอื่นที่คุณต้องการ ( เช่นเครือข่าย IO) สิ่งนี้:

@Override
public void onCreate(Bundle savedInstanceState) {
    // do all your other stuff here

    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            mProgressDialog = ProgressDialog.show(
               YouTube.this.getApplicationContext(), "",
               YouTube.this.getString(R.string.loading), true);

            // start time consuming background process here
        }
    }, 1000); // starting it in 1 second
}

6
ฉันใช้ 1.6 ฉันค่อนข้างแน่ใจว่าควรดำเนินการ UI ใด ๆ ในเธรด UI ดังนั้นการเรียก ProgressDialog.show () ในเธรดแยกต่างหากอาจเป็นปัญหาใหญ่ได้อย่างง่ายดาย ยังคิดว่านี่เป็นเรื่องแปลก
เฟลิกซ์

3
ในตัวอย่างที่ฉันให้คุณไม่ได้ดำเนินการ UI ในเธรดอื่นเธรดอื่นกำลังเรียกกลับไปที่เธรด UI เพื่อบอกให้เปิดกล่องโต้ตอบ
Jeremy Logan

มันแปลกแน่นอนและเป็นการแฮ็กทั้งหมด แต่มันก็ใช้ได้ผลสำหรับฉัน ฉันต้องการทดสอบข้อบกพร่องที่มีใน 1.6 เพื่อดูว่าฉันสามารถหยุดใช้สิ่งนี้ได้หรือไม่
Jeremy Logan

1
ไม่ใช่ 'แปลก' หรือ 'แฮ็ครวม' แต่เป็นวิธีการที่ถูกต้องตามกฎหมายอย่างสมบูรณ์!
KomodoDave

1
@KomodoDave ฉันเพิ่งรู้ว่าฉันมีปัญหาเดียวกัน อย่างไรก็ตามแฮ็คที่ไม่ดีอยู่ในตัวจับเวลา นี่คือช่วงเวลาที่รอให้ล้มเหลวเป็นช่วง ๆ ในบางสถานการณ์ กุญแจสู่ความสำเร็จอาจอยู่ที่การตั้งเวลาให้สั้นลงตรวจสอบว่าแอปพลิเคชันพร้อมหรือไม่แล้วถ่ายทอดการดำเนินการอีกครั้งจนกว่าจะถึงเวลา อาจ จำกัด ว่าจะลองกี่ครั้งก็ได้เช่นกัน
Jim Rush

129

ฉันใช้ Android เวอร์ชัน 2.1 กับ API ระดับ 7 ฉันประสบกับปัญหานี้ (หรือคล้ายกัน) และแก้ไขได้โดยใช้สิ่งนี้:

Dialog dialog = new Dialog(this);

แทนสิ่งนี้:

Dialog dialog = new Dialog(getApplicationContext());

หวังว่านี่จะช่วยได้ :)


4
ฉันมีปัญหาคล้ายกัน แต่กำลังใช้ ActivityGroup วิธีเดียวที่ฉันสามารถแก้ไขข้อผิดพลาดนี้ได้คือใช้ getParent () แทน
วงเล็บ

20
สิ่งนี้ตอบคำถามของเขาอย่างไร? เขาถามว่าทำไมอันที่สองถึงใช้ไม่ได้ในขณะที่อันแรกทำ
Burkhard

3
-1, 'this' เหมือนกับ 'getApplicationContext ()' สำหรับพวกเราบางคน
kellogs

ยังคงเป็นเคล็ดลับที่มีประโยชน์สำหรับ 2.1
Carlos P

64

สำหรับฉันแล้วการเปลี่ยนแปลง

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

ถึง

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

สิ่งที่แปลกคือสิ่งแรกสามารถพบได้ในบทช่วยสอนของ Google และมีคนพบข้อผิดพลาดนี้ ..


1
ทางออกเดียวที่ใช้ได้ผลสำหรับฉันด้วยการแจ้งเตือนภายในเหตุการณ์ onclick
Tobias

นี่คือวิธีที่ถูกต้องในการทำ อย่าใช้ ApplicationContext เว้นแต่จำเป็นอย่างยิ่งและเหนือสิ่งอื่นใดห้ามใช้เพื่อแสดงองค์ประกอบ UI นั่นคือสิ่งที่กิจกรรม (และในที่สุดเศษ) มีไว้สำหรับ
Martin Marconcini

นี่คือหนึ่ง thisเพียงอย่างเดียวจะใช้ไม่ได้ถ้าคุณทำสิ่งนั้นภายในตัวอย่างเช่นคลิกฟัง
Ayman Salah

23

ฉันไม่คิดว่านี่เป็นปัญหาเรื่องเวลาในบริบทแอปพลิเคชันว่าง

ลองขยายแอปพลิเคชันภายในแอปของคุณ (หรือใช้หากคุณมีอยู่แล้ว)

public class MyApp extends Application

ทำให้อินสแตนซ์พร้อมใช้งานเป็นซิงเกิลตันส่วนตัว สิ่งนี้ไม่เคยเป็นโมฆะ

private static MyApp appInstance;

สร้างตัวช่วยแบบคงที่ใน MyApp (ซึ่งจะใช้ซิงเกิลตัน)

    public static void showProgressDialog( CharSequence title, CharSequence message )
{
    prog = ProgressDialog.show(appInstance, title, message, true); // Never Do This!
}

บูม !!

ตรวจสอบคำตอบของวิศวกร Android ที่นี่: WindowManager $ BadTokenException

สาเหตุหนึ่งของข้อผิดพลาดนี้อาจพยายามแสดงหน้าต่าง / กล่องโต้ตอบแอปพลิเคชันผ่านบริบทที่ไม่ใช่กิจกรรม

ตอนนี้ฉันเห็นด้วยมันไม่สมเหตุสมผลเลยที่เมธอดใช้ Context param แทน Activity ..


10

หลังจากอ่านคำตอบข้างต้นฉันพบว่าสำหรับสถานการณ์ของฉันสิ่งต่อไปนี้ได้แก้ไขปัญหาแล้ว

สิ่งนี้ทำให้เกิดข้อผิดพลาด

myButton.setOnClickListener(new OnClickListener(){
    public void onClick(View v) {
        MyDialogue dialog = new MyDialogue(getApplicationContext());
        dialog.show();              
    }
});

จากคำตอบก่อนหน้านี้ที่แนะนำว่าบริบทนั้นผิดฉันเปลี่ยน getApplicationContext () เพื่อดึงบริบทจาก View ที่ส่งไปยังปุ่มบนเมธอดคลิก

myButton.setOnClickListener(new OnClickListener(){
    public void onClick(View v) {
        MyDialogue dialog = new MyDialogue(v.getContext());
        dialog.show();              
    }
});

ฉันไม่เข้าใจการทำงานของ Java อย่างเต็มที่ดังนั้นฉันจึงอาจผิดพลาด แต่ฉันคาดเดาว่าสำหรับสถานการณ์เฉพาะของฉันสาเหตุอาจเกี่ยวข้องกับความจริงที่ว่าตัวอย่างข้างต้นถูกกำหนดไว้ในคลาสกิจกรรมนามธรรม สืบทอดและใช้โดยกิจกรรมจำนวนมากบางทีอาจมีส่วนทำให้ getApplicationContext () ไม่ส่งคืนบริบทที่ถูกต้อง ?? (แค่เดา).


วิธีแก้ปัญหาที่ได้คะแนนดีกว่าอาจใช้ได้กับกรณีส่วนใหญ่ แต่เทคนิค v.getContext () ของคุณดูเหมือนจะแก้ปัญหาที่ดื้อรั้นที่สุดเช่นของฉัน ความรู้เชิงประจักษ์ของจาวาของฉันทำให้ฉันคิดว่าบริบทที่มาจากเมธอด onCreated นั้นไม่เหมือนกับบริบทที่มาจาก View ของเมธอด onClick โหวต!
Josh

วันนี้ช่วยฉันไว้!
Alejandro Luengo

6

ฉันกำลังสร้างมุมมองแผนที่ด้วยการซ้อนทับแบบแยกรายการ ฉันกำลังสร้าง itemizedoverlay แบบนี้จาก mapActivity ของฉัน:

OCItemizedOverlay currentLocationOverlay = new OCItemizedOverlay(pin,getApplicationContext);

ฉันพบว่าฉันจะได้รับ "android.view.WindowManager $ BadTokenException: ไม่สามารถเพิ่มหน้าต่าง - โทเค็น null ไม่ใช่สำหรับแอปพลิเคชัน" ข้อยกเว้นเมื่อเมธอด onTap ของ itemizedoverlay ของฉันถูกทริกเกอร์ (เมื่อมีการแตะตำแหน่งบน mapview)

ฉันพบว่าถ้าฉันเพียงแค่ส่ง 'this' แทน 'getApplicationContext ()' ไปยังตัวสร้างของฉันปัญหาก็หายไป ดูเหมือนว่าจะสนับสนุนข้อสรุปของ alienjazzcat แปลก.


1
ขอบคุณนี่คือสิ่งที่เป็นปัญหาของฉัน
คอน

4

สำหรับกิจกรรมที่แสดงภายใน TabActivities ให้ใช้getParent ()

final AlertDialog.Builder builder = new AlertDialog.Builder(getParent());

แทน

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

3

สำหรับ Android 2.2
ใช้รหัสนี้:

//activity is an instance of a class which extends android.app.Activity
Dialog dialog = new Dialog(activity);

แทนรหัสนี้:

// this code produces an ERROR:
//android.view.WindowManager$BadTokenException: 
//Unable to add window -- token null is not for an application
Context mContext = activity.getApplicationContext();
Dialog dialog = new Dialog(mContext);

หมายเหตุ: กล่องโต้ตอบที่กำหนดเองของฉันถูกสร้างขึ้นภายนอกactivity.onCreateDialog(int dialogId)วิธีการ


3

ลอง -

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

จำเป็นเมื่อกิจกรรมปัจจุบันอยู่ในกลุ่มกิจกรรม
htafoya

2

มีปัญหาที่คล้ายกันกับ (ความเข้ากันได้) Fragments ซึ่งการใช้getActivity()ภายในProgressDialog.show()ขัดข้อง ฉันยอมรับว่ามันเป็นเพราะเวลา

การแก้ไขที่เป็นไปได้:

mContext = getApplicationContext();

if (mContext != null) {
    mProgressDialog = ProgressDialog.show(mContext, "", getString(R.string.loading), true);
}

แทนที่จะใช้

mProgressDialog = ProgressDialog.show(getApplicationContext(), "", getString(R.string.loading), true);

วาง mContext ให้เร็วที่สุดเพื่อให้มีเวลามากขึ้นในการคว้าบริบท ยังไม่มีการรับประกันว่าจะได้ผลเพียงแค่ลดโอกาสที่จะเกิดข้อผิดพลาด หากยังไม่ได้ผลคุณจะต้องใช้แฮ็คตัวจับเวลา (ซึ่งอาจทำให้เกิดปัญหาด้านเวลาอื่น ๆ เช่นการปิดกล่องโต้ตอบในภายหลัง)

แน่นอนว่าถ้าคุณสามารถใช้ได้thisหรือActivityName.thisมันเสถียรกว่าเพราะthisชี้ไปที่บางสิ่งอยู่แล้ว แต่ในบางกรณีเช่นเดียวกับสถาปัตยกรรม Fragment บางอย่างก็ไม่ใช่ตัวเลือก


2

(สำหรับการอ้างอิงในอนาคต)

ฉันคิดว่าเป็นเพราะมีความแตกต่างในบริบทแอปพลิเคชันและบริบทกิจกรรมดังที่อธิบายไว้ที่นี่: http://www.doubleencore.com/2013/06/context/

ซึ่งหมายความว่าเราไม่สามารถแสดงกล่องโต้ตอบโดยใช้ Application Context แค่นั้นแหละ.


2

สำหรับการใช้กล่องโต้ตอบภายในกิจกรรมให้ดำเนินการดังนี้:

private Context mContext;
private AlertDialog.Builder mBuilder;

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

     //using mContext here refering to activity context
     mBuilder = new AlertDialog.Builder(mContext);
     //...
     //rest of the code
     //...
}

สำหรับการใช้กล่องโต้ตอบภายในส่วนย่อยให้ทำดังนี้:

private Context mContext;
private AlertDialog.Builder mBuilder;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
      View mRootView = inflater.inflate(R.layout.fragment_layout, container, false);
      mContext = getActivity();

      //using mContext here refering to fragment's hosting activity context
      mBuilder = new AlertDialog.Builder(mContext);
      //...
      //rest of the code
      //...
      return mRootView;
}

แค่นั้นแหละ ^ _ ^


1

สิ่งที่ฉันทำเพื่อแก้ไขปัญหานี้คือการสร้างคลาสพื้นฐานสำหรับกิจกรรมทั้งหมดของฉันที่ฉันเก็บข้อมูลทั่วโลก ในกิจกรรมแรกฉันบันทึกบริบทในตัวแปรในคลาสพื้นฐานของฉันดังนี้:

คลาสพื้นฐาน

public static Context myucontext; 

กิจกรรมแรกที่ได้มาจากคลาสพื้นฐาน

mycontext = this

จากนั้นฉันใช้ mycontext แทน getApplicationContext เมื่อสร้างไดอะล็อก

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

น่าเสียดายที่โซลูชันนี้ไม่ได้รับคะแนนโหวตเพิ่มขึ้นอีก จากวิธีแก้ปัญหาที่เป็นไปได้ทั้งหมดที่นำเสนอที่นี่นี่เป็นสิ่งเดียวที่เหมาะกับฉันใน AsyncTask
ckn

1

หากคุณกำลังเรียก ProgressDialog.show () เป็นส่วน ๆ การแคสต์ mContext to Activity จะได้ผลสำหรับฉัน

     ProgressDialog pd = new ProgressDialog((Activity) mContext);

1

นี่เป็นปัญหาที่พบบ่อย ใช้thisแทนgetApplicationContext() สิ่งนั้นจะช่วยแก้ปัญหาของคุณได้


0

ฉันได้ติดตั้งกล่องโต้ตอบการแจ้งเตือนสำหรับข้อยกเว้นที่ส่งไปยังมุมมองกิจกรรมปัจจุบันเมื่อใดก็ตามที่ฉันให้เช่นนี้

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

ให้ข้อยกเว้นหน้าต่างเดียวกันฉันเขียนโค้ดสำหรับการแจ้งเตือนออกจาก onCreate () ง่ายมากที่ฉันใช้คำสั่งcontext = this;after setContentView()ในonCreate()วิธีการใช้ตัวแปรบริบทเป็น global เช่นContext context;

ตัวอย่างโค้ดคือ

static Context context;

 public void onCreate(Bundle savedInstanceState)  { 
        super.onCreate(savedInstanceState); 


        setContentView(R.layout.network); 
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

        context = this;
.......

ตัวอย่างวิธีการแจ้งเตือนคือ

private void alertException(String execMsg){
        Log.i(TAG,"in alertException()..."+context);
        Log.e(TAG,"Exception :"+execMsg);
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
.......

มันใช้งานได้ดีสำหรับฉันที่จริงฉันค้นหาข้อผิดพลาดนี้ใน StackOverflow ฉันพบข้อความค้นหานี้หลังจากอ่านคำตอบทั้งหมดของโพสต์นี้ฉันลองวิธีนี้เพื่อให้ได้ผลฉันคิดว่านี่เป็นวิธีง่ายๆในการเอาชนะข้อยกเว้น

ขอบคุณราเจนดาร์


0

หากคุณมีปัญหาใน groupActivity อย่าใช้สิ่งนี้ PARENT เป็นแบบคงที่จาก Parent ActivityGroup

final AlertDialog.Builder builder = new AlertDialog.Builder(GroupActivityParent.PARENT);

แทน

final AlertDialog.Builder builder = new AlertDialog.Builder(getParent());

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.