IME SoftInputWindow窗口添加

news2024/11/26 21:39:38

IME SoftInputWindow窗口添加

  • 1、时序图
  • 2、InputMethodService#onCreate()
  • 3、Dialog添加到WMS

android12-release1


1、时序图

输入法应用继承InputMethodService

frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java
frameworks/base/core/java/android/view/ImeFocusController.java
frameworks/base/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
frameworks/base/core/java/android/inputmethodservice/InputMethodService.java
frameworks/base/core/java/android/inputmethodservice/SoftInputWindow.java
android/app/Dialog.java

在这里插入图片描述

2、InputMethodService#onCreate()

  1. SoftInputWindow界面实质是Dialog
  2. 界面属性Type 为 WindowManager.LayoutParams.TYPE_INPUT_METHOD, WindowToken 标识 mCurToken = new Binder()
  3. 界面主要层级
    在这里插入图片描述
@Override public void onCreate() {
    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.onCreate");
    mTheme = Resources.selectSystemTheme(mTheme,
            getApplicationInfo().targetSdkVersion,
            android.R.style.Theme_InputMethod,
            android.R.style.Theme_Holo_InputMethod,
            android.R.style.Theme_DeviceDefault_InputMethod,
            android.R.style.Theme_DeviceDefault_InputMethod);
    super.setTheme(mTheme);
    super.onCreate();
    mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
    mSettingsObserver = SettingsObserver.createAndRegister(this);
    // cache preference so we don't have to read ContentProvider when IME is requested to be
    // shown the first time (cold start).
    mSettingsObserver.shouldShowImeWithHardKeyboard();

    mIsAutomotive = isAutomotive();
    mAutomotiveHideNavBarForKeyboard = getApplicationContext().getResources().getBoolean(
            com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);

    // TODO(b/111364446) Need to address context lifecycle issue if need to re-create
    // for update resources & configuration correctly when show soft input
    // in non-default display.
    mInflater = (LayoutInflater)getSystemService(
            Context.LAYOUT_INFLATER_SERVICE);
    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initSoftInputWindow");
    mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
            WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
    mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars());
    mWindow.getWindow().getAttributes().setFitInsetsSides(Side.all() & ~Side.BOTTOM);
    mWindow.getWindow().getAttributes().receiveInsetsIgnoringZOrder = true;

    // Automotive devices may request the navigation bar to be hidden when the IME shows up
    // (controlled via config_automotiveHideNavBarForKeyboard) in order to maximize the visible
    // screen real estate. When this happens, the IME window should animate from the bottom of
    // the screen to reduce the jank that happens from the lack of synchronization between the
    // bottom system window and the IME window.
    if (mIsAutomotive && mAutomotiveHideNavBarForKeyboard) {
        mWindow.getWindow().setDecorFitsSystemWindows(false);
    }

    // For ColorView in DecorView to work, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS needs to be set
    // by default (but IME developers can opt this out later if they want a new behavior).
    mWindow.getWindow().setFlags(
            FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);

    initViews();
    mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);

    mInlineSuggestionSessionController = new InlineSuggestionSessionController(
            this::onCreateInlineSuggestionsRequest, this::getHostInputToken,
            this::onInlineSuggestionsResponse);
    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}

void initViews() {
    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initViews");
    mInitialized = false;
    mViewsCreated = false;
    mShowInputRequested = false;
    mShowInputFlags = 0;

    mThemeAttrs = obtainStyledAttributes(android.R.styleable.InputMethodService);
    mRootView = mInflater.inflate(
            com.android.internal.R.layout.input_method, null);
    mWindow.setContentView(mRootView);
    mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
    mFullscreenArea = mRootView.findViewById(com.android.internal.R.id.fullscreenArea);
    mExtractViewHidden = false;
    mExtractFrame = mRootView.findViewById(android.R.id.extractArea);
    mExtractView = null;
    mExtractEditText = null;
    mExtractAccessories = null;
    mExtractAction = null;
    mFullscreenApplied = false;

    mCandidatesFrame = mRootView.findViewById(android.R.id.candidatesArea);
    mInputFrame = mRootView.findViewById(android.R.id.inputArea);
    mInputView = null;
    mIsInputViewShown = false;

    mExtractFrame.setVisibility(View.GONE);
    mCandidatesVisibility = getCandidatesHiddenVisibility();
    mCandidatesFrame.setVisibility(mCandidatesVisibility);
    mInputFrame.setVisibility(View.GONE);
    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}

