AlertDialog的构造方法一共有4种,分别是:

protected AlertDialog(Context context)

创建一个默认配置的AlertDialog对象

protected AlertDialog(Context context, boolean cancelable, OnCancelListener cancelListener)

创建一个AlertDialog对象并设置是否可取消和取消监听

protected AlertDialog(Context context, @StyleRes int themeResId)

创建一个AlertDialog对象并设置主题

AlertDialog(Context context, @StyleRes int themeResId, boolean createContextThemeWrapper)

创建一个AlertDialog对象并设置主题和是否创建主题包装

我们通常却很少这样写,我们通常使用Builer创建:

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

我们使用 setNegativeButton,setButton,setView来为AlertDialog设置样式。

如果我们在AlertDialog上设置的话,方法是这样的:

public void setTitle(CharSequence title) {super.setTitle(title);mAlert.setTitle(title);}public void setCustomTitle(View customTitleView) {mAlert.setCustomTitle(customTitleView);}public void setMessage(CharSequence message) {mAlert.setMessage(message);}
private AlertController mAlert;

都是在给AlertController对象赋值

如果使用Builer来设置,那么他们的方法时这样的:

public Builder setIcon(Drawable icon) {P.mIcon = icon;return this;}public Builder setPositiveButton(@StringRes int textId, final OnClickListener listener) {P.mPositiveButtonText = P.mContext.getText(textId);P.mPositiveButtonListener = listener;return this;}public Builder setItems(@ArrayRes int itemsId, final OnClickListener listener) {P.mItems = P.mContext.getResources().getTextArray(itemsId);P.mOnClickListener = listener;return this;}

我们可以看到,无论设置什么,都是在给P的字段赋值,P是什么呢?

private final AlertController.AlertParams P;

P是一个AlertController.AlertParams对象,装载着对话框的各种设置,它有以下字段

 public static class AlertParams {public final Context mContext;public final LayoutInflater mInflater;public int mIconId = 0;public Drawable mIcon;public int mIconAttrId = 0;public CharSequence mTitle;public View mCustomTitleView;public CharSequence mMessage;public CharSequence mPositiveButtonText;public DialogInterface.OnClickListener mPositiveButtonListener;public CharSequence mNegativeButtonText;public DialogInterface.OnClickListener mNegativeButtonListener;public CharSequence mNeutralButtonText;public DialogInterface.OnClickListener mNeutralButtonListener;public boolean mCancelable;public DialogInterface.OnCancelListener mOnCancelListener;public DialogInterface.OnDismissListener mOnDismissListener;public DialogInterface.OnKeyListener mOnKeyListener;public CharSequence[] mItems;public ListAdapter mAdapter;public DialogInterface.OnClickListener mOnClickListener;public int mViewLayoutResId;public View mView;public int mViewSpacingLeft;public int mViewSpacingTop;public int mViewSpacingRight;public int mViewSpacingBottom;public boolean mViewSpacingSpecified = false;public boolean[] mCheckedItems;public boolean mIsMultiChoice;public boolean mIsSingleChoice;public int mCheckedItem = -1;public DialogInterface.OnMultiChoiceClickListener mOnCheckboxClickListener;public Cursor mCursor;public String mLabelColumn;public String mIsCheckedColumn;public boolean mForceInverseBackground;public AdapterView.OnItemSelectedListener mOnItemSelectedListener;public OnPrepareListViewListener mOnPrepareListViewListener;public boolean mRecycleOnMeasure = true;

在使用Builder设置完后必须调用creat方法来创建AlertDialog对象

/*** Creates an {@link AlertDialog} with the arguments supplied to this* builder.* <p>* Calling this method does not display the dialog. If no additional* processing is needed, {@link #show()} may be called instead to both* create and display the dialog.*/public AlertDialog create() {// Context has already been wrapped with the appropriate theme.final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);P.apply(dialog.mAlert);dialog.setCancelable(P.mCancelable);if (P.mCancelable) {dialog.setCanceledOnTouchOutside(true);}dialog.setOnCancelListener(P.mOnCancelListener);dialog.setOnDismissListener(P.mOnDismissListener);if (P.mOnKeyListener != null) {dialog.setOnKeyListener(P.mOnKeyListener);}return dialog;}
public void apply(AlertController dialog) {if (mCustomTitleView != null) {dialog.setCustomTitle(mCustomTitleView);} else {if (mTitle != null) {dialog.setTitle(mTitle);}if (mIcon != null) {dialog.setIcon(mIcon);}if (mIconId != 0) {dialog.setIcon(mIconId);}if (mIconAttrId != 0) {dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));}}if (mMessage != null) {dialog.setMessage(mMessage);}if (mPositiveButtonText != null) {dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,mPositiveButtonListener, null);}if (mNegativeButtonText != null) {dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,mNegativeButtonListener, null);}if (mNeutralButtonText != null) {dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,mNeutralButtonListener, null);}if (mForceInverseBackground) {dialog.setInverseBackgroundForced(true);}// For a list, the client can either supply an array of items or an// adapter or a cursorif ((mItems != null) || (mCursor != null) || (mAdapter != null)) {createListView(dialog);}if (mView != null) {if (mViewSpacingSpecified) {dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,mViewSpacingBottom);} else {dialog.setView(mView);}} else if (mViewLayoutResId != 0) {dialog.setView(mViewLayoutResId);}/*dialog.setCancelable(mCancelable);dialog.setOnCancelListener(mOnCancelListener);if (mOnKeyListener != null) {dialog.setOnKeyListener(mOnKeyListener);}*/}

