Window的创建

news2025/3/2 1:25:23

Window的创建

上一篇说到了Window和WindowManager的关系并且讲述了WindowManager如何添加Window与Window内部的三个方法的实现

这篇主要讲几个常见的Window的创建比如Activity,Dialog和Toast

其中Activity属于应用Window

Dialog属于子Window

Toast属于系统Window

z-ordered越来越大,它的优先级就越来越大

Activity的Window的创建

Activity的启动最终是由ActivityThreadperformLaunchActivity,这个方法在内部会通过内加载器创建一个Activity的实例变量。

然后调用attach,将一系列上下文环境变量关联起来,确保 Activity 可以正常运行并与应用程序的其他组件进行通信。

我们可以看看attach关联了哪些

我们看看它的源码

通过ctrl+n

 activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.activityConfigCallback,
                        r.assistToken, r.shareableActivityToken);

并且在attach的过程中还会创建Activity所属的Window

记得我们在View的工作原理的时候说到过:

Activity创建完成后,会把DecorView添加到Window中,并创建相应的ViewRootImpl,再把ViewRootImpl与DecorView关联起来

而Activity的Window的创建就是在performLaunchActivity中创建Activity后调用attach来创建的

1.Window的创建

问题:2个Window的不同

attach

《艺术开发探索》中说在attach中通过PolicyManagermakeNewWindow获得window,但我找了半天没找到那段源码

只在attach里面找到了

 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,
            IBinder shareableActivityToken) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(mWindowControllerCallback);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        mUiThread = Thread.currentThread();

        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mAssistToken = assistToken;
        mShareableActivityToken = shareableActivityToken;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        if (voiceInteractor != null) {
            if (lastNonConfigurationInstances != null) {
                mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
            } else {
                mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                        Looper.myLooper());
            }
        }

        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;

        mWindow.setColorMode(info.colorMode);
        mWindow.setPreferMinimalPostProcessing(
                (info.flags & ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING) != 0);

        getAutofillClientController().onActivityAttached(application);
        setContentCaptureOptions(application.getContentCaptureOptions());
    }

即它没有用**PolicyManager.makeNewWindow(this)**来创建Window的

而是

mWindow = new PhoneWindow(this, window, activityConfigCallback);

其中这里面的window对象来自

抽象类

public abstract class Window

chatGPT的回答是:

实际的 Window 创建是在 PhoneWindow 中进行的,而不是在 Activityattach() 方法中直接调用 PolicyManager.makeNewWindow(this)

final void attach(...) {
    ...
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    ...
}

private void performLaunchActivity(...) {
    ...
    // 创建 Activity 的实例
    Activity activity = instantiateActivity(cl, component);
    ...
    // 创建 PhoneWindow 对象
    Window window = policyManager.makeNewWindow(activity);
    ...
    // 调用 attach() 方法
    activity.attach(..., window, ...);
    ...
}

总结起来,mWindow = new PhoneWindow(this, window, activityConfigCallback); 创建了一个具体的 PhoneWindow 对象,用于管理 Activity 的窗口。而 Window window = policyManager.makeNewWindow(activity); 创建了一个基本的 Window 对象,作为系统策略管理器与 Activity 的窗口之间的接口。

第一个Window是具体的窗口实现,用于管理 Activity 的窗口。

第二个Window是系统级别的Window对象,并不是直接与 Activity 关联的窗口对象。

并且给出的最终回复,在 Activity 的创建过程中,主要是创建了一个具体的 PhoneWindow 对象,用于表示和管理 Activity 的窗口。系统级别的 Window 对象由策略管理器创建,用于处理窗口的管理和操作。


我们创建完成PhoneWindow后,Activity实现了windowcallBack接口,可以把Activity自己设置为window的观察者。然后我们就开始初始化WindowManager,记得之前我们说过的Window没办法直接访问,我们只能通过访问WindowManager来访问Window

mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);     

我们继续看看setWindowManager的源码

2.WindowManager的初始化过程

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
        boolean hardwareAccelerated) {
    mAppToken = appToken;
    mAppName = appName;
    mHardwareAccelerated = hardwareAccelerated;
    if (wm == null) {
        //获取到应用服务的WindowManager
        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    }
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

mAppToken,mAppName, mHardwareAccelerated这三个就是给它们赋了个值m主要还是下面的

先判断传进去的WindowManager为不为空,为空的话进行初始化

,这个初始化与上一篇博客我进行完WindowManager的布局后的初始化一样都是

WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);