3、Dialog添加到WMS

参考 WMS侧添加界面:WindowManagerImpl.java#addView -> WindowManagerGlobal.java#addView -> ViewRootImpl.java#setView -> Session extends IWindowSession.Stub#addToDisplayAsUser -> WindowManagerService.java#addWindow

注意界面TYPE_INPUT_METHOD判断
注意WindowToken判断,发生BadTokenException异常WindowManagerGlobal.ADD_BAD_APP_TOKEN
调用DisplayContent.java中setInputMethodWindowLockedcomputeImeTarget 确定 IME 目标的窗口,以便对 IME 窗口进行分层。

public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
        int displayId, int requestUserId, InsetsState requestedVisibility,
        InputChannel outInputChannel, InsetsState outInsetsState,
        InsetsSourceControl[] outActiveControls) {
    Arrays.fill(outActiveControls, null);
    int[] appOp = new int[1];
    final boolean isRoundedCornerOverlay = (attrs.privateFlags
            & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
    int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
            appOp);
    if (res != ADD_OKAY) {
        return res;
    }

    WindowState parentWindow = null;
    final int callingUid = Binder.getCallingUid();
    final int callingPid = Binder.getCallingPid();
    final long origId = Binder.clearCallingIdentity();
    final int type = attrs.type;

    synchronized (mGlobalLock) {
        if (!mDisplayReady) {
            throw new IllegalStateException("Display has not been initialialized");
        }

        final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);

        if (displayContent == null) {
            ProtoLog.w(WM_ERROR, "Attempted to add window to a display that does "
                    + "not exist: %d. Aborting.", displayId);
            return WindowManagerGlobal.ADD_INVALID_DISPLAY;
        }
        if (!displayContent.hasAccess(session.mUid)) {
            ProtoLog.w(WM_ERROR,
                    "Attempted to add window to a display for which the application "
                            + "does not have access: %d.  Aborting.",
                    displayContent.getDisplayId());
            return WindowManagerGlobal.ADD_INVALID_DISPLAY;
        }

        if (mWindowMap.containsKey(client.asBinder())) {
            ProtoLog.w(WM_ERROR, "Window %s is already added", client);
            return WindowManagerGlobal.ADD_DUPLICATE_ADD;
        }

        if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
            parentWindow = windowForClientLocked(null, attrs.token, false);
            if (parentWindow == null) {
                ProtoLog.w(WM_ERROR, "Attempted to add window with token that is not a window: "
                        + "%s.  Aborting.", attrs.token);
                return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
            }
            if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
                    && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
                ProtoLog.w(WM_ERROR, "Attempted to add window with token that is a sub-window: "
                        + "%s.  Aborting.", attrs.token);
                return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
            }
        }

        if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
            ProtoLog.w(WM_ERROR,
                    "Attempted to add private presentation window to a non-private display.  "
                            + "Aborting.");
            return WindowManagerGlobal.ADD_PERMISSION_DENIED;
        }

        if (type == TYPE_PRESENTATION && !displayContent.getDisplay().isPublicPresentation()) {
            ProtoLog.w(WM_ERROR,
                    "Attempted to add presentation window to a non-suitable display.  "
                            + "Aborting.");
            return WindowManagerGlobal.ADD_INVALID_DISPLAY;
        }

        int userId = UserHandle.getUserId(session.mUid);
        if (requestUserId != userId) {
            try {
                mAmInternal.handleIncomingUser(callingPid, callingUid, requestUserId,
                        false /*allowAll*/, ALLOW_NON_FULL, null, null);
            } catch (Exception exp) {
                ProtoLog.w(WM_ERROR, "Trying to add window with invalid user=%d",
                        requestUserId);
                return WindowManagerGlobal.ADD_INVALID_USER;
            }
            // It's fine to use this userId
            userId = requestUserId;
        }

        ActivityRecord activity = null;
        final boolean hasParent = parentWindow != null;
        // Use existing parent window token for child windows since they go in the same token
        // as there parent window so we can apply the same policy on them.
        WindowToken token = displayContent.getWindowToken(
                hasParent ? parentWindow.mAttrs.token : attrs.token);
        // If this is a child window, we want to apply the same type checking rules as the
        // parent window type.
        final int rootType = hasParent ? parentWindow.mAttrs.type : type;

        boolean addToastWindowRequiresToken = false;

        final IBinder windowContextToken = attrs.mWindowContextToken;

        if (token == null) {
            if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type,
                    rootType, attrs.token, attrs.packageName)) {
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
            if (hasParent) {
                // Use existing parent window token for child windows.
                token = parentWindow.mToken;
            } else if (mWindowContextListenerController.hasListener(windowContextToken)) {
                // Respect the window context token if the user provided it.
                final IBinder binder = attrs.token != null ? attrs.token : windowContextToken;
                final Bundle options = mWindowContextListenerController
                        .getOptions(windowContextToken);
                token = new WindowToken.Builder(this, binder, type)
                        .setDisplayContent(displayContent)
                        .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
                        .setRoundedCornerOverlay(isRoundedCornerOverlay)
                        .setFromClientToken(true)
                        .setOptions(options)
                        .build();
            } else {
                final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
                token = new WindowToken.Builder(this, binder, type)
                        .setDisplayContent(displayContent)
                        .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
                        .setRoundedCornerOverlay(isRoundedCornerOverlay)
                        .build();
            }
        } else if (rootType >= FIRST_APPLICATION_WINDOW
                && rootType <= LAST_APPLICATION_WINDOW) {
            activity = token.asActivityRecord();
            if (activity == null) {
                ProtoLog.w(WM_ERROR, "Attempted to add window with non-application token "
                        + ".%s Aborting.", token);
                return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
            } else if (activity.getParent() == null) {
                ProtoLog.w(WM_ERROR, "Attempted to add window with exiting application token "
                        + ".%s Aborting.", token);
                return WindowManagerGlobal.ADD_APP_EXITING;
            } else if (type == TYPE_APPLICATION_STARTING) {
                if (activity.mStartingWindow != null) {
                    ProtoLog.w(WM_ERROR, "Attempted to add starting window to "
                            + "token with already existing starting window");
                    return WindowManagerGlobal.ADD_DUPLICATE_ADD;
                }
                if (activity.mStartingData == null) {
                    ProtoLog.w(WM_ERROR, "Attempted to add starting window to "
                            + "token but already cleaned");
                    return WindowManagerGlobal.ADD_DUPLICATE_ADD;
                }
            }
        } else if (rootType == TYPE_INPUT_METHOD) {
            if (token.windowType != TYPE_INPUT_METHOD) {
                ProtoLog.w(WM_ERROR, "Attempted to add input method window with bad token "
                        + "%s.  Aborting.", attrs.token);
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
        } else if (rootType == TYPE_VOICE_INTERACTION) {
            if (token.windowType != TYPE_VOICE_INTERACTION) {
                ProtoLog.w(WM_ERROR, "Attempted to add voice interaction window with bad token "
                        + "%s.  Aborting.", attrs.token);
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
        } else if (rootType == TYPE_WALLPAPER) {
            if (token.windowType != TYPE_WALLPAPER) {
                ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with bad token "
                        + "%s.  Aborting.", attrs.token);
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
        } else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
            if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {
                ProtoLog.w(WM_ERROR,
                        "Attempted to add Accessibility overlay window with bad token "
                                + "%s.  Aborting.", attrs.token);
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
        } else if (type == TYPE_TOAST) {
            // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
            addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,
                    callingUid, parentWindow);
            if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) {
                ProtoLog.w(WM_ERROR, "Attempted to add a toast window with bad token "
                        + "%s.  Aborting.", attrs.token);
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
        } else if (type == TYPE_QS_DIALOG) {
            if (token.windowType != TYPE_QS_DIALOG) {
                ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with bad token "
                        + "%s.  Aborting.", attrs.token);
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
        } else if (token.asActivityRecord() != null) {
            ProtoLog.w(WM_ERROR, "Non-null activity for system window of rootType=%d",
                    rootType);
            // It is not valid to use an app token with other system types; we will
            // instead make a new token for it (as if null had been passed in for the token).
            attrs.token = null;
            token = new WindowToken.Builder(this, client.asBinder(), type)
                    .setDisplayContent(displayContent)
                    .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
                    .build();
        }

        final WindowState win = new WindowState(this, session, client, token, parentWindow,
                appOp[0], attrs, viewVisibility, session.mUid, userId,
                session.mCanAddInternalSystemWindow);
        if (win.mDeathRecipient == null) {
            // Client has apparently died, so there is no reason to
            // continue.
            ProtoLog.w(WM_ERROR, "Adding window client %s"
                    + " that is dead, aborting.", client.asBinder());
            return WindowManagerGlobal.ADD_APP_EXITING;
        }

        if (win.getDisplayContent() == null) {
            ProtoLog.w(WM_ERROR, "Adding window to Display that has been removed.");
            return WindowManagerGlobal.ADD_INVALID_DISPLAY;
        }

        final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
        displayPolicy.adjustWindowParamsLw(win, win.mAttrs);
        win.updateRequestedVisibility(requestedVisibility);

        res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
        if (res != ADD_OKAY) {
            return res;
        }

        final boolean openInputChannels = (outInputChannel != null
                && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
        if  (openInputChannels) {
            win.openInputChannel(outInputChannel);
        }

        // If adding a toast requires a token for this app we always schedule hiding
        // toast windows to make sure they don't stick around longer then necessary.
        // We hide instead of remove such windows as apps aren't prepared to handle
        // windows being removed under them.
        //
        // If the app is older it can add toasts without a token and hence overlay
        // other apps. To be maximally compatible with these apps we will hide the
        // window after the toast timeout only if the focused window is from another
        // UID, otherwise we allow unlimited duration. When a UID looses focus we
        // schedule hiding all of its toast windows.
        if (type == TYPE_TOAST) {
            if (!displayContent.canAddToastWindowForUid(callingUid)) {
                ProtoLog.w(WM_ERROR, "Adding more than one toast window for UID at a time.");
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            }
            // Make sure this happens before we moved focus as one can make the
            // toast focusable to force it not being hidden after the timeout.
            // Focusable toasts are always timed out to prevent a focused app to
            // show a focusable toasts while it has focus which will be kept on
            // the screen after the activity goes away.
            if (addToastWindowRequiresToken
                    || (attrs.flags & FLAG_NOT_FOCUSABLE) == 0
                    || displayContent.mCurrentFocus == null
                    || displayContent.mCurrentFocus.mOwnerUid != callingUid) {
                mH.sendMessageDelayed(
                        mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),
                        win.mAttrs.hideTimeoutMilliseconds);
            }
        }

        // Switch to listen to the {@link WindowToken token}'s configuration changes when
        // adding a window to the window context. Filter sub window type here because the sub
        // window must be attached to the parent window, which is attached to the window context
        // created window token.
        if (!win.isChildWindow()
                && mWindowContextListenerController.hasListener(windowContextToken)) {
            final int windowContextType = mWindowContextListenerController
                    .getWindowType(windowContextToken);
            if (type != windowContextType) {
                ProtoLog.w(WM_ERROR, "Window types in WindowContext and"
                        + " LayoutParams.type should match! Type from LayoutParams is %d,"
                        + " but type from WindowContext is %d", type, windowContextType);
                return WindowManagerGlobal.ADD_INVALID_TYPE;
            }
            final Bundle options = mWindowContextListenerController
                    .getOptions(windowContextToken);
            mWindowContextListenerController.registerWindowContainerListener(
                    windowContextToken, token, callingUid, type, options);
        }

        // From now on, no exceptions or errors allowed!

        res = ADD_OKAY;

        if (mUseBLAST) {
            res |= WindowManagerGlobal.ADD_FLAG_USE_BLAST;
        }

        if (displayContent.mCurrentFocus == null) {
            displayContent.mWinAddedSinceNullFocus.add(win);
        }

        if (excludeWindowTypeFromTapOutTask(type)) {
            displayContent.mTapExcludedWindows.add(win);
        }

        win.attach();
        mWindowMap.put(client.asBinder(), win);
        win.initAppOpsState();

        final boolean suspended = mPmInternal.isPackageSuspended(win.getOwningPackage(),
                UserHandle.getUserId(win.getOwningUid()));
        win.setHiddenWhileSuspended(suspended);

        final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty();
        win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);

        final ActivityRecord tokenActivity = token.asActivityRecord();
        if (type == TYPE_APPLICATION_STARTING && tokenActivity != null) {
            tokenActivity.mStartingWindow = win;
            ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s",
                    activity, win);
        }

        boolean imMayMove = true;

        win.mToken.addWindow(win);
        displayPolicy.addWindowLw(win, attrs);
        if (type == TYPE_INPUT_METHOD) {
            displayContent.setInputMethodWindowLocked(win);
            imMayMove = false;
        } else if (type == TYPE_INPUT_METHOD_DIALOG) {
            displayContent.computeImeTarget(true /* updateImeTarget */);
            imMayMove = false;
        } else {
            if (type == TYPE_WALLPAPER) {
                displayContent.mWallpaperController.clearLastWallpaperTimeoutTime();
                displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
            } else if (win.hasWallpaper()) {
                displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
            } else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) {
                // If there is currently a wallpaper being shown, and
                // the base layer of the new window is below the current
                // layer of the target window, then adjust the wallpaper.
                // This is to avoid a new window being placed between the
                // wallpaper and its target.
                displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
            }
        }

        final WindowStateAnimator winAnimator = win.mWinAnimator;
        winAnimator.mEnterAnimationPending = true;
        winAnimator.mEnteringAnimation = true;
        // Check if we need to prepare a transition for replacing window first.
        if (activity != null && activity.isVisible()
                && !prepareWindowReplacementTransition(activity)) {
            // If not, check if need to set up a dummy transition during display freeze
            // so that the unfreeze wait for the apps to draw. This might be needed if
            // the app is relaunching.
            prepareNoneTransitionForRelaunching(activity);
        }

        if (displayPolicy.getLayoutHint(win.mAttrs, token, outInsetsState,
                win.isClientLocal())) {
            res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
        }

        if (mInTouchMode) {
            res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
        }
        if (win.mActivityRecord == null || win.mActivityRecord.isClientVisible()) {
            res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
        }

        displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();

        boolean focusChanged = false;
        if (win.canReceiveKeys()) {
            focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
                    false /*updateInputWindows*/);
            if (focusChanged) {
                imMayMove = false;
            }
        }

        if (imMayMove) {
            displayContent.computeImeTarget(true /* updateImeTarget */);
        }

        // Don't do layout here, the window must call
        // relayout to be displayed, so we'll do it there.
        win.getParent().assignChildLayers();

        if (focusChanged) {
            displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus,
                    false /*updateInputWindows*/);
        }
        displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);

        ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s"
                + ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5));

        if (win.isVisibleOrAdding() && displayContent.updateOrientation()) {
            displayContent.sendNewConfiguration();
        }

        // This window doesn't have a frame yet. Don't let this window cause the insets change.
        displayContent.getInsetsStateController().updateAboveInsetsState(
                win, false /* notifyInsetsChanged */);

        getInsetsSourceControls(win, outActiveControls);
    }

    Binder.restoreCallingIdentity(origId);

    return res;
}

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

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

