Hộp thoại ném "Không thể thêm cửa sổ - mã thông báo null không dành cho ứng dụng, với ứng dụng getApplication () làm bối cảnh


665

Hoạt động của tôi đang cố gắng tạo AlertDialog yêu cầu Ngữ cảnh làm tham số. Điều này hoạt động như mong đợi nếu tôi sử dụng:

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

Tuy nhiên, tôi không hài lòng khi sử dụng "cái này" làm bối cảnh do khả năng rò rỉ bộ nhớ khi Activity bị phá hủy và được tạo lại ngay cả trong một thứ đơn giản như xoay màn hình. Từ một bài đăng liên quan trên blog của nhà phát triển Android :

Có hai cách dễ dàng để tránh rò rỉ bộ nhớ liên quan đến ngữ cảnh. Rõ ràng nhất là tránh thoát khỏi bối cảnh bên ngoài phạm vi của chính nó. Ví dụ trên cho thấy trường hợp của một tham chiếu tĩnh nhưng các lớp bên trong và tham chiếu ngầm của chúng đối với lớp bên ngoài có thể nguy hiểm như nhau. Giải pháp thứ hai là sử dụng bối cảnh Ứng dụng. Bối cảnh này sẽ tồn tại miễn là ứng dụng của bạn còn sống và không phụ thuộc vào vòng đời hoạt động. Nếu bạn có kế hoạch giữ các đối tượng tồn tại lâu cần bối cảnh, hãy nhớ đối tượng ứng dụng. Bạn có thể lấy nó dễ dàng bằng cách gọi Context.getApplicationContext () hoặc Activity.getApplication ().

Nhưng đối với AlertDialog()cả hai getApplicationContext()hoặc không getApplication()được chấp nhận như là một bối cảnh, vì nó ném ngoại lệ:

"Không thể thêm cửa sổ - mã thông báo null không dành cho ứng dụng.

mỗi tài liệu tham khảo: 1 , 2 , 3 , v.v.

Vì vậy, điều này có thực sự được coi là một "lỗi" không, vì chúng tôi chính thức được khuyên nên sử dụng Activity.getApplication()và nó không hoạt động như quảng cáo?

Jim


tài liệu tham khảo cho các mục đầu tiên nơi R.Guy khuyên sử dụng getApplication: android-developers.blogspot.com/2009/01/...
gymshoe

tài liệu tham khảo khác: stackoverflow.com/questions/1561804/ từ
gymshoe

1
tài liệu tham khảo khác: stackoverflow.com/questions/2634991/ ích
gymshoe


Câu trả lời:


1354

Thay vì getApplicationContext(), chỉ sử dụng ActivityName.this.


67
Tuyệt quá! Chỉ cần bình luận về điều đó .. đôi khi bạn có thể cần lưu trữ "cái này" trên toàn cầu, (ví dụ) để truy cập nó trong phương thức được thực hiện của người nghe, người sở hữu "cái này". Trong trường hợp đó, bạn sẽ xác định "Bối cảnh bối cảnh" trên toàn cầu và sau đó trong onCreate, đặt "bối cảnh = này", sau đó tham khảo "bối cảnh". Hy vọng rằng có ích trong quá.
Steven L

8
Trên thực tế, vì Listenercác lớp học thường ẩn danh bên trong, tôi có xu hướng chỉ làm final Context ctx = this;và tôi đi;)
Alex

28
@StevenL Để thực hiện những gì bạn đang nói, bạn nên sử dụng ExternalClassName.this để đề cập rõ ràng đến "cái này" của lớp bên ngoài.
Artem Russakovskii

11
Sẽ không sử dụng "this" rò rỉ nó nếu hộp thoại của bạn được sử dụng trong một cuộc gọi lại và bạn rời khỏi hoạt động trước khi gọi lại được gọi? Ít nhất đó là những gì Android dường như phàn nàn trong logcat.
Artem Russakovskii

6
Tôi không khuyên bạn nên tiếp cận @StevenLs vì bạn có thể dễ dàng rò rỉ bộ nhớ của hoạt động đó trừ khi bạn nhớ xóa tham chiếu tĩnh trong onDestroy - Artem là chính xác. Cách tiếp cận của StevenL là sự thiếu hiểu biết về cách thức hoạt động của Java
Dori

191

