Android setContentView执行流程(一)-生成DecorView

news2024/12/28 21:47:39

Android setContentView执行流程(一)-生成DecorView
Android setContentView执行流程(二)-将布局添加到mContentParent

setContentView的流程主要就是讲在Activity的onCreate方法中调用setContentView方法之后,我们自定义的xml文件加载的过程,学习它可以让我们对整个View树的理解更加透彻,并且通过源码的学习,我们可以从根本上理解一些问题,比如

  • 为什么requestWindowFeature要放到setContentView前面
  • merge、include、ViewStub是如何工作的,尤其是include如何通过id查找布局的根View
  • View view = LayoutInflater.from(context).inflate(R.layout.inflate, root, true)中inflate方法的参数有什么用呢?

我们在平时的开发中可能也经常会遇到,遇到之后百度一下ctrl+c ctrl+v就解决了,但是其根本原因可能不是特别清楚,那么今天我们就通过源码来看下setContentView的流程,相信通过本篇的学习你一定会有收获。

随着Android版本的升级我们在声明一个Activity时可以继承自Activity也可以继承自AppCompatActivity,因此对于setContentView的流程也同样分为继承自Activity和继承自AppCompatActivity。这两种方式的大致流程差不多,都可以分为两大步骤

(1) installDecor 生成DecorView并获取到mContentParent对象

(2) 将我们Activity的xml布局添加到mContentParent中

继承自Activity和AppCompatActivity的区别主要在第一步,因此本篇的思路就是对第(1)步分别讲解,对于第(2)步两者相同就共同来讲,另外说明一下我这里采用的是API 30的源码。

一、继承自Activity DecorView生成过程

1 setContentView整体运行时序图

我们先来看下继承自Activity的setContentView的调用流程,我画了个时序图方便大家从整体上掌握各个模块的交互流程,学习一个知识一定要先从整体上了解,然后再去了解细节
在这里插入图片描述

可以看到整个流程其实就是刚提到的两步:

  • 初始化DecorView——获取到mContentParent,并将系统的xml布局文件添加到DecorView
  • 将开发者自定义的Acitivity的xml布局添加到mContentParent中

2 源码执行分析

接下来就根据上述流程来看下源码,我们看源码不应该扣细节,只关注主流程就行,因此对于源码我会进行精简,只把主流程中用到的留下。首先从Activity#setContentView的源码开始

    public void setContentView(View view) {
        getWindow().setContentView(view);
        initWindowDecorActionBar();
    }
    
    public Window getWindow() {
        return mWindow;
    }

可以看到在第二行调用了getWindow方法,而getWindow返回一个mWindow,那么这个mWindow是怎么来的呢?我们知道Activity启动(后续会写一篇Activity启动流程的文章,这里大家先了解下)的时候有个非常重要的类ActivityThread,当我们点击应用图标启动应用时Android系统会创建一个新的进程,并且在启动Activity的时候会调用ActivityThread的performLaunchActivity方法,来看下它的源码ActivityThread#performLaunchActivity

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    	。。。
        Activity activity = null;
        // 初始化activity
        activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
        。。。
		// 调用activity的attach方法
        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.configCallback,
            r.assistToken, r.shareableActivityToken);
		 。。。
		 // 调用activity的onCreate方法		
         if (r.isPersistable()) {
             mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
         } else {
             mInstrumentation.callActivityOnCreate(activity, r.state);
        }
        return activity;
    }

可以看到在第5行初始化了一个activity,然后调用了activity的attach方法,并且从调用顺序上也能看到attach方法要早于onCreate方法执行,来看下Activity的attach方法的源码Activity#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) {
        。。。
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        。。。
    }

Activity中的attach方法参数很多,我们不需要关注,可以看到第10行它初始化了mWindow,这个mWindow其实就是PhoneWindow,因为它是在activity的attach方法中初始化的,并且attach方法早于onCreate方法执行,所以在onCreate->setContentView->getWindow获取window的时候,这个window已经初始化好了,可以直接拿到这个PhoneWindow对象。
因此在Activity中的onCreate方法中调用setContentView的时候,首先会调用getWindow().setContentView而这个getWindow就是在Activity中的attach方法中初始化的PhoneWindow对象。