可以看到两种方法都是一样的,最后都是在给AlertController对象赋值

调用AlertDialog的show方法其实也会调用creat方法

/*** Creates an {@link AlertDialog} with the arguments supplied to this* builder and immediately displays the dialog.* <p>* Calling this method is functionally identical to:* <pre>*     AlertDialog dialog = builder.create();*     dialog.show();* </pre>*/public AlertDialog show() {final AlertDialog dialog = create();dialog.show();return dialog;}

我们再来看看setButton方法,它有4种不同参数的方法,不过有两种被弃用了

/*** Set a message to be sent when a button is pressed.** @param whichButton Which button to set the message for, can be one of*            {@link DialogInterface#BUTTON_POSITIVE},*            {@link DialogInterface#BUTTON_NEGATIVE}, or*            {@link DialogInterface#BUTTON_NEUTRAL}* @param text The text to display in positive button.* @param msg The {@link Message} to be sent when clicked.*/public void setButton(int whichButton, CharSequence text, Message msg) {mAlert.setButton(whichButton, text, null, msg);}/*** Set a listener to be invoked when the positive button of the dialog is pressed.** @param whichButton Which button to set the listener on, can be one of*            {@link DialogInterface#BUTTON_POSITIVE},*            {@link DialogInterface#BUTTON_NEGATIVE}, or*            {@link DialogInterface#BUTTON_NEUTRAL}* @param text The text to display in positive button.* @param listener The {@link DialogInterface.OnClickListener} to use.*/public void setButton(int whichButton, CharSequence text, OnClickListener listener) {mAlert.setButton(whichButton, text, listener, null);}

可能我们用的多是第二种方法,第一种很少见,但其实第一种可以说是 第二种方法的内部实现,看代码

/*** Sets a click listener or a message to be sent when the button is clicked.* You only need to pass one of {@code listener} or {@code msg}.** @param whichButton Which button, can be one of*            {@link DialogInterface#BUTTON_POSITIVE},*            {@link DialogInterface#BUTTON_NEGATIVE}, or*            {@link DialogInterface#BUTTON_NEUTRAL}* @param text The text to display in positive button.* @param listener The {@link DialogInterface.OnClickListener} to use.* @param msg The {@link Message} to be sent when clicked.*/public void setButton(int whichButton, CharSequence text,DialogInterface.OnClickListener listener, Message msg) {if (msg == null && listener != null) {msg = mHandler.obtainMessage(whichButton, listener);}switch (whichButton) {case DialogInterface.BUTTON_POSITIVE:mButtonPositiveText = text;mButtonPositiveMessage = msg;break;case DialogInterface.BUTTON_NEGATIVE:mButtonNegativeText = text;mButtonNegativeMessage = msg;break;case DialogInterface.BUTTON_NEUTRAL:mButtonNeutralText = text;mButtonNeutralMessage = msg;break;default:throw new IllegalArgumentException("Button does not exist");}}

