我的活动正在尝试创建一个AlertDialog,它需要一个上下文作为参数。如果我使用:

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

然而,我对使用“this”作为上下文持怀疑态度,因为即使在屏幕旋转这样简单的操作中,Activity被破坏和重新创建时,也有可能发生内存泄漏。来自Android开发者博客的相关文章:

There are two easy ways to avoid context-related memory leaks. The most obvious one is to avoid escaping the context outside of its own scope. The example above showed the case of a static reference but inner classes and their implicit reference to the outer class can be equally dangerous. The second solution is to use the Application context. This context will live as long as your application is alive and does not depend on the activities life cycle. If you plan on keeping long-lived objects that need a context, remember the application object. You can obtain it easily by calling Context.getApplicationContext() or Activity.getApplication().

但是对于AlertDialog(), getApplicationContext()或getApplication()都不能作为上下文,因为它会抛出异常:

"无法添加窗口-令牌null不是用于应用程序"

参考文献:1、2、3等。

所以,这真的应该被认为是一个“bug”,因为我们被正式建议使用Activity.getApplication(),但它并没有像宣传的那样发挥作用?

Jim


你的对话框不应该是一个“需要上下文的长生命对象”。文档很混乱。基本上,如果你这样做:

static Dialog sDialog;

(注意静态)

然后在你参加的某个活动中

 sDialog = new Dialog(this);

您可能会在旋转或类似的过程中泄漏原始活动,从而破坏活动。(除非你在onDestroy中清理,但在这种情况下,你可能不会使Dialog对象静态)

对于某些数据结构,基于应用程序的上下文将它们设置为静态是有意义的,但对于与UI相关的东西,比如对话框,通常不是这样。就像这样:

Dialog mDialog;

...

mDialog = new Dialog(this);

很好,不应该泄漏活动,因为mDialog将与活动释放,因为它不是静态的。


而不是getApplicationContext(),只需使用ActivityName.this。


使用这个对我不起作用,但是MyActivityName。这做。


在我的案例中:

this.getContext();

小技巧:你可以防止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();
    }
    ...
}

如果您正在使用片段并使用AlertDialog / Toast消息,请在context参数中使用getActivity()。

为我工作。

干杯!


尝试getParent()在上下文的参数位置,如new AlertDialog.Builder(getParent());希望它能起作用,它对我很有效。


在活动中单击按钮显示一个对话框

Dialog dialog = new Dialog(MyActivity.this);

为我工作。


我认为它也可能发生,如果你试图显示一个对话框从一个线程,这不是主UI线程。

在这种情况下使用runOnUiThread()。


使用MyDialog md = new MyDialog(MyActivity.this.getParent());


尝试使用对话框下面的活动上下文。但是当你使用“this”关键字时要小心,因为它不会每次都有效。

例如,如果你有两个选项卡TabActivity作为主机,每个选项卡是另一个活动,如果你试图从其中一个选项卡(活动)创建对话框,如果你使用"this",那么你会得到异常, 在这种情况下,对话框应该连接到主机活动,主机的一切和可见。(你可以说most visible parent Activity’s context)

我没有从任何文件中找到这个信息,但通过尝试。这是我的解决方案,没有强大的背景,如果有人有更好的知识,请随意评论。


如果你正在使用一个片段并使用AlertDialog/Toast消息,那么在context参数中使用getActivity()。

像这样

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

如果你在活动之外,那么你需要在你的函数中使用“NameOfMyActivity”。this" as Activity Activity,示例:

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

我必须通过在片段中显示的自定义适配器上的构造函数发送上下文,并且getApplicationContext()存在此问题。我用:

this.getActivity(). getwindow (). getcontext()在片段的onCreate回调中。


在查看API之后,您可以将您的activity或getActivity(如果您位于片段中)传递给对话框,然后在返回方法中使用dialog.dismiss()强制清理它以防止泄漏。

虽然它没有明确说明我知道的任何地方,似乎你在OnClickHandlers中传递回对话框只是为了这样做。


你可以继续使用getApplicationContext(),但在使用之前,你应该添加这个标志:dialog.getWindow(). settype (WindowManager.LayoutParams.TYPE_SYSTEM_ALERT),错误将不会显示。

将以下权限添加到您的清单:

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

添加

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

and

“android.permission。SYSTEM_ALERT_WINDOW"/>

现在对我有用了。甚至在关闭和打开应用程序后,给了我当时的错误。


或者另一种可能是创建Dialog,如下所示:

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