Sử dụng thiskhông làm việc cho tôi, nhưng MyActivityName.thisđã làm. Hy vọng điều này sẽ giúp bất cứ ai không thể thislàm việc.


63
Đó là những gì xảy ra khi bạn sử dụng thistừ bên trong một lớp bên trong. Nếu bạn muốn tham chiếu thể hiện của một lớp bên ngoài, bạn phải chỉ định như vậy, như bạn làm với OuterClass.this. Chỉ sử dụng thisluôn tham chiếu thể hiện của lớp bên trong nhất.
kaka

60

Bạn có thể tiếp tục sử dụng getApplicationContext(), nhưng trước khi sử dụng, bạn nên thêm cờ này : dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT), và lỗi sẽ không hiển thị.

Thêm quyền sau vào bảng kê khai của bạn:

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

1
Tôi nhận được Không thể thêm cửa sổ android.view.ViewRootImpl$W@426ce670 - quyền bị từ chối cho loại cửa sổ này
Ram G.

thêm quyền: <
used

3
Có vẻ như bạn không thể kích hoạt quyền này trong API 23 trở đi code.google.com/p/android-developer-preview/issues/ gợi
roy zhang

1
Bạn có thể sử dụng nó cho API 23 trở đi, tuy nhiên, bạn cần phải nhắc người dùng: startActivityForResult (Ý định mới (Cài đặt.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse ("gói:" + getPackageName ())), OVERLAY_PERMISS tuy nhiên, liệu bạn có nên sử dụng nó hay không là một vấn đề khác ...
Ben Neill

2
Điều này hữu ích khi bạn đang hiển thị hộp thoại tiến trình bên trong dịch vụ
Anand Savigate 2/12/2016

37

Bạn đã xác định chính xác vấn đề khi bạn nói "... đối với AlertDialog () không getApplicationContext () hoặc getApplication () được chấp nhận dưới dạng Ngữ cảnh, vì nó ném ngoại lệ: 'Không thể thêm cửa sổ - mã thông báo null không dành cho một ứng dụng'"

Để tạo Hộp thoại, bạn cần Bối cảnh hoạt động hoặc Bối cảnh dịch vụ , không phải là Bối cảnh ứng dụng (cả getApplicationContext () và getApplication () trả về Bối cảnh ứng dụng).

Đây là cách bạn có được Bối cảnh hoạt động :

(1) Trong một Hoạt động hoặc Dịch vụ:

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

(2) Trong một đoạn: AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

Rò rỉ bộ nhớ không phải là vấn đề nội tại đối với tham chiếu "này", là tham chiếu của chính đối tượng (tức là tham chiếu đến bộ nhớ được phân bổ thực tế để lưu trữ dữ liệu của đối tượng). Nó xảy ra với bất kỳ bộ nhớ được phân bổ nào mà Bộ thu gom rác (GC) không thể giải phóng sau khi bộ nhớ được phân bổ đã vượt quá tuổi thọ hữu ích của nó.

Hầu hết thời gian, khi một biến đi ra khỏi phạm vi, bộ nhớ sẽ được thu hồi bởi GC. Tuy nhiên, rò rỉ bộ nhớ có thể xảy ra khi tham chiếu đến một đối tượng được giữ bởi một biến, nói "x", vẫn tồn tại ngay cả sau khi đối tượng đã tồn tại lâu hơn tuổi thọ hữu ích của nó. Do đó, bộ nhớ được phân bổ sẽ bị mất miễn là "x" giữ tham chiếu đến nó vì GC sẽ không giải phóng bộ nhớ miễn là bộ nhớ đó vẫn được tham chiếu. Đôi khi, rò rỉ bộ nhớ không rõ ràng do một chuỗi các tham chiếu đến bộ nhớ được phân bổ. Trong trường hợp như vậy, GC sẽ không giải phóng bộ nhớ cho đến khi tất cả các tham chiếu đến bộ nhớ đó đã bị xóa.

Để tránh rò rỉ bộ nhớ, hãy kiểm tra mã của bạn để biết các lỗi logic khiến bộ nhớ được phân bổ được tham chiếu vô thời hạn bởi "this" (hoặc các tham chiếu khác). Hãy nhớ kiểm tra các tham chiếu chuỗi là tốt. Dưới đây là một số công cụ bạn có thể sử dụng để giúp bạn phân tích việc sử dụng bộ nhớ và tìm ra những rò rỉ bộ nhớ phiền phức đó:


Đối với một Hoạt động bạn cũng có thể sử dụng ActivityName.this nơi ActivityName là (rõ ràng) Tên của hoạt động của bạn (ví dụ MainActivity)
Luis Cabrera Benito

34

Hộp thoại của bạn không nên là "đối tượng tồn tại lâu cần bối cảnh". Các tài liệu là khó hiểu. Về cơ bản nếu bạn làm một cái gì đó như:

static Dialog sDialog;

(lưu ý tĩnh )

Sau đó, trong một hoạt động ở đâu đó bạn đã làm

 sDialog = new Dialog(this);

Bạn có thể sẽ bị rò rỉ hoạt động ban đầu trong một vòng quay hoặc tương tự sẽ phá hủy hoạt động. (Trừ khi bạn dọn dẹp trong onDestroy, nhưng trong trường hợp đó, bạn có thể sẽ không làm cho đối tượng Dialog tĩnh)

Đối với một số cấu trúc dữ liệu, sẽ hợp lý khi làm cho chúng tĩnh và dựa trên ngữ cảnh của ứng dụng, nhưng nói chung không dành cho những thứ liên quan đến UI, như các hộp thoại. Vì vậy, một cái gì đó như thế này:

Dialog mDialog;

...

mDialog = new Dialog(this);

Sẽ ổn và không nên rò rỉ hoạt động vì mDialog sẽ được giải phóng với hoạt động vì nó không tĩnh.


Tôi đang gọi nó từ một asynctask, điều này làm việc cho tôi, người bạn đời
MemLeak

hộp thoại của tôi là tĩnh, một khi tôi loại bỏ khai báo tĩnh, nó hoạt động.
Ceddy Muhoza

25

Tôi đã phải gửi bối cảnh của mình thông qua một hàm tạo trên bộ điều hợp tùy chỉnh được hiển thị trong một đoạn và gặp vấn đề này với getApplicationContext (). Tôi đã giải quyết nó bằng:

this.getActivity().getWindow().getContext()trong onCreatecuộc gọi lại của các mảnh .


4
Điều này cũng làm việc với tôi, tôi đã chuyển nó cho nhà xây dựng của AsyncTask bên ngoài mà tôi đang sử dụng (Nó hiển thị Hộp thoại tiến trình).
Rohan Kandwal

3
đây là câu trả lời THỰC SỰ cho các nhiệm vụ phức tạp hơn :)
teejay

