Dialog源码分析
来源:互联网 发布:java pointer 编辑:程序博客网 时间:2024/06/05 20:01
构造函数入手
public Dialog(Context context) { this(context, 0, true);}public Dialog(Context context, int theme) { this(context, theme, true);}Dialog(Context context, int theme, boolean createContextThemeWrapper) { if (createContextThemeWrapper) { if (theme == 0) { TypedValue outValue = new TypedValue(); context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme, outValue, true); theme = outValue.resourceId; } mContext = new ContextThemeWrapper(context, theme); } else { mContext = context; } mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); Window w = PolicyManager.makeNewWindow(mContext); mWindow = w; w.setCallback(this); w.setOnWindowDismissedCallback(this); w.setWindowManager(mWindowManager, null, null); w.setGravity(Gravity.CENTER); mListenersHandler = new ListenersHandler(this);}
对外提供了两个构造函数,最终都是调用了具有三个参数的构造函数:
createContextThemeWrapper默认传递过来的就是true,所以会进入if (createContextThemeWrapper) {}内部
theme:假如调用了的只有一个参数的构造函数,会默认传递0进来,这会使得Dialog最终使用系统提供的dialogTheme作为主题
创建了一个Window对象,并在本地保存了一个mWindowManager的引用
show()方法
接下来,看下它的显示过程(show()方法分析)
public void show() { if (mShowing) { if (mDecor != null) { if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) { mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR); } mDecor.setVisibility(View.VISIBLE); } return; } mCanceled = false; if (!mCreated) { dispatchOnCreate(null); } onStart(); mDecor = mWindow.getDecorView(); if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) { final ApplicationInfo info = mContext.getApplicationInfo(); mWindow.setDefaultIcon(info.icon); mWindow.setDefaultLogo(info.logo); mActionBar = new WindowDecorActionBar(this); } WindowManager.LayoutParams l = mWindow.getAttributes(); if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) { WindowManager.LayoutParams nl = new WindowManager.LayoutParams(); nl.copyFrom(l); nl.softInputMode |= WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; l = nl; } try { mWindowManager.addView(mDecor, l); mShowing = true; sendShowMessage(); } finally { }}
这里边有几个步骤:
1. mCreated默认值为false,于是会调用dispatchOnCreate(null);方法,进入该方法
// internal method to make sure mcreated is set properly without requiring// users to call through to super in onCreatevoid dispatchOnCreate(Bundle savedInstanceState) { if (!mCreated) { onCreate(savedInstanceState); mCreated = true; }}
主要做了两个操作:
- 调用onCreate方法
- 将mCreated标志设置为true
再进入onCreate方法查看
/** * Similar to {@link Activity#onCreate}, you should initialize your dialog * in this method, including calling {@link #setContentView}. * @param savedInstanceState If this dialog is being reinitalized after a * the hosting activity was previously shut down, holds the result from * the most recent call to {@link #onSaveInstanceState}, or null if this * is the first time. */protected void onCreate(Bundle savedInstanceState) {}
这是个空方法,官方给的解析原意大概是:
类似与Activity的onCreate方法,你应该在这个方法里调用setContentView设置需要显示的视图和初始化Dialog
所以,我们在使用Dialog时,需要在这个重写该方法,并调用setContentView设置视图
那setContentView内部做了什么操作呢?
public void setContentView(int layoutResID) { mWindow.setContentView(layoutResID);}public void setContentView(View view) { mWindow.setContentView(view);}public void setContentView(View view, ViewGroup.LayoutParams params) { mWindow.setContentView(view, params);}
最终是调用了Window的setContentView方法
/** * Set the screen content to an explicit view. This view is placed * directly into the screen's view hierarchy. It can itself be a complex * view hierarchy. * * <p>Note that calling this function "locks in" various characteristics * of the window that can not, from this point forward, be changed: the * features that have been requested with {@link #requestFeature(int)}, * and certain window flags as described in {@link #setFlags(int, int)}. * * @param view The desired content to display. * @param params Layout parameters for the view. */public abstract void setContentView(View view, ViewGroup.LayoutParams params);
大概原意:将view的内容设置给屏幕显示,这个视图直接添加到屏幕的视图层次结构中
- 调用onStart();
- 通过mDecor = mWindow.getDecorView();获取根视图
- 设置Window的ActionBar属性
if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) { final ApplicationInfo info = mContext.getApplicationInfo(); mWindow.setDefaultIcon(info.icon); mWindow.setDefaultLogo(info.logo); mActionBar = new WindowDecorActionBar(this);}
- 显示
try { mWindowManager.addView(mDecor, l); mShowing = true; sendShowMessage();} finally {}
这里主要有三个操作:
- 通过WindowManager将根视图Add进去并显示
- 将mShowing标识设置为true
- 调用sendShowMessage方法(下边再进行分析)
以上就是Dialog创建和显示过程,那消失过程又是如何实现的呢?
dismiss()方法
@Overridepublic void dismiss() { if (Looper.myLooper() == mHandler.getLooper()) { dismissDialog(); } else { mHandler.post(mDismissAction); }}
这个方法主要是做了一个线程的判断工作,保证了线程安全:
- 假如调用该方法的线程与mHandler所在的线程是一致的,则直接调用dismissDialog()方法
- 不一致,则调用了mHandler.post(mDismissAction);,查看mDismissAction
private final Runnable mDismissAction = new Runnable() { public void run() { dismissDialog(); }};
这是一个Runnable接口,内部实现也是调用了dismissDialog()方法。通过mHandler.post方法使得切换到mHandler所在的线程执行dismissDialog()
这里就牵扯到了一个重要的知识点:
通过上篇的Loop、Handler、MessageQueue和Message源码分析,我们知道假如在子线程创建Handler,需要先调用Looper.prepare()方法。而从private final Handler mHandler = new Handler();知道mHandler作为Dialog的一个私有变量,所以假如要在子线程中使用Dialog,需要这样使用:
// 例子new Thread(new Runnable() { @Override public void run() { Looper.prepare(); Dialog dialog = new Dialog(MainActivity.this); dialog.setTitle("ffdfdfdf"); dialog.show(); Looper.loop(); }}).start();
回到主题,继续查看dismissDialog()方法
void dismissDialog() { if (mDecor == null || !mShowing) { return; } if (mWindow.isDestroyed()) { Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!"); return; } try { mWindowManager.removeViewImmediate(mDecor); } finally { if (mActionMode != null) { mActionMode.finish(); } mDecor = null; mWindow.closeAllPanels(); onStop(); mShowing = false; sendDismissMessage(); }}
- 这里是Dialog消失的处理方法,主要在于mWindowManager.removeViewImmediate(mDecor),进入查看:
/** * Special variation of {@link #removeView} that immediately invokes * the given view hierarchy's {@link View#onDetachedFromWindow() * View.onDetachedFromWindow()} methods before returning. This is not * for normal applications; using it correctly requires great care. * * @param view The view to be removed. */public void removeViewImmediate(View view);
大概原意:这是removeView的特别版本,会立马调用给定的视图层次结构的onDetachedFromWindow()方法,然后再返回
2. 主要做一些收尾工作:将mDecor置为空,调用onStop()方法,设置标识mShowing为false。
3. 调用sendDismissMessage()方法
sendShowMessage和sendDismissMessage分析
我们上边的分析时有涉及到,在show()方法的最后调用了sendShowMessage()方法;在dismiss()方法的最后调用了sendDismissMessage()方法。这两个方法的作用是什么,我们接下来分析
sendShowMessage
private void sendShowMessage() { if (mShowMessage != null) { // Obtain a new message so this dialog can be re-used Message.obtain(mShowMessage).sendToTarget(); }}
当mShowMessage不为空时,再调用sendToTarget()方法。那mShowMessage是什么时候赋值的呢?在Dialog进行全文搜索,可以发现:
/** * Sets a listener to be invoked when the dialog is shown. * @param listener The {@link DialogInterface.OnShowListener} to use. */public void setOnShowListener(OnShowListener listener) { if (listener != null) { mShowMessage = mListenersHandler.obtainMessage(SHOW, listener); } else { mShowMessage = null; }}
假如调用了setOnShowListener方法,并且传入的参数不为空,则对mShowMessage进行赋值,将一个SHOW类型的消息赋值个它。
再回到sendShowMessage方法,在这里调用了sendToTarget的方法。这是使用了Handler进行消息分发处理,那我们进入到mListenersHandler分析,该类是一个Handler对象
private static final class ListenersHandler extends Handler { private WeakReference<DialogInterface> mDialog; public ListenersHandler(Dialog dialog) { mDialog = new WeakReference<DialogInterface>(dialog); } @Override public void handleMessage(Message msg) { switch (msg.what) { case DISMISS: ((OnDismissListener) msg.obj).onDismiss(mDialog.get()); break; case CANCEL: ((OnCancelListener) msg.obj).onCancel(mDialog.get()); break; case SHOW: ((OnShowListener) msg.obj).onShow(mDialog.get()); break; } }}
可以看到,在case SHOW的分支,调用了OnShowListener的onShow方法:
case SHOW: ((OnShowListener) msg.obj).onShow(mDialog.get());
可以看到整个流程的目的就是为了用户可以设置OnShowListener,在dialog显示时获得回调,做其他处理
sendDismissMessage
整个流程与sendShowMessage相似,这里不再描述
总结
通过上边的代码分析,我们可以有以下几个结论:
- 假如使用只有一个参数的构造函数,则会使用系统提供的默认主题来创建Dialog
- 在onCreate()方法中对Dialog进行初始化,并调用setContentView()方法为Dialog设置视图
- 在子线程中使用Dialog时,需要先调用Looper.prepare()方法,因为Dialog内部使用了Handler
- 假如需要监听Dialog的显示和消失,可以通过方法setOnShowListener()和setOnDismissListener()方法
- 假如需要在Dialog启动时进行其他的操作,可以重写onStart()方法;假如需要在其消失时进行其他的操作,可以重写onStop()方法
- Dialog源码分析
- Dialog源码分析
- Dialog源码分析
- Dialog源码分析
- 源码分析Dialog自定义大小无效坑
- AlertDialog 源码分析及Bulider 模式打造万能的dialog
- Android应用Activity、Dialog、PopWindow、Toast窗口添加机制及源码分析 《三》-Dialog
- android源码解析--Dialog
- dialog工具栏初始化源码
- dialog源码解析笔记
- Android Dialog 源码研究
- Android应用Activity、Dialog、PopWindow、Toast窗口添加机制及源码分析
- Android应用Activity、Dialog、PopWindow、Toast窗口添加机制及源码分析
- Android应用Activity、Dialog、PopWindow、Toast窗口添加机制及源码分析
- Android应用Activity、Dialog、PopWindow、Toast窗口添加机制及源码分析 《一》
- Android应用Activity、Dialog、PopWindow、Toast窗口添加机制及源码分析 《二》
- Android应用Activity、Dialog、PopWindow、Toast窗口添加机制及源码分析 《四》-PopWindow
- Android应用Activity、Dialog、PopWindow、Toast窗口添加机制及源码分析 《五》-Toast
- 客户端跳转与服务器端跳转的区别
- java中集合删除元素的两种常用方式及新手易错
- hdu 2069 普通母函数 +dp
- makefile教程
- 关于Android Studio里的Gradle文件
- Dialog源码分析
- C++结构体对齐和位域
- 第13节--神经网络算法
- windows平台下基于VisualStudio的Clang安装和配置
- SurfaceView播放视频的截图
- 27、Java入门—多线程之线程的正确停止
- Eclipse RCP - Multiple platforms build
- 视频编解码(1)
- 弹性文本框