对于以后的读者,这应该有帮助:

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

当你说“…”时,你已经正确地发现了问题。对于AlertDialog(), getApplicationContext()或getApplication()都不能作为上下文,因为它会抛出异常:'无法添加窗口-令牌null不是用于应用程序'"

要创建一个对话框,您需要一个活动上下文或服务上下文,而不是应用程序上下文(getApplicationContext()和getApplication()都返回一个应用程序上下文)。

下面是获取活动上下文的方法:

(1)在一项活动或服务中:

AlertDialog。生成器生成器=新的AlertDialog.Builder(这个);

(2)片段: AlertDialog。生成器生成器= new AlertDialog.Builder(getActivity());

内存泄漏不是“this”引用固有的问题,它是对象对自身的引用(即引用用于存储对象数据的实际分配内存)。它发生在垃圾收集器(GC)在分配的内存超过其有效寿命后无法释放的任何已分配内存上。

Most of the time, when a variable goes out of scope, the memory will be reclaimed by the GC. However, memory leaks can occur when the reference to an object held by a variable, say "x", persists even after the object has outlived its useful lifespan. The allocated memory will hence be lost for as long as "x" holds a reference to it because GC will not free up the memory for as long as that memory is still being referenced. Sometimes, memory leaks are not apparent because of a chain of references to the allocated memory. In such a case, the GC will not free up the memory until all references to that memory have been removed.

为了防止内存泄漏,请检查代码中是否有逻辑错误导致已分配的内存被“this”(或其他引用)无限引用。还记得检查链引用。这里有一些工具可以帮助你分析内存使用,并找到那些讨厌的内存泄漏:

JRockit任务控制中心 JProbe YourKit AD4J


我在一个片段中使用ProgressDialog,并在传递getActivity(). getapplicationcontext()作为构造函数参数时得到这个错误。将其更改为getActivity(). getbasecontext()也不起作用。

对我有效的解决方案是传递getActivity();即。

progressDialog = new progressDialog (getActivity());


在Activity中使用:

MyActivity.this

在片段:

getActivity();

*****版本*****

您应该传递this@YourActivity而不是applicationContext或baseContext


下面是我如何解决我的应用程序同样的错误: 在创建对话框后添加以下行:

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

您不需要获取上下文。这是特别有用的,如果你弹出另一个对话框超过当前弹出的对话框。或者当获取上下文不方便时。

希望这对您的应用程序开发有所帮助。

大卫


如果你的对话框是在适配器上创建的:

将活动传递给适配器构造函数:

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

适配器接收:

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

现在你可以使用你的建造者

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

只需使用以下语句:

Java用户

如果你正在使用活动——> AlertDialog。生成器生成器=新的AlertDialog.Builder(这个);

OR

AlertDialog。生成器生成器= new AlertDialog.Builder(your_activity.this);

如果你正在使用fragment——> AlertDialog。生成器生成器= new AlertDialog.Builder(getActivity());

对于kotlin用户

如果你正在使用活动——> val builder = AlertDialog.Builder(这个)

OR

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

如果你正在使用fragment——> val builder = AlertDialog.Builder(activity!!)


伙计们,我有个小抄。 创建一个文件,给它任何名字,然后在它写这段代码

fun Context.alertdialog(context: Context, msg: String, icon: Drawable, title:String){
    val alertDialog = AlertDialog.Builder(context)
    alertDialog.setIcon(icon)
        .setTitle(title)
        .setMessage(msg)
    alertDialog.show()
}

现在,当你需要显示一个警告对话框时,只需要在任何地方使用这个方法

requireActivity().alertdialog(requireContext(), resources.getString(R.string.pass_title),
                resources.getDrawable(R.drawable.pass_ic_name), "title")

古德勒克 在芬兰湾的科特林


对我有用的是传递活动而不是上下文。

我想为我的对话框创建一个自定义布局,但为了保持我的代码独立,我在一个单独的类中创建了它,否则我将不得不复制并粘贴那块代码到我想使用对话框的每个活动中。

Solution解释了我的情况,但它给出了核心解决方案:

当我使用ViewAdapter时,我初始化了活动适配器(不是上下文ex. ->[kotlin]活动:活动)作为参数-> ex. [kotlin] this@MainActivity 然后我把这个参数传递给Viewholder 然后再次将它传递给将膨胀Dialog的类。

在任何地方使用activity[可选名称]:activity[强制类型],直到它到达你想要膨胀的对话框

它有很多传递,但它比复制和粘贴相同的代码在任何地方更有意义