ViewRootImpl简析

news2024/11/28 2:46:23

ViewRootImpl简析

  • 如何实现视图和wms沟通桥梁的作用
    • Session的创建
    • 获取画布
    • 如何实现事件分发的桥梁作用

The top of a view hierarchy, implementing the needed protocol between View and the WindowManager. This is for the most part an internal implementation detail of WindowManagerGlobal. --google api 31

ViewRootImpl是视图结构的顶层,实现了视图和WM通信的协议。很大程度上是WindowManagerGlobal的内部实现细节。
ViewRootImpl不仅是视图和WM沟通的桥梁,也是事件分发的桥梁。

如何实现视图和wms沟通桥梁的作用

你好! 这是你第一次使用 **Markdown编辑器** 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。
WindowManagerGlobal是单例模式,其中维护ViewRootImpl数组,每一个window对应一个ViewRootImpl,在添加View时会通过requestLayout注册Choreagrapher监听,等待系统下发绘制信号,触发View的绘制流程,也就是measure、layout、draw流程。

Session的创建

我们知道在绘制流程draw之后,应用的视图会显示在屏幕上,这中间需要通过wms拿到可在其上作画的画布。
在setView中会通过mWindowSession对象将window添加到wms,如下(只摘取关键代码):

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
        int userId) {
    synchronized (this) {
        if (mView == null) {
            mView = view;
            // Schedule the first layout -before- adding to the window
            // manager, to make sure we do the relayout before receiving
            // any other events from the system.
            requestLayout();
            
            res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
                  getHostVisibility(), mDisplay.getDisplayId(), userId,
                  mInsetsController.getRequestedVisibility(), inputChannel, mTempInsets,
                  mTempControls);

  

mWindowSession是什么?

This class represents an active client session. There is generally one Session object per process that is interacting with the window manager.

Session表示客户端和WMS交互的会话,每个进程都有一个Session对象与WMS交互。
看几个Session的成员方法:

@Override
public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, InsetsState requestedVisibility,
        InputChannel outInputChannel, InsetsState outInsetsState,
        InsetsSourceControl[] outActiveControls) {
    return mService.addWindow(this, window, attrs, viewVisibility, displayId,
            UserHandle.getUserId(mUid), requestedVisibility, outInputChannel, outInsetsState,
            outActiveControls);
}


@Override
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
        InputChannel outInputChannel, InsetsState outInsetsState,
        InsetsSourceControl[] outActiveControls) {
    return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
            requestedVisibility, outInputChannel, outInsetsState, outActiveControls);
}

@Override
public int addToDisplayWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, InsetsState outInsetsState) {
    return mService.addWindow(this, window, attrs, viewVisibility, displayId,
            UserHandle.getUserId(mUid), mDummyRequestedVisibility, null /* outInputChannel */,
            outInsetsState, mDummyControls);
}

@Override
public void remove(IWindow window) {
    mService.removeWindow(this, window);
}

@Override
public void prepareToReplaceWindows(IBinder appToken, boolean childrenOnly) {
    mService.setWillReplaceWindows(appToken, childrenOnly);
}

最终都是在和WMS打交道。
在创建ViewRootImple对象时,默认调用WindowManagerGlobal的静态方法getWindowSession,获取WMS本地代理对象,调用WMS的openSession方法创建Session对象,流程如下:
Session创建流程

获取画布

由前文我们知道ViewRootImple在setView时会注册Choreographer的监听,等下次系统下发绘制同步信号时,ViewRootImpl执行performTraversals方法,其中有这样一行代码:

if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
        || mForceNextWindowRelayout) {
        ...省略
        relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
        ...省略
}