然后将**((WindowManagerImpl)wm).createLocalWindowManager(this)赋给mWindowManager**

话说mWindowManager是什么,我们点击它,会发现它是一个全局变量

private WindowManager mWindowManager;

而((WindowManagerImpl)wm).createLocalWindowManager(this)是在创建一个本地的窗口管理器对象。

本地窗口管理器是一个与设备本地窗口系统交互的对象,它提供了与底层窗口系统通信的功能。通过创建本地窗口管理器,可以在应用程序中实现对窗口的创建、显示、布局、交互等操作。


PhoneWindow类型的mWindow已经通过

mWindow = new PhoneWindow();

创建好了

刚才在setWindowManager中的内部也成功创建好了WindowManager

便可以成功绑定WindowWindowManager

又因为我们之前说过WindowManager主要是由WindowManagerImpl实现的,

所以刚才那句话也就相当于WindowWindowManagerImpl进行成功的绑定

我们就可以通过调用WindowManagerImpl的那三个方法来实现Windowadd,remove,update

3.流程图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fVB1PIU0-1685279766886)(../../assets/流程图-导出 (3)]-1685262524947-1.png)

4.把Activity的布局文件设置给PhoneWindow

上面提到调用Activityattach方法之后,会回调ActivityonCreate方法,在其中会调用setContentView来设置布局,如下:

public void setContentView(View view, ViewGroup.LayoutParams params) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

Activity将setContentView具体实现交给了Window处理,这里的getWindow返回我们上面创建的PhoneWindow对象。我们继续看下去:

// 注意他有多个重载的方法,要选择参数对应的方法
public void setContentView(int layoutResID) {
    // 创建DecorView
    if (mContentParent == null) {
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
    final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
            getContext());
    transitionTo(newScene);
} else {
    // **这里根据布局id加载布局,把Activity的布局加载到DecorView的**mContentParent**中**
    mLayoutInflater.inflate(layoutResID, mContentParent);

}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
    // 回调activity的方法,**通知Activity视图已经发生改变**
    cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}

首先我们先看第一个if

虽然不知道它在干什么但是我们看到了

  installDecor();

就可以明白它创建了一个DecorView

首先判断 mContentParent 是否为 null。mContentParent 是 Window 的内容视图的父级容器,如果为 null,表示 DecorView 还未创建,因此需要调用 installDecor() 来创建并安装 DecorView。

如果 mContentParent 不为 null,则进一步判断是否存在 FEATURE_CONTENT_TRANSITIONS 特性。如果不存在该特性,表示不需要进行内容转场动画,那么可以通过 mContentParent.removeAllViews() 清空已有的内容视图。

之后第二个if就是判断是否存在 FEATURE_CONTENT_TRANSITIONS 特性,如果开启了该特性,则会使用场景切换的方式来加载布局

没开启的话会直接使用 mLayoutInflater.inflate(layoutResID, mContentParent) 方法将指定布局文件加载到 DecorView 的 mContentParent 容器中,完成 Activity 的布局加载。

之后就是 回调Activity的callBack方法

4.1简单流程

总之把Activity的布局文件加载到PhoneWindow就以下几个流程

1.判断DecorView是否创建,没有创建就创建

2.将Activity的布局加载到DecorView的mContentParent

3.进行Activity的回调

所以我们就可以知道了Activity的加载布局文件为什么是setContentView了,因为Activity的加载布局文件最后加载到了Window的DecorWindow的**ContentView**中


5.总结前三步

现在回顾以下

我们刚才做的

1是创建了Window,(Window是在ActivityThread中调用performLaunchActivity中创建的Activity调用attach中创建的)

2.如何创建WindowManager,并且进行了绑定(在Window调用setWindowManager中创建的WindowManager)

3.如何将Activity的布局文件设置给PhoneWindow(1.判断DecorView是否创建

​ 2.将布局文件传给DecorView的ContentParent

​ 3.进行Activity的回调,给Activity说DecorView已经创建好了)

现在就差最后一步了,那就是把DecorView作为window添加到屏幕上。

肯定想的是那很简单啊,WindowManager已经和Window绑定了,那么我们直接WindowManager.add不就好了