1
Tôi đồng ý với @teejay
Erdi İzgi

23

trong Hoạt động chỉ cần sử dụng:

MyActivity.this

trong đoạn:

getActivity();

Điều này đã sửa nó cho tôi trong Hoạt động của tôi. Cảm ơn
Werner

20

Trong Activitykhi nhấp vào nút hiển thị một hộp thoại

Dialog dialog = new Dialog(MyActivity.this);

Đã làm cho tôi.


19

***** phiên bản kotlin *****

Bạn nên vượt qua this@YourActivitythay vì applicationContexthoặcbaseContext


18

Ít hack: bạn có thể ngăn chặn phá hủy hoạt động của bạn bằng phương pháp GC (bạn không nên làm điều đó, nhưng nó có thể giúp đỡ trong một số tình huống Đừng quên thiết lập. contextForDialogĐến nullkhi nó không còn cần thiết):

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 Nó hoạt động vì điều này == PostActivity kế thừa từ Activity-> kế thừa từ Ngữ cảnh, vì vậy khi bạn chuyển qua hộp thoại, bối cảnh của bạn thực sự đang chuyển qua hoạt động
Elad Gelman

13

Nếu bạn đang sử dụng một đoạn và sử dụng thông báo AlertDialog / Toast thì hãy sử dụng getActivity () trong tham số ngữ cảnh.

như thế này

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

12

Chỉ cần sử dụng như sau:

CHO NGƯỜI SỬ DỤNG JAVA

Trong trường hợp bạn đang sử dụng hoạt động -> AlertDialog.Builder builder = new AlertDialog.Builder(this);

HOẶC LÀ

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

Trong trường hợp bạn đang sử dụng đoạn -> AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

CHO NGƯỜI SỬ DỤNG KOTLINE