其中会调用Session对象的relayout方法:

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
        boolean insetsPending) throws RemoteException {

    ...省略
    int relayoutResult = mWindowSession.relayout(mWindow, params,
            (int) (mView.getMeasuredWidth() * appScale + 0.5f),
            (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
            insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
            mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
            mTempControls, mSurfaceSize);
    ...省略

Session对象调用WMS的relayout方法:

@Override
public int relayout(IWindow window, WindowManager.LayoutParams attrs,
        int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
        ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
        SurfaceControl outSurfaceControl, InsetsState outInsetsState,
        InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
    int res = mService.relayoutWindow(this, window, attrs,
            requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
            outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
            outActiveControls, outSurfaceSize);
    return res;
}

WMS调用自身的createSurfaceControl方法:

public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
        int requestedWidth, int requestedHeight, int viewVisibility, int flags,
        long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
        SurfaceControl outSurfaceControl, InsetsState outInsetsState,
        InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
             ...省略
        // Create surfaceControl before surface placement otherwise layout will be skipped
        // (because WS.isGoneForLayout() is true when there is no surface.
        if (shouldRelayout) {
            try {
                result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
            } catch (Exception e) {
               ...省略
            }
        }

接着调用WindowStateAnimator的createSurfaceLocked方法:

private int createSurfaceControl(SurfaceControl outSurfaceControl, int result,
        WindowState win, WindowStateAnimator winAnimator) {
    if (!win.mHasSurface) {
        result |= RELAYOUT_RES_SURFACE_CHANGED;
    }

    WindowSurfaceController surfaceController;
    try {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createSurfaceControl");
        surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type);
    } finally {
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }

在WindowStateAnimator的createSurfaceLocked方法中创建WindowSurfaceController对象并返回WindowSurfaceController对象:

WindowSurfaceController createSurfaceLocked(int windowType) {
        ...省略
        mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), width,
                height, format, flags, this, windowType);
        ...省略

WindowSurfaceController在构造函数中创建SurfaceControl对象:

WindowSurfaceController(String name, int w, int h, int format,
        int flags, WindowStateAnimator animator, int windowType) {
    ...省略
    final SurfaceControl.Builder b = win.makeSurface()
            .setParent(win.getSurfaceControl())
            .setName(name)
            .setBufferSize(w, h)
            .setFormat(format)
            .setFlags(flags)
            .setMetadata(METADATA_WINDOW_TYPE, windowType)
            .setMetadata(METADATA_OWNER_UID, mWindowSession.mUid)
            .setMetadata(METADATA_OWNER_PID, mWindowSession.mPid)
            .setCallsite("WindowSurfaceController");
    ...省略
    mSurfaceControl = b.build();
}


@NonNull
public SurfaceControl build() {
    ...省略
    return new SurfaceControl(
            mSession, mName, mWidth, mHeight, mFormat, mFlags, mParent, mMetadata,
            mLocalOwnerView, mCallsite);
}

在SurfaceControl构造函数中调用了本地方法nativeCreate:

private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
        SurfaceControl parent, SparseIntArray metadata, WeakReference<View> localOwnerView,
        String callsite)
                throws OutOfResourcesException, IllegalArgumentException {
    ...省略
    try {
        if (metadata != null && metadata.size() > 0) {
            metaParcel.writeInt(metadata.size());
            for (int i = 0; i < metadata.size(); ++i) {
                metaParcel.writeInt(metadata.keyAt(i));
                metaParcel.writeByteArray(
                        ByteBuffer.allocate(4).order(ByteOrder.nativeOrder())
                                .putInt(metadata.valueAt(i)).array());
            }
            metaParcel.setDataPosition(0);
        }
        mNativeObject = nativeCreate(session, name, w, h, format, flags,
                parent != null ? parent.mNativeObject : 0, metaParcel);
    } finally {
        metaParcel.recycle();
    }
    ...省略
}

到这里由java层转至native层,先总结下java层的流程:
创建surface java层流程
nativeCreate返回的就是画布的底层指针,保存在SurfaceControl中,java层后续的操作都是基于这个指针操作画布。
接着来看native层的创建流程:
frameworks/base/core/jni/android_view_SurfaceControl.cpp

 static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,401          jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