相关文章

【JAVA】数据类型,类型转换与提升,运算符,标识符命名规则

&#x1f349;内容专栏&#xff1a;【JAVA】 &#x1f349;本文脉络&#xff1a;数据类型&#xff0c;类型转换与提升&#xff0c;运算符&#xff0c;标识符命名规则 &#x1f349;本文作者&#xff1a;Melon_西西 &#x1f349;发布时间 &#xff1a;2023.7.12 目录 1. 字面常…

【数据结构】数据结构的基本概念

文章目录 思维导图数据结构的基本概念和术语数据结构三要素逻辑结构存储结构数据的运算 重要知识总结 思维导图 数据结构的基本概念和术语 数据&#xff1a;数据是信息的载体。是描述客观事物属性的数、字符及所有能输入到计算机中并被计算机程序识别和处理的符号的集合。数据…

【计算机视觉 | 目标检测 | 图像分割】arxiv 计算机视觉关于目标检测和图像分割的学术速递(7 月 12 日论文合集)

文章目录 一、检测相关(7篇)1.1 3D detection of roof sections from a single satellite image and application to LOD2-building reconstruction1.2 Towards exploring adversarial learning for anomaly detection in complex driving scenes1.3 Unveiling the invisible: …

探索嵌入式系统:初学者必知的核心概念解析