所以接下来我们就要分析PhoneWindow的setContentView方法,它的源码如下PhoneWindow#setContentView

    @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            // 初始化DecorView 并获取到mContentParent
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
		。。。
        } else {
            // 将我们自定义Activity的布局添加到mContentParent
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        。。。
        // setContentView执行完的标识
        mContentParentExplicitlySet = true;
    }

这里就比较重要了,它主要进行了两个操作,分别是第5行和第14行

  • installDecor:初始化DecorView 并获取到mContentParent
  • mLayoutInflater.inflate(layoutResID, mContentParent):将我们自定义Activity的布局添加到mContentParent

在最后它还会将mContentParentExplicitlySet置为true,随后我们也会讲到它的用处

我们一个一个来看,首先来看PhoneWindow#installDecor

public class PhoneWindow extends Window implements MenuBuilder.Callback {
    private void installDecor() {
    	。。。
        if (mDecor == null) {
            // 初始化DecorView对象
            mDecor = generateDecor(-1);
            。。。
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            // 将id为ID_ANDROID_CONTENT的布局赋值给mContentParent
            mContentParent = generateLayout(mDecor);
            。。。
            }
        }
    }

    protected DecorView generateDecor(int featureId) {
        。。。
        // 将new的DecorView返回
        return new DecorView(context, featureId, this, getAttributes());
    }
}

可以看到第6行调用了generateDecor方法,而generateDecor方法其实就是根据各种特性new一个DecorView并返回。第13行调用了generateLayout,我们来看下它的源码PhoneWindow#generateLayout

    protected ViewGroup generateLayout(DecorView decor) {
    	TypedArray a = getWindowStyle();
    	。。。
        // Inflate the window decor.
        int layoutResource;
        int features = getLocalFeatures();
        。。。省略多个if分支判断
        if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
            。。。
            if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                layoutResource = a.getResourceId(R.styleable.Window_windowActionBarFullscreenDecorLayout,
                R.layout.screen_action_bar);
            } else {
                layoutResource = R.layout.screen_title;
            }
           	。。。
        } else {
            // Embedded, so no decoration is needed.
            layoutResource = R.layout.screen_simple;
        }
        mDecor.startChanging();
		// 将layoutResource布局添加到DecorView
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
		// 调用findViewById将layoutResource布局中id为ID_ANDROID_CONTENT的ViewGroup赋值给contentParent
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        。。。
        return contentParent;
    }

generateLayout它其实就是根据我们设置的特性去加载不同的布局,比如FEATURE_NO_TITLE、FEATURE_ACTION_BAR等,下面我们就假设进入了最后一个分支即第19行layoutResource = R.layout.screen_simple,然后到第23行调用DecorView的onResourcesLoaded方法将R.layout.screen_simple布局添加到DecorView,然后第25行获取R.layout.screen_simple这个布局的ID_ANDROID_CONTENT,并将其返回。接着来看下DecorView中onResourcesLoaded的源码,DecorView#onResourcesLoaded

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
    	。。。
        final View root = inflater.inflate(layoutResource, null);
       	。。。
        // Put it below the color views.
        addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}

可以看到DecorView是继承自FrameLayout的,第4行调用LayoutInflater方法将R.layout.screen_simple转化成View,然后第7行调用自身的addView方法将刚生成的View添加到DecorView。到这里大家可能有个疑惑就是R.layout.screen_simple,这里面的内容是啥呢?R.layout.screen_simple.xml内容如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

它是android系统里的布局,根据不同的特性会有不同的布局,R.layout.screen_simple算是比较简单的,讲解原理的话用这种简单的布局更好理解。到这里installDecor的流程就走完了,也就是setContentView的第一步:installDecor 生成DecorView并获取到mContentParent对象,我们来看下此时的布局是怎样的,如下图所示
在这里插入图片描述

3 requestWindowFeature使用

再回头看下PhoneWindow的setContentView的源码最后一个行mContentParentExplicitlySet = true这个作用是什么呢?还记得在刚开始做Android的时候想去掉顶部的Title需要做如下处理

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
    }