402          jobject metadataParcel) {
403      ScopedUtfChars name(env, nameStr);
404      sp<SurfaceComposerClient> client;
405      if (sessionObj != NULL) {
406          client = android_view_SurfaceSession_getClient(env, sessionObj);
407      } else {
408          client = SurfaceComposerClient::getDefault();
409      }
410      SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
411      sp<SurfaceControl> surface;
412      LayerMetadata metadata;
413      Parcel* parcel = parcelForJavaObject(env, metadataParcel);
414      if (parcel && !parcel->objectsCount()) {
415          status_t err = metadata.readFromParcel(parcel);
416          if (err != NO_ERROR) {
417            jniThrowException(env, "java/lang/IllegalArgumentException",
418                              "Metadata parcel has wrong format");
419          }
420      }
421  422      sp<IBinder> parentHandle;
423      if (parent != nullptr) {
424          parentHandle = parent->getHandle();
425      }
426  427      status_t err = client->createSurfaceChecked(String8(name.c_str()), w, h, format, &surface,
428                                                  flags, parentHandle, std::move(metadata));
429      if (err == NAME_NOT_FOUND) {
430          jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
431          return 0;
432      } else if (err != NO_ERROR) {
433          jniThrowException(env, OutOfResourcesException, statusToString(err).c_str());
434          return 0;
435      }
436  437      surface->incStrong((void *)nativeCreate);
438      return reinterpret_cast<jlong>(surface.get());
439  }

这里会创建SurfaceComposerClient、SurfaceControl和Surface,这三件套。
看下SurfaceComposerClient的createSurfaceChecked方法:
路径:/framework/native/native/libs/gui/SurfaceComposerClient.cpp

status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
2376                                                       PixelFormat format,
2377                                                       sp<SurfaceControl>* outSurface, int32_t flags,
2378                                                       const sp<IBinder>& parentHandle,
2379                                                       LayerMetadata metadata,
2380                                                       uint32_t* outTransformHint) {
2381      sp<SurfaceControl> sur;
2382      status_t err = mStatus;
2383  2384      if (mStatus == NO_ERROR) {
2385          gui::CreateSurfaceResult result;
2386          binder::Status status = mClient->createSurface(std::string(name.string()), flags,
2387                                                         parentHandle, std::move(metadata), &result);
2388          err = statusTFromBinderStatus(status);
2389          if (outTransformHint) {
2390              *outTransformHint = result.transformHint;
2391          }
2392          ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
2393          if (err == NO_ERROR) {
2394              *outSurface = new SurfaceControl(this, result.handle, result.layerId,
2395                                               toString(result.layerName), w, h, format,
2396                                               result.transformHint, flags);
2397          }
2398      }
2399      return err;
2400  }

这里先调用mClient对象创建Surface,然后将创建结果作为参数创建SurfaceControl对象,并赋值给入参出参的outSurface指针。接下来看下Client的createSurface方法。
路径:/frameworks/native/services/surfaceflinger/Client.cpp

63  binder::Status Client::createSurface(const std::string& name, int32_t flags,
64                                       const sp<IBinder>& parent, const gui::LayerMetadata& metadata,
65                                       gui::CreateSurfaceResult* outResult) {
66      // We rely on createLayer to check permissions.67      sp<IBinder> handle;
68      LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this), name.c_str(),
69                             static_cast<uint32_t>(flags), std::move(metadata));
70      args.parentHandle = parent;
71      const status_t status = mFlinger->createLayer(args, *outResult);
72      return binderStatusFromStatusT(status);
73  }

调用mFlinger对象的createlayer方法,这里的mFlinger对象是什么呢?
路径:/frameworks/native/services/surfaceflinger/Client.h

  private:
46      // ISurfaceComposerClient interface
47  
48      binder::Status createSurface(const std::string& name, int32_t flags, const sp<IBinder>& parent,
49                                   const gui::LayerMetadata& metadata,
50                                   gui::CreateSurfaceResult* outResult) override;
51  
52      binder::Status clearLayerFrameStats(const sp<IBinder>& handle) override;
53  
54      binder::Status getLayerFrameStats(const sp<IBinder>& handle,
55                                        gui::FrameStats* outStats) override;
56  
57      binder::Status mirrorSurface(const sp<IBinder>& mirrorFromHandle,
58                                   gui::CreateSurfaceResult* outResult) override;
59  
60      binder::Status mirrorDisplay(int64_t displayId, gui::CreateSurfaceResult* outResult) override;
61  
62      // constant
63      sp<SurfaceFlinger> mFlinger;

mFlinger就是SurfaceFlinger的指针。
/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

6215  status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, gui::CreateSurfaceResult& outResult) {
6216      status_t result = NO_ERROR;
6217  
6218      sp<Layer> layer;
6219  
6220      switch (args.flags & ISurfaceComposerClient::eFXSurfaceMask) {
6221          case ISurfaceComposerClient::eFXSurfaceBufferQueue:
6222          case ISurfaceComposerClient::eFXSurfaceContainer:
6223          case ISurfaceComposerClient::eFXSurfaceBufferState:
6224              args.flags |= ISurfaceComposerClient::eNoColorFill;
6225              FMT_FALLTHROUGH;
6226          case ISurfaceComposerClient::eFXSurfaceEffect: {
6227              result = createBufferStateLayer(args, &outResult.handle, &layer);
6228              std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter();
6229              if (pendingBufferCounter) {
6230                  std::string counterName = layer->getPendingBufferCounterName();
6231                  mBufferCountTracker.add(outResult.handle->localBinder(), counterName,
6232                                          pendingBufferCounter);
6233  #ifdef MTK_SF_DEBUG_SUPPORT
6234                  mLayerTracker.track(layer->getName(), android::base::StringPrintf("%s,%d", __func__, __LINE__));
6235  #endif
6236              }
6237  #ifdef MTK_SF_MSYNC
6238              if (mMSyncSfApi && result == NO_ERROR) {
6239                  mMSyncSfApi->registerLayer(layer.get());
6240              }
6241  #endif
6242          } break;
6243          default:
6244              result = BAD_VALUE;
6245              break;
6246      }
6247  
6248      if (result != NO_ERROR) {
6249          return result;
6250      }
6251  
6252      args.addToRoot = args.addToRoot && callingThreadHasUnscopedSurfaceFlingerAccess();
6253      // We can safely promote the parent layer in binder thread because we have a strong reference6254      // to the layer's handle inside this scope.6255      sp<Layer> parent = LayerHandle::getLayer(args.parentHandle.promote());
6256      if (args.parentHandle != nullptr && parent == nullptr) {
6257          ALOGE("Invalid parent handle %p", args.parentHandle.promote().get());
6258          args.addToRoot = false;
6259      }
6260  
6261      uint32_t outTransformHint;
6262      result = addClientLayer(args, outResult.handle, layer, parent, &outTransformHint);
6263      if (result != NO_ERROR) {
6264          return result;
6265      }
6266  
6267      outResult.transformHint = static_cast<int32_t>(outTransformHint);
6268      outResult.layerId = layer->sequence;
6269      outResult.layerName = String16(layer->getDebugName());
6270      return result;
6271  }

这里就是根据标志创建不同的layer,layer就是Surface合成图层的基本单位了。创建的图层注册添加到SurfaceFlinger,并向上保存到SurfaceContral对象当中,并把指针返回到java层,完成画布的创建。

如何实现事件分发的桥梁作用

setView中会创建WindowInputEventReceiver,在构造方法中会把WindowInputEventReceiver注册到系统层的事件分发流程:

public InputEventReceiver(InputChannel inputChannel, Looper looper) {
    if (inputChannel == null) {
        throw new IllegalArgumentException("inputChannel must not be null");
    }
    if (looper == null) {
        throw new IllegalArgumentException("looper must not be null");
    }

    mInputChannel = inputChannel;
    mMessageQueue = looper.getQueue();
    mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
            inputChannel, mMessageQueue);

    mCloseGuard.open("dispose");
}