而且在Activity的创建的时候我们说过:

Activity创建完成后,会把DecorView添加到Window中,并创建相应的ViewRootImpl,再把ViewRootImpl与DecorView关联起来

但是

但是这个时候由于DecorView并没有被WindowManager识别,所以这个时候的Window无法提供具体功能,因为它还无法接收外界的输入信息

6.将DecorView添加到Window中

还是在ActivityThread中

我们调用

public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {
    // 调用Activity的onResume方法
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
    ...
    // 让decorView显示到屏幕上
	if (r.activity.mVisibleFromClient) {
        r.activity.makeVisible();
}

首先第一步是performResumeActivity进行了onResume方法的回调

第二步是**makeVisible()**让decorView显示到屏幕上

我们点击makeVisible()看里面的源码

void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}

会发现它在这个地方调用了addView,并且将mDecor设置为可见

7.Activity创建的流程图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X9W5bacP-1685279766887)(../../assets/流程图-导出 (4)]-1685265053644-3.png)

Dialog的Window创建

只要你把Activity的Window的创建好了,Dialog的Window创建就很容易了

Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
    ...
    // 获取windowManager
    mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
//mWindowManager其实是Activity的WindowManager,这里的context一般是activity
	// 构造PhoneWindow
    final Window w = new PhoneWindow(mContext);
    mWindow = w;
    // 初始化PhoneWindow
    w.setCallback(this);
    w.setOnWindowDismissedCallback(this);
    w.setOnWindowSwipeDismissedCallback(() -> {
        if (mCancelable) {
            cancel();
        }
    });
    w.setWindowManager(mWindowManager, null, null);
    w.setGravity(Gravity.CENTER);
    mListenersHandler = new ListenersHandler(this);
}

public Object getSystemService(@ServiceName @NonNull String name) {
    if (getBaseContext() == null) {
        throw new IllegalStateException(
                "System services not available to Activities before onCreate()");
    }
	// 获取的是activity的windowManager
    if (WINDOW_SERVICE.equals(name)) {
        return mWindowManager;
    } else if (SEARCH_SERVICE.equals(name)) {
        ensureSearchManager();
        return mSearchManager;
    }
    return super.getSystemService(name);
}

1.创建Window

这步和Activity的Window创建一样,就没必要讲了

2.初始化DecorView并将Dialog视图添加到DecorView中

  public void setContentView(int layoutResID) {
      mWindow.setContentView(layoutResID);
  }

3.将DecorView添加到Window并显示

public void show() {
   ...
    // 回调onStart方法,获取前面初始化好的decorview
    onStart();
    mDecor = mWindow.getDecorView();
    ...
    WindowManager.LayoutParams l = mWindow.getAttributes();
    ...
    // 利用windowManager来添加window    
    mWindowManager.addView(mDecor, l);
//这里的mWindowManager是Activity的WindowManager
    ...
    mShowing = true;
    sendShowMessage();
}

Dialog的Window创建和Activity的Window创建过程有很多类似的地方,二者几乎没有区别

Dialog在被关闭的时候,会通过WindowManager来移除DecorView:

mWindowManager.removeViewImmediate(mDecor);

removeViewImmediate是同步方法

removeView是异步方法

普通的Dialog有一个特殊之处,那就是必须采用Activity的Context,如果采用Application的Context,那么就会报错

是没有应用token导致的,而应用token一般只有Activity拥有,所以只需要用Activity作为Context来显示对话框。

系统Window比较特殊,它可以不要token

我们只需要这么改就可以运行了

dialog.getWindow().setType(LayoutParams.TYPE_SYSTEM.ERROR);

还有

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

Toast的Window创建

Toast比Dialog稍微难一点,虽然Toast也是基于Window来实现的,但是Toast有定时取消这一功能。

所以系统采用了Handler

Toast内部有2类IPC过程:

1.Toast访问NotificationManagerService

2.NotificationManagerService回调Toast里的TN接口

Toast属于系统Window,它内部的视图由两种方式指定,一种是系统默认的样式另一种是通过setView方法来指定一个自定义View,视图都对应Toast的一个View类型的内部成员mNextView。Toast提供了show和cancel分别用于显示和隐藏Toast,show和cancel的内部是IPC过程,

Toast的show()