第4行调用requestWindowFeature(Window.FEATURE_NO_TITLE)并且这个方法只能放到setContentView之前,如果放到setContentView之后的话会报错

在这里插入图片描述

为什么会报错呢?这需要看下requestWindowFeature的源码,Activity#requestWindowFeature

    public final boolean requestWindowFeature(int featureId) {
        return getWindow().requestFeature(featureId);
    }

这里的getWindow同样是PhoneWindow,因此最终调用的是PhoneWindow#requestFeature

    @Override
    public boolean requestFeature(int featureId) {
        if (mContentParentExplicitlySet) {
            throw new AndroidRuntimeException("requestFeature() must be called before adding content");
        }
        。。。
        return super.requestFeature(featureId);
    }

可以看到第3行if语句的条件就是如果mContentParentExplicitlySet为true就会抛异常,而刚才提到setContentView的最后一句将mContentParentExplicitlySet置为了true,所以如果requestWindowFeature方法放到setContentView之后就会抛异常,为什么这么设计呢?回到上面的PhoneWindow#generateLayout的源码可以看到DecorView的布局会用到requestWindowFeature设置的特性,根据不同的特性来加载不同的布局。好了到这里继承自Activity生成DecorView的过程就介绍完了。

二、继承自AppCompatActivity DecorView生成过程

1 源码执行流程分析

接下来看下继承自AppCompatActivity的相关内容,同样我们先看它的调用流程,AppCompatActivity#setContentView

public class AppCompatActivity extends FragmentActivity implements ...{
    
	@Override
    public void setContentView(@LayoutRes int layoutResID) {
        initViewTreeOwners();
        getDelegate().setContentView(layoutResID);
    }

    public AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, this);
        }
        return mDelegate;
    }
}

可以看到第6行调用了getDelegate,在getDelegate方法中第11行调用了AppCompatDelegate.create方法

    public static AppCompatDelegate create(@NonNull Activity activity,
            @Nullable AppCompatCallback callback) {
        return new AppCompatDelegateImpl(activity, callback);
    }

这个方法会返回一个AppCompatDelegateImpl对象,因此在AppCompatActivity的setContentView方法中调用getDelegate().setContentView(layoutResID)其实就是调用AppCompatDelegateImpl中的setContentView,我们来看下它的源码AppCompatDelegateImpl#setContentView

class AppCompatDelegateImpl extends AppCompatDelegate implements ... {
	@Override
    public void setContentView(View v) {
        ensureSubDecor();
        ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        contentParent.addView(v);
       	。。。
    }

    private void ensureSubDecor() {
        if (!mSubDecorInstalled) {
            mSubDecor = createSubDecor();
            。。。
        }
    } 
}

在第4行调用了ensureSubDecor方法,在ensureSubDecor中调用createSubDecor来创建subDecor对象,重点来看下AppCompatDelegateImpl#createSubDecor

class AppCompatDelegateImpl extends AppCompatDelegate implements ... {
	private ViewGroup createSubDecor() {
        。。。
        ensureWindow();
        mWindow.getDecorView();

        final LayoutInflater inflater = LayoutInflater.from(mContext);
        ViewGroup subDecor = null;
		// 。。。此处省略很多if分支判断
        if (mOverlayActionMode) {
            subDecor = (ViewGroup) inflater.inflate(
                    R.layout.abc_screen_simple_overlay_action_mode, null);
        } else {
            subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
        }

        final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                R.id.action_bar_activity_content);

        final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
        if (windowContentView != null) {
      		while (windowContentView.getChildCount() > 0) {
                final View child = windowContentView.getChildAt(0);
                windowContentView.removeViewAt(0);
                contentView.addView(child);
            }
            // Change our content FrameLayout to use the android.R.id.content id.
            // Useful for fragments.
            windowContentView.setId(View.NO_ID);
            contentView.setId(android.R.id.content);
        }

        // Now set the Window's content view with the decor
        mWindow.setContentView(subDecor);