路径:/frameworks/base/core/jni/android_view_InputEventReceiver.cpp

 static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
529          jobject inputChannelObj, jobject messageQueueObj) {
530      std::shared_ptr<InputChannel> inputChannel =
531              android_view_InputChannel_getInputChannel(env, inputChannelObj);
532      if (inputChannel == nullptr) {
533          jniThrowRuntimeException(env, "InputChannel is not initialized.");
534          return 0;
535      }
536  
537      sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
538      if (messageQueue == nullptr) {
539          jniThrowRuntimeException(env, "MessageQueue is not initialized.");
540          return 0;
541      }
542  
543      sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
544              receiverWeak, inputChannel, messageQueue);
545      status_t status = receiver->initialize();
546      if (status) {
547          std::string message = android::base::
548                  StringPrintf("Failed to initialize input event receiver.  status=%s(%d)",
549                               statusToString(status).c_str(), status);
550          jniThrowRuntimeException(env, message.c_str());
551          return 0;
552      }
553  
554      receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object555      return reinterpret_cast<jlong>(receiver.get());
556  }

这里会基于java层传入的参数创建native层的receiver,后续通过reveiver分发事件到java层

326  status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,327          bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
328      ...省略
478                  env->CallVoidMethod(receiverObj.get(),
479                          gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
480                  if (env->ExceptionCheck()) {
481                      ALOGE("Exception dispatching input event.");
482                      skipCallbacks = true;
483                  }
484                  env->DeleteLocalRef(inputEventObj);
485              } else {
486                  ALOGW("channel '%s' ~ Failed to obtain event object.",
487                          getInputChannelName().c_str());
488                  skipCallbacks = true;
489              }
490          }
491      }
492  }

这里通过反射调用java层的dispatchInputEvent方法把时间分发到java层,事件进入WindowInputEventReceiver的onInputEvent方法。后续流转顺序:onInputEvent-》enqueueInputEvent-》doProcessInputEvents-》deliverInputEvent-》进入InputStage
InputState采用责任链模式,进行事件的流转,责任链组装过程:

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);

触摸事件的分发流程:mSyntheticInputStage --> viewPostImeStage
通过在在view分发事件方法processPointerEvent中添加trace,

        private int processPointerEvent(QueuedInputEvent q) {
            final MotionEvent event = (MotionEvent)q.mEvent;
            boolean handled = mHandwritingInitiator.onTouchEvent(event);

            mAttachInfo.mUnbufferedDispatchRequested = false;
            mAttachInfo.mHandlingPointerEvent = true;

            //@ M: control the flag when input move and up
            BoostFwkFactory.getInstance().makeBoostFwkManager().perfHint(
                mScrollScenario.setAction(BoostFwkManager.Scroll.INPUT_EVENT)
                        .setBoostStatus(BoostFwkManager.BOOST_BEGIN)
                        .setMotionEvent(event)
                        .setContext(mContext)
            );
            //@ M: End of input control

            // If the event was fully handled by the handwriting initiator, then don't dispatch it
            // to the view tree.
            handled = handled || mView.dispatchPointerEvent(event);
            //if (handled && ViewDebugManager.DEBUG_ENG) {
                        if (handled) {
                Log.v(mTag, "App handle pointer event: event = " + event
                             + ", mView = " + mView
                             + ", this = " + this, new Throwable());
            }

可以看到触摸事件在责任链上的流转过程:
责任链流转过程
到这里事件流转到mView中,mView就是DecorView
/DecorView.java

@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);
}

这里拿到的callback就是activity,事件流转到activity,后边就是大家熟悉的视图层的事件流转过程了。
总结下:
ViewRootImpl作为桥梁注册事件监听,底层事件先分发到ViewRootImpl,再分发到视图层。
系统事件分发到WindowInputEventReceiver,进入onInputEvent方法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/759575.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