探索嵌入式系统&#xff1a;初学者必知的核心概念解析 嵌入式系统是指嵌入在其他设备或系统中的特定目的的计算机系统。它们通常用于控制、监测或执行特定任务&#xff0c;例如汽车的引擎控制单元、智能手机的操作系统或家电中的微控制器。对于初学者来说&#xff0c;以下是一…

Android自动化测试中如何处理各种弹窗

目录 弹窗的种类: APP内的业务弹窗 弹窗处理 watcher的使用 实战案例 总结&#xff1a; 在UI自动化测试中弹窗是影响自动化用例稳定性的一大因素&#xff0c;如何方便快捷的处理各种情况下的弹窗&#xff0c;是搞UI自动化测试必须要面临的问题. 弹窗的种类: 安装APP时的…

MySQL事务 | 隔离级别 | 数据一致性

文章目录 简介一、事务并发问题1. 脏读&#xff08;Dirty Read&#xff09;2. 不可重复读&#xff08;Non-repeatable Read&#xff09;3. 幻读&#xff08;Phantom Read&#xff09;幻读和不可重复读的区别 二、事务隔离级别1. 回顾事务2. 事务级别3. 特点和优缺点 三、事务隔…

科研笔记:一些有用的网站整理(更新中)

1 论文整理网站 整理AI相关领域的一些综述GitHub - KaiyuanGao/AI-Surveys: 整理AI相关领域的一些综述时间序列的paper/code汇总GitHub - qingsongedu/awesome-AI-for-time-series-papers: A professional list of Papers, Tutorials, and Surveys on AI for Time Series in t…