        return subDecor;
    }

    private void ensureWindow() {
        // We lazily fetch the Window for Activities, to allow DayNight to apply in
        // attachBaseContext
        if (mWindow == null && mHost instanceof Activity) {
            attachToWindow(((Activity) mHost).getWindow());
        }
        if (mWindow == null) {
            throw new IllegalStateException("We have not been given a Window");
        }
    }
}

在createSubDecor中首先调用了ensureWindow()这个方法的作用在第43行,通过Activity获取PhoneWindow对象,拿到PhoneWindow对象之后在第5行会调用mWindow.getDecorView(),mWindow其实就是PhoneWidow,我们就来看下它的代码

public class PhoneWindow extends Window implements MenuBuilder.Callback {
	@Override
    public final @NonNull View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
            installDecor();
        }
        return mDecor;
    }
}

看到没,它调用了installDecor()方法,这个installDecor的流程跟继承自Activity的流程是一样的。再回到createSubDecor方法的源码,刚才我们讲了第4行和第5行的代码,接着往下看它会根据不同的特性加载不同的布局,这里为了让主流程更加清晰我省略了很多if分支判断,只留了第10~15行代码。可以看到在第15行会加载abc_screen_simple.xml布局(假设if分支走到这里),abc_screen_simple.xml如下

<?xml version="1.0" encoding="utf-8"?>

<androidx.appcompat.widget.FitWindowsLinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/action_bar_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:fitsSystemWindows="true">

    <androidx.appcompat.widget.ViewStubCompat
        android:id="@+id/action_mode_bar_stub"
        android:inflatedId="@+id/action_mode_bar"
        android:layout="@layout/abc_action_mode_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <include layout="@layout/abc_screen_content_include" />

</androidx.appcompat.widget.FitWindowsLinearLayout>

abc_screen_content_include.xml

<?xml version="1.0" encoding="utf-8"?>

<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <androidx.appcompat.widget.ContentFrameLayout
        android:id="@id/action_bar_activity_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:foregroundGravity="fill_horizontal|top"
        android:foreground="?android:attr/windowContentOverlay" />
</merge>

再回到createSubDecor方法的源码,加载好abc_screen_simple.xml之后会执行如下代码

        final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                R.id.action_bar_activity_content);

        final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
        if (windowContentView != null) {
      		while (windowContentView.getChildCount() > 0) {
                final View child = windowContentView.getChildAt(0);
                windowContentView.removeViewAt(0);
                contentView.addView(child);
            }
            // Change our content FrameLayout to use the android.R.id.content id.
            // Useful for fragments.
            windowContentView.setId(View.NO_ID);
            contentView.setId(android.R.id.content);
        }
        // Now set the Window's content view with the decor
        mWindow.setContentView(subDecor);

        return subDecor;

这个方法很重要,需要重点理解下,可以看到第一行根据id找到contentView,这个id:action_bar_activity_content就是上面abc_screen_content_include.xml中的id,接下来第4行中的android.R.id.content其实是继承自Acitivity的时候xml中的布局,将此id赋值给windowContentView,然后第6行的while循环是判断如果windowContentView有子View会把它们移除并添加到contentView中,最后第13行将windowContentView的id设置为NO_ID,然后将contentView的id设置为android.R.id.content,这么做的原因是为了兼容之前的版本。然后会执行第17行代码将subDecor添加到PhoneWindow。同样我们也来画一下继承自AppComaptActivity的层级图
在这里插入图片描述

跟继承自Activity的层级图差不太多,只是这里为了兼容老版本底层做了一些处理,确保我们应用层不受影响。

2 supportRequestWindowFeature使用注意事项

同样的继承自AppCompatActivity进行 Window特性设置时不再调用requestWindowFeature而是调用supportRequestWindowFeature并且也是要放到setContentView前面。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
    }

到这里DecorView的生成过程就讲完了

三、总结

(1) 我们activity中自定义的xml文件的根布局是DecorView,DecorView是继承自FrameLayout的

(2) 在DecorView的子View中有个id为R.id.content的布局,将其转化为mContentParent

(3) 最后使用LayoutInflater.from(mContext).inflate(resId, contentParent)将我们定义的xml文件resId添加到mContentParent中

篇幅原因第(3)点我们下篇博客讲解。