快速搭建Python(Django)+Vue环境并实现页面登录注册功能

文章目录 一. 创建vue项目及环境搭建1. 创建vue项目2. 配置axios3. 创建vue组件login和register4. 设置并引用路由vue-router5. 完成login&#xff0c;register组件代码6. 完成App.vue的代码 二. 创建django项目及环境搭建1. 创建django项目2.配置mysql数据库3. 创建应用app4.创…

学习babylon.js --- [4] 体验WebVR

本文基于babylonjs来创建一个简单的WebVR工程&#xff0c;来体验一下babylonjs带来的VR效果&#xff0c;由于没有VR头显&#xff0c;所以只能使用Win10自带的混合现实模拟器&#xff0c;开启模拟器请参考这篇文章 一 简单工程 本文基于第三篇文章中的工程进行修改&#xff0c;…

Deep Residual Learning for Image Recognition

2015-ResNet 关键词: residual connection CV论文:一般在第一页就放一个很“好看”的图 从图可以得到的结论:深的网络更难训练 ( 不仅因为过拟合,因为训练时误差都很难降低 ) 网络结构图 1*1卷积作用:改变通道数 ,提出维度匹配方法:1)补零;2)投影projection…

map和set的模拟实现|利用红黑树封装map和set|STL源码剖析

前言 那么这里博主先安利一些干货满满的专栏了&#xff01; 首先是博主的高质量博客的汇总&#xff0c;这个专栏里面的博客&#xff0c;都是博主最最用心写的一部分&#xff0c;干货满满&#xff0c;希望对大家有帮助。 高质量干货博客汇总https://blog.csdn.net/yu_cblog/c…

再战Nvidia,安装 Windows 11 和 EndeavourOS 双系统

吐血刚装了一周的Ubuntu23.04就挂了&#xff0c;由于买的是最新的显卡就上了Test版本&#xff0c;结果Ubuntu自带的nvidia驱动535居然会失灵&#xff0c;nvidia-smi直接fail。于是换了一堆发行版本&#xff0c;但是我这个主板没带hdmi不支持集显输出&#xff0c;必须安装时就上…

WEB:warmup

背景知识 文件包含 目录遍历 代码审计 题目 打开链接后只有一张图片 f12和查看源代码都试一下&#xff0c;查看源代码发现如下图 访问一下source.php&#xff0c;有如下代码&#xff0c;进行代码审计 <?phphighlight_file(__FILE__);class emmm{public static function c…

#pragma pack不成对出现造成的异常

#pragma pack需要成对出现&#xff0c;若不成对出现会造成异常&#xff0c;难以调查。 现有5个文件&#xff0c;分别是main.c&#xff0c;A.h&#xff0c;A.c&#xff0c;B.h&#xff0c;B.c。 A.h文件内容如下&#xff1a; #include <stdio.h> #include <stdlib.h&g…

ylb-接口14购买理财产品

总览&#xff1a; 在api模块service包&#xff0c;Invest类下添加&#xff08;投资理财产品&#xff0c; int 是投资的结果 &#xff0c; 1 投资成功&#xff09;&#xff1a; package com.bjpowernode.api.service;import com.bjpowernode.api.pojo.BidInfoProduct;import j…

go mod vendor简明介绍

Go 语言在 go 1.6 版本以后编译 go 代码会优先从 vendor 目录先寻找依赖包&#xff0c;它具有以下优点&#xff1a; 复制依赖&#xff1a;go mod vendor 会把程序所依赖的所有包复制到项目目录下的vendor 文件夹中&#xff0c;所以即使这些依赖包在外部源&#xff08;如 GitHu…

MySQL 约束、聚合查询和联合查询练习

1. 数据库约束 设置一个考勤系统, 包含员工表&#xff0c;考勤记录表 首先要明白, 员工表对考勤记录表是一对多 员工表中起码包含 id , name 考勤记录表则包含 id, 日期, 考勤记录 考勤记录表为子表, id 则为外键约束 员工表为父键, id则为主键约束 搞明白这些, 接下来就…