首先看Toast的显示过程,它调用了NMS中的enqueueToast方法,如下所示。

public void show() {
        if (mNextView == null) {
            throw new RuntimeException("setView must have been called");
        }
 	INotificationManager service = getService();
    String pkg = mContext.getOpPackageName();
    TN tn = mTN;
    tn.mNextView = mNextView;

    try {
        service.enqueueToast(pkg, tn, mDuration);
    } catch (RemoteException e) {
        // Empty
    }
}

NMSenqueueToast方法的第一个参数表示当前应用的包名,第二个参数tn表示远程回调,第三个参数表示Toast的时长。

enqueueToast会先将Toast请求封装成为ToastRecord对象并将它添加到一个名为mToastQueue的ArrayList集合中

对于非系统应用来说,mToastQueue最多同时存在50个ToastRecord,这样做是为了防止DOS,如果不这样做,使用大量循环弹出Toast会导致其他应用没机会弹出Toast,那么对于其他应用的Toast请求,系统的行为就是拒绝服务

// Limit the number of toasts that any given package except the android
// package can enqueue.  Prevents DOS attacks and deals with leaks.
if (! isSystemToast) {
    int count = 0;
    final int N = mToastQueue.size();
    for (int i=0; i<N; i++) {
        final ToastRecord r = mToastQueue.get(i);
        if (r.pkg.equals(pkg)) {
                  count++;
                  if (count >= MAX_PACKAGE_NOTIFICATIONS) {
                      Slog.e(TAG, "Package has already posted " + count
                              + " toasts. Not showing more. Package=" + pkg);
                      return;
                  }
              }
          }
      }
}

正常情况下,一个app的ToastRecord不会到达上限,当ToastRecord被添加到mToastQueue后,NMS会通过showNextToastLocked方法显示当前的Toast

void showNextToastLocked() {
    ToastRecord record = mToastQueue.get(0);
    while (record ! = null) {
        if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.
        callback);
        try {
            record.callback.show();
            scheduleTimeoutLocked(record);
            return;
        } catch (RemoteException e) {
            Slog.w(TAG, "Object died trying to show notification " + record.
            callback
                    + " in package " + record.pkg);
            // remove it from the list and let the process die
            int index = mToastQueue.indexOf(record);
            if (index >= 0) {
                mToastQueue.remove(index);
            }
            keepProcessAliveLocked(record.pid);
            if (mToastQueue.size() > 0) {
                record = mToastQueue.get(0);
            } else {
                record = null;
                  }
              }
          }
      }

需要注意的是Toast的显示是由ToastRecord的callback完成的,这个callback是Toast中的TN对象的远程Binder

通过callback访问TN中的方法需要跨进程完成,最终被调用的TN中的方法会运行在发起Toast请求的应用的Binder线程池

Toast显示以后,NMS还会通过scheduleTimeoutLocked方法发送一个延迟消息,消息的延迟取决于Toast的时长

private void scheduleTimeoutLocked(ToastRecord r)
{
    mHandler.removeCallbacksAndMessages(r);
    Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
    long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
    mHandler.sendMessageDelayed(m, delay);
}

LONG_DELAY为3.5s,而 SHORT_DELAY为2.5s

我最开始以为这个就决定着

Toast.makeText(MainActivity.this,"nihao",Toast.LENGTH_LONG).show()

的这个的持续时间,后来搜了一下发现:

实际上,delay 的值是用于处理 Toast 消息的显示时间间隔,而不是 Toast 消息的总显示时间。在 Android 中,LONG_DELAYSHORT_DELAY 是表示延迟时间的常量,具体的值可能会根据系统的设置而有所不同。

Toast的show()总结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R23kHBJI-1685279766888)(../../assets/流程图-导出 (5)].png)

我对show方法的理解就是Toast把它封装到ToastRecord并把它放入ToastQueue集合中

然后NMS会进行回调ToastRecord中的callback即Toast中的TN方法

Toast的hide()

try {
    record.callback.hide();
} catch (RemoteException e) {
    Slog.w(TAG, "Object died trying to hide notification " + record.callback
            + " in package " + record.pkg);
    // don't worry about this, we're about to remove it from
    // the list anyway
}

Toast的显示和隐藏的本质

可以看出来Toast的显示和隐藏过程实际上是通过Toast的TN这个类实现的,它有一个**show()方法和一个hide()**方法