如果设置了监听,监听就会被封装到message中,最终都会被赋值给对应按钮的message中,如果视图被点击就会取对应的信息,使用handler发送:

 private final View.OnClickListener mButtonHandler = new View.OnClickListener() {@Overridepublic void onClick(View v) {final Message m;if (v == mButtonPositive && mButtonPositiveMessage != null) {m = Message.obtain(mButtonPositiveMessage);} else if (v == mButtonNegative && mButtonNegativeMessage != null) {m = Message.obtain(mButtonNegativeMessage);} else if (v == mButtonNeutral && mButtonNeutralMessage != null) {m = Message.obtain(mButtonNeutralMessage);} else {m = null;}if (m != null) {m.sendToTarget();}// Post a message so we dismiss after the above handlers are executedmHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface).sendToTarget();}};

如果是设置的监听,会使用AlertController中的mHandler发送,它在AlertController的构造方法中被初始化,也就是说调用了creat方法或者show方法,mHandler都会被初始化

protected AlertController(Context context, DialogInterface di, Window window) {mContext = context;mDialogInterface = di;mWindow = window;mHandler = new ButtonHandler(di);final TypedArray a = context.obtainStyledAttributes(null,R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);mAlertDialogLayout = a.getResourceId(R.styleable.AlertDialog_layout, R.layout.alert_dialog);mButtonPanelSideLayout = a.getResourceId(R.styleable.AlertDialog_buttonPanelSideLayout, 0);mListLayout = a.getResourceId(R.styleable.AlertDialog_listLayout, R.layout.select_dialog);mMultiChoiceItemLayout = a.getResourceId(R.styleable.AlertDialog_multiChoiceItemLayout,R.layout.select_dialog_multichoice);mSingleChoiceItemLayout = a.getResourceId(R.styleable.AlertDialog_singleChoiceItemLayout,R.layout.select_dialog_singlechoice);mListItemLayout = a.getResourceId(R.styleable.AlertDialog_listItemLayout,R.layout.select_dialog_item);mShowTitle = a.getBoolean(R.styleable.AlertDialog_showTitle, true);a.recycle();/* We use a custom title so never request a window title */window.requestFeature(Window.FEATURE_NO_TITLE);}

如果是设置的Message,那么就会使用Message对象的target Handler

/*package*/ Handler target;

它可以使用public void setTarget(Handler target)来赋值

看个例子:

AlertDialog alertDialog=new AlertDialog.Builder(this).create();Message message=new Message();message.setTarget(new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);showToast("哈哈");}});alertDialog.setButton(DialogInterface.BUTTON_POSITIVE,"显示",message);alertDialog.show();

所以明白了吧,AlertDialog的按钮监听其实是使用handler实现的,那么就会有内存泄漏问题

在AlertDialog构建过程中传入的参数 int whichButton, OnClickListener listener都包装成了msg来处理,这样子就造成了msg对listener的引用 msg→ OnClickListener

有两种情况可能会发生内存泄漏:

(1)Message是任何线程共用的,HandlerThread中,Looper会不停的从阻塞队列MessageQueue中取Message进行处理.当没有可消费Message对象时,就会开始阻塞,而此时最后一个被取出的Message就会被本地变量引用,一直不会释放引用,除非有新的message
(2)Dialog从消息队列中可能会恰巧取到一个“仍然被某个阻塞中的HandlerThread本地变量引用的Message实例”,代码msg = mHandler.obtainMessage(whichButton, listener),把listener赋给Message的obj,并一直保存在Dialog实例中
如此产生引用: Thread → Mesage → Listener → Dialog → Activity. 当Activity关闭时,Thread仍然引用着Activity, 这样内存泄漏就发生了.

那么发现了问题如何解决呢?看一下示例代码

public class DetachClickListener implements DialogInterface.OnClickListener {public static DetachClickListener wrap(DialogInterface.OnClickListener delegate) {return new DetachClickListener(delegate);}private DialogInterface.OnClickListener mDelegate;private DetachClickListener(DialogInterface.OnClickListener delegate) {this.mDelegate = delegate;}@Overridepublic void onClick(DialogInterface dialog, int which) {if (mDelegate != null) {mDelegate.onClick(dialog, which);}}public void clearOnDetach(Dialog dialog) {dialog.getWindow().getDecorView().getViewTreeObserver().addOnWindowAttachListener(new ViewTreeObserver.OnWindowAttachListener() {@Overridepublic void onWindowAttached() {}@Overridepublic void onWindowDetached() {mDelegate = null;}});}}DetachClickListener clickListener = DetachClickListener.wrap(new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {}
});AlertDialog dialog = new AlertDialog.Builder(context).setPositiveButton("confirm", clickListener).create();
dialog.show();
clickListener.clearOnDetach(dialog);

以上写法在Dialog退出后,清除了对DialogInterface.OnClickListener的引用,在中间层截断, 故在Activity关闭时避免了内存泄露.

参考网址:https://blog.csdn.net/awangyunke/article/details/78933854

