Android弹窗组件工作机制之Dialog、DialogFragment(二)
二、Dialog的消失
1、dismiss
private final Runnable mDismissAction = this::dismissDialog;public void dismiss() {if (Looper.myLooper() == mHandler.getLooper()) {dismissDialog();} else {mHandler.post(mDismissAction);} } 復制代碼保證UI操作都在主線程執行,而且引用了Java8新特性寫法this::dismissDialog,最后都會調用dismissDialog()
2、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 {//這里移除DecorViewmWindowManager.removeViewImmediate(mDecor);} finally {if (mActionMode != null) {mActionMode.finish();}mDecor = null;mWindow.closeAllPanels();onStop();mShowing = false;sendDismissMessage();} } 復制代碼從show中知道,我們將DecorView加入到WindowManager中去,所以這里移除的是DecorView
3、WindowManagerImpl.removeViewImmediate
public void removeViewImmediate(View view) {//委托給mGlobal來進行實現mGlobal.removeView(view, true); } 復制代碼同樣的交給WindowManagerGlobal去處理
4、WindowManagerGlobal.removeView
public void removeView(View view, boolean immediate) {if (view == null) {throw new IllegalArgumentException("view must not be null");}synchronized (mLock) {//待remove view的索引int index = findViewLocked(view, true);//mRoots保存著每一個viewRootImpl對象View curView = mRoots.get(index).getView();//真正對view進行了remove操作removeViewLocked(index, immediate);if (curView == view) {return;}throw new IllegalStateException("Calling with view " + view+ " but the ViewAncestor is attached to " + curView);} } 復制代碼找到對應要移除的View后進行View邏輯處理工作
5、WindowManagerGlobal.removeViewLocked
private void removeViewLocked(int index, boolean immediate) {ViewRootImpl root = mRoots.get(index);View view = root.getView();if (view != null) {InputMethodManager imm = InputMethodManager.getInstance();if (imm != null) {imm.windowDismissed(mViews.get(index).getWindowToken());}}//重點在ViewRootImpl中的die方法中boolean deferred = root.die(immediate);if (view != null) {view.assignParent(null);if (deferred) {mDyingViews.add(view);}} } 復制代碼找到對應的ViewRootImpl,進行移除并釋放工作
6、ViewRootImpl.die
boolean die(boolean immediate) {if (immediate && !mIsInTraversal) {//繼續跟蹤doDie();return false;}if (!mIsDrawing) {destroyHardwareRenderer();} else {Log.e(TAG, "Attempting to destroy the window while drawing!\n" +" window=" + this + ", title=" + mWindowAttributes.getTitle());}mHandler.sendEmptyMessage(MSG_DIE);return true; } 復制代碼7、ViewRootImpl.doDie
void doDie() {checkThread();if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);synchronized (this) {if (mRemoved) {return;}mRemoved = true;if (mAdded) {//這里是真正移除Dialog的ViewdispatchDetachedFromWindow();}if (mAdded && !mFirst) {//硬件渲染destroydestroyHardwareRenderer();if (mView != null) {int viewVisibility = mView.getVisibility();boolean viewVisibilityChanged = mViewVisibility != viewVisibility;if (mWindowAttributesChanged || viewVisibilityChanged) {// If layout params have been changed, first give them// to the window manager to make sure it has the correct// animation info.try {if ((relayoutWindow(mWindowAttributes, viewVisibility, false)& WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {mWindowSession.finishDrawing(mWindow);}} catch (RemoteException e) {}}//Surface的釋放mSurface.release();}}mAdded = false;}//移除之前存儲的變量WindowManagerGlobal.getInstance().doRemoveView(this); } 復制代碼保證線程安全后,做移除和釋放工作
8、WindowManagerGlobal.doRemoveView
一般程序最后的工作都是釋放工作,移除之前存儲的變量
void doRemoveView(ViewRootImpl root) {synchronized (mLock) {final int index = mRoots.indexOf(root);if (index >= 0) {//釋放工作mRoots.remove(index);mParams.remove(index);final View view = mViews.remove(index);mDyingViews.remove(view);}}if (HardwareRenderer.sTrimForeground && HardwareRenderer.isAvailable()) {doTrimForeground();} } 復制代碼9、ViewRootImpl.dispatchDetachedFromWindow
void dispatchDetachedFromWindow() {if (mView != null && mView.mAttachInfo != null) {mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);//此方法會回調onDetachedFromWindow方法,會做資源的回收mView.dispatchDetachedFromWindow();}mAccessibilityInteractionConnectionManager.ensureNoConnection();mAccessibilityManager.removeAccessibilityStateChangeListener(mAccessibilityInteractionConnectionManager);mAccessibilityManager.removeHighTextContrastStateChangeListener(mHighContrastTextManager);removeSendWindowContentChangedCallback();destroyHardwareRenderer();setAccessibilityFocus(null, null);mView.assignParent(null);mView = null;mAttachInfo.mRootView = null;mSurface.release();if (mInputQueueCallback != null && mInputQueue != null) {mInputQueueCallback.onInputQueueDestroyed(mInputQueue);mInputQueue.dispose();mInputQueueCallback = null;mInputQueue = null;}if (mInputEventReceiver != null) {mInputEventReceiver.dispose();mInputEventReceiver = null;}try {//這里調用了mWindowSession的remove方法,在WindowManagerService層通過IPC機制完成真正的window刪除mWindowSession.remove(mWindow);} catch (RemoteException e) {}// Dispose the input channel after removing the window so the Window Manager// doesn't interpret the input channel being closed as an abnormal termination.if (mInputChannel != null) {mInputChannel.dispose();mInputChannel = null;}mDisplayManager.unregisterDisplayListener(mDisplayListener);unscheduleTraversals(); } 復制代碼到最后會和添加View的時候完成閉環,還是通過WindowSession的IPC機制去調用的,最后在WindowManagerService層通過IPC機制去實現的 ##總結 1.Dialog的dismiss和show形成閉環,調用的過程是相似的,只不過多了資源的釋放環節
DialogFragment
DialogFragment本身繼承自Fragment
public class DialogFragment extends Fragmentimplements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener 復制代碼在平時中,我們需要自定義WeDialogFragment,而且在正式開發中踩過的坑:
- 需要對參數進行onSaveInstanceState操作,這類操作主要是防止異步吊起DialogFragment報nullPoint的Bug
- 需要重寫show(),對show做一層彈出時候的保護,這類操作主要是防止異步吊起DialogFragment報onSaveInstanceState的Bug
然后在Activity中彈出DialogFragment
WeDialogFragment weDialogFragment = new WeDialogFragment(); weDialogFragment.show(activity.getSupportFragmentManager(),"weDialogFragment"); 復制代碼一、DialogFragment的顯示
1、DialogFragment.show
public void show(FragmentManager manager, String tag) {mDismissed = false;mShownByMe = true;FragmentTransaction ft = manager.beginTransaction();ft.add(this, tag);ft.commit(); } 復制代碼show的方法其實就是對Fragment的處理,將Fragment添加到Fragment棧中
二、DialogFragment的隱藏
####1、DialogFragment.dismiss
public void dismiss() {dismissInternal(false); }public void dismissAllowingStateLoss() {dismissInternal(true); }void dismissInternal(boolean allowStateLoss) {if (mDismissed) {return;}mDismissed = true;mShownByMe = false;if (mDialog != null) {mDialog.dismiss();}mViewDestroyed = true;if (mBackStackId >= 0) {getFragmentManager().popBackStack(mBackStackId,FragmentManager.POP_BACK_STACK_INCLUSIVE);mBackStackId = -1;} else {FragmentTransaction ft = getFragmentManager().beginTransaction();ft.remove(this);if (allowStateLoss) {ft.commitAllowingStateLoss();} else {ft.commit();}} } 復制代碼dismiss的方法也是對Fragment的處理,將Fragment移除到Fragment棧中
三、Dialog的創建
1、DialogFragment.onCreateDialog
@NonNull public Dialog onCreateDialog(Bundle savedInstanceState) {return new Dialog(getActivity(), getTheme()); } 復制代碼和創建普通的Dialog沒什么區別,我們重寫該方法,可以自定義彈出AlertDialog等其他自定義Dialog
四、Dialog的視圖
1、DialogFragment.onActivityCreated
@Override public void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);if (!mShowsDialog) {return;}//拿到的就是onCreateView返回值的view對象,具體可以在Fragment源碼找到View view = getView();if (view != null) {if (view.getParent() != null) {throw new IllegalStateException("DialogFragment can not be attached to a container view");}//真正設置viewmDialog.setContentView(view);}final Activity activity = getActivity();if (activity != null) {mDialog.setOwnerActivity(activity);}mDialog.setCancelable(mCancelable);mDialog.setOnCancelListener(this);mDialog.setOnDismissListener(this);if (savedInstanceState != null) {Bundle dialogState = savedInstanceState.getBundle(SAVED_DIALOG_STATE_TAG);if (dialogState != null) {mDialog.onRestoreInstanceState(dialogState);}} } 復制代碼在Activity創建的時候,Fragment的周期會回調onActivityCreated,從而對Dialog設置視圖
五、Dialog的顯示隱藏
Dialog顯示隱藏就簡單了,隨著Fragment的生命周期顯示和隱藏,直接看代碼就行了
@Override public void onStart() {super.onStart();if (mDialog != null) {mViewDestroyed = false;mDialog.show();} }@Override public void onStop() {super.onStop();if (mDialog != null) {mDialog.hide();} } 復制代碼總結
DialogFragment = Fragment + Dialog,DialogFragment本身繼承Fragment,Fragment只是用來依附在Activity上,可以監聽Activity的生命周期,從而去通知Dialog做對應的操作,而Dialog才是我們正在顯示在屏幕上的彈窗,而非一個Fragment。這里的Dialog真正顯示出來的View是從**onCreateView()中獲取view后,在源碼中調用dialog的setContentView()**顯示出來的
文末送福利啦!!
同時我經過多年的收藏目前也算收集到了一套完整的學習資料以及高清詳細的Android架構進階學習導圖及筆記免費分享給大家,希望對想成為架構師的朋友有一定的參考和幫助。 **下面是部分資料截圖,誠意滿滿:特別適合有開發經驗的Android程序員們學習。
資料免費領取方式:現在關注我并且加入群聊 群號:1018342383 或者是點擊鏈接加入群聊【Android開發交流】:jq.qq.com/?_wv=1027&a…總結
以上是生活随笔為你收集整理的Android弹窗组件工作机制之Dialog、DialogFragment(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python安装selenium启动浏览
- 下一篇: python源码用于查找指定具有相同内容