分别对应Toast的显示和隐藏

由于这两个方法是被NMS跨进程调用的,因此它们都在Binder线程池中,为了将执行环境切换到Toast所在的线程,内部使用Handler

/**
 * schedule handleShow into the right thread
 */
@Override
public void show() {
    if (localLOGV) Log.v(TAG, "SHOW: " + this);
    mHandler.post(mShow);
}

/**
 * schedule handleHide into the right thread
 */
@Override
public void hide() {
    if (localLOGV) Log.v(TAG, "HIDE: " + this);
    mHandler.post(mHide);
}

可以发现mShow和mHide是两个Runnable,内部分别调用了handleShow和handleHide,所以它们两个才是真正完成显示和隐藏的

TN的handleShow会将Toast视图加载到Window中

mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
mWM.addView(mView, mParams)

而TN的handleHide会将Toast视图从Window中移除

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

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

相关文章

python基础知识(二):变量和常用数据类型

目录 1. 变量1.1 变量的定义1.2 变量的命名规则 2. 常用数据类型2.1 字符串2.1.1 字符串的常用方法2.1.1.1 title()方法&#xff1a;将字符串中的单词首字母大写2.1.1.2 upper()方法&#xff1a;将字符串中的单词字母全大写2.1.1.3 lower()方法&#xff1a;将字符串中的单词字母…

什么是分段路由?如何在网络中实施分段路由?

在计算机网络中&#xff0c;分段路由&#xff08;Subnetting&#xff09;是一种将一个大的网络划分为多个较小子网的技术。它允许网络管理员更有效地分配 IP 地址和管理网络流量。本文将详细介绍分段路由的概念、原理以及如何在网络中实施分段路由。 1. 分段路由的概念 分段路…

【深入浅出Spring Security(一)】Spring Security的整体架构

Spring Security的整体架构 一、整体架构认证&#xff08;Authentication&#xff09;AuthenticationManagerAuthentication登录后的数据保存&#xff08;SecurityContextHolder&#xff09; 授权&#xff08;Authorization&#xff09;ConfigAttribute 二、总结 这篇博客所述主…

CISCN 2023 初赛 pwn——Shellwego 题解

这是一个用go语言写的elf程序&#xff0c;没有PIE。这也是本蒟蒻第一次解go pwn题&#xff0c;故在此记录以便参考。 而且&#xff0c;这还是一个全部符号表被抠的go elf&#xff0c;直接面对一堆不知名的函数实在有些应付不来&#xff0c;因此在比赛时委托逆向的队友把符号表…

2023/5/28总结

static static:静态&#xff0c;可以修饰成员方法&#xff0c;成员变量。&#xff08;是所有成员共享的&#xff09; static修饰的特点&#xff1a; 被类的所有对象共享&#xff08;判断是否使用静态关键字的条件&#xff09;可以通过类名和对象名调用在定义对象时&#xff0c;…

图【数据结构】

目录 一、图的定义和基本术语 二、图的类型定义 三、图的存储结构 1、数组&#xff08;邻接矩阵&#xff09;表示法 二、邻接表&#xff08;链式&#xff09;表示法 三、图的邻接表的存储表示 四、十字链表与邻接多重链表 &#xff08;1&#xff09;十字链表 &#xff…

113.删除有序数组中的重复项 removeDuplicatesFromSortedArray

文章目录 题目描述解题思路代码详解运行截图 题目描述 题目链接 给你一个 升序排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元…

Java中ReentrantLock的概念深入理解

ReentrantLock和Synchronized的区别 核心区别 ReentrantLock是一个类&#xff0c;Synchronized是Java中的一个关键字。 两者都是JVM层面实现互斥锁的方式 效率区别 线程竞争激烈推荐使用ReentrantLock去实现&#xff0c;不存在锁竞争观念&#xff1b; Synchronized是存在锁升…

大数据Doris(二十九):Broker Load导入HDFS csv 格式数据并提取文件路径中的分区字段

文章目录 Broker Load导入HDFS csv 格式数据并提取文件路径中的分区字段 一、创建Doris表 二、准备HDFS数据<

蚁群算法(解决TSP问题)