【C语言】矩阵相乘

#include <stdio.h>#define M 3 #define N 4 #define P 3void fun(int a[M][N], int b[N][P], int m, int p, int n)//定义函数fun {printf("m: %d, p: %d, n: %d\n", m, p, n);int c[M][P]{0};int i, j, k;for(i 0; i < m; i){for(j 0; j < n; j){fo…

【数据结构】朴素模式匹配 KMP算法

&#x1f387;【数据结构】朴素模式匹配 & KMP 算法&#x1f387; &#x1f308; 自在飞花轻似梦,无边丝雨细如愁 &#x1f308; &#x1f31f; 正式开始学习数据结构啦~此专栏作为学习过程中的记录&#x1f31f; 文章目录 &#x1f387;【数据结构】朴素模式匹配 & K…

IDEA中设置鼠标滚轮修改字体大小

IDEA中设置鼠标滚轮修改字体大小&#xff1f; 选择File--Settings--Editor--General&#xff0c;把 Mouse Control 前的对勾勾选 后点击 OK 即可。 勾选此设置后&#xff0c;增加 Ctrl 鼠标滚轮 快捷键来控制代码字体大小显 示。字体随时可以放大缩小~~可真好用呢~

IDEA运行TOMCAT出现404

就这种问题&#xff0c;每个人的原因都不一定一样&#xff0c;我出现这种问题的解决方法在这里记录一下。顺便把我的配置记录一下。 除了本文的问题&#xff0c;还有可能是默认打开的文件名错了&#xff0c;或者端口被占用。 软件版本IDEA2023 TOMCAT9 亲测&#xff1a;IDE…

❤️创意网页:如何使用HTML制作漂亮的搜索框

✨博主&#xff1a;命运之光 &#x1f338;专栏&#xff1a;Python星辰秘典 &#x1f433;专栏&#xff1a;web开发&#xff08;简单好用又好看&#xff09; ❤️专栏&#xff1a;Java经典程序设计 ☀️博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;欢迎踏入…

pycharm python 函数添加注释 快捷键

写完函数定义后,在函数名下面点击,并按下敲入""",完后回车,这会自动生成函数的文档注释格式 完后找我要插件,会阅读函数的内容,自动生成注释的内容.

RootThis靶机复盘

RootThis靶机复盘 在这个靶机中收获良多&#xff0c;首先学会了一个新的交互方式&#xff0c;不需要用python了&#xff0c;同时知道了什么是静态链接文件&#xff0c;还有就是学会了遇到数据库文件应该怎么解决。 /usr/bin/script -qc /bin/bash /dev/null 静态链接版本的程…

openpnp - 相机图像亮度太高的解决方法

文章目录 openpnp - 相机图像亮度太高的解决方法概述笔记设置相机的合适参数白平衡END openpnp - 相机图像亮度太高的解决方法 概述 看到同学在群里讨论问题, 说相机补光灯亮度太高了, 导致openpnp图像惨白惨白的, 根本不能用. 能根本解决问题的方法, 就是群里同学说的, 用恒…

Linux —— 进程状态

目录 一&#xff0c;进程状态分类 二&#xff0c;僵尸进程 三&#xff0c;孤儿进程 一&#xff0c;进程状态分类 进程状态反应进程执行过程中的变化&#xff0c;状态会随外界条件的变化而转换&#xff1b; 三态模型&#xff0c;运行态、就绪态、阻塞态&#xff1b;五态模型…

PyTorch: 权值初始化

文章和代码已经归档至【Github仓库&#xff1a;https://github.com/timerring/dive-into-AI 】或者公众号【AIShareLab】回复 pytorch教程 也可获取。 文章目录 Pytorch&#xff1a;权值初始化梯度消失与梯度爆炸 Xavier 方法与 Kaiming 方法Xavier 方法nn.init.calculate_gain…