如果大家有疑问或者发现错误,欢迎在下方留言,我会在第一时间回答!!
如果觉得文章对你有帮助就帮忙点个赞吧。

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

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

相关文章

【计算机网络】【网络层】【习题】

计算机网络-网络层-习题 文章目录 13. 图 4-69 给出了距离-向量协议工作过程&#xff0c;表&#xff08;a&#xff09;是路由表 R1 初始的路由表&#xff0c;表&#xff08;b&#xff09;是相邻路由器 R2 传送来的路由表。请写出 R1 更新后的路由表&#xff08;c&#xff09;。…

图像处理实验四(Adaptive Filter)

一、Adaptive Filter简介 自适应滤波器&#xff08;Adaptive Filter&#xff09;是一种能够根据输入信号的统计特性自动调整自身参数以达到最佳滤波效果的滤波器。它广泛应用于信号处理领域&#xff0c;如信道均衡、系统识别、声学回波抵消、生物医学、雷达、波束形成等模块。 …

typedef 与 extern 的结合:一场误解的澄清

typedef 与 extern 的结合:一场误解的澄清 一、typedef 的基本用法二、extern 的基本用法三、typedef 与 extern 的结合:一场误解的澄清示例二:使用 extern 声明外部变量示例三:错误的用法:尝试在 typedef 中使用 extern四、总结在C语言编程的世界里,typedef和extern是两…

Qt_day5_常用类

常用类 目录 1. QString 字符串类&#xff08;掌握&#xff09; 2. 容器类&#xff08;掌握&#xff09; 2.1 顺序容器QList 2.2 关联容器QMap 3. 几种Qt数据类型&#xff08;熟悉&#xff09; 3.1 跨平台数据类型 3.2 QVariant 统一数据类型 3.3 QStringList 字符串列表 4. QD…

HashMap的put流程知道吗

HashMap 的 put 方法算是 HashMap 中比较核心的功能了&#xff0c;复杂程度高但是算法巧妙&#xff0c;同时在上一版本的基础之上优化了存储结构&#xff0c;从链表逐步进化成了红黑树&#xff0c;以满足存取性能上的需要。本文逐行分析了 put 方法的执行流程&#xff0c;重点放…

鸿蒙UI开发——实现环形文字

1、背 景 有朋友提问&#xff1a;您好关于鸿蒙UI想咨询一个问题 如果我想实现展示环形文字是需要通过在Text组件中设置transition来实现么&#xff0c;还是需要通过其他方式来实现。 针对这位粉丝朋友的提问&#xff0c;我们做一下解答。 2、实现环形文字效果 ❓ 什么是环形…

保存pytest的执行日志;在日志中显示当前是第几次执行

1、在本地保存执行日志&#xff1a; 在终端中执行时因为指定了-s参数&#xff0c;所以会打印相关信息&#xff0c;可以帮助我们后续定位问题&#xff1a; 但是显示在终端时后面无法查看&#xff0c;所以需要把执行日志保存在本地&#xff0c;使用tee 或 重定向符号>&#x…

2024年8个最佳在线websocket调试工具选择

精选了 8 款功能强大且易于使用的 WebSocket 测试工具&#xff1a; 工具名称支持的系统是否免费ApifoxWindows, Mac, Linux是WebSocket KingWindows, Mac, Linux是PostmanWindows, Mac, Linux是Socket.IO Test ClientWindows, Mac, Linux是InsomniaWindows, Mac, Linux是Wires…

H5流媒体播放器EasyPlayer.js播放器wasm编译打包之后报uncaught referenceErro的原因排查

EasyPlayer.js H5播放器&#xff0c;是一款能够同时支持HTTP、HTTP-FLV、HLS&#xff08;m3u8&#xff09;、WS、WEBRTC、FMP4视频直播与视频点播等多种协议&#xff0c;支持H.264、H.265、AAC、G711A、Mp3等多种音视频编码格式&#xff0c;支持MSE、WASM、WebCodec等多种解码方…

初识算法 · 位运算(2)