OpenSource - Spring Startup Ananlyzer

文章目录 &#x1f680;Optimization of Spring Startup核心能力&#x1f4c8;Spring应用启动数据采集报告应用启动时长优化 &#x1f4c8;Spring应用启动数据采集报告安装jar包配置项应用启动自定义扩展 &#x1f680;应用启动时长优化支持异步化的Bean类型接入异步Bean优化 开…

idea配置类注解和方法注解最详细教材

1.配置类注释 点击setting 方法一&#xff1a; 1.按照要求添加下图中的模板 /*** Author: aaa* Date: ${YEAR}/${MONTH}/${DAY}* Description: */ 2.勾选下面的两个勾后点击apply 方法二&#xff1a; 我们看到了在刚刚那个模板中有这样一句话 这句话是表示创建项目的时候会…

CASAIM与大疆达成全自动化测量技术合作,CASAIM IS全自动化蓝光测量仪实现无人机叶片全尺寸检测及质量控制

近期&#xff0c;CASAIM与大疆达成全自动化测量技术合作&#xff0c;CASAIM IS全自动化蓝光测量仪实现无人机叶片全尺寸检测及质量控制。 无人机行业在过去几年里取得了迅猛发展&#xff0c;大疆是全球领先的无人飞行器控制系统及无人机解决方案的研发商和生产商&#xff0c;客…