Trong trường hợp bạn đang sử dụng hoạt động -> val builder = AlertDialog.Builder(this)

HOẶC LÀ

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

Trong trường hợp bạn đang sử dụng đoạn -> val builder = AlertDialog.Builder(activity!!)


9

thêm

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

"android.permission.SYSTEM_ALERT_WINDOW"/> trong bảng kê khai

Nó làm việc cho tôi bây giờ. Sau khi thậm chí đóng và mở ứng dụng, đã cho tôi lỗi tại thời điểm đó.


9

Tôi đã sử dụng ProgressDialogtrong một đoạn và nhận được lỗi này khi chuyển getActivity().getApplicationContext()dưới dạng tham số hàm tạo. Thay đổi nó thành getActivity().getBaseContext()không hoạt động.

Giải pháp hiệu quả với tôi là vượt qua getActivity(); I E

progressDialog = new ProgressDialog(getActivity());


6

Sử dụng MyDialog md = new MyDialog(MyActivity.this.getParent());


6

Nếu bạn ở ngoài Hoạt động thì bạn cần sử dụng chức năng của mình "NameOfMyActivity.this" làm hoạt động, ví dụ:

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

Nếu bạn đang sử dụng một đoạn và sử dụng một AlertDialog / Toasttin nhắn, hãy sử dụng getActivity()trong tham số ngữ cảnh.

Đã làm cho tôi.

Chúc mừng!


5

Cố gắng sử dụng bối cảnh của một hoạt động sẽ nằm dưới hộp thoại. Nhưng hãy cẩn thận khi bạn sử dụng từ khóa "này", vì nó sẽ không hoạt động mọi lúc.

Forexample, nếu bạn có TabActivity là máy chủ lưu trữ với hai tab và mỗi tab là một hoạt động khác và nếu bạn cố gắng tạo hộp thoại từ một trong các tab (hoạt động) và nếu bạn sử dụng "này", thì bạn sẽ có ngoại lệ, Trong này hộp thoại trường hợp nên được kết nối với hoạt động máy chủ lưu trữ mọi thứ và hiển thị. (bạn có thể nói bối cảnh Hoạt động của cha mẹ rõ ràng nhất)

Tôi không tìm thấy thông tin này từ bất kỳ tài liệu nào ngoài việc thử. Đây là giải pháp của tôi mà không có nền tảng vững chắc, Nếu bất cứ ai có kiến ​​thức tốt hơn, hãy bình luận.


4

Đối với độc giả trong tương lai, điều này sẽ giúp:

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


2

Hoặc một khả năng khác là tạo Hộp thoại như sau:

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

2

Tôi nghĩ điều đó cũng có thể xảy ra nếu bạn đang cố gắng hiển thị hộp thoại từ một luồng không phải là luồng UI chính.

Sử dụng runOnUiThread()trong trường hợp đó.


2

Hãy thử getParent()tại vị trí tranh luận của bối cảnh như mới AlertDialog.Builder(getParent());Hy vọng nó sẽ hoạt động, nó hoạt động với tôi.


1

Sau khi xem API, bạn có thể chuyển hộp thoại hoạt động của mình hoặc getActivity nếu bạn đang ở trong một đoạn, sau đó mạnh mẽ làm sạch nó bằng hộp thoại.dismiss () trong các phương thức trả về để tránh rò rỉ.

Mặc dù nó không được nêu rõ ràng ở bất cứ nơi nào tôi biết, nhưng có vẻ như bạn được trả lại hộp thoại trong OnClickHandlers chỉ để làm điều này.


0

Nếu Hộp thoại của bạn đang tạo trên bộ điều hợp:

Truyền hoạt động cho Trình xây dựng bộ điều hợp:

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

Nhận trên Adaptor:

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

Bây giờ bạn có thể sử dụng trên Builder của mình

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

-1

Đây là cách tôi giải quyết lỗi tương tự cho ứng dụng của mình:
Thêm dòng sau đây sau khi tạo hộp thoại:

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

Bạn sẽ không cần phải có được một bối cảnh. Điều này đặc biệt hữu ích nếu bạn đang bật lên một hộp thoại khác trên hộp thoại hiện lên. Hoặc khi nó không thuận tiện để có được một bối cảnh.

Hy vọng điều này có thể giúp bạn phát triển ứng dụng của bạn.

David


-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();
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.