目录 前言&#xff1a; 判定字符是否唯一 丢失的数字 比特位计数 只出现一次的数字III 前言&#xff1a; ​本文的主题是位运算&#xff0c;通过四道题目讲解&#xff0c;一道是判断字符是否唯一&#xff0c;一道是只出现一次的数字III&#xff0c;一道是比特位计数&…

丹摩征文活动 | 丹摩智算平台:服务器虚拟化的璀璨明珠与实战秘籍

丹摩DAMODEL&#xff5c;让AI开发更简单&#xff01;算力租赁上丹摩&#xff01; 目录 一、引言 二、丹摩智算平台概述 &#xff08;一&#xff09;平台架构 &#xff08;二&#xff09;平台特点 三、服务器虚拟化基础 &#xff08;一&#xff09;虚拟化的概念 &#xf…

[Docker#6] 镜像 | 常用命令 | 迁移镜像 | 压缩与共享

目录 Docker 镜像是什么 生活案例 为什么需要镜像 镜像命令详解 实验 1.一些操作 1. 遍历查看镜像 2. 查看镜像仓库在本地的存储信息 进入镜像存储目录 查看 repositories.json 文件 3. 镜像过滤 4. 下载镜像时的分层 实战一&#xff1a;离线迁移镜像 实战二&…

信用租赁系统的灵活配置与智能化管理助力租赁市场发展

内容概要 在现代租赁市场中&#xff0c;信用租赁系统就像一把金钥匙&#xff0c;打开了灵活配置与智能化管理的大门。首先&#xff0c;让我们看看它是如何运作的。这个系统允许用户根据自身需求自定义设备类型和信用分比例&#xff0c;不同租赁形式的选择使得整个过程更加个性…

Java基于SpringBoot+Vue框架的宠物寄养系统(V2.0),附源码,文档

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

游戏引擎学习第四天

视频参考:https://www.bilibili.com/video/BV1aDmqYnEnc/ BitBlt 是 Windows GDI&#xff08;图形设备接口&#xff09;中的一个函数&#xff0c;用于在设备上下文&#xff08;device context, DC&#xff09;之间复制位图数据。BitBlt 的主要用途是将一个图像区域从一个地方复…

OpenGL ES 文字渲染进阶--渲染中文字体

旧文 OpenGL ES 文字渲染方式有几种? 一文中分别介绍了 OpenGL 利用 Canvas 和 FreeType 绘制文字的方法。 无论采用哪种方式进行渲染,本质上原理都是纹理贴图:将带有文字的图像上传到纹理,然后进行贴图。 渲染中文字体 利用 Canvas 绘制中文字体和绘制其他字体在操作方式上…

T265相机双目鱼眼+imu联合标定(全记录)

最近工作用到t265&#xff0c;记录一遍标定过程 1.安装驱动 首先安装realsense驱动&#xff0c;因为笔者之前使用过d435i&#xff0c;装的librealsense版本为2.55.1&#xff0c;直接使用t265会出现找不到设备的问题&#xff0c;经查阅发现是因为realsense在2.53.1后就不再支持…

python数据分析|二 IPython和JupyterNotebooks

一 python 解释器 Python解释器同一时间只能运行一个程序的一条语句。 如何适用&#xff1a; win r cmd 要退出Python解释器返回终端&#xff0c;可以输入 exit() 或 Ctrl-D。 假设创建了一个 hello_world.py 文件&#xff0c;它的内容是&#xff1a; 可以用下面的命令运…

集群聊天服务器(2)Json介绍

目录 Json序列化Json反序列化 大家之间交流用json&#xff0c;想要发送数据&#xff0c;就把数据序列化成json,想要接收数据&#xff0c;就反序列化成自己程序的语言。 Json序列化 可以直接赋值一个容器对象 js[‘xx’]vec; #include "json.hpp" using jsonnlohman…

ES信息防泄漏:策略与实践

Elasticsearch(简称ES)作为一个开源的搜索和分析引擎&#xff0c;在实时搜索和分析大量数据方面发挥着重要作用。然而&#xff0c;随着数据的增长&#xff0c;ES的信息安全和隐私保护成为了关键问题。信息防泄漏对于ES来说至关重要&#xff0c;它不仅关乎数据的完整性&#xff…