日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android事件分发溯源详解

發布時間:2024/4/15 Android 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android事件分发溯源详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

前兩天華仔給我出了一道難題,我們倆研究了小半天,借著這個契機正好回顧了一下Android事件分發的相關知識點,于是有了這篇文章。

Android事件分發機制大家都非常熟悉,大部分文章對這個過程的描述都是開始于Activity,但是事件是怎么傳到Activity的?

這里就涉及到幾個重要的部分:Window,WMS,ViewRoot和DecorView。

如果要理解事件分發的源頭,就需要搞明白他們之間的關系,所以我們先來看看它們到底有什么關系?

Window

Window是我們比較熟悉的,那么它是如何創建的?

我們來看Activity的attach函數:

@UnsupportedAppUsage final void attach(Context context, ActivityThread aThread,Instrumentation instr, IBinder token, int ident,Application application, Intent intent, ActivityInfo info,CharSequence title, Activity parent, String id,NonConfigurationInstances lastNonConfigurationInstances,Configuration config, String referrer, IVoiceInteractor voiceInteractor,Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {attachBaseContext(context);...mWindow = new PhoneWindow(this, window, activityConfigCallback);mWindow.setWindowControllerCallback(mWindowControllerCallback);mWindow.setCallback(this);mWindow.setOnWindowDismissedCallback(this);...mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken, mComponent.flattenToString(),(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);...mWindowManager = mWindow.getWindowManager();... }

我這里只展示一部分關鍵代碼。當我們的activity創建完成后會執行attach,這是可以看到創建了PhoneWindow,同時也設置了WindowManager。

注意mWindow.setCallback(this);這行代碼,Activity本身實現了Window.Callback接口:

public class Activity extends ContextThemeWrapperimplements LayoutInflater.Factory2,Window.Callback, ... {

這里將activity賦值給window的callback,在后面的流程中會發揮作用。

DecorView

DecorView是整個布局的最頂端的view,也就根布局。它很容易與ViewRoot搞混,ViewRoot其實不是View,后面再來說它。

DecorView是如何創建的,這一切要從setContentView說起:

public void setContentView(@LayoutRes int layoutResID) {getWindow().setContentView(layoutResID);initWindowDecorActionBar(); }

可以看到執行了window的setContentView,它的源碼:

@Overridepublic void setContentView(int layoutResID) {if (mContentParent == null) {installDecor();} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {mContentParent.removeAllViews();}...}

可以看到一開始就執行installDecor,這里就是初始化DecorView:

private void installDecor() {mForceDecorInstall = false;if (mDecor == null) {mDecor = generateDecor(-1);...} else {mDecor.setWindow(this);}if (mContentParent == null) {mContentParent = generateLayout(mDecor);...}}

可以看到先通過generateDecor創建了DecorView:

protected DecorView generateDecor(int featureId) {Context context;...return new DecorView(context, featureId, this, getAttributes());}

創建時將Window也傳入了,所以DecorView中保存了一份Window的引用。

然后回到installDecor代碼中,又執行了generateLayout,這里創建了mContentParent:

protected ViewGroup generateLayout(DecorView decor) {...ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);...return contentParent;}

可以看到這個mContentParent就是ID_ANDROID_CONTENT,所以它才是真正裝載我們通過setContentView所設置的布局那個ViewGroup。所以這個層級應該是:

DecorView -> mContentParent -> 實際布局

ViewRoot

通過上面可以看出,Window的創建是在attach環節,而DecorView則是在create環節。目前雖然創建了DecorView,但是還沒有真正添加到Window中,而且ViewRoot還沒有創建出來,這兩步實際上是一起的,下面來看一下。

Activity創建完成后會通知AMS,AMS處理一些事務后會通知Activity顯示,這樣就會執行ActivityThread的handleResumeActivity():

@Overridepublic void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,boolean isForward, String reason) {...if (r.window == null && !a.mFinished && willBeVisible) {r.window = r.activity.getWindow();View decor = r.window.getDecorView();decor.setVisibility(View.INVISIBLE);ViewManager wm = a.getWindowManager();WindowManager.LayoutParams l = r.window.getAttributes();a.mDecor = decor;...if (a.mVisibleFromClient) {if (!a.mWindowAdded) {a.mWindowAdded = true;wm.addView(decor, l);} else {a.onWindowAttributesChanged(l);}}} else if (!willBeVisible) {...}...}

