我的 Activity 正在尝试创建一个需要 Context 作为参数的 AlertDialog。如果我使用,这将按预期工作:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
但是,我对使用“this”作为上下文持怀疑态度,因为即使在屏幕旋转之类的简单操作中,当 Activity 被销毁和重新创建时也可能发生内存泄漏。从 related post on the Android developer's blog:
有两种简单的方法可以避免与上下文相关的内存泄漏。最明显的一个是避免将上下文转义到其自身范围之外。上面的示例显示了静态引用的情况,但内部类及其对外部类的隐式引用可能同样危险。第二种解决方案是使用应用程序上下文。只要您的应用程序处于活动状态并且不依赖于活动生命周期,此上下文就会存在。如果您打算保留需要上下文的长期对象,请记住应用程序对象。您可以通过调用 Context.getApplicationContext() 或 Activity.getApplication() 轻松获取它。
但对于 AlertDialog()
,getApplicationContext()
或 getApplication()
都不能作为上下文,因为它会引发异常:
“无法添加窗口 - 令牌 null 不适用于应用程序”
那么,这是否真的应该被视为一个“错误”,因为我们被正式建议使用 Activity.getApplication()
,但它并没有像宣传的那样发挥作用?
吉姆
而不是 getApplicationContext()
,只需使用 ActivityName.this
。
使用 this
对我不起作用,但 MyActivityName.this
对我有用。希望这可以帮助那些无法让 this
工作的人。
this
时会发生这种情况。如果要引用外部类的实例,则必须指定,就像使用 OuterClass.this
一样。仅使用 this
总是引用最内部类的实例。
您可以继续使用getApplicationContext()
,但在使用之前,您应该添加这个标志:dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)
,并且错误不会显示。
将以下权限添加到您的清单中:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
当您说“...对于 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”(或其他引用)无限期地引用。记住也要检查链引用。这里有一些工具可以帮助你分析内存使用情况并找出那些讨厌的内存泄漏:
JRockit 任务控制
JP探针
你的套件
AD4J
您的对话框不应该是“需要上下文的长期对象”。文档令人困惑。基本上,如果您执行以下操作:
static Dialog sDialog;
(注意静态)
然后在你做过的某个活动中
sDialog = new Dialog(this);
您可能会在轮换或类似情况下泄漏原始活动,从而破坏活动。 (除非您在 onDestroy 中进行清理,但在这种情况下,您可能不会将 Dialog 对象设为静态)
对于某些数据结构,将它们设为静态并基于应用程序的上下文是有意义的,但通常不适用于与 UI 相关的事物,例如对话框。所以是这样的:
Dialog mDialog;
...
mDialog = new Dialog(this);
很好,不应该泄漏活动,因为 mDialog 将与活动一起释放,因为它不是静态的。
在活动中只需使用:
MyActivity.this
在片段中:
getActivity();
我必须通过片段中显示的自定义适配器上的构造函数发送我的上下文,并且 getApplicationContext() 遇到了这个问题。我解决了它:
片段的 onCreate
回调中的 this.getActivity().getWindow().getContext()
。
***** 科特林版本 *****
您应该通过 this@YourActivity
而不是 applicationContext
或 baseContext
在 Activity
中单击显示对话框的按钮
Dialog dialog = new Dialog(MyActivity.this);
为我工作。
小技巧:您可以防止 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();
}
...
}
只需使用以下内容:
对于 JAVA 用户
如果您正在使用活动 --> 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!!)
如果您正在使用片段并使用 AlertDialog/Toast 消息,则在上下文参数中使用 getActivity()。
像这样
ProgressDialog pdialog;
pdialog = new ProgressDialog(getActivity());
pdialog.setCancelable(true);
pdialog.setMessage("Loading ....");
pdialog.show();
添加
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
和
"android.permission.SYSTEM_ALERT_WINDOW"/>
在清单中
它现在对我有用。甚至关闭并打开应用程序后,当时给了我错误。
我在片段中使用 ProgressDialog
并在将 getActivity().getApplicationContext()
作为构造函数参数传递时收到此错误。将其更改为 getActivity().getBaseContext()
也不起作用。
对我有用的解决方案是通过 getActivity()
; IE
progressDialog = new ProgressDialog(getActivity());
使用MyDialog md = new MyDialog(MyActivity.this.getParent());
如果您在 Activity 之外,则需要在函数中使用“NameOfMyActivity.this”作为 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);
如果您正在使用片段并使用 AlertDialog / Toast
消息,请在上下文参数中使用 getActivity()
。
为我工作。
干杯!
尝试使用将在对话框下的活动的上下文。但是使用“this”关键字时要小心,因为它不会每次都起作用。
例如,如果您将 TabActivity 作为具有两个选项卡的主机,并且每个选项卡都是另一个活动,并且如果您尝试从其中一个选项卡(活动)创建对话框并且如果您使用“this”,那么您将得到异常,在这个案例对话框应连接到托管所有内容且可见的主机活动。 (你可以说最可见的父 Activity 的上下文)
我没有从任何文档中找到此信息,而是通过尝试。这是我没有强大背景的解决方案,如果有更好的知识,请随时发表评论。
对于未来的读者,这应该会有所帮助:
public void show() {
if(mContext instanceof Activity) {
Activity activity = (Activity) mContext;
if (!activity.isFinishing() && !activity.isDestroyed()) {
dialog.show();
}
}
}
在上下文的参数位置尝试 getParent()
,例如 new AlertDialog.Builder(getParent());
希望它会起作用,它对我有用。
在我的情况下工作:
this.getContext();
或者另一种可能性是创建对话框如下:
final Dialog dialog = new Dialog(new ContextThemeWrapper(
this, R.style.MyThemeDialog));
我认为如果您尝试从不是主 UI 线程的线程显示对话框,也可能会发生这种情况。
在这种情况下使用 runOnUiThread()
。
查看 API 后,您可以将您的活动或 getActivity 传递给对话框(如果您在片段中),然后在返回方法中使用 dialog.dismiss() 强制清理它以防止泄漏。
尽管在我所知道的任何地方都没有明确说明,但似乎您只是为了执行此操作而将 OnClickHandlers 中的对话框传回。
如果您的 Dialog 正在适配器上创建:
将 Activity 传递给适配器构造函数:
adapter = new MyAdapter(getActivity(),data);
在适配器上接收:
public MyAdapter(Activity activity, List<Data> dataList){
this.activity = activity;
}
现在您可以在 Builder 上使用
AlertDialog.Builder alert = new 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")
祝 Kotlin 好运
对我有用的是传递活动而不是上下文。
我想要我的对话框的自定义布局,但为了保持我的代码独立,我在一个单独的类中创建它,否则我必须复制并粘贴该代码块到我想使用对话框的每个活动中。
解决方案解释了我的情况,但它提供了核心解决方案:
当我使用 ViewAdapter 时,我使用 Activity(不是上下文 ex.->[kotlin] 活动:Activity)作为参数初始化适配器 -> ex。 [kotlin] this@MainActivity 然后我将该参数传递给 Viewholder 之后再次将其传递给将膨胀对话框的类。
在任何地方使用活动[可选名称]:活动[强制类型],直到它到达您要膨胀的对话框
它有很多传递,但它确实比在任何地方复制和粘贴相同的代码更有意义
这是我为我的应用程序解决相同错误的方法:在创建对话框后添加以下行:
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
您不需要获取上下文。如果您在当前弹出的对话框上弹出另一个对话框,这将特别有用。或者当不方便获取上下文时。
希望这可以帮助您进行应用程序开发。
大卫
Listener
类通常是匿名内部的,我倾向于只做final Context ctx = this;
并且我离开了;)