AlertDialog源码浅析相关推荐

  1. Android开发之Theme、Style探索及源码浅析

    1 背景 前段时间群里有伙伴问到了关于Android开发中Theme与Style的问题,当然,这类东西在网上随便一搜一大把模板,所以关于怎么用的问题我想这里也就不做太多的说明了,我们这里把重点放在理解 ...

  2. hashmap允许null键和值吗_hashMap底层源码浅析

    来源:https://blog.csdn.net/qq_35824590/article/details/111769203 hashmap是我们经常使用的一个工具类.那么知道它的一些原理和特性吗? ...

  3. Android Loader机制全面详解及源码浅析

    原文出处:csdn@工匠若水,http://blog.csdn.net/yanbober/article/details/48861457 一.概述 在Android中任何耗时的操作都不能放在UI主线 ...

  4. 内核启动流程分析(四)源码浅析

    目录 kernel(四)源码浅析 建立工程 启动简析 head.s 入口点 查询处理器 查询机器ID 启动MMU 其他操作 start_kernel 处理命令行 分区 kernel(四)源码浅析 建立 ...

  5. harbor登录验证_Harbor 源码浅析

    Harbor 源码浅析​www.qikqiak.com Harbor 是一个CNCF基金会托管的开源的可信的云原生docker registry项目,可以用于存储.签名.扫描镜像内容,Harbor 通 ...

  6. fetch first mysql_MySQL多版本并发控制机制(MVCC)源码浅析

    MySQL多版本并发控制机制(MVCC)-源码浅析 前言 作为一个数据库爱好者,自己动手写过简单的SQL解析器以及存储引擎,但感觉还是不够过瘾.<>诚然讲的非常透彻,但只能提纲挈领,不能让 ...

  7. 【flink】Flink 1.12.2 源码浅析 : Task数据输入

    1.概述 转载:Flink 1.12.2 源码浅析 : Task数据输入 在 Task 中,InputGate 是对输入的封装,InputGate 是和 JobGraph 中 JobEdge 一一对应 ...

  8. 【flink】Flink 1.12.2 源码浅析 :Task数据输出

    1.概述 转载:Flink 1.12.2 源码浅析 :Task数据输出 Stream的计算模型采用的是PUSH模式, 上游主动向下游推送数据, 上下游之间采用生产者-消费者模式, 下游收到数据触发计算 ...

  9. 【flink】Flink 1.12.2 源码浅析 : StreamTask 浅析

    1.概述 转载:Flink 1.12.2 源码浅析 : StreamTask 浅析 在Task类的doRun方法中, 首先会构建一个运行环境变量RuntimeEnvironment . 然后会调用lo ...

最新文章

  1. 6个快速优化回归测试套件的方法,你都知道吗?
  2. 拖拽插入Table的列(I.E. ONLY)
  3. DevExpress 使用 XtraTabbedMdiManager 控件以 Tab样式加载 Mdi窗体并合并 RibbonControl 解决方案
  4. Spring全局异常处理
  5. JAVA代码实现多级树结构封装对象
  6. 为什么说一次一密加是密抗窃听无条件安全的?
  7. SQL Server数据文件迁移
  8. android call require api level
  9. 软件架构设计案例_透过现象看本质:常见的前端架构风格和案例
  10. oracle的in集合,oracle中in与not in集合中有空值问题
  11. socket和http间的区别
  12. mysql8.0卡cpu_MySQL 8.0资源组有效解决慢SQL引发CPU告警
  13. Android SDK Manager 中如果没有相应的镜像ARM XX Image
  14. atitit.web原理 理论attilax总结
  15. linux appium 安装教程,Ubuntu 系统安装 Appium 及样例运行教程
  16. matlab两矩阵相似性,两个矩阵同时相似对角化MATLAB程序.docx
  17. python 实现软件激活码验证
  18. H5标签input标签上传文件(一)
  19. ztree树与列表名字获取
  20. 小米机顶盒显示网络无法连接服务器,小米盒子无线网络连接不上怎么回事 - 卡饭网...

热门文章

  1. 中小企业如何差异化“生意表达”,成为最了不起的小企业?
  2. 软件测试--基础知识1--测试简介、软件质量等
  3. 计算机更新失败变的很卡,win 8.1 运行慢,更新kb2919355失败频繁重启等问题
  4. Portal技术介绍
  5. css阴影设置透明度,css3圆角 阴影 透明度
  6. Ubuntu学习(六)Linux安装压缩包版的软件
  7. Android 使用自带的MediaCodec 框架进行本地视频压缩
  8. php学生成绩管理系统,数据库使用MySQL,包括源代码和数据库SQL文件,具有学生和教师登录管理功能
  9. 技术人如何通过了解业务,获取晋升机会
  10. 谷粒商城项目笔记之分布式基础(三)