2023年网页设计制作工具盘点

过去&#xff0c;专业人员使用HTMLL、CSS、Javascript等代码手动编写和构建网站。现在有越来越多的智能网页制作工具来帮助任何人实现零代码基础&#xff0c;随意建立和设计网站。在本文中&#xff0c;我们将向您介绍2023年流行的网页制作工具。 1.即时设计 即时设计是一款UI…

​python接口自动化(三十一)--html测试报告通过邮件发出去——下(详解)​

简介  本篇总结了 QQ &#xff08;SSL&#xff09;邮箱和 163&#xff08;非SSL&#xff09; 邮箱发送邮件&#xff0c;专治各种不行&#xff0c;总之看完这篇以后麻麻再也不用担心我的邮件收不到了。以下代码兼容 python2 和 python3&#xff0c;运行无异常&#xff0c;放心大…

Jmeter脚本参数化和正则匹配

目录 一、参数化 1.用户定义的变量 2.CSV数据文件设置 3.用户参数 二、正则提取 1.JSON提取器 2.正则表达式提取器 我们在做接口测试过程中&#xff0c;往往会遇到以下几种情况 每次发送请求&#xff0c;都需要更改参数值为未使用的参数值&#xff0c;比如手机号注册、…

python爬虫_requests获取小黄人表情保存到文件夹