一、概述 蚂蚁在寻找食物源时&#xff0c;会在其经过的路径上释放一种信息素&#xff0c;并能够感知其它蚂蚁释放的信息素。信息素浓度的大小表征到食物源路径的远近&#xff0c;信息素浓度越高&#xff0c;表示对应的路径距离越短。通常&#xff0c;蚂蚁会以较大的概率优先…

chatgpt赋能python:Python文件复制粘贴到另一个目录

Python文件复制粘贴到另一个目录 Python是一种通用编程语言&#xff0c;可用于各种任务&#xff0c;包括文件复制和移动。在本文中&#xff0c;我们将探讨Python中的文件复制粘贴到另一个目录。 为什么要使用Python进行文件复制粘贴&#xff1f; Python提供了强大的文件处理…

case when用法

case when的基本使用&#xff1a; Case when 的用法: 一旦满足了某一个WHEN, 则这一条数据就会退出CASE WHEN , 而不再考虑 其他CASE。 Case when 的用法 -- -搜索Case函数: Case函数(Case搜索函数): 判断表达式的真假,如果为真,返回结果;如果为假,返回else值;如果未定义el…

批处理文件(.bat)启动redis及任何软件(同理)

批处理文件 每次从文件根目录用配置文件格式来启动redis太麻烦了 可以在桌面上使用批处理文件&#xff08;.bat&#xff09;启动Redis&#xff0c;请按照以下步骤进行操作&#xff1a; 打开文本编辑器&#xff0c;如记事本。 在编辑器中输入以下内容&#xff1a; 将文件保存…

70.爬楼梯问题+746.使用最小花费爬楼梯

目录 一、70.爬楼梯问题分析 二、代码 三、746.使用最小花费爬楼梯分析 四、代码 一、70.爬楼梯问题分析 70. 爬楼梯 - 力扣&#xff08;LeetCode&#xff09; 二、代码 class Solution { public:int climbStairs(int n) {if(n1||n2)return n;vector<int>dp(n1);dp…

线性表的链式表示——单链表

目录 一、单链表的定义二、单链表上基本操作的实现1、采用头插法建立单链表2、采用尾插法建立单链表3、按序号查找结点值4、按值查找表结点5、插入结点操作6、删除结点操作7、求表长操作 三、双链表、循环链表、静态链表 顺序表可以随时存取表中的任意一个元素&#xff0c;它的…

翻译:开源软件的架构(volume2): 可伸缩web框架及分布式系统

英文源地址 开源软件已经成为构建一些超大型网站的基础组成部分了.随着这些网站的成长, 围绕着它们软件架构的最佳实践与指导思想开始涌现.本文尝试去阐述设计大型网站时的需要考虑一些关键问题, 以及用于实现这些目标的基础组件. 本文主要关注web系统,尽管其中一些内容也适用于…

网络安全-01-VMware安装Kali部署DVWA

网络安全-01-VMware安装Kali&部署DVWA &#x1f53b;一、Kali简介&下载&#x1f4d7; 二、VMware安装Kali&#x1f4f0; 2.1 新建虚拟机&#x1f4f0; 2.2 开始安装Kali&#x1f4f0; 2.3 更换apt源为国内源&#x1f4f0; 2.4 启动mysql-这里使用自带的maridb&#x1f…

【Python 垃圾回收】零基础也能轻松掌握的学习路线与参考资料

Python 垃圾回收是 Python 运行机制中的重要环节。了解 Python 垃圾回收机制可帮助开发者高效编写 Python 代码&#xff0c;并避免潜在的内存泄漏问题。本文将介绍 Python 垃圾回收的学习路线&#xff0c;并给出参考资料和优秀实践。 Python 垃圾回收机制 Python 使用引用计数…

关于社会脑研究的fMRI和fNIRS超扫描方法

导读 近来&#xff0c;“社会脑”(即大脑在社会情境中是如何工作的&#xff0c;以及我们社会行为的机制是什么)在神经科学文献中获得了很多关注&#xff0c;主要是因为最近开发的技术允许研究人类社会认知的不同方面及其与大脑的关联。在这种情况下&#xff0c;超扫描技术拓宽…

如何在华为OD机试中获得满分?Java实现【质数因子】一文详解!

✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 🌟专栏地址: Java华为OD机试真题(2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述4. Java算法源码5. 测试6.解题思路1. 题目描述 功能:输入一个正整数,…