android 소스 분석(20)-->Dialog 그리기 취소
어떤 학우들은 앞의 몇 편의 글에서Activity의 로드 그리기 프로세스를 소개할 때 왜 Activity의 창 취소 프로세스를 설명하지 않았는지 물어볼 수도 있다. 여기에 설명해 준다.그것은 당시에 설명한 중점이 Activity의 불러오기와 그리기 프로세스였기 때문이다. 취소 그리기 프로세스는 Activity의 생명주기 관리에 혼합되어 뚜렷하지 않을 수 있기 때문에 여기서 윈도 창의 취소 그리기 프로세스를 Dialog에 두었다. 사실 그들의 취소 그리기 프로세스는 모두 비슷하다. Dialog의 취소 그리기 프로세스를 본 후에 Activity의 취소 그리기 프로세스를 다시 보면 매우 간단하다.
우리 지난 글에서Dialog에 대한 예를 기억하십니까?우리는 AlertDialog를 통과했다.Builder에서 AlertDialog를 만들고 Activity의 단추를 누르면 이벤트를 표시합니다. AlertDialog에는 '알겠습니다' 단추가 정의되어 있으며, 이 단추를 누르면 AlertDialog가 터치됩니다.cancel 방법, 이 방법을 실행함으로써 우리의alertDialog는 표시되지 않습니다. 분명히 cancel 방법은 실행 과정에서 그리기를 취소하는 논리를 실행했습니다. 여기서 우리의 예 핵심 코드를 살펴보겠습니다.
title.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this.getApplication());
builder.setIcon(R.mipmap.ic_launcher);
builder.setMessage("this is the content view!!!");
builder.setTitle("this is the title view!!!");
builder.setView(R.layout.activity_second);
builder.setPositiveButton(" ", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
alertDialog.cannel();
}
});
alertDialog = builder.create();
alertDialog.show();
}
});
이 title는 우리의Activity에 있는 TextView입니다. 이 TextView의 클릭 이벤트를 등록해서 AlertDialog를 표시합니다. AlertDialog에 있는 단추의 클릭 이벤트를 등록해서alertDialog의 cancel 방법을 실행합니다.
알겠습니다. Dialog의 cannel 방법의 구체적인 실현을 살펴보겠습니다.
public void cancel() {
if (!mCanceled && mCancelMessage != null) {
mCanceled = true;
// Obtain a new message so this dialog can be re-used
Message.obtain(mCancelMessage).sendToTarget();
}
dismiss();
}
메소드에서 현재 Dialog가 취소되지 않고 메시지 취소가 설정되어 있으면 Message를 호출합니다.obtain(mCancel).sendToTarget (), 앞에서 분석한 바와 같이 이 sendToTarget 방법은 우리가 등록한 비동기 메시지 처리 논리를 되돌려줍니다.
public void setOnCancelListener(final OnCancelListener listener) {
if (mCancelAndDismissTaken != null) {
throw new IllegalStateException(
"OnCancelListener is already taken by "
+ mCancelAndDismissTaken + " and can not be replaced.");
}
if (listener != null) {
mCancelMessage = mListenersHandler.obtainMessage(CANCEL, listener);
} else {
mCancelMessage = null;
}
}
AlertDialog를 초기화하는 것을 볼 수 있습니다.Builder에서 set Oncancel Listener를 설정하면 mListenersHandler의 비동기적인 메시지 처리를 수행할 것입니다. 알겠습니다. mListenersHandler의 정의를 보십시오.
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;
}
}
}
알겠습니다. 여기는 설정된 Oncancel Listener의 onCancel 방법을 사용합니다. 즉, 우리는 다이어로그를 사용합니다.cancel 방법은 먼저 dialog가 set Oncancel Listener를 호출했는지 여부를 판단합니다. 만약 우리가 다이얼로그를 사용하지 않았다면 다이얼로그를 호출한 다음에 디스미스 방법을 다시 실행합니다.Builder 설정 Oncancel Listener 그러면 cancel 방법과 dismiss 방법은 같은 효과가 있습니다.
이제 Dismiss 접근 방식의 실현 논리를 살펴보겠습니다.
public void dismiss() {
if (Looper.myLooper() == mHandler.getLooper()) {
dismissDialog();
} else {
mHandler.post(mDismissAction);
}
}
이를 통해 알 수 있듯이 현재 라인의 Looper가 주 라인의 Looper인지를 먼저 판단할 수 있다. (mHandler는 주 라인에서 만들어졌기 때문에 mHandler.getLooper는 주 라인에서 만들어진 Looper의 대상으로 되돌아간다. 만약 그렇다면dismissDialog () 방법을 직접 실행한다. 그렇지 않으면 mHandler를 통해 주 라인에 비동기적인 메시지를 보내는 것이다. 간단하게 말하면 현재 라인이 주 라인인지 아닌지를 판단하는 것이다.만약에 주 루틴이dismissDialog 방법을 실행하지 않으면 비동기 메시지를 보냅니다. 우리는 mHandler가 비동기 메시지에 대한 처리 메커니즘을 보겠습니다. 이곳의 mDismissAction은 Runnable 대상이기 때문에 mDismissAction의 정의를 직접 보겠습니다.
private final Runnable mDismissAction = new Runnable() {
public void run() {
dismissDialog();
}
};
좋아, 이곳의 비동기적인 소식도 결국은 디스미스 다이얼로그 방법을 호출하는 거야...
그래서 우리가 실행하는 cancel 방법이든dismiss 방법이든 우리의 방법이 주 라인에서 집행하든 하위 라인에서 집행하든 최종적으로 호출하는 것은 모두dismissDialog 방법이다. 그러면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();
}
}
알겠습니다. 코드가 특별히 많지 않은 것 같습니다. 방법체에서 현재 mDector가 비어 있는지, 아니면 현재 다이얼로그가 표시되어 있는지, 비어 있거나 표시되지 않으면 바로return이 떨어집니다. 즉, 현재 우리의 다이얼로그가 더 이상 표시되지 않으면 아래로 실행할 필요가 없습니다.
그리고 우리는 mWindow를 호출했다.isDestroyed () 방법으로 윈도 대상이 소각되었는지 판단합니다. 소각되었을 경우,return을 직접 출력하고 오류 로그를 출력합니다.
그리고 우리는 mWindowManager를 호출했다.removeViewImmediate(mDector), 여기 mDector는 우리Dialog 창의 루트 레이아웃입니다. 이 방법의 이름을 보면 Dialog가 루트 레이아웃을 제거하는 작업일 것입니다. 이 방법의 구체적인 실현을 볼 수 있습니다.앞의 몇 편의 글에서 우리는 이미 이곳의 mWindow Manager가 사실은 Window Manager Impl의 실례를 분석한 적이 있다. 그러므로 여기의remove View Immediate 방법은 Window Manager Impl의 방법일 것이다. 우리는 그것의 구체적인 실현을 살펴보자.
@Override
public void removeViewImmediate(View view) {
mGlobal.removeView(view, true);
}
여기에서 mGlobal을 호출한 것을 발견할 수 있습니다.removeView 방법, 그리고 여기 mGlobal은 Window Manager Global의 실례입니다. 그래서 Window Manager Global에서removeView의 실현 논리를 다시 한 번 보겠습니다.
public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
synchronized (mLock) {
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}
여기에서 저장된 mDector 구성 요소를 얻은 다음에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());
}
}
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
보시다시피 mDector 구성 요소의 ViewRootImpl을 가져와die 방법을 호출했습니다. 이 방법을 통해 윈도 구성 요소의 삭제 절차를 실현합니다.
boolean die(boolean immediate) {
// Make sure we do execute immediately if we are in the middle of a traversal or the damage
// done by dispatchDetachedFromWindow will cause havoc on return.
if (immediate && !mIsInTraversal) {
doDie();
return false;
}
if (!mIsDrawing) {
destroyHardwareRenderer();
} else {
Log.e(TAG, "Attempting to destroy the window while drawing!
" +
" window=" + this + ", title=" + mWindowAttributes.getTitle());
}
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
방법체에서 DoDie 방법을 사용한 것을 볼 수 있다. 이름을 보면 윈도우 소각 작업을 진정으로 수행하는 방법일 것이다. DoDie 방법의 구체적인 실현을 살펴보자.
void doDie() {
checkThread();
if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
synchronized (this) {
if (mRemoved) {
return;
}
mRemoved = true;
if (mAdded) {
dispatchDetachedFromWindow();
}
if (mAdded && !mFirst) {
destroyHardwareRenderer();
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) {
}
}
mSurface.release();
}
}
mAdded = false;
}
WindowManagerGlobal.getInstance().doRemoveView(this);
}
이를 통해 알 수 있듯이 방법체에서 먼저 checkThread 방법을 호출하여Activity의 그리기 절차를 소개할 때 소개를 했고 현재 실행 코드의 라인을 판단했다. 만약에 메인 라인이 아니라면 이상을 던진다.
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
우리는doDie의 방법을 따라 아래를 내려다보았고dispatch Detached From Window () 방법을 사용했다. 이 방법은 주로 Window의 각 구성원 변수, 임시 변수 등을 없애는 것이다
void dispatchDetachedFromWindow() {
if (mView != null && mView.mAttachInfo != null) {
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
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(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();
}
방법에서 mView를 호출한 것을 볼 수 있습니다.dispatch Detached From Window 방법, 이 방법의 작용은 mView를 Window에서detach로 나오는 것이다. 우리는 이 방법의 구체적인 실현을 볼 수 있다.
void dispatchDetachedFromWindow() {
AttachInfo info = mAttachInfo;
if (info != null) {
int vis = info.mWindowVisibility;
if (vis != GONE) {
onWindowVisibilityChanged(GONE);
}
}
onDetachedFromWindow();
onDetachedFromWindowInternal();
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
imm.onViewDetachedFromWindow(this);
}
ListenerInfo li = mListenerInfo;
final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =
li != null ? li.mOnAttachStateChangeListeners : null;
if (listeners != null && listeners.size() > 0) {
// NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
// perform the dispatching. The iterator is a safe guard against listeners that
// could mutate the list by calling the various add/remove methods. This prevents
// the array from being modified while we iterate it.
for (OnAttachStateChangeListener listener : listeners) {
listener.onViewDetachedFromWindow(this);
}
}
if ((mPrivateFlags & PFLAG_SCROLL_CONTAINER_ADDED) != 0) {
mAttachInfo.mScrollContainers.remove(this);
mPrivateFlags &= ~PFLAG_SCROLL_CONTAINER_ADDED;
}
mAttachInfo = null;
if (mOverlay != null) {
mOverlay.getOverlayView().dispatchDetachedFromWindow();
}
}
그 중에서 onDetached From Window 방법은 빈 리셋 방법입니다. 여기서 우리는 onDetached From Window 인터넷 방법을 중점적으로 살펴보겠습니다.
protected void onDetachedFromWindowInternal() {
mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
removeUnsetPressCallback();
removeLongPressCallback();
removePerformClickCallback();
removeSendViewScrolledAccessibilityEventCallback();
stopNestedScroll();
// Anything that started animating right before detach should already
// be in its final state when re-attached.
jumpDrawablesToCurrentState();
destroyDrawingCache();
cleanupDraw();
mCurrentAnimation = null;
}
Detached From Window 인터넷 방법의 방법체도 특별히 길지 않고 모두 호출 함수들이다. 여기서 destrop Drawing Cache 방법을 살펴보자. 이 방법은 주로View의 캐시 Drawing을 없애는 것이다. 구체적인 실현을 살펴보자.
public void destroyDrawingCache() {
if (mDrawingCache != null) {
mDrawingCache.recycle();
mDrawingCache = null;
}
if (mUnscaledDrawingCache != null) {
mUnscaledDrawingCache.recycle();
mUnscaledDrawingCache = null;
}
}
이 mDrawing Cache는 비트맵 형식의 구성원 변수입니다. 여기에서 호출된 Recycler와 비우기 동작은 뷰에서draw 방법을 실행한 후 캐시된bitmap을 비우는 것입니다.
여기서 설명해야 할 것은 우리 View 구성 요소의 최종 디스플레이 구현은 draw 방법을 통해 그려진 것이고 우리 draw 방법의 매개 변수는 Canvas이다. 이것은 캔버스의 대상이다. draw 방법을 통해 이 대상을 조작하고 나타내는 것이다. 캔버스 대상이 디스플레이 효과를 실현할 수 있는 이유는 내부에 비트맵 대상이 저장되어 있기 때문이다.Canvas 객체를 조작하는 것은 실질적으로 Canvas 객체 내부의 Bitmap 객체를 조작하는 것이고 View 구성 요소의 표시는 바로 이곳의 Bitmap을 통해 이루어진다.
위의bitmap 대상을 비우면 View 구성 요소의 디스플레이 효과를 비우는 것과 같다. 즉, 우리가 View의draw 방법의 실행 효과를 취소하고 디스패치 Detached From Window 방법으로 돌아가 mView를 실행하는 것과 같다.dispatch Detached From Window () 방법에 이어 mView = null을 호출했습니다.방법, 여기 mView를 비워 두면, 우리는View의meature와layouot의 실행 효과를 취소할 수 있습니다.
이렇게 일련의 조작을 거친 후에 우리의Dialog의 취소 그리기 프로세스가 끝났습니다. 이제Activity의 취소 그리기 프로세스를 살펴보겠습니다.저희 "Activity 폐기 절차"기억나세요?참조:http://blog.csdn.net/qq_23547831/article/details/51232309activity의finish 방법을 호출할 때ActivityThread의handleDestroyActivity 방법을 다시 호출합니다. 이 방법의 실현을 살펴보겠습니다.
private void handleDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance) {
...
wm.removeViewImmediate(v);
...
}
여기 호출된 거 보여요. 여기 호출된wm.removeViewImmediate 방법, 이 방법은Dialog가 그림 그리기 프로세스를 없애는 것을 방금 분석한 시작 방법이 아닙니까?앞으로의 논리는 모두 상세하다. 그러면 우리는Activity의 그림 그리기 취소 절차를 실현할 수 있다.
요약:
4
4
4
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.