文章目录 ⭐前言&#x1f496; requests简介&#xff08;发送请求&#xff09;&#x1f496; urlretrieve简介&#xff08;下载远程图片&#xff09;&#x1f496; 获取图片的接口保存到本地&#x1f496; 加餐环节&#xff1a;前端vue3antd展示图片 ⭐结束 ⭐前言 大家好&…

Hightopo 使用心得(4)- 3D 场景 Graph3dView 与 Obj 模型

在前一篇文章《Hightopo 使用心得&#xff08;3&#xff09;- 吸附与锚点》中&#xff0c;我们在结尾处提到过 HT 的 3D 场景。这里我们通过代码建立一个 3D 场景并添加一个 Obj 模型来介绍一下 HT for Web 在 3D 场景和模型加载方面的使用。 这是我们最终实现的效果&#xff…

vue3使用monaco-editor插件,报错Unexpected usage

在使用monaco-editor插件的时候&#xff0c;运行时没有报错&#xff0c;打开页面报如上错误&#xff0c;可以关掉&#xff0c;但是页面变了 还会再出现&#xff0c;上网找了一圈报错&#xff0c;是因为插件引入时产生的问题 原来的 import * as Monaco from monaco-editor更改…

论文笔记--TinyBERT: Distilling BERT for Natural Language Understanding

论文笔记--TinyBERT: Distilling BERT for Natural Language Understanding 1. 文章简介2. 文章概括3 文章重点技术3.1 Transformer Distillation3.2 两阶段蒸馏 4. 数值实验5. 文章亮点5. 原文传送门6. References 1. 文章简介 标题&#xff1a;TinyBERT: Distilling BERT fo…

Java四个月学不学的会?我分享一下我的经历来告诉你

先说结论&#xff0c;java培训 四个月一般是能达到能上手工作的水平的&#xff0c;但是高中毕业就算了。高中毕业哪怕你能上手工作了&#xff0c;需求能完成了&#xff0c;也是大概率找不到工作的&#xff0c;这一行可能最看重学历的行业之一了。虽然说句实在话&#xff0c;就…

【Linux】基础开发工具——gcc/g++篇

文章目录 一、预处理1.1 头文件展开1.2 条件编译 二、编译三、汇编四、链接4.1 什么是库?4.2 库的分类4.3 目标文件和库是如何链接的&#xff1f;4.3.1 动态链接4.3.2 静态链接 4.4 动静态链接的优缺点对比 五、Debug&&release 前言 &#xff1a;  在前面的文章里给大…

VMware将虚拟机网络设置为NAT模式

虚拟机有vmware和desktop&#xff0c;本人一直使用的是vmware。安装好vmware并激活后&#xff0c;创建完虚拟机。(需要vmware和激活码的可留言) 进入虚拟机设置&#xff0c;网络适配器选择NAT模式 在虚拟机工具栏->菜单栏点击编辑&#xff0c;选择“虚拟网络编辑器”。 选择…