這里會通過WindowManager的addView函數將DecorView添加到屏幕上。WindowManager的實現類是WindowManagerImpl,它的函數代碼如下:

@Overridepublic void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {applyTokens(params);mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,mContext.getUserId());}

實際上是執行了WindowManagerGlobal的addView:

public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow, int userId) {...ViewRootImpl root;View panelParentView = null;synchronized (mLock) {...root = new ViewRootImpl(view.getContext(), display);view.setLayoutParams(wparams);mViews.add(view);mRoots.add(root);mParams.add(wparams);// do this last because it fires off messages to start doing thingstry {root.setView(view, wparams, panelParentView, userId);} catch (RuntimeException e) {...}}}

這里我們看到創建了ViewRootImpl,這就是ViewRoot。然后將DecorView也傳入了,這樣ViewRoot就持有了DecorView。

那么ViewRoot到底是什么?

我們可以把看看成是管理DecorView的一個類,比如它初始化的時候得到了一個WindowSession:

public ViewRootImpl(Context context, Display display) {this(context, display, WindowManagerGlobal.getWindowSession(),false /* useSfChoreographer */);}

通過WindowSession可以與WMS進行通信實現一些窗口信息的傳遞。

另外在它的setView中還創建了一個WindowInputEventReceiver:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {synchronized (this) {if (mView == null) {...InputChannel inputChannel = null;if ((mWindowAttributes.inputFeatures& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {inputChannel = new InputChannel();}...try {...res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), userId,mInsetsController.getRequestedVisibility(), inputChannel, mTempInsets,mTempControls);...} catch (RemoteException e) {...} finally {...}...if (inputChannel != null) {...mInputEventReceiver = new WindowInputEventReceiver(inputChannel,Looper.myLooper());...}}}}

這個就是用來接收事件的,下面我們來順著這個看看事件是如何分發到view的。

事件源頭

上面創建WindowInputEventReceiver時,可以看到傳入了一個InputChannel,它創建之后又傳入了WindowSession,即WMS。InputChannel就是底層通知上層事件的核心,系統服務捕獲到屏幕事件后,會通過它通知到上層,也就是WindowInputEventReceiver。

所以WindowInputEventReceiver是整個事件的源頭:

final class WindowInputEventReceiver extends InputEventReceiver {...@Overridepublic void onInputEvent(InputEvent event) {...if (processedEvents != null) {if (processedEvents.isEmpty()) {// InputEvent consumed by mInputCompatProcessorfinishInputEvent(event, true);} else {for (int i = 0; i < processedEvents.size(); i++) {enqueueInputEvent(processedEvents.get(i), this,QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);}}} else {enqueueInputEvent(event, this, 0, true);}}...}

事件進入它的onInputEvent后會執行enqueueInputEvent:

void enqueueInputEvent(InputEvent event,InputEventReceiver receiver, int flags, boolean processImmediately) {...if (processImmediately) {doProcessInputEvents();} else {scheduleProcessInputEvents();}}

這里通過判斷立即執行還是延遲處理,結果差不多,來看立即執行的代碼:

void doProcessInputEvents() {// Deliver all pending input events in the queue.while (mPendingInputEventHead != null) {...deliverInputEvent(q);}...}

進入deliverInputEvent:

private void deliverInputEvent(QueuedInputEvent q) {...try {...InputStage stage;if (q.shouldSendToSynthesizer()) {stage = mSyntheticInputStage;} else {stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;}...if (stage != null) {handleWindowFocusChanged();stage.deliver(q);} else {finishInputEvent(q);}} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}}

可以看到通過stage進行了deliver,這個stage是什么?

在setView的最后有這么一段代碼:

// Set up the input pipeline.CharSequence counterSuffix = attrs.getTitle();mSyntheticInputStage = new SyntheticInputStage();InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,"aq:native-post-ime:" + counterSuffix);InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);InputStage imeStage = new ImeInputStage(earlyPostImeStage,"aq:ime:" + counterSuffix);InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,"aq:native-pre-ime:" + counterSuffix);mFirstInputStage = nativePreImeStage;mFirstPostImeInputStage = earlyPostImeStage;mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;

可以看到是一個套一個,我們重點來看ViewPostImeInputStage這個:

final class ViewPostImeInputStage extends InputStage {...@Overrideprotected int onProcess(QueuedInputEvent q) {if (q.mEvent instanceof KeyEvent) {return processKeyEvent(q);} else {final int source = q.mEvent.getSource();if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {return processPointerEvent(q);} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {return processTrackballEvent(q);} else {return processGenericMotionEvent(q);}}}...private int processPointerEvent(QueuedInputEvent q) {final MotionEvent event = (MotionEvent)q.mEvent;mAttachInfo.mUnbufferedDispatchRequested = false;mAttachInfo.mHandlingPointerEvent = true;boolean handled = mView.dispatchPointerEvent(event);...return handled ? FINISH_HANDLED : FORWARD;}...}

InputStage的deliver最終會通過onProcess來區分事件處理(這塊就不細說了,沒意義),其中我們最關心的事件交給了processPointerEvent來處理。在這里可以看到執行了mView.dispatchPointerEvent(event),這個View就是我們提到的DecorView。這樣我們總算找到了源頭,下面看看是怎么傳遞下去的。

事件傳遞

dispatchPointerEvent這個函數是View的一個函數,源碼:

public final boolean dispatchPointerEvent(MotionEvent event) {if (event.isTouchEvent()) {return dispatchTouchEvent(event);} else {return dispatchGenericMotionEvent(event);} }

到了我們熟悉的dispatchTouchEvent了,這樣直接就將事件分發到各個View了?并不是,來看看DecorView中這塊是如何處理的:

@Override public boolean dispatchTouchEvent(MotionEvent ev) {final Window.Callback cb = mWindow.getCallback();return cb != null && !mWindow.isDestroyed() && mFeatureId < 0? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev); }

可以看到它通過Window獲取了callback,然后執行了callback的dispatchTouchEvent。

不知道大家還是否記得我們一開始分析Window的創建的時候提到過(不記得可以回到文章開始看一下),Activity本身實現了Window.Callback接口,并設置給了window的callback。所以這里的callback其實就是Activity,這樣事件就傳遞到了Activity:

public boolean dispatchTouchEvent(MotionEvent ev) {if (ev.getAction() == MotionEvent.ACTION_DOWN) {onUserInteraction();}if (getWindow().superDispatchTouchEvent(ev)) {return true;}return onTouchEvent(ev); }

Activity中之間將事件傳遞給了Window,調用了它的superDispatchTouchEvent函數,實際上是PhoneWindow的實現:

@Overridepublic boolean superDispatchTouchEvent(MotionEvent event) {return mDecor.superDispatchTouchEvent(event);}

這樣就又傳遞回DecorView了:

public boolean superDispatchTouchEvent(MotionEvent event) {return super.dispatchTouchEvent(event); }

在DecorView中執行了super.dispatchTouchEvent(event);,它的父類就是ViewGroup,這樣就進入了我們熟悉的ViewGroup分發事件的環節了。

setCallBack

總結

總結

以上是生活随笔為你收集整理的Android事件分发溯源详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。